diff --git a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
index 46d6191835..251d2b25af 100644
--- a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
+++ b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
@@ -19,8 +19,8 @@
-
-
+
+
@@ -29,10 +29,10 @@
-
+
-
-
+
+
diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
index 7b526033dd..35a50b6090 100644
--- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
+++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py
@@ -100,6 +100,49 @@ class TestHelper:
TestHelper.wait_for_condition(lambda : general.is_in_game_mode(), 1.0)
Report.critical_result(msgtuple_success_fail, general.is_in_game_mode())
+ @staticmethod
+ def find_line(window, line, print_infos):
+ """
+ Looks for an expected line in a list of tracer log lines
+ :param window: The log's window name. For example, logs printed via script-canvas use the "Script" window.
+ :param line: The log message to search for.
+ :param print_infos: A list of PrintInfos collected by Tracer to search. Example options: your_tracer.warnings, your_tracer.errors, your_tracer.asserts, or your_tracer.prints
+
+ :return: True if the line is found, otherwise false.
+ """
+ for printInfo in print_infos:
+ if printInfo.window == window.strip() and printInfo.message.strip() == line:
+ return True
+ return False
+
+ @staticmethod
+ def succeed_if_log_line_found(window, line, print_infos, time_out):
+ """
+ Looks for a line in a list of tracer log lines and reports success if found.
+ :param window: The log's window name. For example, logs printed via script-canvas use the "Script" window.
+ :param line: The log message we're hoping to find.
+ :param print_infos: A list of PrintInfos collected by Tracer to search. Example options: your_tracer.warnings, your_tracer.errors, your_tracer.asserts, or your_tracer.prints
+ :param time_out: The total amount of time to wait before giving up looking for the expected line.
+
+ :return: No return value, but if the message is found, a successful critical result is reported; otherwise failure.
+ """
+ TestHelper.wait_for_condition(lambda : TestHelper.find_line(window, line, print_infos), time_out)
+ Report.critical_result(("Found expected line: " + line, "Failed to find expected line: " + line), TestHelper.find_line(window, line, print_infos))
+
+ @staticmethod
+ def fail_if_log_line_found(window, line, print_infos, time_out):
+ """
+ Reports a failure if a log line in a list of tracer log lines is found.
+ :param window: The log's window name. For example, logs printed via script-canvas use the "Script" window.
+ :param line: The log message we're hoping to not find.
+ :param print_infos: A list of PrintInfos collected by Tracer to search. Example options: your_tracer.warnings, your_tracer.errors, your_tracer.asserts, or your_tracer.prints
+ :param time_out: The total amount of time to wait before giving up looking for the unexpected line. If time runs out and we don't see the unexpected line then report a success.
+
+ :return: No return value, but if the line is found, a failed critical result is reported; otherwise success.
+ """
+ TestHelper.wait_for_condition(lambda : TestHelper.find_line(window, line, print_infos), time_out)
+ Report.critical_result(("Unexpected line not found: " + line, "Unexpected line found: " + line), not TestHelper.find_line(window, line, print_infos))
+
@staticmethod
def multiplayer_enter_game_mode(msgtuple_success_fail: Tuple[str, str], sv_default_player_spawn_asset: str) -> None:
"""
@@ -108,23 +151,6 @@ class TestHelper:
:return: None
"""
-
- # looks for an expected line in a list of tracers lines
- # lines: the tracer list of lines to search. options are section_tracer.warnings, section_tracer.errors, section_tracer.asserts, section_tracer.prints
- # return: true if the line is found, otherwise false
- def find_expected_line(expected_line, lines):
- found_lines = [printInfo.message.strip() for printInfo in lines]
- return expected_line in found_lines
-
- def wait_for_critical_expected_line(expected_line, lines, time_out):
- TestHelper.wait_for_condition(lambda : find_expected_line(expected_line, lines), time_out)
- Report.critical_result(("Found expected line: " + expected_line, "Failed to find expected line: " + expected_line), find_expected_line(expected_line, lines))
-
- def wait_for_critical_unexpected_line(unexpected_line, lines, time_out):
- TestHelper.wait_for_condition(lambda : find_expected_line(unexpected_line, lines), time_out)
- Report.critical_result(("Unexpected line not found: " + unexpected_line, "Unexpected line found: " + unexpected_line), not find_expected_line(unexpected_line, lines))
-
-
Report.info("Entering game mode")
if sv_default_player_spawn_asset :
general.set_cvar("sv_defaultPlayerSpawnAsset", sv_default_player_spawn_asset)
@@ -135,20 +161,20 @@ class TestHelper:
multiplayer.PythonEditorFuncs_enter_game_mode()
# make sure the server launcher binary exists
- wait_for_critical_unexpected_line("LaunchEditorServer failed! The ServerLauncher binary is missing!", section_tracer.errors, 0.5)
+ TestHelper.fail_if_log_line_found("MultiplayerEditor", "LaunchEditorServer failed! The ServerLauncher binary is missing!", section_tracer.errors, 0.5)
# 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)
- wait_for_critical_expected_line("MultiplayerEditorConnection: Editor-server activation has found and connected to the editor.", section_tracer.prints, 15.0)
+ TestHelper.succeed_if_log_line_found("EditorServer", "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)
+ TestHelper.succeed_if_log_line_found("MultiplayerEditor", "Editor is sending the editor-server the level data packet.", section_tracer.prints, 5.0)
- wait_for_critical_expected_line("Logger: Editor Server completed receiving the editor's level assets, responding to Editor...", section_tracer.prints, 5.0)
+ TestHelper.succeed_if_log_line_found("EditorServer", "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)
+ TestHelper.succeed_if_log_line_found("MultiplayerEditorConnection", "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.fail_if_log_line_found("EditorServer", 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())
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py
index 8f50d4d36d..8c5f33993d 100644
--- a/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/TestSuite_Sandbox.py
@@ -32,3 +32,6 @@ class TestAutomation(TestAutomationBase):
from .tests import Multiplayer_AutoComponent_NetworkInput as test_module
self._run_prefab_test(request, workspace, editor, test_module)
+ def test_Multiplayer_AutoComponent_RPC(self, request, workspace, editor, launcher_platform):
+ from .tests import Multiplayer_AutoComponent_RPC as test_module
+ self._run_prefab_test(request, workspace, editor, test_module)
diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py
new file mode 100644
index 0000000000..827826cbcc
--- /dev/null
+++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py
@@ -0,0 +1,83 @@
+"""
+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
+"""
+
+
+# Test Case Title : Check that the four network RPCs can be sent and received
+
+
+# fmt: off
+class TestSuccessFailTuples():
+ enter_game_mode = ("Entered game mode", "Failed to enter game mode")
+ exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
+ find_network_player = ("Found network player", "Couldn't find network player")
+# fmt: on
+
+
+def Multiplayer_AutoComponent_RPC():
+ r"""
+ Summary:
+ Runs a test to make sure that RPCs can be sent and received via script canvas
+
+ Level Description:
+ - Dynamic
+ 1. Although the level is nearly empty, when the server and editor connect the server will spawn and replicate the player network prefab.
+ a. The player network prefab has a NetworkTestPlayerComponent.AutoComponent and a script canvas attached which sends and receives various RPCs.
+ Print logs occur upon sending and receiving the RPCs; we are testing to make sure the expected events and values are received.
+ - Static
+ 1. NetLevelEntity. This is a networked entity which has a script attached. Used for cross-entity communication. The net-player prefab will send this level entity Server->Authority RPCs
+
+
+ Expected Outcome:
+ We should see editor logs stating that RPCs have been sent and received.
+ However, if the script receives unexpected values for the Process event we will see print logs for bad data as well.
+
+ :return:
+ """
+ import azlmbr.legacy.general as general
+ from editor_python_test_tools.utils import Report
+ from editor_python_test_tools.utils import Tracer
+
+ from editor_python_test_tools.utils import TestHelper as helper
+ from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
+
+ level_name = "AutoComponent_RPC"
+ player_prefab_name = "Player"
+ player_prefab_path = f"levels/multiplayer/{level_name}/{player_prefab_name}.network.spawnable"
+
+ helper.init_idle()
+
+ # 1) Open Level
+ helper.open_level("Multiplayer", level_name)
+
+ with Tracer() as section_tracer:
+ # 2) Enter game mode
+ helper.multiplayer_enter_game_mode(TestSuccessFailTuples.enter_game_mode, player_prefab_path.lower())
+
+ # 3) Make sure the network player was spawned
+ player_id = general.find_game_entity(player_prefab_name)
+ Report.critical_result(TestSuccessFailTuples.find_network_player, player_id.IsValid())
+
+ # 4) Check the editor logs for expected and unexpected log output
+ PLAYERID_RPC_WAIT_TIME_SECONDS = 1.0 # The player id is sent from the server as soon as the player script is spawned. 1 second should be more than enough time to send/receive that RPC.
+ helper.succeed_if_log_line_found('EditorServer', 'Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS)
+ helper.succeed_if_log_line_found('Script', "AutoComponent_RPC: I'm Player #1", section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS)
+
+ # Uncomment once editor game-play mode supports level entities with net-binding
+ #PLAYFX_RPC_WAIT_TIME_SECONDS = 1.1 # The server will send an RPC to play an fx on the client every second.
+ #helper.succeed_if_log_line_found('EditorServer', "Script: AutoComponent_RPC_NetLevelEntity Activated on entity: NetLevelEntity", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
+ #helper.succeed_if_log_line_found('EditorServer', "Script: AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
+ #helper.succeed_if_log_line_found('Script', "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx.", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
+
+
+ # Exit game mode
+ helper.exit_game_mode(TestSuccessFailTuples.exit_game_mode)
+
+
+
+if __name__ == "__main__":
+ from editor_python_test_tools.utils import Report
+ Report.start_test(Multiplayer_AutoComponent_RPC)
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab
new file mode 100644
index 0000000000..758cb4fca8
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab
@@ -0,0 +1,747 @@
+{
+ "ContainerEntity": {
+ "Id": "Entity_[1146574390643]",
+ "Name": "Level",
+ "Components": {
+ "Component_[10641544592923449938]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 10641544592923449938
+ },
+ "Component_[12039882709170782873]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 12039882709170782873
+ },
+ "Component_[12265484671603697631]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 12265484671603697631
+ },
+ "Component_[14126657869720434043]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 14126657869720434043,
+ "Child Entity Order": [
+ "Entity_[1176639161715]",
+ "Entity_[12685882829720]",
+ "Entity_[31145534614197]"
+ ]
+ },
+ "Component_[15230859088967841193]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 15230859088967841193,
+ "Parent Entity": ""
+ },
+ "Component_[16239496886950819870]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 16239496886950819870
+ },
+ "Component_[5688118765544765547]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 5688118765544765547
+ },
+ "Component_[6545738857812235305]": {
+ "$type": "SelectionComponent",
+ "Id": 6545738857812235305
+ },
+ "Component_[7247035804068349658]": {
+ "$type": "EditorPrefabComponent",
+ "Id": 7247035804068349658
+ },
+ "Component_[9307224322037797205]": {
+ "$type": "EditorLockComponent",
+ "Id": 9307224322037797205
+ },
+ "Component_[9562516168917670048]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 9562516168917670048
+ }
+ }
+ },
+ "Entities": {
+ "Entity_[1155164325235]": {
+ "Id": "Entity_[1155164325235]",
+ "Name": "Sun",
+ "Components": {
+ "Component_[10440557478882592717]": {
+ "$type": "SelectionComponent",
+ "Id": 10440557478882592717
+ },
+ "Component_[13620450453324765907]": {
+ "$type": "EditorLockComponent",
+ "Id": 13620450453324765907
+ },
+ "Component_[2134313378593666258]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 2134313378593666258
+ },
+ "Component_[234010807770404186]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 234010807770404186
+ },
+ "Component_[2970359110423865725]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 2970359110423865725
+ },
+ "Component_[3722854130373041803]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 3722854130373041803
+ },
+ "Component_[5992533738676323195]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5992533738676323195
+ },
+ "Component_[7378860763541895402]": {
+ "$type": "AZ::Render::EditorDirectionalLightComponent",
+ "Id": 7378860763541895402,
+ "Controller": {
+ "Configuration": {
+ "Intensity": 1.0,
+ "CameraEntityId": "",
+ "ShadowFilterMethod": 1
+ }
+ }
+ },
+ "Component_[7892834440890947578]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 7892834440890947578,
+ "Parent Entity": "Entity_[1176639161715]",
+ "Transform Data": {
+ "Translate": [
+ 0.0,
+ 0.0,
+ 13.487043380737305
+ ],
+ "Rotate": [
+ -76.13099670410156,
+ -0.847000002861023,
+ -15.8100004196167
+ ]
+ }
+ },
+ "Component_[8599729549570828259]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 8599729549570828259
+ },
+ "Component_[952797371922080273]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 952797371922080273
+ }
+ }
+ },
+ "Entity_[1159459292531]": {
+ "Id": "Entity_[1159459292531]",
+ "Name": "Ground",
+ "Components": {
+ "Component_[11701138785793981042]": {
+ "$type": "SelectionComponent",
+ "Id": 11701138785793981042
+ },
+ "Component_[12260880513256986252]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 12260880513256986252
+ },
+ "Component_[13711420870643673468]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 13711420870643673468
+ },
+ "Component_[138002849734991713]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 138002849734991713
+ },
+ "Component_[16578565737331764849]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 16578565737331764849,
+ "Parent Entity": "Entity_[1176639161715]"
+ },
+ "Component_[16919232076966545697]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 16919232076966545697
+ },
+ "Component_[5182430712893438093]": {
+ "$type": "EditorMaterialComponent",
+ "Id": 5182430712893438093
+ },
+ "Component_[5675108321710651991]": {
+ "$type": "AZ::Render::EditorMeshComponent",
+ "Id": 5675108321710651991,
+ "Controller": {
+ "Configuration": {
+ "ModelAsset": {
+ "assetId": {
+ "guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
+ "subId": 277889906
+ },
+ "assetHint": "objects/groudplane/groundplane_512x512m.azmodel"
+ }
+ }
+ }
+ },
+ "Component_[5681893399601237518]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 5681893399601237518
+ },
+ "Component_[592692962543397545]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 592692962543397545
+ },
+ "Component_[7090012899106946164]": {
+ "$type": "EditorLockComponent",
+ "Id": 7090012899106946164
+ },
+ "Component_[9410832619875640998]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 9410832619875640998
+ }
+ }
+ },
+ "Entity_[1163754259827]": {
+ "Id": "Entity_[1163754259827]",
+ "Name": "Camera",
+ "Components": {
+ "Component_[11895140916889160460]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 11895140916889160460
+ },
+ "Component_[16880285896855930892]": {
+ "$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent",
+ "Id": 16880285896855930892,
+ "Controller": {
+ "Configuration": {
+ "Field of View": 55.0,
+ "EditorEntityId": 11050384689878106598
+ }
+ }
+ },
+ "Component_[17187464423780271193]": {
+ "$type": "EditorLockComponent",
+ "Id": 17187464423780271193
+ },
+ "Component_[17495696818315413311]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 17495696818315413311
+ },
+ "Component_[18086214374043522055]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 18086214374043522055,
+ "Parent Entity": "Entity_[1176639161715]",
+ "Transform Data": {
+ "Translate": [
+ -2.3000001907348633,
+ -3.9368600845336914,
+ 1.0
+ ],
+ "Rotate": [
+ -2.050307512283325,
+ 1.9552897214889526,
+ -43.623355865478516
+ ]
+ }
+ },
+ "Component_[18387556550380114975]": {
+ "$type": "SelectionComponent",
+ "Id": 18387556550380114975
+ },
+ "Component_[2654521436129313160]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 2654521436129313160
+ },
+ "Component_[5265045084611556958]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5265045084611556958
+ },
+ "Component_[7169798125182238623]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 7169798125182238623
+ },
+ "Component_[7255796294953281766]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 7255796294953281766,
+ "m_template": {
+ "$type": "FlyCameraInputComponent"
+ }
+ },
+ "Component_[8866210352157164042]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 8866210352157164042
+ },
+ "Component_[9129253381063760879]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 9129253381063760879
+ }
+ }
+ },
+ "Entity_[1168049227123]": {
+ "Id": "Entity_[1168049227123]",
+ "Name": "Grid",
+ "Components": {
+ "Component_[11443347433215807130]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 11443347433215807130
+ },
+ "Component_[11779275529534764488]": {
+ "$type": "SelectionComponent",
+ "Id": 11779275529534764488
+ },
+ "Component_[14249419413039427459]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14249419413039427459
+ },
+ "Component_[15448581635946161318]": {
+ "$type": "AZ::Render::EditorGridComponent",
+ "Id": 15448581635946161318,
+ "Controller": {
+ "Configuration": {
+ "primarySpacing": 4.0,
+ "primaryColor": [
+ 0.501960813999176,
+ 0.501960813999176,
+ 0.501960813999176
+ ],
+ "secondarySpacing": 0.5,
+ "secondaryColor": [
+ 0.250980406999588,
+ 0.250980406999588,
+ 0.250980406999588
+ ]
+ }
+ }
+ },
+ "Component_[1843303322527297409]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 1843303322527297409
+ },
+ "Component_[380249072065273654]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 380249072065273654,
+ "Parent Entity": "Entity_[1176639161715]"
+ },
+ "Component_[7476660583684339787]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 7476660583684339787
+ },
+ "Component_[7557626501215118375]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 7557626501215118375
+ },
+ "Component_[7984048488947365511]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 7984048488947365511
+ },
+ "Component_[8118181039276487398]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 8118181039276487398
+ },
+ "Component_[9189909764215270515]": {
+ "$type": "EditorLockComponent",
+ "Id": 9189909764215270515
+ }
+ }
+ },
+ "Entity_[1172344194419]": {
+ "Id": "Entity_[1172344194419]",
+ "Name": "Shader Ball",
+ "Components": {
+ "Component_[10789351944715265527]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 10789351944715265527
+ },
+ "Component_[12037033284781049225]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 12037033284781049225
+ },
+ "Component_[13759153306105970079]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 13759153306105970079
+ },
+ "Component_[14135560884830586279]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14135560884830586279
+ },
+ "Component_[16247165675903986673]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 16247165675903986673
+ },
+ "Component_[18082433625958885247]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 18082433625958885247
+ },
+ "Component_[6472623349872972660]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 6472623349872972660,
+ "Parent Entity": "Entity_[1176639161715]",
+ "Transform Data": {
+ "Rotate": [
+ 0.0,
+ 0.10000000149011612,
+ 180.0
+ ]
+ }
+ },
+ "Component_[6495255223970673916]": {
+ "$type": "AZ::Render::EditorMeshComponent",
+ "Id": 6495255223970673916,
+ "Controller": {
+ "Configuration": {
+ "ModelAsset": {
+ "assetId": {
+ "guid": "{FD340C30-755C-5911-92A3-19A3F7A77931}",
+ "subId": 281415304
+ },
+ "assetHint": "objects/shaderball/shaderball_default_1m.azmodel"
+ }
+ }
+ }
+ },
+ "Component_[8056625192494070973]": {
+ "$type": "SelectionComponent",
+ "Id": 8056625192494070973
+ },
+ "Component_[8550141614185782969]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 8550141614185782969
+ },
+ "Component_[9439770997198325425]": {
+ "$type": "EditorLockComponent",
+ "Id": 9439770997198325425
+ }
+ }
+ },
+ "Entity_[1176639161715]": {
+ "Id": "Entity_[1176639161715]",
+ "Name": "Atom Default Environment",
+ "Components": {
+ "Component_[10757302973393310045]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 10757302973393310045,
+ "Parent Entity": "Entity_[1146574390643]"
+ },
+ "Component_[14505817420424255464]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14505817420424255464,
+ "ComponentOrderEntryArray": [
+ {
+ "ComponentId": 10757302973393310045
+ }
+ ]
+ },
+ "Component_[14988041764659020032]": {
+ "$type": "EditorLockComponent",
+ "Id": 14988041764659020032
+ },
+ "Component_[15808690248755038124]": {
+ "$type": "SelectionComponent",
+ "Id": 15808690248755038124
+ },
+ "Component_[15900837685796817138]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 15900837685796817138
+ },
+ "Component_[3298767348226484884]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 3298767348226484884
+ },
+ "Component_[4076975109609220594]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 4076975109609220594
+ },
+ "Component_[5679760548946028854]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 5679760548946028854
+ },
+ "Component_[5855590796136709437]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 5855590796136709437,
+ "Child Entity Order": [
+ "Entity_[1155164325235]",
+ "Entity_[1180934129011]",
+ "Entity_[1172344194419]",
+ "Entity_[1168049227123]",
+ "Entity_[1163754259827]",
+ "Entity_[1159459292531]"
+ ]
+ },
+ "Component_[9277695270015777859]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 9277695270015777859
+ }
+ }
+ },
+ "Entity_[1180934129011]": {
+ "Id": "Entity_[1180934129011]",
+ "Name": "Global Sky",
+ "Components": {
+ "Component_[11231930600558681245]": {
+ "$type": "AZ::Render::EditorHDRiSkyboxComponent",
+ "Id": 11231930600558681245,
+ "Controller": {
+ "Configuration": {
+ "CubemapAsset": {
+ "assetId": {
+ "guid": "{215E47FD-D181-5832-B1AB-91673ABF6399}",
+ "subId": 1000
+ },
+ "assetHint": "lightingpresets/highcontrast/goegap_4k_skyboxcm.exr.streamingimage"
+ }
+ }
+ }
+ },
+ "Component_[11980494120202836095]": {
+ "$type": "SelectionComponent",
+ "Id": 11980494120202836095
+ },
+ "Component_[1428633914413949476]": {
+ "$type": "EditorLockComponent",
+ "Id": 1428633914413949476
+ },
+ "Component_[14936200426671614999]": {
+ "$type": "AZ::Render::EditorImageBasedLightComponent",
+ "Id": 14936200426671614999,
+ "Controller": {
+ "Configuration": {
+ "diffuseImageAsset": {
+ "assetId": {
+ "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+ "subId": 3000
+ },
+ "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_ibldiffuse.exr.streamingimage"
+ },
+ "specularImageAsset": {
+ "assetId": {
+ "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+ "subId": 2000
+ },
+ "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_iblspecular.exr.streamingimage"
+ }
+ }
+ }
+ },
+ "Component_[14994774102579326069]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 14994774102579326069
+ },
+ "Component_[15417479889044493340]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 15417479889044493340
+ },
+ "Component_[15826613364991382688]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 15826613364991382688
+ },
+ "Component_[1665003113283562343]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 1665003113283562343
+ },
+ "Component_[3704934735944502280]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 3704934735944502280
+ },
+ "Component_[5698542331457326479]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 5698542331457326479
+ },
+ "Component_[6644513399057217122]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 6644513399057217122,
+ "Parent Entity": "Entity_[1176639161715]"
+ },
+ "Component_[931091830724002070]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 931091830724002070
+ }
+ }
+ },
+ "Entity_[12685882829720]": {
+ "Id": "Entity_[12685882829720]",
+ "Name": "GlobalGameData",
+ "Components": {
+ "Component_[11240656689650225106]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 11240656689650225106
+ },
+ "Component_[13863201640354873385]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 13863201640354873385
+ },
+ "Component_[14671754037021562789]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14671754037021562789
+ },
+ "Component_[14750978061505735417]": {
+ "$type": "EditorScriptCanvasComponent",
+ "Id": 14750978061505735417,
+ "m_name": "GlobalGameData",
+ "m_assetHolder": {
+ "m_asset": {
+ "assetId": {
+ "guid": "{B16589A0-EA01-56BC-8141-91A3967FB95F}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/globalgamedata.scriptcanvas"
+ }
+ },
+ "runtimeDataIsValid": true,
+ "runtimeDataOverrides": {
+ "source": {
+ "assetId": {
+ "guid": "{B16589A0-EA01-56BC-8141-91A3967FB95F}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/globalgamedata.scriptcanvas"
+ }
+ }
+ },
+ "Component_[16436925042043744033]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 16436925042043744033,
+ "Parent Entity": "Entity_[1146574390643]",
+ "Transform Data": {
+ "Translate": [
+ 0.0,
+ 0.10000038146972656,
+ 4.005393981933594
+ ]
+ }
+ },
+ "Component_[16974524495698916088]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 16974524495698916088
+ },
+ "Component_[2753700837834389204]": {
+ "$type": "EditorLockComponent",
+ "Id": 2753700837834389204
+ },
+ "Component_[3766473509503096065]": {
+ "$type": "SelectionComponent",
+ "Id": 3766473509503096065
+ },
+ "Component_[4025955184206569130]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 4025955184206569130
+ },
+ "Component_[7909743395732791573]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 7909743395732791573
+ },
+ "Component_[9550161640684119498]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 9550161640684119498
+ }
+ }
+ },
+ "Entity_[31145534614197]": {
+ "Id": "Entity_[31145534614197]",
+ "Name": "NetLevelEntity",
+ "Components": {
+ "Component_[12132849363414901338]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 12132849363414901338
+ },
+ "Component_[12302672911455629152]": {
+ "$type": "SelectionComponent",
+ "Id": 12302672911455629152
+ },
+ "Component_[14169903623243423134]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 14169903623243423134
+ },
+ "Component_[14607413934411389854]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 14607413934411389854
+ },
+ "Component_[15396284312416541768]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 15396284312416541768,
+ "m_template": {
+ "$type": "Multiplayer::LocalPredictionPlayerInputComponent"
+ }
+ },
+ "Component_[15494977028055234270]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 15494977028055234270
+ },
+ "Component_[16325088972532345964]": {
+ "$type": "EditorLockComponent",
+ "Id": 16325088972532345964
+ },
+ "Component_[1986030426392465743]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 1986030426392465743
+ },
+ "Component_[4591476848838823508]": {
+ "$type": "AZ::Render::EditorMeshComponent",
+ "Id": 4591476848838823508,
+ "Controller": {
+ "Configuration": {
+ "ModelAsset": {
+ "assetId": {
+ "guid": "{80FA8BAF-4A5B-5937-9679-2E592E841A3A}",
+ "subId": 275306041
+ },
+ "assetHint": "assets/physics/collider_pxmeshautoassigned/spherebot/r0-b_body.azmodel"
+ }
+ }
+ }
+ },
+ "Component_[7256163899440301540]": {
+ "$type": "EditorScriptCanvasComponent",
+ "Id": 7256163899440301540,
+ "m_name": "AutoComponent_RPC_NetLevelEntity",
+ "m_assetHolder": {
+ "m_asset": {
+ "assetId": {
+ "guid": "{1D517006-AC01-5ECA-AE66-0E007871F0CD}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/autocomponent_rpc_netlevelentity.scriptcanvas"
+ }
+ },
+ "runtimeDataIsValid": true,
+ "runtimeDataOverrides": {
+ "source": {
+ "assetId": {
+ "guid": "{1D517006-AC01-5ECA-AE66-0E007871F0CD}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/autocomponent_rpc_netlevelentity.scriptcanvas"
+ }
+ }
+ },
+ "Component_[731336627222243355]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 731336627222243355,
+ "Parent Entity": "Entity_[1146574390643]",
+ "Transform Data": {
+ "Translate": [
+ 2.561863899230957,
+ -1.038161277770996,
+ 0.1487259864807129
+ ]
+ }
+ },
+ "Component_[8012379125499217348]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 8012379125499217348
+ },
+ "Component_[8122568562140740597]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 8122568562140740597,
+ "m_template": {
+ "$type": "Multiplayer::NetworkTransformComponent"
+ }
+ },
+ "Component_[8805228647591404845]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 8805228647591404845,
+ "m_template": {
+ "$type": "NetBindComponent"
+ }
+ },
+ "Component_[9816897251206708579]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 9816897251206708579,
+ "m_template": {
+ "$type": "AutomatedTesting::NetworkTestPlayerComponent"
+ }
+ },
+ "Component_[9880860858035405475]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 9880860858035405475
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas
new file mode 100644
index 0000000000..c1c77b14fe
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas
@@ -0,0 +1,5019 @@
+{
+ "Type": "JsonSerialization",
+ "Version": 1,
+ "ClassName": "ScriptCanvasData",
+ "ClassData": {
+ "m_scriptCanvas": {
+ "Id": {
+ "id": 2816238339133127497
+ },
+ "Name": "AutoComponent_RPC",
+ "Components": {
+ "Component_[6790521910463264404]": {
+ "$type": "EditorGraphVariableManagerComponent",
+ "Id": 6790521910463264404,
+ "m_variableData": {
+ "m_nameVariableMap": [
+ {
+ "Key": {
+ "m_id": "{71675BCB-6546-4B79-9D5E-912982945852}"
+ },
+ "Value": {
+ "Datum": {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0
+ },
+ "VariableId": {
+ "m_id": "{71675BCB-6546-4B79-9D5E-912982945852}"
+ },
+ "VariableName": "PlayerNumber"
+ }
+ }
+ ]
+ }
+ },
+ "Component_[9755768666831861951]": {
+ "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph",
+ "Id": 9755768666831861951,
+ "m_graphData": {
+ "m_nodes": [
+ {
+ "Id": {
+ "id": 8706984967056
+ },
+ "Name": "SC-EventNode(AuthorityToAutonomous_PlayerNumber Notify Event)",
+ "Components": {
+ "Component_[10688723972761024546]": {
+ "$type": "AzEventHandler",
+ "Id": 10688723972761024546,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{46E6B649-CAA4-4318-960D-5E4854E21BB2}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "ConnectionLimitContract",
+ "limit": 1
+ },
+ {
+ "$type": "RestrictedNodeContract",
+ "m_nodeId": {
+ "id": 8719869868944
+ }
+ }
+ ],
+ "slotName": "Connect",
+ "toolTip": "Connect the AZ Event to this AZ Event Handler.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{221AFED2-BD9E-4CFF-8EC7-75ABA019134D}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Disconnect",
+ "toolTip": "Disconnect current AZ Event from this AZ Event Handler.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{D139A8D2-F57B-4EBF-B7F8-1D65C2D97C25}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Connected",
+ "toolTip": "Signaled when a connection has taken place.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{3F47A140-182E-4B32-AE14-467555A6640A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Disconnected",
+ "toolTip": "Signaled when this event handler is disconnected.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{586BE222-2DEA-4E08-968F-07CA4EB96C54}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnEvent",
+ "toolTip": "Triggered when the AZ Event invokes Signal() function.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ },
+ {
+ "id": {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "player_number",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{8C7E88EA-8FEE-45D0-9A2E-78BC4A18F508}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "ConnectionLimitContract",
+ "limit": 1
+ },
+ {
+ "$type": "RestrictedNodeContract",
+ "m_nodeId": {
+ "id": 8719869868944
+ }
+ }
+ ],
+ "slotName": "AuthorityToAutonomous_PlayerNumber Notify Event",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 4,
+ "m_azType": "{DB613438-34F0-5B2E-A413-77424F4254CD}"
+ },
+ "isNullPointer": true,
+ "label": "AuthorityToAutonomous_PlayerNumber Notify Event"
+ }
+ ],
+ "m_azEventEntry": {
+ "m_eventName": "AuthorityToAutonomous_PlayerNumber Notify Event",
+ "m_parameterSlotIds": [
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ }
+ ],
+ "m_parameterNames": [
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ },
+ {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ }
+ ],
+ "m_eventSlotId": {
+ "m_id": "{8C7E88EA-8FEE-45D0-9A2E-78BC4A18F508}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8724164836240
+ },
+ "Name": "SC-Node(IsNetEntityRoleAuthority)",
+ "Components": {
+ "Component_[11076422520044215441]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 11076422520044215441,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{4EAB8D16-C0B4-44E1-885D-8E6754CD1A55}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "EntityId: 0",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{B6C4BE5E-CDE4-4EC7-98D0-A019CD80041C}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{1071F455-D5A0-4C7A-AF91-648A0F197885}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{BA87B4ED-9A52-4A0D-8920-CD0ED4E839EA}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Is Role Authority",
+ "DisplayDataType": {
+ "m_type": 0
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "Entity Id"
+ }
+ ],
+ "methodType": 2,
+ "methodName": "IsNetEntityRoleAuthority",
+ "className": "NetBindComponent",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{4EAB8D16-C0B4-44E1-885D-8E6754CD1A55}"
+ }
+ ],
+ "prettyClassName": "NetBindComponent"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8737049738128
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[15185116749844245504]": {
+ "$type": "Print",
+ "Id": 15185116749844245504,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{F3ED8C08-D751-492A-A39A-7B7734727A5A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{D4FA5CCA-34FB-412B-826E-BC7050E684A6}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value",
+ "toolTip": "Value which replaces instances of {Value} in the resulting string.",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "DisplayGroup": {
+ "Value": 1015031923
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{71675BCB-6546-4B79-9D5E-912982945852}"
+ }
+ },
+ {
+ "id": {
+ "m_id": "{5A4FB037-120E-4BF8-A170-D827E5DC161B}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Value"
+ }
+ ],
+ "m_format": "AutoComponent_RPC: Sending client PlayerNumber {Value}\n",
+ "m_numericPrecision": 0,
+ "m_arrayBindingMap": [
+ {
+ "Key": 1,
+ "Value": {
+ "m_id": "{D4FA5CCA-34FB-412B-826E-BC7050E684A6}"
+ }
+ }
+ ],
+ "m_unresolvedString": [
+ "AutoComponent_RPC: Sending client PlayerNumber ",
+ {},
+ "\n"
+ ],
+ "m_formatSlotMap": {
+ "Value": {
+ "m_id": "{D4FA5CCA-34FB-412B-826E-BC7050E684A6}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8719869868944
+ },
+ "Name": "SC-Node(GetAuthorityToAutonomous_PlayerNumberEventByEntityId)",
+ "Components": {
+ "Component_[1841271567102345236]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 1841271567102345236,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{B556F66D-84DB-4118-8AFE-B3D88D6D75CA}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "EntityId: 0",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{211BD78B-E01E-44F8-B44E-2FD988047BE9}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{1A6BCB27-F99F-4B6B-89CC-F2BE2A698331}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{7262E6CD-BADF-4DD0-8FCE-3A02C3F31CC8}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Event",
+ "DisplayDataType": {
+ "m_type": 4,
+ "m_azType": "{DB613438-34F0-5B2E-A413-77424F4254CD}"
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "EntityId: 0"
+ }
+ ],
+ "methodType": 2,
+ "methodName": "GetAuthorityToAutonomous_PlayerNumberEventByEntityId",
+ "className": "NetworkTestPlayerComponent",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{B556F66D-84DB-4118-8AFE-B3D88D6D75CA}"
+ }
+ ],
+ "prettyClassName": "NetworkTestPlayerComponent"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8711279934352
+ },
+ "Name": "SendScriptEvent",
+ "Components": {
+ "Component_[5751772243856660980]": {
+ "$type": "SendScriptEvent",
+ "Id": 5751772243856660980,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{2FF1A0A1-59C6-4DCF-AE1D-296BD5964D4E}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Fires the specified ScriptEvent when signaled",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{F2955681-A901-432D-99A3-53818F46CDE7}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "toolTip": "Trigged after the ScriptEvent has been signaled and returns",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_version": 1,
+ "m_scriptEventAssetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "m_asset": {
+ "assetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/globalgamedata.scriptevents"
+ },
+ "m_busId": {
+ "Value": 1375178404
+ },
+ "m_eventId": {
+ "Value": 2930121176
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8749934640016
+ },
+ "Name": "SendScriptEvent",
+ "Components": {
+ "Component_[6456267108920901297]": {
+ "$type": "SendScriptEvent",
+ "Id": 6456267108920901297,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{23DBACD8-7784-4E18-A51C-258B25DF4C19}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Fires the specified ScriptEvent when signaled",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{19C850D7-7F1E-4D41-AFD9-5A4FA03D54F0}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "toolTip": "Trigged after the ScriptEvent has been signaled and returns",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{B6CA4982-245A-45F1-8AA6-3295F49DC071}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Number",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{71675BCB-6546-4B79-9D5E-912982945852}"
+ }
+ }
+ ],
+ "m_version": 1,
+ "m_eventSlotMapping": {
+ "{C7E99974-D1C0-4108-B731-120AF000060C}": {
+ "m_id": "{B6CA4982-245A-45F1-8AA6-3295F49DC071}"
+ }
+ },
+ "m_scriptEventAssetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "m_asset": {
+ "assetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/globalgamedata.scriptevents"
+ },
+ "m_busId": {
+ "Value": 1375178404
+ },
+ "m_eventId": {
+ "Value": 242067946
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8732754770832
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[6838720237293185886]": {
+ "$type": "Print",
+ "Id": 6838720237293185886,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{D206E5BC-3746-4A18-80C7-DBD335FD05FE}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value",
+ "toolTip": "Value which replaces instances of {Value} in the resulting string.",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "DisplayGroup": {
+ "Value": 1015031923
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{A4555039-973F-43F6-BC9D-CCC0C9B34252}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Value"
+ }
+ ],
+ "m_format": "AutoComponent_RPC: I'm Player #{Value}\n",
+ "m_numericPrecision": 0,
+ "m_arrayBindingMap": [
+ {
+ "Key": 1,
+ "Value": {
+ "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}"
+ }
+ }
+ ],
+ "m_unresolvedString": [
+ "AutoComponent_RPC: I'm Player #",
+ {},
+ "\n"
+ ],
+ "m_formatSlotMap": {
+ "Value": {
+ "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8715574901648
+ },
+ "Name": "SC-Node(TimeDelayNodeableNode)",
+ "Components": {
+ "Component_[8216689437045635826]": {
+ "$type": "TimeDelayNodeableNode",
+ "Id": 8216689437045635826,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{D2337DC6-F126-4254-B296-2DE6BA03993F}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{5809EA65-214B-43A9-8674-B89E9ADF1AE6}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Delay",
+ "toolTip": "The amount of time to delay before the Done is signalled.",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{AC3B9B74-5A7A-4AC9-89AA-B255879D6F63}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{25568B15-5372-4C2D-8280-2B433251EACA}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Done",
+ "toolTip": "Signaled after waiting for the specified amount of times.",
+ "DisplayGroup": {
+ "Value": 271442091
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 1.0,
+ "label": "Delay"
+ }
+ ],
+ "nodeable": {
+ "m_timeUnits": 2
+ },
+ "slotExecutionMap": {
+ "ins": [
+ {
+ "_slotId": {
+ "m_id": "{D2337DC6-F126-4254-B296-2DE6BA03993F}"
+ },
+ "_inputs": [
+ {
+ "_slotId": {
+ "m_id": "{5809EA65-214B-43A9-8674-B89E9ADF1AE6}"
+ }
+ }
+ ],
+ "_outs": [
+ {
+ "_slotId": {
+ "m_id": "{AC3B9B74-5A7A-4AC9-89AA-B255879D6F63}"
+ },
+ "_name": "On Start",
+ "_interfaceSourceId": "{C071AF8D-5D00-0000-E074-ECBC15020000}"
+ }
+ ],
+ "_interfaceSourceId": "{C3B60A69-2ADF-0000-4062-ABA615020000}"
+ }
+ ],
+ "latents": [
+ {
+ "_slotId": {
+ "m_id": "{25568B15-5372-4C2D-8280-2B433251EACA}"
+ },
+ "_name": "Done",
+ "_interfaceSourceId": "{C3B60A69-2ADF-0000-4062-ABA615020000}"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8741344705424
+ },
+ "Name": "SC-Node(AuthorityToAutonomous_PlayerNumberByEntityId)",
+ "Components": {
+ "Component_[8243210565080727934]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 8243210565080727934,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{D8767DB8-4BD8-4907-A3A4-BE0AE4F1A774}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Source",
+ "toolTip": "The Source containing the NetworkTestPlayerComponentController",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{2B948544-DD45-4DE1-A9FC-7AFBE1CAB202}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "player_number",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{71675BCB-6546-4B79-9D5E-912982945852}"
+ }
+ },
+ {
+ "id": {
+ "m_id": "{1FDB4A9A-D46A-49E8-B734-475230EB7C06}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{964EAD23-8DF5-47A9-BF07-1E5C7CD0CC6C}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "Source"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "player_number"
+ }
+ ],
+ "methodType": 2,
+ "methodName": "AuthorityToAutonomous_PlayerNumberByEntityId",
+ "className": "NetworkTestPlayerComponent",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{D8767DB8-4BD8-4907-A3A4-BE0AE4F1A774}"
+ },
+ {
+ "m_id": "{2B948544-DD45-4DE1-A9FC-7AFBE1CAB202}"
+ }
+ ],
+ "prettyClassName": "NetworkTestPlayerComponent"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8745639672720
+ },
+ "Name": "SC-Node(Start)",
+ "Components": {
+ "Component_[8447409406288787781]": {
+ "$type": "Start",
+ "Id": 8447409406288787781,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "toolTip": "Signaled when the entity that owns this graph is fully activated.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8728459803536
+ },
+ "Name": "SC-Node(Gate)",
+ "Components": {
+ "Component_[8679383768392231909]": {
+ "$type": "Gate",
+ "Id": 8679383768392231909,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{3E89B67B-1EF2-4DAB-8028-59A97527F3DF}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Condition",
+ "toolTip": "If true the node will signal the Output and proceed execution",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{8AE7F430-C4A9-444E-B42A-BDDFE96C387A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{78C92586-C0E1-449F-8EB2-B1CE3BFC8AE7}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "True",
+ "toolTip": "Signaled if the condition provided evaluates to true.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{47D87C86-2D61-46D3-A0FC-138F84FD352B}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "False",
+ "toolTip": "Signaled if the condition provided evaluates to false.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 0
+ },
+ "isNullPointer": false,
+ "$type": "bool",
+ "value": false,
+ "label": "Condition"
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "m_connections": [
+ {
+ "Id": {
+ "id": 8754229607312
+ },
+ "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Out), destEndpoint=(If: In)",
+ "Components": {
+ "Component_[3645153988172561571]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 3645153988172561571,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8724164836240
+ },
+ "slotId": {
+ "m_id": "{1071F455-D5A0-4C7A-AF91-648A0F197885}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8728459803536
+ },
+ "slotId": {
+ "m_id": "{8AE7F430-C4A9-444E-B42A-BDDFE96C387A}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8758524574608
+ },
+ "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Is Role Authority), destEndpoint=(If: Condition)",
+ "Components": {
+ "Component_[17942926291787646743]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 17942926291787646743,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8724164836240
+ },
+ "slotId": {
+ "m_id": "{BA87B4ED-9A52-4A0D-8920-CD0ED4E839EA}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8728459803536
+ },
+ "slotId": {
+ "m_id": "{3E89B67B-1EF2-4DAB-8028-59A97527F3DF}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8762819541904
+ },
+ "Name": "srcEndpoint=(If: True), destEndpoint=(Send Script Event: In)",
+ "Components": {
+ "Component_[3298020356088639785]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 3298020356088639785,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8728459803536
+ },
+ "slotId": {
+ "m_id": "{78C92586-C0E1-449F-8EB2-B1CE3BFC8AE7}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8711279934352
+ },
+ "slotId": {
+ "m_id": "{2FF1A0A1-59C6-4DCF-AE1D-296BD5964D4E}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8767114509200
+ },
+ "Name": "srcEndpoint=(Send Script Event: Out), destEndpoint=(Send Script Event: In)",
+ "Components": {
+ "Component_[5482500452520221078]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 5482500452520221078,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8711279934352
+ },
+ "slotId": {
+ "m_id": "{F2955681-A901-432D-99A3-53818F46CDE7}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8749934640016
+ },
+ "slotId": {
+ "m_id": "{23DBACD8-7784-4E18-A51C-258B25DF4C19}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8771409476496
+ },
+ "Name": "srcEndpoint=(Send Script Event: Out), destEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: In)",
+ "Components": {
+ "Component_[16156808606878296902]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 16156808606878296902,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8749934640016
+ },
+ "slotId": {
+ "m_id": "{19C850D7-7F1E-4D41-AFD9-5A4FA03D54F0}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8741344705424
+ },
+ "slotId": {
+ "m_id": "{1FDB4A9A-D46A-49E8-B734-475230EB7C06}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8775704443792
+ },
+ "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: Out), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[17367899649716276273]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 17367899649716276273,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8741344705424
+ },
+ "slotId": {
+ "m_id": "{964EAD23-8DF5-47A9-BF07-1E5C7CD0CC6C}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8737049738128
+ },
+ "slotId": {
+ "m_id": "{F3ED8C08-D751-492A-A39A-7B7734727A5A}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8779999411088
+ },
+ "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(TimeDelay: Start)",
+ "Components": {
+ "Component_[13463448697016307967]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 13463448697016307967,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8745639672720
+ },
+ "slotId": {
+ "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8715574901648
+ },
+ "slotId": {
+ "m_id": "{D2337DC6-F126-4254-B296-2DE6BA03993F}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8784294378384
+ },
+ "Name": "srcEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: Event), destEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: AuthorityToAutonomous_PlayerNumber Notify Event)",
+ "Components": {
+ "Component_[9477060643694737434]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 9477060643694737434,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8719869868944
+ },
+ "slotId": {
+ "m_id": "{7262E6CD-BADF-4DD0-8FCE-3A02C3F31CC8}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8706984967056
+ },
+ "slotId": {
+ "m_id": "{8C7E88EA-8FEE-45D0-9A2E-78BC4A18F508}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8788589345680
+ },
+ "Name": "srcEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: Out), destEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: Connect)",
+ "Components": {
+ "Component_[16543380658701699472]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 16543380658701699472,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8719869868944
+ },
+ "slotId": {
+ "m_id": "{1A6BCB27-F99F-4B6B-89CC-F2BE2A698331}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8706984967056
+ },
+ "slotId": {
+ "m_id": "{46E6B649-CAA4-4318-960D-5E4854E21BB2}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8792884312976
+ },
+ "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: player_number), destEndpoint=(Print: Value)",
+ "Components": {
+ "Component_[864863482692400383]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 864863482692400383,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8706984967056
+ },
+ "slotId": {
+ "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8732754770832
+ },
+ "slotId": {
+ "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8797179280272
+ },
+ "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: OnEvent), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[2451057837093425972]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 2451057837093425972,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8706984967056
+ },
+ "slotId": {
+ "m_id": "{586BE222-2DEA-4E08-968F-07CA4EB96C54}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8732754770832
+ },
+ "slotId": {
+ "m_id": "{D206E5BC-3746-4A18-80C7-DBD335FD05FE}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8801474247568
+ },
+ "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: In)",
+ "Components": {
+ "Component_[12180981889720748145]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 12180981889720748145,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8745639672720
+ },
+ "slotId": {
+ "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8719869868944
+ },
+ "slotId": {
+ "m_id": "{211BD78B-E01E-44F8-B44E-2FD988047BE9}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8805769214864
+ },
+ "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(IsNetEntityRoleAuthority: In)",
+ "Components": {
+ "Component_[5772191552099194657]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 5772191552099194657,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8715574901648
+ },
+ "slotId": {
+ "m_id": "{25568B15-5372-4C2D-8280-2B433251EACA}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8724164836240
+ },
+ "slotId": {
+ "m_id": "{B6C4BE5E-CDE4-4EC7-98D0-A019CD80041C}"
+ }
+ }
+ }
+ }
+ }
+ ],
+ "m_scriptEventAssets": [
+ [
+ {
+ "id": 8749934640016
+ },
+ {}
+ ],
+ [
+ {
+ "id": 8711279934352
+ },
+ {}
+ ]
+ ]
+ },
+ "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}",
+ "versionData": {
+ "_grammarVersion": 1,
+ "_runtimeVersion": 1,
+ "_fileVersion": 1
+ },
+ "m_variableCounter": 1,
+ "GraphCanvasData": [
+ {
+ "Key": {
+ "id": 8706984967056
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "HandlerNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 820.0,
+ 620.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".azeventhandler"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{F112D217-FAC8-4577-B67A-DFDF47842F7D}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8711279934352
+ },
+ "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": [
+ 1240.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{13BD3C1C-EDBB-419E-8465-495935414A1A}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8715574901648
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "TimeNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 200.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{5DF902FE-B0C4-4563-B2BA-17996B24E211}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8719869868944
+ },
+ "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": [
+ 380.0,
+ 620.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{BBBE60FC-0215-4A5A-BEBE-4FC869E5EECE}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8724164836240
+ },
+ "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": [
+ 500.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{348E780E-3A4E-47C7-ACF5-C7B2CB387517}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8728459803536
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "LogicNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 940.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{53C8DAF1-FFF3-4AF8-A7FB-BC0B4E492B37}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8732754770832
+ },
+ "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": [
+ 1440.0,
+ 620.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{74ED5AE1-A269-46D4-B968-FEDF513C9CF1}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8737049738128
+ },
+ "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": [
+ 2280.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{B91C0E41-E6B5-46DE-885F-6BC6CFB2E813}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8741344705424
+ },
+ "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": [
+ 1840.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{59FFF3C1-2D7B-47EA-AE35-ED15DCB95CC8}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8745639672720
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "TimeNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 40.0,
+ 280.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{B4CB62D9-9965-42E2-81FF-BDADDBD14CBE}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8749934640016
+ },
+ "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": [
+ 1540.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{FD844BFD-72DA-44BA-B92A-AFDB58263B91}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 2816238339133127497
+ },
+ "Value": {
+ "ComponentData": {
+ "{5F84B500-8C45-40D1-8EFC-A5306B241444}": {
+ "$type": "SceneComponentSaveData",
+ "ViewParams": {
+ "AnchorX": 260.0,
+ "AnchorY": 479.0
+ }
+ }
+ }
+ }
+ }
+ ],
+ "StatisticsHelper": {
+ "InstanceCounter": [
+ {
+ "Key": 4199610336680704683,
+ "Value": 1
+ },
+ {
+ "Key": 4847610523576971761,
+ "Value": 1
+ },
+ {
+ "Key": 6462358712820489356,
+ "Value": 1
+ },
+ {
+ "Key": 7760188923571293852,
+ "Value": 1
+ },
+ {
+ "Key": 8065262779685207188,
+ "Value": 1
+ },
+ {
+ "Key": 8452971738487658154,
+ "Value": 1
+ },
+ {
+ "Key": 10684225535275896474,
+ "Value": 2
+ },
+ {
+ "Key": 12248403816200817622,
+ "Value": 1
+ },
+ {
+ "Key": 12248403882232298424,
+ "Value": 1
+ },
+ {
+ "Key": 16232169425081397848,
+ "Value": 1
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas
new file mode 100644
index 0000000000..3869dfdfcb
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas
@@ -0,0 +1,1946 @@
+{
+ "Type": "JsonSerialization",
+ "Version": 1,
+ "ClassName": "ScriptCanvasData",
+ "ClassData": {
+ "m_scriptCanvas": {
+ "Id": {
+ "id": 1685762441320719908
+ },
+ "Name": "AutoComponent_RPC_NetLevelEntity",
+ "Components": {
+ "Component_[2936040539888065977]": {
+ "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph",
+ "Id": 2936040539888065977,
+ "m_graphData": {
+ "m_nodes": [
+ {
+ "Id": {
+ "id": 56986727032248
+ },
+ "Name": "SC-Node(RepeaterNodeableNode)",
+ "Components": {
+ "Component_[11069913642528675281]": {
+ "$type": "RepeaterNodeableNode",
+ "Id": 11069913642528675281,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{07267CBA-B377-4B57-8A04-E322F8BFC07F}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{A9CCCCF4-BC3E-44B8-B2BB-2EEFBF475F82}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Repetitions",
+ "toolTip": "How many times to repeat.",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{E490B225-8A15-4CA1-9F25-6D8F9F8E398F}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Interval",
+ "toolTip": "The Interval between repetitions. If zero, all repititions execute immediately, before On Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{92077333-D7F5-4E54-80FD-0363876B5510}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{91CF89F3-906C-4860-B84E-9BD7D6842CA8}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Complete",
+ "toolTip": "Signaled upon node exit",
+ "DisplayGroup": {
+ "Value": 1114099747
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ },
+ {
+ "id": {
+ "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Action",
+ "toolTip": "Signaled every repetition",
+ "DisplayGroup": {
+ "Value": 1204587666
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 99.0,
+ "label": "Repetitions"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 1.0,
+ "label": "Interval"
+ }
+ ],
+ "nodeable": {
+ "m_timeUnits": 2
+ },
+ "slotExecutionMap": {
+ "ins": [
+ {
+ "_slotId": {
+ "m_id": "{07267CBA-B377-4B57-8A04-E322F8BFC07F}"
+ },
+ "_inputs": [
+ {
+ "_slotId": {
+ "m_id": "{A9CCCCF4-BC3E-44B8-B2BB-2EEFBF475F82}"
+ }
+ },
+ {
+ "_slotId": {
+ "m_id": "{E490B225-8A15-4CA1-9F25-6D8F9F8E398F}"
+ }
+ }
+ ],
+ "_outs": [
+ {
+ "_slotId": {
+ "m_id": "{92077333-D7F5-4E54-80FD-0363876B5510}"
+ },
+ "_name": "On Start",
+ "_interfaceSourceId": "{60330D5B-FB7F-0000-8039-14DD7C020000}"
+ }
+ ],
+ "_interfaceSourceId": "{E1D0AF7E-837B-0000-0033-BA267C020000}"
+ }
+ ],
+ "latents": [
+ {
+ "_slotId": {
+ "m_id": "{91CF89F3-906C-4860-B84E-9BD7D6842CA8}"
+ },
+ "_name": "Complete",
+ "_interfaceSourceId": "{E1D0AF7E-837B-0000-0033-BA267C020000}"
+ },
+ {
+ "_slotId": {
+ "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}"
+ },
+ "_name": "Action",
+ "_interfaceSourceId": "{E1D0AF7E-837B-0000-0033-BA267C020000}"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 57003906901432
+ },
+ "Name": "SC-Node(DrawTextOnEntity)",
+ "Components": {
+ "Component_[14125366736968050670]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 14125366736968050670,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{6BD7BF07-D1B6-4CC2-A861-C4334AA3A025}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "EntityId: 0",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{AB0304C4-CD52-4351-A009-CD959CDA6E2B}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "String: 1",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{EB877F87-C03A-4692-ABBB-2317E98E8C6F}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Color: 2",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{E2D11762-F47C-4341-806A-DBB4FBAEC235}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Number: 3",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{5F720C70-EA4F-4883-B5BE-4278E75370E8}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "Entity Id"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 5
+ },
+ "isNullPointer": false,
+ "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string",
+ "value": "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx",
+ "label": "Color"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 12
+ },
+ "isNullPointer": false,
+ "$type": "Color",
+ "value": [
+ 1.0,
+ 0.0,
+ 0.0,
+ 1.0
+ ],
+ "label": "Color: 2"
+ },
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 2.0,
+ "label": "Number: 3"
+ }
+ ],
+ "methodType": 0,
+ "methodName": "DrawTextOnEntity",
+ "className": "DebugDrawRequestBus",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{6BD7BF07-D1B6-4CC2-A861-C4334AA3A025}"
+ },
+ {
+ "m_id": "{AB0304C4-CD52-4351-A009-CD959CDA6E2B}"
+ },
+ {
+ "m_id": "{EB877F87-C03A-4692-ABBB-2317E98E8C6F}"
+ },
+ {
+ "m_id": "{E2D11762-F47C-4341-806A-DBB4FBAEC235}"
+ }
+ ],
+ "prettyClassName": "DebugDrawRequestBus"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 56999611934136
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[15472692142045278273]": {
+ "$type": "Print",
+ "Id": 15472692142045278273,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{6CE4DAB2-4D8E-461E-B9B2-34338EBBAD97}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_format": "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.\n",
+ "m_unresolvedString": [
+ "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.\n"
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 56995316966840
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[15717135232290736332]": {
+ "$type": "Print",
+ "Id": 15717135232290736332,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{C38BB068-BF1B-4734-BFFA-10B258870349}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "m_format": "AutoComponent_RPC_NetLevelEntity: I'm a client playing some fx.\n",
+ "m_unresolvedString": [
+ "AutoComponent_RPC_NetLevelEntity: I'm a client playing some fx.\n"
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8318619017825
+ },
+ "Name": "SC-EventNode(AuthorityToClientNoParams_PlayFx Notify Event)",
+ "Components": {
+ "Component_[15772128920819427182]": {
+ "$type": "AzEventHandler",
+ "Id": 15772128920819427182,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{2A42C379-8E3B-46EF-BC63-C1D5395CB583}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "ConnectionLimitContract",
+ "limit": 1
+ },
+ {
+ "$type": "RestrictedNodeContract",
+ "m_nodeId": {
+ "id": 7820402811489
+ }
+ }
+ ],
+ "slotName": "Connect",
+ "toolTip": "Connect the AZ Event to this AZ Event Handler.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{3DB9829E-6088-49B9-A56D-4D1884C679BD}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Disconnect",
+ "toolTip": "Disconnect current AZ Event from this AZ Event Handler.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{9AC3CF57-B648-4B43-8FCA-8576B6EA350B}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Connected",
+ "toolTip": "Signaled when a connection has taken place.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{5FB8D529-6FC6-4543-99B5-5B147EBD7BE6}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Disconnected",
+ "toolTip": "Signaled when this event handler is disconnected.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{0481BBFE-D31E-421F-A6C2-8A7AF3012545}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnEvent",
+ "toolTip": "Triggered when the AZ Event invokes Signal() function.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ },
+ {
+ "id": {
+ "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "ConnectionLimitContract",
+ "limit": 1
+ },
+ {
+ "$type": "RestrictedNodeContract",
+ "m_nodeId": {
+ "id": 7820402811489
+ }
+ }
+ ],
+ "slotName": "AuthorityToClientNoParams_PlayFx Notify Event",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 4,
+ "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}"
+ },
+ "isNullPointer": true,
+ "label": "AuthorityToClientNoParams_PlayFx Notify Event"
+ }
+ ],
+ "m_azEventEntry": {
+ "m_eventName": "AuthorityToClientNoParams_PlayFx Notify Event",
+ "m_eventSlotId": {
+ "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 56991021999544
+ },
+ "Name": "SC-Node(Print)",
+ "Components": {
+ "Component_[15785913678997669879]": {
+ "$type": "Print",
+ "Id": 15785913678997669879,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{8E1B9705-148A-42E4-831E-D7B2877358E3}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "toolTip": "Input signal",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{CEBD2B9C-7DBA-486A-88DB-19F9C406B18E}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Value",
+ "toolTip": "Value which replaces instances of {Value} in the resulting string.",
+ "DisplayDataType": {
+ "m_type": 5
+ },
+ "DisplayGroup": {
+ "Value": 1015031923
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{E9B979F9-D682-4B58-8E88-6102387FE70B}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 5
+ },
+ "isNullPointer": false,
+ "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string",
+ "value": "",
+ "label": "Value"
+ }
+ ],
+ "m_format": "AutoComponent_RPC_NetLevelEntity Activated on entity: {Value}\n",
+ "m_arrayBindingMap": [
+ {
+ "Key": 1,
+ "Value": {
+ "m_id": "{CEBD2B9C-7DBA-486A-88DB-19F9C406B18E}"
+ }
+ }
+ ],
+ "m_unresolvedString": [
+ "AutoComponent_RPC_NetLevelEntity Activated on entity: ",
+ {},
+ "\n"
+ ],
+ "m_formatSlotMap": {
+ "Value": {
+ "m_id": "{CEBD2B9C-7DBA-486A-88DB-19F9C406B18E}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 57025381737912
+ },
+ "Name": "SC-Node(Start)",
+ "Components": {
+ "Component_[1888047318201703857]": {
+ "$type": "Start",
+ "Id": 1888047318201703857,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "toolTip": "Signaled when the entity that owns this graph is fully activated.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8310662318335
+ },
+ "Name": "SC-Node(GetEntityName)",
+ "Components": {
+ "Component_[2232030369305027010]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 2232030369305027010,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{F69C5762-0CAB-4A54-9C81-C99CDB73C834}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "EntityId: 0",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{03554C56-8237-4C19-B9F8-87DEA1AC3ED0}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{6ECBD749-25FE-4813-B34E-EF46BC09E616}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{36D47097-8F0C-4CBD-8DE2-32F4754C569F}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "String",
+ "DisplayDataType": {
+ "m_type": 5
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "Entity Id"
+ }
+ ],
+ "methodType": 0,
+ "methodName": "GetEntityName",
+ "className": "GameEntityContextRequestBus",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{F69C5762-0CAB-4A54-9C81-C99CDB73C834}"
+ }
+ ],
+ "prettyClassName": "GameEntityContextRequestBus"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 8400576252253
+ },
+ "Name": "SC-Node(AuthorityToClientNoParams_PlayFxByEntityId)",
+ "Components": {
+ "Component_[6332803108634970671]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 6332803108634970671,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{87B7266B-D7B1-4CAD-9898-4D7F0274DAB0}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Source",
+ "toolTip": "The Source containing the NetworkTestPlayerComponentController",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{AB0D7C00-A334-449A-AC56-EA3167AB8900}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{A52302D6-9DF9-45C2-960D-19BF90A4A931}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "Source"
+ }
+ ],
+ "methodType": 2,
+ "methodName": "AuthorityToClientNoParams_PlayFxByEntityId",
+ "className": "NetworkTestPlayerComponent",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{87B7266B-D7B1-4CAD-9898-4D7F0274DAB0}"
+ }
+ ],
+ "prettyClassName": "NetworkTestPlayerComponent"
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 57012496836024
+ },
+ "Name": "SC-Node(TimeDelayNodeableNode)",
+ "Components": {
+ "Component_[8951348653904382148]": {
+ "$type": "TimeDelayNodeableNode",
+ "Id": 8951348653904382148,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{EA0DF0AE-2FF7-4670-8D99-7CF863038E68}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{9C6D8500-B49B-4407-B2EB-40B1CD4CE762}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Delay",
+ "toolTip": "The amount of time to delay before the Done is signalled.",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{FE2A3D2F-7AC2-431D-B80C-C363DB475919}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "On Start",
+ "DisplayGroup": {
+ "Value": 2675529103
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{158B30BE-BD39-40AE-A8A8-F0E5694F0180}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Done",
+ "toolTip": "Signaled after waiting for the specified amount of times.",
+ "DisplayGroup": {
+ "Value": 271442091
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 1.0,
+ "label": "Delay"
+ }
+ ],
+ "nodeable": {
+ "m_timeUnits": 2
+ },
+ "slotExecutionMap": {
+ "ins": [
+ {
+ "_slotId": {
+ "m_id": "{EA0DF0AE-2FF7-4670-8D99-7CF863038E68}"
+ },
+ "_inputs": [
+ {
+ "_slotId": {
+ "m_id": "{9C6D8500-B49B-4407-B2EB-40B1CD4CE762}"
+ }
+ }
+ ],
+ "_outs": [
+ {
+ "_slotId": {
+ "m_id": "{FE2A3D2F-7AC2-431D-B80C-C363DB475919}"
+ },
+ "_name": "On Start",
+ "_interfaceSourceId": "{4C400000-7C02-0000-B86E-0FACE0000000}"
+ }
+ ],
+ "_interfaceSourceId": "{24000000-0000-0000-0000-F421FC7F0000}"
+ }
+ ],
+ "latents": [
+ {
+ "_slotId": {
+ "m_id": "{158B30BE-BD39-40AE-A8A8-F0E5694F0180}"
+ },
+ "_name": "Done",
+ "_interfaceSourceId": "{24000000-0000-0000-0000-F421FC7F0000}"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 7820402811489
+ },
+ "Name": "SC-Node(GetAuthorityToClientNoParams_PlayFxEventByEntityId)",
+ "Components": {
+ "Component_[9263945554457190064]": {
+ "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method",
+ "Id": 9263945554457190064,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{F22A7438-E72F-4757-90D9-99F03C91E10D}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "EntityId: 0",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{510F56FD-6778-4DB8-BDDE-258335431CC6}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{94C2AF04-6BFA-4E5A-9490-C7479A7AF61E}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{9C6DDF96-6BF0-45ED-B15F-2E6C2FF5F886}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Event<>",
+ "DisplayDataType": {
+ "m_type": 4,
+ "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}"
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DataType": 1
+ }
+ ],
+ "Datums": [
+ {
+ "scriptCanvasType": {
+ "m_type": 1
+ },
+ "isNullPointer": false,
+ "$type": "EntityId",
+ "value": {
+ "id": 2901262558
+ },
+ "label": "EntityId: 0"
+ }
+ ],
+ "methodType": 2,
+ "methodName": "GetAuthorityToClientNoParams_PlayFxEventByEntityId",
+ "className": "NetworkTestPlayerComponent",
+ "resultSlotIDs": [
+ {}
+ ],
+ "inputSlots": [
+ {
+ "m_id": "{F22A7438-E72F-4757-90D9-99F03C91E10D}"
+ }
+ ],
+ "prettyClassName": "NetworkTestPlayerComponent"
+ }
+ }
+ }
+ ],
+ "m_connections": [
+ {
+ "Id": {
+ "id": 57029676705208
+ },
+ "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(TimeDelay: Start)",
+ "Components": {
+ "Component_[5204535376548158590]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 5204535376548158590,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 57025381737912
+ },
+ "slotId": {
+ "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 57012496836024
+ },
+ "slotId": {
+ "m_id": "{EA0DF0AE-2FF7-4670-8D99-7CF863038E68}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 57055446508984
+ },
+ "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(Repeater: Start)",
+ "Components": {
+ "Component_[6292481678297438578]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 6292481678297438578,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 57012496836024
+ },
+ "slotId": {
+ "m_id": "{158B30BE-BD39-40AE-A8A8-F0E5694F0180}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 56986727032248
+ },
+ "slotId": {
+ "m_id": "{07267CBA-B377-4B57-8A04-E322F8BFC07F}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 9392713697629
+ },
+ "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(AuthorityToClientNoParams_PlayFxByEntityId: In)",
+ "Components": {
+ "Component_[17811480012084226596]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 17811480012084226596,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 56986727032248
+ },
+ "slotId": {
+ "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8400576252253
+ },
+ "slotId": {
+ "m_id": "{AB0D7C00-A334-449A-AC56-EA3167AB8900}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 10269167405311
+ },
+ "Name": "srcEndpoint=(GetEntityName: String), destEndpoint=(Print: Value)",
+ "Components": {
+ "Component_[11931728297561282182]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 11931728297561282182,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8310662318335
+ },
+ "slotId": {
+ "m_id": "{36D47097-8F0C-4CBD-8DE2-32F4754C569F}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 56991021999544
+ },
+ "slotId": {
+ "m_id": "{CEBD2B9C-7DBA-486A-88DB-19F9C406B18E}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 11042261518591
+ },
+ "Name": "srcEndpoint=(GetEntityName: Out), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[5559381044656171146]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 5559381044656171146,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8310662318335
+ },
+ "slotId": {
+ "m_id": "{6ECBD749-25FE-4813-B34E-EF46BC09E616}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 56991021999544
+ },
+ "slotId": {
+ "m_id": "{8E1B9705-148A-42E4-831E-D7B2877358E3}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 36365388695807
+ },
+ "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(GetEntityName: In)",
+ "Components": {
+ "Component_[5574600651313925988]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 5574600651313925988,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 57012496836024
+ },
+ "slotId": {
+ "m_id": "{158B30BE-BD39-40AE-A8A8-F0E5694F0180}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8310662318335
+ },
+ "slotId": {
+ "m_id": "{03554C56-8237-4C19-B9F8-87DEA1AC3ED0}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 9022993654369
+ },
+ "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Event<>), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: AuthorityToClientNoParams_PlayFx Notify Event)",
+ "Components": {
+ "Component_[4910818715692868417]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 4910818715692868417,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 7820402811489
+ },
+ "slotId": {
+ "m_id": "{9C6DDF96-6BF0-45ED-B15F-2E6C2FF5F886}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8318619017825
+ },
+ "slotId": {
+ "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 9078828229217
+ },
+ "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Out), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: Connect)",
+ "Components": {
+ "Component_[16758724763058723803]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 16758724763058723803,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 7820402811489
+ },
+ "slotId": {
+ "m_id": "{94C2AF04-6BFA-4E5A-9490-C7479A7AF61E}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 8318619017825
+ },
+ "slotId": {
+ "m_id": "{2A42C379-8E3B-46EF-BC63-C1D5395CB583}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 9808972669537
+ },
+ "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: In)",
+ "Components": {
+ "Component_[597205010205160938]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 597205010205160938,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 57012496836024
+ },
+ "slotId": {
+ "m_id": "{158B30BE-BD39-40AE-A8A8-F0E5694F0180}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 7820402811489
+ },
+ "slotId": {
+ "m_id": "{510F56FD-6778-4DB8-BDDE-258335431CC6}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 10148275085921
+ },
+ "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[1594149632687531010]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 1594149632687531010,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8318619017825
+ },
+ "slotId": {
+ "m_id": "{0481BBFE-D31E-421F-A6C2-8A7AF3012545}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 56995316966840
+ },
+ "slotId": {
+ "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 10629311423073
+ },
+ "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(DrawTextOnEntity: In)",
+ "Components": {
+ "Component_[17976200298405988971]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 17976200298405988971,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 8318619017825
+ },
+ "slotId": {
+ "m_id": "{0481BBFE-D31E-421F-A6C2-8A7AF3012545}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 57003906901432
+ },
+ "slotId": {
+ "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 42045766425613
+ },
+ "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(Print: In)",
+ "Components": {
+ "Component_[1911118463107071864]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 1911118463107071864,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 56986727032248
+ },
+ "slotId": {
+ "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 56999611934136
+ },
+ "slotId": {
+ "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}",
+ "versionData": {
+ "_grammarVersion": 1,
+ "_runtimeVersion": 1,
+ "_fileVersion": 1
+ },
+ "GraphCanvasData": [
+ {
+ "Key": {
+ "id": 7820402811489
+ },
+ "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": [
+ -100.0,
+ 400.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{F35F8202-B5EE-4ADD-9FF6-AF214A094266}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8310662318335
+ },
+ "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": [
+ 80.0,
+ -320.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{346AFC26-B2EF-4495-AA1A-347BF77CB99D}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8318619017825
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "HandlerNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 340.0,
+ 400.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".azeventhandler"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{67499699-CA73-48B4-87E0-C66F4A3EA7CB}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 8400576252253
+ },
+ "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": [
+ 420.0,
+ -60.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{2B6329F7-4CE7-4E01-B1A4-1FFCAB2D0B72}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 56986727032248
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "DefaultNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ 80.0,
+ -60.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{41B7477C-D5B9-49AC-AFA3-AAAE2A6ED7C5}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 56991021999544
+ },
+ "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": [
+ 540.0,
+ -320.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{0CF0846C-1D21-4B33-8723-1538FD4FD04A}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 56995316966840
+ },
+ "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": [
+ 800.0,
+ 260.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{0D04EBA6-E1E7-4DF7-BAA9-CC87F4689CD2}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 56999611934136
+ },
+ "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": [
+ 420.0,
+ 100.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{A4FDCB87-B021-48B9-ABCB-AECA986B33D6}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 57003906901432
+ },
+ "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": [
+ 800.0,
+ 460.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData",
+ "SubStyle": ".method"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{82D0ED1B-98D3-4AEF-B1DA-2F27CACD3A4D}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 57012496836024
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "DefaultNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ -220.0,
+ -40.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{D3F081D7-40C1-4C31-B298-B18C6AFDFD25}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 57025381737912
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{328FF15C-C302-458F-A43D-E1794DE0904E}": {
+ "$type": "GeneralNodeTitleComponentSaveData",
+ "PaletteOverride": "TimeNodeTitlePalette"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ -380.0,
+ -20.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{675C7E90-B89E-4347-A3C6-B8D12B6EA698}"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 1685762441320719908
+ },
+ "Value": {
+ "ComponentData": {
+ "{5F84B500-8C45-40D1-8EFC-A5306B241444}": {
+ "$type": "SceneComponentSaveData",
+ "ViewParams": {
+ "AnchorX": -349.0,
+ "AnchorY": 10.0
+ }
+ }
+ }
+ }
+ }
+ ],
+ "StatisticsHelper": {
+ "InstanceCounter": [
+ {
+ "Key": 4199610336680704683,
+ "Value": 1
+ },
+ {
+ "Key": 4847610523576971761,
+ "Value": 1
+ },
+ {
+ "Key": 6462358712820489356,
+ "Value": 1
+ },
+ {
+ "Key": 7087687843968394353,
+ "Value": 1
+ },
+ {
+ "Key": 8679770052035517025,
+ "Value": 1
+ },
+ {
+ "Key": 10684225535275896474,
+ "Value": 3
+ },
+ {
+ "Key": 11983076003173356132,
+ "Value": 1
+ },
+ {
+ "Key": 13774516196858047560,
+ "Value": 1
+ },
+ {
+ "Key": 13774516226790665785,
+ "Value": 1
+ }
+ ]
+ }
+ },
+ "Component_[630514155173445772]": {
+ "$type": "EditorGraphVariableManagerComponent",
+ "Id": 630514155173445772
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptcanvas
new file mode 100644
index 0000000000..3ba5cc6b7d
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptcanvas
@@ -0,0 +1,662 @@
+{
+ "Type": "JsonSerialization",
+ "Version": 1,
+ "ClassName": "ScriptCanvasData",
+ "ClassData": {
+ "m_scriptCanvas": {
+ "Id": {
+ "id": 17732469402520
+ },
+ "Name": "Untitled-1",
+ "Components": {
+ "Component_[16492301523567686923]": {
+ "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph",
+ "Id": 16492301523567686923,
+ "m_graphData": {
+ "m_nodes": [
+ {
+ "Id": {
+ "id": 17741059337112
+ },
+ "Name": "SC-Node(OperatorAdd)",
+ "Components": {
+ "Component_[11612963594766700030]": {
+ "$type": "OperatorAdd",
+ "Id": 11612963594766700030,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{B7529112-C29F-45F0-811C-DB8EE18EB8B8}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "In",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{49618851-F6B2-4B90-BFDF-ADBAAA84BBA4}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Out",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{4612C904-82DF-4B48-8485-4C878AF9A4D1}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "MathOperatorContract",
+ "NativeTypes": [
+ {
+ "m_type": 3
+ },
+ {
+ "m_type": 6
+ },
+ {
+ "m_type": 8
+ },
+ {
+ "m_type": 9
+ },
+ {
+ "m_type": 10
+ },
+ {
+ "m_type": 11
+ },
+ {
+ "m_type": 12
+ },
+ {
+ "m_type": 14
+ },
+ {
+ "m_type": 15
+ }
+ ]
+ }
+ ],
+ "slotName": "Number",
+ "toolTip": "An operand to use in performing the specified Operation",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "DisplayGroup": {
+ "Value": 1114760223
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 1114760223
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{8451E795-6A0E-44CE-81FD-AEF0EE5B0400}"
+ }
+ },
+ {
+ "id": {
+ "m_id": "{610B3BFB-9043-47A0-9694-5F14A1947E36}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "MathOperatorContract",
+ "NativeTypes": [
+ {
+ "m_type": 3
+ },
+ {
+ "m_type": 6
+ },
+ {
+ "m_type": 8
+ },
+ {
+ "m_type": 9
+ },
+ {
+ "m_type": 10
+ },
+ {
+ "m_type": 11
+ },
+ {
+ "m_type": 12
+ },
+ {
+ "m_type": 14
+ },
+ {
+ "m_type": 15
+ }
+ ]
+ }
+ ],
+ "slotName": "Number",
+ "toolTip": "An operand to use in performing the specified Operation",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "DisplayGroup": {
+ "Value": 1114760223
+ },
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 1114760223
+ },
+ "DataType": 1
+ },
+ {
+ "id": {
+ "m_id": "{36FFF1AB-C208-47CB-8271-436C85E0AE42}"
+ },
+ "DynamicTypeOverride": 3,
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ },
+ {
+ "$type": "MathOperatorContract",
+ "NativeTypes": [
+ {
+ "m_type": 3
+ },
+ {
+ "m_type": 6
+ },
+ {
+ "m_type": 8
+ },
+ {
+ "m_type": 9
+ },
+ {
+ "m_type": 10
+ },
+ {
+ "m_type": 11
+ },
+ {
+ "m_type": 12
+ },
+ {
+ "m_type": 14
+ },
+ {
+ "m_type": 15
+ }
+ ]
+ }
+ ],
+ "slotName": "Result",
+ "toolTip": "The result of the specified operation",
+ "DisplayDataType": {
+ "m_type": 3
+ },
+ "DisplayGroup": {
+ "Value": 1114760223
+ },
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 2
+ },
+ "DynamicGroup": {
+ "Value": 1114760223
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{8451E795-6A0E-44CE-81FD-AEF0EE5B0400}"
+ }
+ }
+ ],
+ "Datums": [
+ {
+ "isOverloadedStorage": false,
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Number"
+ },
+ {
+ "isOverloadedStorage": false,
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 1.0,
+ "label": "Number"
+ }
+ ]
+ }
+ }
+ },
+ {
+ "Id": {
+ "id": 17736764369816
+ },
+ "Name": "ReceiveScriptEvent",
+ "Components": {
+ "Component_[16408183651077237195]": {
+ "$type": "ReceiveScriptEvent",
+ "Id": 16408183651077237195,
+ "Slots": [
+ {
+ "id": {
+ "m_id": "{8DC10581-B8DF-473C-9C75-996111DBF560}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Connect",
+ "toolTip": "Connect this event handler to the specified entity.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{E60D1951-E56D-41F8-84C5-AD0BA803DD51}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Disconnect",
+ "toolTip": "Disconnect this event handler.",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{1BCE6CAC-B1C0-43FA-B4F5-E9A34D5064E5}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnConnected",
+ "toolTip": "Signaled when a connection has taken place.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{FEB42E9A-D562-4BBD-90AB-32255124BFE8}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnDisconnected",
+ "toolTip": "Signaled when this event handler is disconnected.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{C9EF936B-8C74-42B4-8793-67FC7FD3BCBC}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "OnFailure",
+ "toolTip": "Signaled when it is not possible to connect this handler.",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ }
+ },
+ {
+ "id": {
+ "m_id": "{76985C7A-761A-4CEF-9F55-6DD2B136317A}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "ExecutionSlot:NewPlayerScriptActive",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ },
+ {
+ "id": {
+ "m_id": "{5CD8E1E9-6192-4B7D-9C2C-6C18BE99CF44}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "Number",
+ "Descriptor": {
+ "ConnectionType": 1,
+ "SlotType": 2
+ },
+ "DataType": 1,
+ "IsReference": true,
+ "VariableReference": {
+ "m_id": "{8451E795-6A0E-44CE-81FD-AEF0EE5B0400}"
+ }
+ },
+ {
+ "id": {
+ "m_id": "{9FDB71AA-0F19-406D-82CA-508A2CA10F95}"
+ },
+ "contracts": [
+ {
+ "$type": "SlotTypeContract"
+ }
+ ],
+ "slotName": "ExecutionSlot:GetNumberOfActivePlayers",
+ "Descriptor": {
+ "ConnectionType": 2,
+ "SlotType": 1
+ },
+ "IsLatent": true
+ }
+ ],
+ "Datums": [
+ {
+ "isOverloadedStorage": false,
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0,
+ "label": "Number"
+ }
+ ],
+ "m_version": 1,
+ "m_eventMap": [
+ {
+ "Key": {
+ "Value": 242067946
+ },
+ "Value": {
+ "m_scriptEventAssetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "m_eventName": "GetNumberOfActivePlayers",
+ "m_eventSlotId": {
+ "m_id": "{9FDB71AA-0F19-406D-82CA-508A2CA10F95}"
+ },
+ "m_resultSlotId": {
+ "m_id": "{5CD8E1E9-6192-4B7D-9C2C-6C18BE99CF44}"
+ }
+ }
+ },
+ {
+ "Key": {
+ "Value": 2930121176
+ },
+ "Value": {
+ "m_scriptEventAssetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "m_eventName": "NewPlayerScriptActive",
+ "m_eventSlotId": {
+ "m_id": "{76985C7A-761A-4CEF-9F55-6DD2B136317A}"
+ }
+ }
+ }
+ ],
+ "m_eventSlotMapping": {
+ "{155BF981-AD70-4D29-81A6-1517FAE59FB1}": {
+ "m_id": "{5CD8E1E9-6192-4B7D-9C2C-6C18BE99CF44}"
+ },
+ "{65D394D3-F90D-4F10-94BF-F5E1581CF2CF}": {
+ "m_id": "{76985C7A-761A-4CEF-9F55-6DD2B136317A}"
+ },
+ "{67784749-9B41-429C-9C97-3D296182EB67}": {
+ "m_id": "{9FDB71AA-0F19-406D-82CA-508A2CA10F95}"
+ }
+ },
+ "m_scriptEventAssetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "m_asset": {
+ "assetId": {
+ "guid": "{FE1B1992-8220-5DD3-A60A-AEC85EB91C54}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/globalgamedata.scriptevents"
+ }
+ }
+ }
+ }
+ ],
+ "m_connections": [
+ {
+ "Id": {
+ "id": 17745354304408
+ },
+ "Name": "srcEndpoint=(Receive Script Event: ExecutionSlot:NewPlayerScriptActive), destEndpoint=(Add (+): In)",
+ "Components": {
+ "Component_[8782209668839578826]": {
+ "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection",
+ "Id": 8782209668839578826,
+ "sourceEndpoint": {
+ "nodeId": {
+ "id": 17736764369816
+ },
+ "slotId": {
+ "m_id": "{76985C7A-761A-4CEF-9F55-6DD2B136317A}"
+ }
+ },
+ "targetEndpoint": {
+ "nodeId": {
+ "id": 17741059337112
+ },
+ "slotId": {
+ "m_id": "{B7529112-C29F-45F0-811C-DB8EE18EB8B8}"
+ }
+ }
+ }
+ }
+ }
+ ],
+ "m_scriptEventAssets": [
+ [
+ {
+ "id": 17736764369816
+ },
+ {}
+ ]
+ ]
+ },
+ "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}",
+ "versionData": {
+ "_grammarVersion": 1,
+ "_runtimeVersion": 1,
+ "_fileVersion": 1
+ },
+ "m_variableCounter": 1,
+ "GraphCanvasData": [
+ {
+ "Key": {
+ "id": 17732469402520
+ },
+ "Value": {
+ "ComponentData": {
+ "{5F84B500-8C45-40D1-8EFC-A5306B241444}": {
+ "$type": "SceneComponentSaveData"
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 17736764369816
+ },
+ "Value": {
+ "ComponentData": {
+ "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": {
+ "$type": "NodeSaveData"
+ },
+ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": {
+ "$type": "GeometrySaveData",
+ "Position": [
+ -360.0,
+ -60.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{C419A1CF-CBA8-416B-BF6C-4B574C3E59E3}"
+ },
+ "{D8BBE799-7E4D-495A-B69A-1E3940670891}": {
+ "$type": "ScriptEventReceiverHandlerNodeDescriptorSaveData",
+ "EventNames": [
+ [
+ {
+ "Value": 242067946
+ },
+ "GetNumberOfActivePlayers"
+ ],
+ [
+ {
+ "Value": 2930121176
+ },
+ "NewPlayerScriptActive"
+ ]
+ ]
+ }
+ }
+ }
+ },
+ {
+ "Key": {
+ "id": 17741059337112
+ },
+ "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": [
+ 120.0,
+ 100.0
+ ]
+ },
+ "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": {
+ "$type": "StylingComponentSaveData"
+ },
+ "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": {
+ "$type": "PersistentIdComponentSaveData",
+ "PersistentId": "{0D0751AD-8164-4196-9C09-8CDB9AAA296F}"
+ }
+ }
+ }
+ }
+ ],
+ "StatisticsHelper": {
+ "InstanceCounter": [
+ {
+ "Key": 1244476766431948410,
+ "Value": 1
+ },
+ {
+ "Key": 1678857390775488101,
+ "Value": 1
+ },
+ {
+ "Key": 1678857392390856307,
+ "Value": 1
+ }
+ ]
+ }
+ },
+ "Component_[16498171485036643402]": {
+ "$type": "EditorGraphVariableManagerComponent",
+ "Id": 16498171485036643402,
+ "m_variableData": {
+ "m_nameVariableMap": [
+ {
+ "Key": {
+ "m_id": "{8451E795-6A0E-44CE-81FD-AEF0EE5B0400}"
+ },
+ "Value": {
+ "Datum": {
+ "isOverloadedStorage": false,
+ "scriptCanvasType": {
+ "m_type": 3
+ },
+ "isNullPointer": false,
+ "$type": "double",
+ "value": 0.0
+ },
+ "VariableId": {
+ "m_id": "{8451E795-6A0E-44CE-81FD-AEF0EE5B0400}"
+ },
+ "VariableName": "ActivePlayerCount"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptevents b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptevents
new file mode 100644
index 0000000000..059713e14b
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptevents
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/Player.prefab b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/Player.prefab
new file mode 100644
index 0000000000..2653809dda
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/Player.prefab
@@ -0,0 +1,195 @@
+{
+ "ContainerEntity": {
+ "Id": "ContainerEntity",
+ "Name": "Player",
+ "Components": {
+ "Component_[10603663676997462041]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 10603663676997462041
+ },
+ "Component_[11066377844757909329]": {
+ "$type": "EditorPrefabComponent",
+ "Id": 11066377844757909329
+ },
+ "Component_[11664640320098005944]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 11664640320098005944
+ },
+ "Component_[12551690377468870725]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 12551690377468870725,
+ "Parent Entity": ""
+ },
+ "Component_[16402163080075698011]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 16402163080075698011
+ },
+ "Component_[3491366785918494447]": {
+ "$type": "EditorLockComponent",
+ "Id": 3491366785918494447
+ },
+ "Component_[4830373679514129871]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 4830373679514129871
+ },
+ "Component_[5144323498211834874]": {
+ "$type": "SelectionComponent",
+ "Id": 5144323498211834874
+ },
+ "Component_[5267607163086533733]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 5267607163086533733
+ },
+ "Component_[6678300504118618849]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 6678300504118618849
+ },
+ "Component_[8384628950786300469]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 8384628950786300469,
+ "Child Entity Order": [
+ "Entity_[10070247746456]"
+ ]
+ }
+ }
+ },
+ "Entities": {
+ "Entity_[10070247746456]": {
+ "Id": "Entity_[10070247746456]",
+ "Name": "Player",
+ "Components": {
+ "Component_[1059478843478789313]": {
+ "$type": "EditorInspectorComponent",
+ "Id": 1059478843478789313,
+ "ComponentOrderEntryArray": [
+ {
+ "ComponentId": 9878555871810913249
+ },
+ {
+ "ComponentId": 11481641385923146202,
+ "SortIndex": 1
+ },
+ {
+ "ComponentId": 11440172471478606933,
+ "SortIndex": 2
+ },
+ {
+ "ComponentId": 17461691807054668218,
+ "SortIndex": 3
+ },
+ {
+ "ComponentId": 15530420875454157766,
+ "SortIndex": 4
+ },
+ {
+ "ComponentId": 10596595655489113153,
+ "SortIndex": 5
+ }
+ ]
+ },
+ "Component_[10596595655489113153]": {
+ "$type": "EditorScriptCanvasComponent",
+ "Id": 10596595655489113153,
+ "m_name": "AutoComponent_RPC",
+ "m_assetHolder": {
+ "m_asset": {
+ "assetId": {
+ "guid": "{5ED120C4-07DC-56F1-80A7-37BFC98FD74E}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/autocomponent_rpc.scriptcanvas"
+ }
+ },
+ "runtimeDataIsValid": true,
+ "runtimeDataOverrides": {
+ "source": {
+ "assetId": {
+ "guid": "{5ED120C4-07DC-56F1-80A7-37BFC98FD74E}"
+ },
+ "assetHint": "levels/multiplayer/autocomponent_rpc/autocomponent_rpc.scriptcanvas"
+ }
+ }
+ },
+ "Component_[11440172471478606933]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 11440172471478606933,
+ "m_template": {
+ "$type": "Multiplayer::NetworkTransformComponent"
+ }
+ },
+ "Component_[11481641385923146202]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 11481641385923146202,
+ "m_template": {
+ "$type": "NetBindComponent"
+ }
+ },
+ "Component_[13110996849704981748]": {
+ "$type": "EditorVisibilityComponent",
+ "Id": 13110996849704981748
+ },
+ "Component_[1472895075383059499]": {
+ "$type": "EditorLockComponent",
+ "Id": 1472895075383059499
+ },
+ "Component_[1526920553231193509]": {
+ "$type": "EditorPendingCompositionComponent",
+ "Id": 1526920553231193509
+ },
+ "Component_[15530420875454157766]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 15530420875454157766,
+ "m_template": {
+ "$type": "Multiplayer::LocalPredictionPlayerInputComponent"
+ }
+ },
+ "Component_[1699895912837266792]": {
+ "$type": "EditorDisabledCompositionComponent",
+ "Id": 1699895912837266792
+ },
+ "Component_[17461691807054668218]": {
+ "$type": "GenericComponentWrapper",
+ "Id": 17461691807054668218,
+ "m_template": {
+ "$type": "AutomatedTesting::NetworkTestPlayerComponent"
+ }
+ },
+ "Component_[3622545398462507871]": {
+ "$type": "EditorOnlyEntityComponent",
+ "Id": 3622545398462507871
+ },
+ "Component_[5778259918231688598]": {
+ "$type": "AZ::Render::EditorMeshComponent",
+ "Id": 5778259918231688598,
+ "Controller": {
+ "Configuration": {
+ "ModelAsset": {
+ "assetId": {
+ "guid": "{F322592F-43BC-50E7-903C-CC231846093F}",
+ "subId": 276443623
+ },
+ "assetHint": "objects/_primitives/_cylinder_1x1.azmodel"
+ }
+ }
+ }
+ },
+ "Component_[7004633483882343256]": {
+ "$type": "SelectionComponent",
+ "Id": 7004633483882343256
+ },
+ "Component_[8469628382507693850]": {
+ "$type": "EditorEntitySortComponent",
+ "Id": 8469628382507693850
+ },
+ "Component_[9407892837096707905]": {
+ "$type": "EditorEntityIconComponent",
+ "Id": 9407892837096707905
+ },
+ "Component_[9878555871810913249]": {
+ "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+ "Id": 9878555871810913249,
+ "Parent Entity": "ContainerEntity"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/tags.txt b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/tags.txt
new file mode 100644
index 0000000000..0d6c1880e7
--- /dev/null
+++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/tags.txt
@@ -0,0 +1,12 @@
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
+0,0,0,0,0,0
diff --git a/AutomatedTesting/project.json b/AutomatedTesting/project.json
index 9222777ef3..fc14645d2b 100644
--- a/AutomatedTesting/project.json
+++ b/AutomatedTesting/project.json
@@ -7,7 +7,7 @@
"android_settings": {
"package_name": "com.lumberyard.yourgame",
"version_number": 1,
- "version_name": "1.0.0.0",
+ "version_name": "1.0.0",
"orientation": "landscape"
},
"engine": "o3de",
diff --git a/Code/Editor/AboutDialog.cpp b/Code/Editor/AboutDialog.cpp
index c0fd39e1ae..ba086cad5d 100644
--- a/Code/Editor/AboutDialog.cpp
+++ b/Code/Editor/AboutDialog.cpp
@@ -30,8 +30,6 @@ CAboutDialog::CAboutDialog(QString versionText, QString richTextCopyrightNotice,
m_ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
- connect(m_ui->m_transparentAgreement, &QLabel::linkActivated, this, &CAboutDialog::OnCustomerAgreement);
-
m_ui->m_transparentTrademarks->setText(versionText);
m_ui->m_transparentAllRightReserved->setObjectName("copyrightNotice");
@@ -84,9 +82,4 @@ void CAboutDialog::mouseReleaseEvent(QMouseEvent* event)
QDialog::mouseReleaseEvent(event);
}
-void CAboutDialog::OnCustomerAgreement()
-{
- QDesktopServices::openUrl(QUrl(QStringLiteral("https://www.o3debinaries.org/license")));
-}
-
#include
diff --git a/Code/Editor/AboutDialog.h b/Code/Editor/AboutDialog.h
index db079a6ec7..279d7cd399 100644
--- a/Code/Editor/AboutDialog.h
+++ b/Code/Editor/AboutDialog.h
@@ -30,8 +30,6 @@ public:
private:
- void OnCustomerAgreement();
-
void mouseReleaseEvent(QMouseEvent* event) override;
void paintEvent(QPaintEvent* event) override;
diff --git a/Code/Editor/AboutDialog.ui b/Code/Editor/AboutDialog.ui
index 09a7c18841..a7433c620f 100644
--- a/Code/Editor/AboutDialog.ui
+++ b/Code/Editor/AboutDialog.ui
@@ -181,14 +181,17 @@
-
-
+
- Terms of Use
+ <a href="https://www.o3debinaries.org/license">Terms of Use</a>
+
+
+ Qt::RichText
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
-
+
true
@@ -274,11 +277,6 @@
QWidget
-
- ClickableLabel
- QLabel
-
-
diff --git a/Code/Editor/EditorPanelUtils.cpp b/Code/Editor/EditorPanelUtils.cpp
index a270de5978..1a3c9bcb4a 100644
--- a/Code/Editor/EditorPanelUtils.cpp
+++ b/Code/Editor/EditorPanelUtils.cpp
@@ -319,8 +319,8 @@ public:
keys.push_back(QPair("Edit Menu.Duplicate", "Ctrl+D"));
keys.push_back(QPair("Edit Menu.Undo", "Ctrl+Z"));
keys.push_back(QPair("Edit Menu.Redo", "Ctrl+Shift+Z"));
- keys.push_back(QPair("Edit Menu.Group", "Ctrl+G"));
- keys.push_back(QPair("Edit Menu.Ungroup", "Ctrl+Shift+G"));
+ keys.push_back(QPair("Edit Menu.Group", "Ctrl+Alt+O"));
+ keys.push_back(QPair("Edit Menu.Ungroup", "Ctrl+Alt+P"));
keys.push_back(QPair("Edit Menu.Rename", "Ctrl+R"));
keys.push_back(QPair("Edit Menu.Reset", ""));
keys.push_back(QPair("Edit Menu.Edit Hotkeys", ""));
diff --git a/Code/Editor/Lib/Tests/test_ClickableLabel.cpp b/Code/Editor/Lib/Tests/test_ClickableLabel.cpp
deleted file mode 100644
index a676714992..0000000000
--- a/Code/Editor/Lib/Tests/test_ClickableLabel.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 "EditorDefs.h"
-#include
-#include
-#include
-
-#include
-
-using namespace AZ;
-using namespace ::testing;
-
-namespace UnitTest
-{
- class TestingClickableLabel
- : public ScopedAllocatorSetupFixture
- {
- public:
- ClickableLabel m_clickableLabel;
- };
-
- TEST_F(TestingClickableLabel, CursorDoesNotUpdateWhileDisabled)
- {
- m_clickableLabel.setEnabled(false);
-
- QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
- QEnterEvent enterEvent{ QPointF(), QPointF(), QPointF() };
- QApplication::sendEvent(&m_clickableLabel, &enterEvent);
-
- const Qt::CursorShape cursorShape = QApplication::overrideCursor()->shape();
- EXPECT_THAT(cursorShape, Ne(Qt::PointingHandCursor));
- EXPECT_THAT(cursorShape, Eq(Qt::BlankCursor));
- }
-
- TEST_F(TestingClickableLabel, DoesNotRespondToDblClickWhileDisabled)
- {
- m_clickableLabel.setEnabled(false);
-
- bool linkActivated = false;
- QObject::connect(&m_clickableLabel, &QLabel::linkActivated, [&linkActivated]()
- {
- linkActivated = true;
- });
-
- QMouseEvent mouseEvent {
- QEvent::MouseButtonDblClick, QPointF(),
- Qt::LeftButton, Qt::LeftButton, Qt::NoModifier };
- QApplication::sendEvent(&m_clickableLabel, &mouseEvent);
-
- EXPECT_THAT(linkActivated, Eq(false));
- }
-} // namespace UnitTest
diff --git a/Code/Editor/QtUI/ClickableLabel.cpp b/Code/Editor/QtUI/ClickableLabel.cpp
deleted file mode 100644
index b0694e5f18..0000000000
--- a/Code/Editor/QtUI/ClickableLabel.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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 "EditorDefs.h"
-
-#include "ClickableLabel.h"
-
-
-ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
- : QLabel(parent)
- , m_text(text)
- , m_showDecoration(false)
-{
- setTextFormat(Qt::RichText);
- setTextInteractionFlags(Qt::TextBrowserInteraction);
-}
-
-ClickableLabel::ClickableLabel(QWidget* parent)
- : QLabel(parent)
- , m_showDecoration(false)
-{
- setTextFormat(Qt::RichText);
- setTextInteractionFlags(Qt::TextBrowserInteraction);
-}
-
-void ClickableLabel::showEvent([[maybe_unused]] QShowEvent* event)
-{
- updateFormatting(false);
-}
-
-void ClickableLabel::enterEvent(QEvent* ev)
-{
- if (!isEnabled())
- {
- return;
- }
-
- updateFormatting(true);
- QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor));
- QLabel::enterEvent(ev);
-}
-
-void ClickableLabel::leaveEvent(QEvent* ev)
-{
- if (!isEnabled())
- {
- return;
- }
-
- updateFormatting(false);
- QApplication::restoreOverrideCursor();
- QLabel::leaveEvent(ev);
-}
-
-void ClickableLabel::setText(const QString& text)
-{
- m_text = text;
- QLabel::setText(text);
- updateFormatting(false);
-}
-
-void ClickableLabel::setShowDecoration(bool b)
-{
- m_showDecoration = b;
- updateFormatting(false);
-}
-
-void ClickableLabel::updateFormatting(bool mouseOver)
-{
- //FIXME: this should be done differently. Using a style sheet would be easiest.
-
- QColor c = palette().color(QPalette::WindowText);
- if (mouseOver || m_showDecoration)
- {
- QLabel::setText(QString(R"(%2)").arg(c.name(), m_text));
- }
- else
- {
- QLabel::setText(m_text);
- }
-}
-
-bool ClickableLabel::event(QEvent* e)
-{
- if (isEnabled())
- {
- if (e->type() == QEvent::MouseButtonDblClick)
- {
- emit linkActivated(QString());
- return true; //ignore
- }
- }
-
- return QLabel::event(e);
-}
-
-#include
diff --git a/Code/Editor/QtUI/ClickableLabel.h b/Code/Editor/QtUI/ClickableLabel.h
deleted file mode 100644
index 2b14676eae..0000000000
--- a/Code/Editor/QtUI/ClickableLabel.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-#pragma once
-#ifndef CRYINCLUDE_EDITORCOMMON_CLICKABLELABEL_H
-#define CRYINCLUDE_EDITORCOMMON_CLICKABLELABEL_H
-
-#if !defined(Q_MOC_RUN)
-#include
-#endif
-
-class SANDBOX_API ClickableLabel
- : public QLabel
-{
- Q_OBJECT
-public:
- explicit ClickableLabel(const QString& text, QWidget* parent = nullptr);
- explicit ClickableLabel(QWidget* parent = nullptr);
- bool event(QEvent* e) override;
-
- void setText(const QString& text);
- void setShowDecoration(bool b);
-
-protected:
- void showEvent(QShowEvent* event) override;
- void enterEvent(QEvent* ev) override;
- void leaveEvent(QEvent* ev) override;
-
-private:
- void updateFormatting(bool mouseOver);
- QString m_text;
- bool m_showDecoration;
-};
-
-#endif
diff --git a/Code/Editor/editor_lib_files.cmake b/Code/Editor/editor_lib_files.cmake
index fa627176d8..78a15123d6 100644
--- a/Code/Editor/editor_lib_files.cmake
+++ b/Code/Editor/editor_lib_files.cmake
@@ -526,8 +526,6 @@ set(FILES
PythonEditorFuncs.h
QtUI/QCollapsibleGroupBox.h
QtUI/QCollapsibleGroupBox.cpp
- QtUI/ClickableLabel.h
- QtUI/ClickableLabel.cpp
QtUI/PixmapLabelPreview.h
QtUI/PixmapLabelPreview.cpp
QtUI/WaitCursor.h
diff --git a/Code/Editor/editor_lib_test_files.cmake b/Code/Editor/editor_lib_test_files.cmake
index 17f36228db..5b66357c41 100644
--- a/Code/Editor/editor_lib_test_files.cmake
+++ b/Code/Editor/editor_lib_test_files.cmake
@@ -8,7 +8,6 @@
set(FILES
Lib/Tests/IEditorMock.h
- Lib/Tests/test_ClickableLabel.cpp
Lib/Tests/test_CryEditPythonBindings.cpp
Lib/Tests/test_CryEditDocPythonBindings.cpp
Lib/Tests/test_EditorPythonBindings.cpp
diff --git a/Code/Framework/AzCore/AzCore/Math/Plane.inl b/Code/Framework/AzCore/AzCore/Math/Plane.inl
index f33d356312..795ee8b4bc 100644
--- a/Code/Framework/AzCore/AzCore/Math/Plane.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Plane.inl
@@ -19,12 +19,14 @@ namespace AZ
AZ_MATH_INLINE Plane Plane::CreateFromNormalAndPoint(const Vector3& normal, const Vector3& point)
{
+ AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized");
return Plane(Simd::Vec4::ConstructPlane(normal.GetSimdValue(), point.GetSimdValue()));
}
AZ_MATH_INLINE Plane Plane::CreateFromNormalAndDistance(const Vector3& normal, float dist)
{
+ AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized");
Plane result;
result.Set(normal, dist);
return result;
@@ -33,6 +35,7 @@ namespace AZ
AZ_MATH_INLINE Plane Plane::CreateFromCoefficients(const float a, const float b, const float c, const float d)
{
+ AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized");
Plane result;
result.Set(a, b, c, d);
return result;
@@ -65,18 +68,21 @@ namespace AZ
AZ_MATH_INLINE void Plane::Set(const Vector3& normal, float d)
{
+ AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized");
m_plane.Set(normal, d);
}
AZ_MATH_INLINE void Plane::Set(float a, float b, float c, float d)
{
+ AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized");
m_plane.Set(a, b, c, d);
}
AZ_MATH_INLINE void Plane::SetNormal(const Vector3& normal)
{
+ AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized");
m_plane.SetX(normal.GetX());
m_plane.SetY(normal.GetY());
m_plane.SetZ(normal.GetZ());
diff --git a/Code/Framework/AzCore/AzCore/Math/Quaternion.h b/Code/Framework/AzCore/AzCore/Math/Quaternion.h
index 83e8012a62..ad454aab4f 100644
--- a/Code/Framework/AzCore/AzCore/Math/Quaternion.h
+++ b/Code/Framework/AzCore/AzCore/Math/Quaternion.h
@@ -54,11 +54,11 @@ namespace AZ
//! Sets components using a Vector3 for the imaginary part and a float for the real part.
static Quaternion CreateFromVector3AndValue(const Vector3& v, float w);
- //! Sets the quaternion to be a rotation around a specified axis.
+ //! Sets the quaternion to be a rotation around a specified axis in radians.
//! @{
- static Quaternion CreateRotationX(float angle);
- static Quaternion CreateRotationY(float angle);
- static Quaternion CreateRotationZ(float angle);
+ static Quaternion CreateRotationX(float angleInRadians);
+ static Quaternion CreateRotationY(float angleInRadians);
+ static Quaternion CreateRotationZ(float angleInRadians);
//! @}
//! Creates a quaternion from a Matrix3x3
@@ -168,6 +168,14 @@ namespace AZ
float NormalizeWithLengthEstimate();
//! @}
+ //! Get the shortest equivalent of the rotation.
+ //! In case the w component of the quaternion is negative the rotation is > 180° and taking the longer path.
+ //! The quaternion will be inverted in that case to take the shortest path of rotation.
+ //! @{
+ Quaternion GetShortestEquivalent() const;
+ void ShortestEquivalent();
+ //! @}
+
//! Linearly interpolate towards a destination quaternion.
//! @param[in] dest The quaternion to interpolate towards.
//! @param[in] t Normalized interpolation value where 0.0 represents the current and 1.0 the destination value.
diff --git a/Code/Framework/AzCore/AzCore/Math/Quaternion.inl b/Code/Framework/AzCore/AzCore/Math/Quaternion.inl
index 37319ae146..bd51a90fca 100644
--- a/Code/Framework/AzCore/AzCore/Math/Quaternion.inl
+++ b/Code/Framework/AzCore/AzCore/Math/Quaternion.inl
@@ -73,27 +73,27 @@ namespace AZ
}
- AZ_MATH_INLINE Quaternion Quaternion::CreateRotationX(float angle)
+ AZ_MATH_INLINE Quaternion Quaternion::CreateRotationX(float angleInRadians)
{
- const float halfAngle = 0.5f * angle;
+ const float halfAngle = 0.5f * angleInRadians;
float sin, cos;
SinCos(halfAngle, sin, cos);
return Quaternion(sin, 0.0f, 0.0f, cos);
}
- AZ_MATH_INLINE Quaternion Quaternion::CreateRotationY(float angle)
+ AZ_MATH_INLINE Quaternion Quaternion::CreateRotationY(float angleInRadians)
{
- const float halfAngle = 0.5f * angle;
+ const float halfAngle = 0.5f * angleInRadians;
float sin, cos;
SinCos(halfAngle, sin, cos);
return Quaternion(0.0f, sin, 0.0f, cos);
}
- AZ_MATH_INLINE Quaternion Quaternion::CreateRotationZ(float angle)
+ AZ_MATH_INLINE Quaternion Quaternion::CreateRotationZ(float angleInRadians)
{
- const float halfAngle = 0.5f * angle;
+ const float halfAngle = 0.5f * angleInRadians;
float sin, cos;
SinCos(halfAngle, sin, cos);
return Quaternion(0.0f, 0.0f, sin, cos);
@@ -345,6 +345,23 @@ namespace AZ
}
+ AZ_MATH_INLINE Quaternion Quaternion::GetShortestEquivalent() const
+ {
+ if (GetW() < 0.0f)
+ {
+ return -(*this);
+ }
+
+ return *this;
+ }
+
+
+ AZ_MATH_INLINE void Quaternion::ShortestEquivalent()
+ {
+ *this = GetShortestEquivalent();
+ }
+
+
AZ_MATH_INLINE Quaternion Quaternion::Lerp(const Quaternion& dest, float t) const
{
if (Dot(dest) >= 0.0f)
diff --git a/Code/Framework/AzCore/Tests/Math/QuaternionTests.cpp b/Code/Framework/AzCore/Tests/Math/QuaternionTests.cpp
index 9ef38d7115..3de60a1fd4 100644
--- a/Code/Framework/AzCore/Tests/Math/QuaternionTests.cpp
+++ b/Code/Framework/AzCore/Tests/Math/QuaternionTests.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
using namespace AZ;
@@ -453,7 +454,7 @@ namespace UnitTest
AZ::Quaternion backFromScaledAxisAngle = AZ::Quaternion::CreateFromScaledAxisAngle(scaledAxisAngle);
// Compare the original quaternion with the one after the conversion.
- EXPECT_TRUE(testQuat.IsClose(backFromScaledAxisAngle, 1e-6f));
+ EXPECT_THAT(testQuat, IsCloseTolerance(backFromScaledAxisAngle, 1e-6f));
}
TEST_P(QuaternionScaledAxisAngleConversionFixture, AxisAngleQuatRoundtripTests)
@@ -467,7 +468,7 @@ namespace UnitTest
// Convert the axis-angle back into a quaternion and compare the original quaternion with the one after the conversion.
const AZ::Quaternion backFromAxisAngle = AZ::Quaternion::CreateFromAxisAngle(axis, angle);
- EXPECT_TRUE(testQuat.IsClose(backFromAxisAngle, 1e-6f));
+ EXPECT_THAT(testQuat, IsCloseTolerance(backFromAxisAngle, 1e-6f));
}
TEST_P(QuaternionScaledAxisAngleConversionFixture, CompareAxisAngleConversionTests)
@@ -506,8 +507,20 @@ namespace UnitTest
}
const AZ::Quaternion backFromAxisAngle = AZ::Quaternion::CreateFromAxisAngle(axisFromScaledResult, angleFromScaledResult);
- EXPECT_TRUE(testQuat.IsClose(backFromAxisAngle, 1e-6f));
+ EXPECT_THAT(testQuat, IsCloseTolerance(backFromAxisAngle, 1e-6f));
}
INSTANTIATE_TEST_CASE_P(MATH_Quaternion, QuaternionScaledAxisAngleConversionFixture, ::testing::ValuesIn(RotationRepresentationConversionTestQuats));
+
+ TEST(MATH_Quaternion, ShortestEquivalent)
+ {
+ const AZ::Quaternion testQuat = AZ::Quaternion::CreateRotationX(AZ::Constants::HalfPi * 3.0f);
+
+ AZ::Quaternion absQuat = testQuat;
+ absQuat.ShortestEquivalent();
+ EXPECT_THAT(testQuat.GetShortestEquivalent(), IsCloseTolerance(absQuat, 1e-6f));
+
+ const float angle = absQuat.GetEulerRadians().GetX();
+ EXPECT_THAT(angle, testing::FloatEq(-AZ::Constants::HalfPi));
+ }
}
diff --git a/Code/Tools/AssetProcessor/CMakeLists.txt b/Code/Tools/AssetProcessor/CMakeLists.txt
index 431a167163..98c5f8e5e8 100644
--- a/Code/Tools/AssetProcessor/CMakeLists.txt
+++ b/Code/Tools/AssetProcessor/CMakeLists.txt
@@ -52,7 +52,7 @@ get_property(asset_builders GLOBAL PROPERTY LY_ASSET_BUILDERS)
string (REPLACE ";" "," asset_builders "${asset_builders}")
ly_add_source_properties(
SOURCES native/utilities/ApplicationManager.cpp
- PROPERTY COMPILE_DEFINITIONS
+ PROPERTY COMPILE_DEFINITIONS
VALUES LY_ASSET_BUILDERS="${asset_builders}"
)
@@ -147,9 +147,9 @@ endif()
# Tests
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
-
+
ly_add_target(
- NAME AssetProcessor.Tests EXECUTABLE
+ NAME AssetProcessor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
AUTOMOC
AUTORCC
@@ -167,12 +167,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
)
ly_add_source_properties(
SOURCES native/tests/assetBuilderSDK/assetBuilderSDKTest.cpp
- PROPERTY COMPILE_DEFINITIONS
+ PROPERTY COMPILE_DEFINITIONS
VALUES ${LY_PAL_TOOLS_DEFINES}
)
ly_add_source_properties(
SOURCES native/unittests/AssetProcessorManagerUnitTests.cpp
- PROPERTY COMPILE_DEFINITIONS
+ PROPERTY COMPILE_DEFINITIONS
VALUES LY_CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}"
)
@@ -266,7 +266,10 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_googletest(
NAME AZ::AssetProcessor.Tests
- TEST_COMMAND $ --unittest --gtest_filter=-*.SUITE_sandbox*
+ )
+ ly_add_googlebenchmark(
+ NAME AZ::AssetProcessor.Benchmarks
+ TARGET AZ::AssetProcessor.Tests
)
endif()
diff --git a/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake b/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake
index 400a3f7ba5..7daca9bbc1 100644
--- a/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake
+++ b/Code/Tools/AssetProcessor/assetprocessor_static_files.cmake
@@ -63,13 +63,6 @@ set(FILES
native/resourcecompiler/RCJobSortFilterProxyModel.h
native/resourcecompiler/RCQueueSortModel.cpp
native/resourcecompiler/RCQueueSortModel.h
- native/shadercompiler/shadercompilerjob.cpp
- native/shadercompiler/shadercompilerjob.h
- native/shadercompiler/shadercompilerManager.cpp
- native/shadercompiler/shadercompilerManager.h
- native/shadercompiler/shadercompilerMessages.h
- native/shadercompiler/shadercompilerModel.cpp
- native/shadercompiler/shadercompilerModel.h
native/utilities/ApplicationManagerAPI.h
native/utilities/ApplicationManager.cpp
native/utilities/ApplicationManager.h
diff --git a/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake b/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake
index 3a336ce97b..2c4c53642c 100644
--- a/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake
+++ b/Code/Tools/AssetProcessor/assetprocessor_test_files.cmake
@@ -67,8 +67,6 @@ set(FILES
native/unittests/PlatformConfigurationUnitTests.h
native/unittests/RCcontrollerUnitTests.cpp
native/unittests/RCcontrollerUnitTests.h
- native/unittests/ShaderCompilerUnitTests.cpp
- native/unittests/ShaderCompilerUnitTests.h
native/unittests/UnitTestRunner.cpp
native/unittests/UnitTestRunner.h
native/unittests/UtilitiesUnitTests.cpp
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.cpp b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.cpp
deleted file mode 100644
index 47b2ca9047..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * 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 "shadercompilerManager.h"
-#include "shadercompilerjob.h"
-
-#include
-
-#include "native/utilities/assetUtils.h"
-
-ShaderCompilerManager::ShaderCompilerManager(QObject* parent)
- : QObject(parent)
- , m_isUnitTesting(false)
- , m_numberOfJobsStarted(0)
- , m_numberOfJobsEnded(0)
- , m_numberOfErrors(0)
-{
-}
-
-ShaderCompilerManager::~ShaderCompilerManager()
-{
-}
-
-void ShaderCompilerManager::process(unsigned int connID, unsigned int type, unsigned int serial, QByteArray payload)
-{
- (void)type;
- (void)serial;
- Q_ASSERT(AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest") == type);
- decodeShaderCompilerRequest(connID, payload);
-}
-
-void ShaderCompilerManager::decodeShaderCompilerRequest(unsigned int connID, QByteArray payload)
-{
- if (payload.length() < sizeof(unsigned int) + sizeof(unsigned int) + 2 + sizeof(unsigned short))
- {
- QString error = "Payload size is too small";
- AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
- emit sendErrorMessage(error);
- return;
- }
-
- unsigned char* data_end = reinterpret_cast(payload.data() + payload.size());
- unsigned int* requestId = reinterpret_cast(data_end - sizeof(unsigned int));
- unsigned int* serverListSizePtr = reinterpret_cast(data_end - sizeof(unsigned int) - sizeof(unsigned int));
- unsigned short* serverPortPtr = reinterpret_cast(data_end - sizeof(unsigned int) - sizeof(unsigned int) - sizeof(unsigned short));
-
- ShaderCompilerRequestMessage msg;
- QString error;
-
- msg.requestId = *requestId;
- msg.serverListSize = *serverListSizePtr;
- msg.serverPort = *serverPortPtr;
- if ((msg.serverListSize <= 0) || (msg.serverListSize > 100000))
- {
- error = "Shader Compiler Server List is wrong";
- AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
- emit sendErrorMessage(error);
- return;
- }
- if (msg.serverPort == 0)
- {
- error = "Shader Compiler port is wrong";
- AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
- emit sendErrorMessage(error);
- return;
- }
-
- char* position_of_first_null = reinterpret_cast(serverPortPtr) - 1;// -1 for null
- if ((*position_of_first_null) != '\0')
- {
- error = "Shader Compiler payload is corrupt,position is not null";
- AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
- emit sendErrorMessage(error);
- return;
- }
- char* beginning_of_serverList = position_of_first_null - msg.serverListSize;
- char* position_of_second_null = beginning_of_serverList - 1;//-1 for null
-
- if ((*position_of_second_null) != '\0')
- {
- error = "Shader Compiler payload is corrupt,position is not null";
- AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
- emit sendErrorMessage(error);
- return;
- }
-
- unsigned int originalPayloadSize = static_cast(payload.size()) - sizeof(unsigned int) - sizeof(unsigned int) - sizeof(unsigned short) - static_cast(msg.serverListSize) - 2;
- msg.serverList = beginning_of_serverList;
- msg.originalPayload.insert(0, payload.data(), static_cast(originalPayloadSize));
- ShaderCompilerJob* shaderCompilerJob = new ShaderCompilerJob();
- shaderCompilerJob->initialize(this, msg);
- shaderCompilerJob->setIsUnitTesting(m_isUnitTesting);
- m_shaderCompilerJobMap[msg.requestId] = connID;
- shaderCompilerJob->setAutoDelete(true);
- QThreadPool* threadPool = QThreadPool::globalInstance();
- threadPool->start(shaderCompilerJob);
-}
-
-void ShaderCompilerManager::OnShaderCompilerJobComplete(QByteArray payload, unsigned int requestId)
-{
- auto iterator = m_shaderCompilerJobMap.find(requestId);
- if (iterator != m_shaderCompilerJobMap.end())
- {
- sendResponse(iterator.value(), AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyResponse"), 0, payload);
- }
- else
- {
- QString error = "Shader Compiler cannot find the connection id";
- AZ_Warning(AssetProcessor::ConsoleChannel, false, error.toUtf8().data());
- emit sendErrorMessage(error);
- }
-}
-
-void ShaderCompilerManager::sendResponse(unsigned int connId, unsigned int /*type*/, unsigned int /*serial*/, QByteArray payload)
-{
- EBUS_EVENT_ID(connId, AssetProcessor::ConnectionBus, SendRaw, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyResponse"), 0, payload);
-}
-
-void ShaderCompilerManager::shaderCompilerError(QString errorMessage, QString server, QString timestamp, QString payload)
-{
- m_numberOfErrors++;
- emit numberOfErrorsChanged();
- emit sendErrorMessageFromShaderJob(errorMessage, server, timestamp, payload);
-}
-
-void ShaderCompilerManager::jobStarted()
-{
- m_numberOfJobsStarted++;
- emit numberOfJobsStartedChanged();
-}
-
-void ShaderCompilerManager::jobEnded()
-{
- m_numberOfJobsEnded++;
- numberOfJobsEndedChanged();
-}
-
-
-void ShaderCompilerManager::setIsUnitTesting(bool isUnitTesting)
-{
- m_isUnitTesting = isUnitTesting;
-}
-
-int ShaderCompilerManager::numberOfJobsStarted()
-{
- return m_numberOfJobsStarted;
-}
-
-int ShaderCompilerManager::numberOfJobsEnded()
-{
- return m_numberOfJobsEnded;
-}
-
-int ShaderCompilerManager::numberOfErrors()
-{
- return m_numberOfErrors;
-}
-
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.h b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.h
deleted file mode 100644
index cb51ee98f6..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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
- *
- */
-#ifndef SHADERCOMPILERMANAGER_H
-#define SHADERCOMPILERMANAGER_H
-
-#if !defined(Q_MOC_RUN)
-#include
-#include
-#include
-#include
-#endif
-
-typedef QHash ShaderCompilerJobMap;
-
-/**
- * The Shader Compiler Manager class receive a shader compile request
- * and starts a shader compiler job for it
- */
-class ShaderCompilerManager
- : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int numberOfJobsStarted READ numberOfJobsStarted NOTIFY numberOfJobsStartedChanged)
- Q_PROPERTY(int numberOfJobsEnded READ numberOfJobsEnded NOTIFY numberOfJobsEndedChanged)
- Q_PROPERTY(int numberOfErrors READ numberOfErrors NOTIFY numberOfErrorsChanged)
-public:
-
- explicit ShaderCompilerManager(QObject* parent = 0);
- virtual ~ShaderCompilerManager();
-
- void process(unsigned int connID, unsigned int type, unsigned int serial, QByteArray payload);
- void decodeShaderCompilerRequest(unsigned int connID, QByteArray payload);
- void setIsUnitTesting(bool isUnitTesting);
- int numberOfJobsStarted();
- int numberOfJobsEnded();
- int numberOfErrors();
- virtual void sendResponse(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
-
-signals:
- void sendErrorMessage(QString errorMessage);
- void sendErrorMessageFromShaderJob(QString errorMessage, QString server, QString timestamp, QString payload);
- void numberOfJobsStartedChanged();
- void numberOfJobsEndedChanged();
- void numberOfErrorsChanged();
-
-
-public slots:
- void OnShaderCompilerJobComplete(QByteArray payload, unsigned int requestId);
- void shaderCompilerError(QString errorMessage, QString server, QString timestamp, QString payload);
- void jobStarted();
- void jobEnded();
-
-
-private:
- ShaderCompilerJobMap m_shaderCompilerJobMap;
- bool m_isUnitTesting;
- int m_numberOfJobsStarted;
- int m_numberOfJobsEnded;
- int m_numberOfErrors;
-};
-
-#endif // SHADERCOMPILERMANAGER_H
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerMessages.h b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerMessages.h
deleted file mode 100644
index be90a7b3e5..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerMessages.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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
- *
- */
-#ifndef SHADERCOMPILERMESSAGES_H
-#define SHADERCOMPILERMESSAGES_H
-
-#include
-#include
-
-struct ShaderCompilerRequestMessage
-{
- QByteArray originalPayload;
- QString serverList;
- unsigned short serverPort;
- unsigned int serverListSize;
- unsigned int requestId;
-};
-
-#endif //SHADERCOMPILERMESSAGES_H
-
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.cpp b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.cpp
deleted file mode 100644
index 50b5254e79..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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 "shadercompilerModel.h"
-
-namespace
-{
- ShaderCompilerModel* s_singleton = nullptr;
-}
-
-ShaderCompilerModel::ShaderCompilerModel(QObject* parent)
- : QAbstractItemModel(parent)
-{
- Q_ASSERT(s_singleton == nullptr);
- s_singleton = this;
-}
-
-ShaderCompilerModel::~ShaderCompilerModel()
-{
- s_singleton = nullptr;
-}
-
-ShaderCompilerModel* ShaderCompilerModel::Get()
-{
- return s_singleton;
-}
-
-QVariant ShaderCompilerModel::data(const QModelIndex& index, int role) const
-{
- if (!index.isValid())
- {
- return QVariant();
- }
-
- int row = index.row();
-
- if (row < 0)
- {
- return QVariant();
- }
- if (row >= m_shaderErrorInfoList.count())
- {
- return QVariant();
- }
-
- switch (role)
- {
- case TimeStampRole:
- return m_shaderErrorInfoList[row].m_shaderTimestamp;
- case ServerRole:
- return m_shaderErrorInfoList[row].m_shaderServerName;
- case ErrorRole:
- return m_shaderErrorInfoList[row].m_shaderError;
- case OriginalRequestRole:
- return m_shaderErrorInfoList[row].m_shaderOriginalPayload;
-
- case Qt::DisplayRole:
- switch (index.column())
- {
- case ColumnTimeStamp:
- return m_shaderErrorInfoList[row].m_shaderTimestamp;
- case ColumnServer:
- return m_shaderErrorInfoList[row].m_shaderServerName;
- case ColumnError:
- return m_shaderErrorInfoList[row].m_shaderServerName;
- }
- }
-
- return QVariant();
-}
-
-
-Qt::ItemFlags ShaderCompilerModel::flags(const QModelIndex& index) const
-{
- (void)index;
- return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
-}
-
-
-int ShaderCompilerModel::rowCount(const QModelIndex& parent) const
-{
- (void)parent;
- return m_shaderErrorInfoList.count();
-}
-
-
-QModelIndex ShaderCompilerModel::parent(const QModelIndex&) const
-{
- return QModelIndex();
-}
-
-
-QModelIndex ShaderCompilerModel::index(int row, int column, const QModelIndex& parent) const
-{
- if (row >= rowCount(parent) || column >= columnCount(parent))
- {
- return QModelIndex();
- }
- return createIndex(row, column);
-}
-
-
-int ShaderCompilerModel::columnCount(const QModelIndex& parent) const
-{
- return parent.isValid() ? 0 : Column::Max;
-}
-
-
-QVariant ShaderCompilerModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
- {
- switch (section)
- {
- case ColumnTimeStamp:
- return tr("Time Stamp");
- case ColumnServer:
- return tr("Server");
- case ColumnError:
- return tr("Error");
- default:
- break;
- }
- }
-
- return QAbstractItemModel::headerData(section, orientation, role);
-}
-
-
-QHash ShaderCompilerModel::roleNames() const
-{
- QHash result;
- result[TimeStampRole] = "timestamp";
- result[ServerRole] = "server";
- result[ErrorRole] = "error";
- result[OriginalRequestRole] = "originalRequest";
- return result;
-}
-void ShaderCompilerModel::addShaderErrorInfoEntry(QString errorMessage, QString timestamp, QString payload, QString server)
-{
- ShaderCompilerErrorInfo shaderCompileErrorInfo(errorMessage, timestamp, payload, server);
- beginInsertRows(QModelIndex(), m_shaderErrorInfoList.size(), m_shaderErrorInfoList.size());
- m_shaderErrorInfoList.append(shaderCompileErrorInfo);
- endInsertRows();
-}
-
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.h b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.h
deleted file mode 100644
index 464ca9b5b0..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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
- *
- */
-#ifndef SHADERCOMPILERMODEL_H
-#define SHADERCOMPILERMODEL_H
-
-#if !defined(Q_MOC_RUN)
-#include
-#include
-#include
-#include
-#include
-#include
-#endif
-
-class QModelIndex;
-class QObject;
-
-struct ShaderCompilerErrorInfo
-{
- QString m_shaderError;
- QString m_shaderTimestamp;
- QString m_shaderOriginalPayload;
- QString m_shaderServerName;
-
- ShaderCompilerErrorInfo(QString shaderError, QString shaderTimestamp, QString shaderOriginalPayload, QString shaderServerName)
- : m_shaderError(shaderError)
- , m_shaderTimestamp(shaderTimestamp)
- , m_shaderOriginalPayload(shaderOriginalPayload)
- , m_shaderServerName(shaderServerName)
- {
- }
-};
-
-/** The Shader Compiler model is responsible for capturing error requests
- */
-class ShaderCompilerModel
- : public QAbstractItemModel
-{
- Q_OBJECT
-public:
-
- enum DataRoles
- {
- TimeStampRole = Qt::UserRole + 1,
- ServerRole,
- ErrorRole,
- OriginalRequestRole,
- };
-
- enum Column
- {
- ColumnTimeStamp,
- ColumnServer,
- ColumnError,
- Max
- };
-
- /// standard Qt constructor
- explicit ShaderCompilerModel(QObject* parent = 0);
- virtual ~ShaderCompilerModel();
-
- // singleton pattern
- static ShaderCompilerModel* Get();
-
-
- /// QAbstractListModel interface
- QModelIndex parent(const QModelIndex&) const override;
- QModelIndex index(int row, int column, const QModelIndex& parent) const override;
- int columnCount(const QModelIndex&) const override;
- virtual int rowCount(const QModelIndex& parent) const override;
- QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- virtual QVariant data(const QModelIndex& index, int role) const override;
- virtual QHash roleNames() const override;
- virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
-
-public slots:
- void addShaderErrorInfoEntry(QString errorMessage, QString timestamp, QString payload, QString server);
-
-private:
-
- QList m_shaderErrorInfoList;
-};
-
-
-#endif // SHADERCOMPILERMODEL_H
-
-
-
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.cpp b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.cpp
deleted file mode 100644
index 58f9244616..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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 "shadercompilerjob.h"
-#include "native/assetprocessor.h"
-
-#include
-
-ShaderCompilerJob::ShaderCompilerJob()
- : m_isUnitTesting(false)
- , m_manager(nullptr)
-{
-}
-
-ShaderCompilerJob::~ShaderCompilerJob()
-{
- m_manager = nullptr;
-}
-
-ShaderCompilerRequestMessage ShaderCompilerJob::ShaderCompilerMessage() const
-{
- return m_ShaderCompilerMessage;
-}
-
-void ShaderCompilerJob::initialize(QObject* pManager, const ShaderCompilerRequestMessage& ShaderCompilerMessage)
-{
- m_manager = pManager;
- m_ShaderCompilerMessage = ShaderCompilerMessage;
-}
-
-QString ShaderCompilerJob::getServerAddress()
-{
- if (isServerListEmpty())
- {
- return QString();
- }
-
- QString serverAddress;
- if (!m_ShaderCompilerMessage.serverList.contains(","))
- {
- serverAddress = m_ShaderCompilerMessage.serverList;
- m_ShaderCompilerMessage.serverList.clear();
- return serverAddress;
- }
-
- QStringList serverList = m_ShaderCompilerMessage.serverList.split(",");
- serverAddress = serverList.takeAt(0);
- m_ShaderCompilerMessage.serverList = serverList.join(",");
- return serverAddress;
-}
-
-bool ShaderCompilerJob::isServerListEmpty()
-{
- return m_ShaderCompilerMessage.serverList.isEmpty();
-}
-
-bool ShaderCompilerJob::attemptDelivery(QString serverAddress, QByteArray& payload)
-{
- QTcpSocket socket;
- QString error;
- int waitingTime = 8000; // 8 sec timeout for sending.
- int jobCompileMaxTime = 1000 * 60; // 60 sec timeout for compilation
- if (m_isUnitTesting)
- {
- waitingTime = 500;
- jobCompileMaxTime = 500;
- }
-
- socket.connectToHost(serverAddress, m_ShaderCompilerMessage.serverPort, QIODevice::ReadWrite);
-
- if (socket.waitForConnected(waitingTime))
- {
- qint64 bytesWritten = 0;
- qint64 payloadSize = static_cast(m_ShaderCompilerMessage.originalPayload.size());
- // send payload size to server
- while (bytesWritten != sizeof(qint64))
- {
- qint64 currentWrite = socket.write(reinterpret_cast(&payloadSize) + bytesWritten,
- sizeof(qint64) - bytesWritten);
- if (currentWrite == -1)
- {
- //It is important to note that we are only outputting the error to debugchannel only here because
- //we are forwarding these error messages upstream to the manager,who will take the appropriate action
- error = "Connection Lost:Unable to send data";
- AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
- QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
- return false;
- }
- socket.flush();
- bytesWritten += currentWrite;
- }
- bytesWritten = 0;
- //send actual payload to server
- while (bytesWritten != m_ShaderCompilerMessage.originalPayload.size())
- {
- qint64 currentWrite = socket.write(m_ShaderCompilerMessage.originalPayload.data() + bytesWritten,
- m_ShaderCompilerMessage.originalPayload.size() - bytesWritten);
- if (currentWrite == -1)
- {
- error = "Connection Lost:Unable to send data";
- AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
- QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
- }
- socket.flush();
- bytesWritten += currentWrite;
- }
- }
- else
- {
- error = "Unable to connect to IP Address " + serverAddress;
- AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
- QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
- return false;
- }
-
- unsigned int expectedBytes = sizeof(unsigned int) + sizeof(qint8);
- unsigned int bytesReadTotal = 0;
- unsigned int messageSize = 0;
- bool isMessageSizeKnown = false;
- //read the entire payload
- while ((bytesReadTotal < expectedBytes + messageSize))
- {
- if (socket.bytesAvailable() == 0)
- {
- if (!socket.waitForReadyRead(jobCompileMaxTime))
- {
- error = "Remote IP is taking too long to respond: " + serverAddress;
- AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
- QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
- payload.clear();
- return false;
- }
- }
-
- qint64 bytesAvailable = socket.bytesAvailable();
-
- if (bytesAvailable >= expectedBytes && !isMessageSizeKnown)
- {
- socket.peek(reinterpret_cast(&messageSize), sizeof(unsigned int));
- payload.resize(expectedBytes + messageSize);
- isMessageSizeKnown = true;
- }
-
- if (bytesAvailable > 0)
- {
- qint64 bytesRead = socket.read(payload.data() + bytesReadTotal, bytesAvailable);
-
- if (bytesRead <= 0)
- {
- error = "Connection closed by remote IP Address " + serverAddress;
- AZ_TracePrintf(AssetProcessor::DebugChannel, error.toUtf8().data());
- QMetaObject::invokeMethod(m_manager, "shaderCompilerError", Qt::QueuedConnection, Q_ARG(QString, error), Q_ARG(QString, QDateTime::currentDateTime().toString()), Q_ARG(QString, QString(m_ShaderCompilerMessage.originalPayload)), Q_ARG(QString, serverAddress));
- payload.clear();
- return false;
- }
-
- bytesReadTotal = aznumeric_cast(bytesReadTotal + bytesRead);
- }
- }
-
- return true; // payload successfully send
-}
-
-void ShaderCompilerJob::run()
-{
- QMetaObject::invokeMethod(m_manager, "jobStarted", Qt::QueuedConnection);
- QByteArray payload;
- //until server list is empty, keep trying
- while (!isServerListEmpty())
- {
- QString serverAddress = getServerAddress();
- //attempt to send payload
- if (attemptDelivery(serverAddress, payload))
- {
- break;
- }
- }
- //we are appending request id at the end of every payload,
- //therefore in the case of any errors also
- //we will be sending atleast four bytes to the game
- payload.append(reinterpret_cast(&m_ShaderCompilerMessage.requestId), sizeof(unsigned int));
- QMetaObject::invokeMethod(m_manager, "OnShaderCompilerJobComplete", Qt::QueuedConnection, Q_ARG(QByteArray, payload), Q_ARG(unsigned int, m_ShaderCompilerMessage.requestId));
- QMetaObject::invokeMethod(m_manager, "jobEnded", Qt::QueuedConnection);
-}
-
-
-void ShaderCompilerJob::setIsUnitTesting(bool isUnitTesting)
-{
- m_isUnitTesting = isUnitTesting;
-}
diff --git a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.h b/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.h
deleted file mode 100644
index 1d72365331..0000000000
--- a/Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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
- *
- */
-#ifndef SHADERCOMPILERJOB_H
-#define SHADERCOMPILERJOB_H
-
-#include
-#include "shadercompilerMessages.h"
-
-class QByteArray;
-class QObject;
-
-/**
- * This class is responsible for connecting to the shader compiler server
- * and getting back the response to the shader compiler manager
- */
-class ShaderCompilerJob
- : public QRunnable
-{
-public:
-
- explicit ShaderCompilerJob();
- virtual ~ShaderCompilerJob();
- ShaderCompilerRequestMessage ShaderCompilerMessage() const;
- void initialize(QObject* pManager, const ShaderCompilerRequestMessage& ShaderCompilerMessage);
- QString getServerAddress();
- bool isServerListEmpty();
- virtual void run() override;
-
- void setIsUnitTesting(bool isUnitTesting);
-
- bool attemptDelivery(QString serverAddress, QByteArray& payload);
-
-private:
- ShaderCompilerRequestMessage m_ShaderCompilerMessage;
- QObject* m_manager;
- bool m_isUnitTesting;
-};
-
-#endif // SHADERCOMPILERJOB_H
diff --git a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp
index 5be9436145..e2b28c1260 100644
--- a/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/AssetProcessorTest.cpp
@@ -18,8 +18,6 @@
#include
-AZ_UNIT_TEST_HOOK(new BaseAssetProcessorTestEnvironment)
-
namespace AssetProcessor
{
class UnitTestAppManager : public BatchApplicationManager
@@ -35,7 +33,7 @@ namespace AssetProcessor
{
return false;
}
-
+
// tests which use the builder bus plug in their own mock version, so disconnect ours.
AssetProcessor::AssetBuilderInfoBus::Handler::BusDisconnect();
@@ -59,7 +57,7 @@ namespace AssetProcessor
void SetUp() override
{
AssetProcessorTest::SetUp();
-
+
static int numParams = 1;
static char processName[] = {"AssetProcessorBatch"};
static char* namePtr = &processName[0];
@@ -147,7 +145,7 @@ namespace AssetProcessor
time.start();
actualTest->StartTest();
-
+
while (!testIsComplete)
{
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
diff --git a/Code/Tools/AssetProcessor/native/tests/test_main.cpp b/Code/Tools/AssetProcessor/native/tests/test_main.cpp
index e0cdc8cb3c..69f6e9a6c8 100644
--- a/Code/Tools/AssetProcessor/native/tests/test_main.cpp
+++ b/Code/Tools/AssetProcessor/native/tests/test_main.cpp
@@ -5,76 +5,8 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
-#include "utilities/BatchApplicationManager.h"
-
#include
-#include
#include
-DECLARE_AZ_UNIT_TEST_MAIN()
-
-int RunUnitTests(int argc, char* argv[], bool& ranUnitTests)
-{
- ranUnitTests = true;
-
- INVOKE_AZ_UNIT_TEST_MAIN(nullptr); // nullptr turns off default test environment used to catch stray asserts
-
- // This looks a bit weird, but the macro returns conditionally, so *if* we get here, it means the unit tests didn't run
- ranUnitTests = false;
- return 0;
-}
-
-int main(int argc, char* argv[])
-{
- qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "1");
-
- AZ::Debug::Trace::HandleExceptions(true);
- AZ::Test::ApplyGlobalParameters(&argc, argv);
-
- // If "--unittest" is present on the command line, run unit testing
- // and return immediately. Otherwise, continue as normal.
- AZ::Test::addTestEnvironment(new BaseAssetProcessorTestEnvironment());
-
- bool pauseOnComplete = false;
-
- if (AZ::Test::ContainsParameter(argc, argv, "--pause-on-completion"))
- {
- pauseOnComplete = true;
- }
-
- bool ranUnitTests;
- int result = RunUnitTests(argc, argv, ranUnitTests);
-
- if (ranUnitTests)
- {
- if (pauseOnComplete)
- {
- system("pause");
- }
-
- return result;
- }
-
- BatchApplicationManager applicationManager(&argc, &argv);
- setvbuf(stdout, NULL, _IONBF, 0); // Disabling output buffering to fix test failures due to incomplete logs
-
- ApplicationManager::BeforeRunStatus status = applicationManager.BeforeRun();
-
- if (status != ApplicationManager::BeforeRunStatus::Status_Success)
- {
- if (status == ApplicationManager::BeforeRunStatus::Status_Restarting)
- {
- //AssetProcessor will restart
- return 0;
- }
- else
- {
- //Initialization failed
- return 1;
- }
- }
-
- return applicationManager.Run() ? 0 : 1;
-}
-
+AZ_UNIT_TEST_HOOK(new BaseAssetProcessorTestEnvironment)
diff --git a/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.cpp b/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.cpp
index d8b69a80f5..75823374a0 100644
--- a/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.cpp
+++ b/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.cpp
@@ -29,14 +29,16 @@ namespace AssetProcessor
AssetTreeItem::AssetTreeItem(
AZStd::shared_ptr data,
QIcon errorIcon,
+ QIcon folderIcon,
+ QIcon fileIcon,
AssetTreeItem* parentItem) :
m_data(data),
m_parent(parentItem),
- m_errorIcon(errorIcon), // QIcon is implicitily shared.
- m_folderIcon(QIcon(QStringLiteral(":/Gallery/Asset_Folder.svg"))),
- m_fileIcon(QIcon(QStringLiteral(":/Gallery/Asset_File.svg")))
+ m_errorIcon(errorIcon), // QIcon is implicitly shared.
+ m_folderIcon(folderIcon),
+ m_fileIcon(fileIcon)
{
- m_folderIcon.addFile(QStringLiteral(":/Gallery/Asset_Folder.svg"), QSize(), QIcon::Selected);
+
}
AssetTreeItem::~AssetTreeItem()
@@ -45,7 +47,7 @@ namespace AssetProcessor
AssetTreeItem* AssetTreeItem::CreateChild(AZStd::shared_ptr data)
{
- m_childItems.emplace_back(new AssetTreeItem(data, m_errorIcon, this));
+ m_childItems.emplace_back(new AssetTreeItem(data, m_errorIcon, m_folderIcon, m_fileIcon, this));
return m_childItems.back().get();
}
diff --git a/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.h b/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.h
index c9bc926c34..f36855adfe 100644
--- a/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.h
+++ b/Code/Tools/AssetProcessor/native/ui/AssetTreeItem.h
@@ -50,6 +50,8 @@ namespace AssetProcessor
explicit AssetTreeItem(
AZStd::shared_ptr data,
QIcon errorIcon,
+ QIcon folderIcon,
+ QIcon fileIcon,
AssetTreeItem* parentItem = nullptr);
virtual ~AssetTreeItem();
diff --git a/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.cpp b/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.cpp
index 8d889343f9..e8540c3d8d 100644
--- a/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.cpp
+++ b/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.cpp
@@ -16,9 +16,12 @@ namespace AssetProcessor
AssetTreeModel::AssetTreeModel(AZStd::shared_ptr sharedDbConnection, QObject *parent) :
QAbstractItemModel(parent),
- m_sharedDbConnection(sharedDbConnection),
- m_errorIcon(QStringLiteral(":/stylesheet/img/logging/error.svg"))
+ m_sharedDbConnection(sharedDbConnection)
+ , m_errorIcon(QStringLiteral(":/stylesheet/img/logging/error.svg"))
+ , m_folderIcon(QIcon(QStringLiteral(":/Gallery/Asset_Folder.svg")))
+ , m_fileIcon(QIcon(QStringLiteral(":/Gallery/Asset_File.svg")))
{
+ m_folderIcon.addFile(QStringLiteral(":/Gallery/Asset_Folder.svg"), QSize(), QIcon::Selected);
ApplicationManagerNotifications::Bus::Handler::BusConnect();
AzToolsFramework::AssetDatabase::AssetDatabaseNotificationBus::Handler::BusConnect();
}
@@ -40,7 +43,7 @@ namespace AssetProcessor
void AssetTreeModel::Reset()
{
beginResetModel();
- m_root.reset(new AssetTreeItem(AZStd::make_shared("", "", true, AZ::Uuid::CreateNull()), m_errorIcon));
+ m_root.reset(new AssetTreeItem(AZStd::make_shared("", "", true, AZ::Uuid::CreateNull()), m_errorIcon, m_folderIcon, m_fileIcon));
ResetModel();
diff --git a/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.h b/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.h
index d33bc0bc48..27201c55a3 100644
--- a/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.h
+++ b/Code/Tools/AssetProcessor/native/ui/AssetTreeModel.h
@@ -54,5 +54,7 @@ namespace AssetProcessor
AZStd::shared_ptr m_sharedDbConnection;
QIcon m_errorIcon;
+ QIcon m_folderIcon;
+ QIcon m_fileIcon;
};
}
diff --git a/Code/Tools/AssetProcessor/native/ui/MainWindow.cpp b/Code/Tools/AssetProcessor/native/ui/MainWindow.cpp
index 70b7ceb009..c3674e6431 100644
--- a/Code/Tools/AssetProcessor/native/ui/MainWindow.cpp
+++ b/Code/Tools/AssetProcessor/native/ui/MainWindow.cpp
@@ -37,7 +37,6 @@
#include "../connection/connection.h"
#include "../resourcecompiler/rccontroller.h"
#include "../resourcecompiler/RCJobSortFilterProxyModel.h"
-#include "../shadercompiler/shadercompilerModel.h"
#include
@@ -148,7 +147,6 @@ void MainWindow::Activate()
ui->buttonList->addTab(QStringLiteral("Jobs"));
ui->buttonList->addTab(QStringLiteral("Assets"));
ui->buttonList->addTab(QStringLiteral("Logs"));
- ui->buttonList->addTab(QStringLiteral("Shaders"));
ui->buttonList->addTab(QStringLiteral("Connections"));
ui->buttonList->addTab(QStringLiteral("Tools"));
@@ -317,14 +315,6 @@ void MainWindow::Activate()
connect(ui->jobFilteredSearchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged,
this, writeJobFilterSettings);
- //Shader view
- ui->shaderTreeView->setModel(m_guiApplicationManager->GetShaderCompilerModel());
- ui->shaderTreeView->header()->resizeSection(ShaderCompilerModel::ColumnTimeStamp, 80);
- ui->shaderTreeView->header()->resizeSection(ShaderCompilerModel::ColumnServer, 40);
- ui->shaderTreeView->header()->resizeSection(ShaderCompilerModel::ColumnError, 220);
- ui->shaderTreeView->header()->setSectionResizeMode(ShaderCompilerModel::ColumnError, QHeaderView::Stretch);
- ui->shaderTreeView->header()->setStretchLastSection(false);
-
// Asset view
m_sourceAssetTreeFilterModel = new AssetProcessor::AssetTreeFilterModel(this);
m_sourceModel = new AssetProcessor::SourceAssetTreeModel(m_sharedDbConnection, this);
diff --git a/Code/Tools/AssetProcessor/native/ui/MainWindow.h b/Code/Tools/AssetProcessor/native/ui/MainWindow.h
index 8dc1f3e356..1bec6f982d 100644
--- a/Code/Tools/AssetProcessor/native/ui/MainWindow.h
+++ b/Code/Tools/AssetProcessor/native/ui/MainWindow.h
@@ -65,7 +65,6 @@ public:
Jobs,
Assets,
Logs,
- Shaders,
Connections,
Tools
};
diff --git a/Code/Tools/AssetProcessor/native/ui/MainWindow.ui b/Code/Tools/AssetProcessor/native/ui/MainWindow.ui
index d4f341d2b5..9d90beec13 100644
--- a/Code/Tools/AssetProcessor/native/ui/MainWindow.ui
+++ b/Code/Tools/AssetProcessor/native/ui/MainWindow.ui
@@ -763,59 +763,6 @@
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- Shaders
-
-
-
- -
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
-
-
-
-
diff --git a/Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.cpp b/Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.cpp
deleted file mode 100644
index f8bab90118..0000000000
--- a/Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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 "ShaderCompilerUnitTests.h"
-#include "native/connection/connectionManager.h"
-#include "native/connection/connection.h"
-#include "native/utilities/assetUtils.h"
-
-#define UNIT_TEST_CONNECT_PORT 12125
-
-ShaderCompilerUnitTest::ShaderCompilerUnitTest()
-{
- m_connectionManager = ConnectionManager::Get();
- connect(this, SIGNAL(StartUnitTestForGoodShaderCompiler()), this, SLOT(UnitTestForGoodShaderCompiler()));
- connect(this, SIGNAL(StartUnitTestForFirstBadShaderCompiler()), this, SLOT(UnitTestForFirstBadShaderCompiler()));
- connect(this, SIGNAL(StartUnitTestForSecondBadShaderCompiler()), this, SLOT(UnitTestForSecondBadShaderCompiler()));
- connect(this, SIGNAL(StartUnitTestForThirdBadShaderCompiler()), this, SLOT(UnitTestForThirdBadShaderCompiler()));
- connect(&m_shaderCompilerManager, SIGNAL(sendErrorMessageFromShaderJob(QString, QString, QString, QString)), this, SLOT(ReceiveShaderCompilerErrorMessage(QString, QString, QString, QString)));
-
- m_shaderCompilerManager.setIsUnitTesting(true);
- m_connectionManager->RegisterService(AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), AZStd::bind(&ShaderCompilerManager::process, &m_shaderCompilerManager, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4));
- ContructPayloadForShaderCompilerServer(m_testPayload);
-}
-
-ShaderCompilerUnitTest::~ShaderCompilerUnitTest()
-{
- m_connectionManager->removeConnection(m_connectionId);
-}
-
-void ShaderCompilerUnitTest::ContructPayloadForShaderCompilerServer(QByteArray& payload)
-{
- QString testString = "This is a test string";
- QString testServerList = "127.0.0.3,198.51.100.0,127.0.0.1"; // note - 198.51.100.0 is in the 'test' range that will never be assigned to anyone.
- unsigned int testServerListLength = static_cast(testServerList.size());
- unsigned short testServerPort = 12348;
- unsigned int testRequestId = 1;
- qint64 testStringLength = static_cast(testString.size());
- payload.resize(static_cast(testStringLength));
- memcpy(payload.data(), (testString.toStdString().c_str()), testStringLength);
- unsigned int payloadSize = payload.size();
- payload.resize(payloadSize + 1 + static_cast(testServerListLength) + 1 + sizeof(unsigned short) + sizeof(unsigned int) + sizeof(unsigned int));
- char* dataStart = payload.data() + payloadSize;
- *dataStart = 0;// null
- memcpy(payload.data() + payloadSize + 1, (testServerList.toStdString().c_str()), testServerListLength);
- dataStart += 1 + testServerListLength;
- *dataStart = 0; //null
- memcpy(payload.data() + payloadSize + 1 + testServerListLength + 1, reinterpret_cast(&testServerPort), sizeof(unsigned short));
- memcpy(payload.data() + payloadSize + 1 + testServerListLength + 1 + sizeof(unsigned short), reinterpret_cast(&testServerListLength), sizeof(unsigned int));
- memcpy(payload.data() + payloadSize + 1 + testServerListLength + 1 + sizeof(unsigned short) + sizeof(unsigned int), reinterpret_cast(&testRequestId), sizeof(unsigned int));
-}
-
-void ShaderCompilerUnitTest::StartTest()
-{
- m_connectionId = m_connectionManager->addConnection();
- Connection* connection = m_connectionManager->getConnection(m_connectionId);
- connection->SetPort(UNIT_TEST_CONNECT_PORT);
- connection->SetIpAddress("127.0.0.1");
- connection->SetAutoConnect(true);
- UnitTestForGoodShaderCompiler();
-}
-
-int ShaderCompilerUnitTest::UnitTestPriority() const
-{
- return -4;
-}
-
-void ShaderCompilerUnitTest::UnitTestForGoodShaderCompiler()
-{
- AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'good' shader compiler...\n");
- m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForGoodShaderCompiler, this , AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
- m_server.Init("127.0.0.1", 12348);
- m_server.setServerStatus(UnitTestShaderCompilerServer::GoodServer);
- m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
-}
-
-void ShaderCompilerUnitTest::UnitTestForFirstBadShaderCompiler()
-{
- AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'bad' shader compiler... (Incomplete Payload)\n");
- m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForFirstBadShaderCompiler, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
- m_server.setServerStatus(UnitTestShaderCompilerServer::BadServer_SendsIncompletePayload);
- m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
-}
-
-void ShaderCompilerUnitTest::UnitTestForSecondBadShaderCompiler()
-{
- AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'bad' shader compiler... (Payload followed by disconnection)\n");
- m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForSecondBadShaderCompiler, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
- m_server.setServerStatus(UnitTestShaderCompilerServer::BadServer_ReadsPayloadAndDisconnect);
- m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
-}
-
-void ShaderCompilerUnitTest::UnitTestForThirdBadShaderCompiler()
-{
- AZ_TracePrintf("ShaderCompilerUnitTest", " ... Starting test of 'bad' shader compiler... (Connect but disconnect without data)\n");
- m_shaderCompilerManager.m_sendResponseCallbackFn = AZStd::bind(&ShaderCompilerUnitTest::VerifyPayloadForThirdBadShaderCompiler, this, AZStd::placeholders::_1, AZStd::placeholders::_2, AZStd::placeholders::_3, AZStd::placeholders::_4);
- m_server.setServerStatus(UnitTestShaderCompilerServer::BadServer_DisconnectAfterConnect);
- m_server.startServer();
- m_connectionManager->SendMessageToService(m_connectionId, AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), 0, m_testPayload);
-}
-
-void ShaderCompilerUnitTest::VerifyPayloadForGoodShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
-{
- (void) connId;
- (void) type;
- (void) serial;
- m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
-
- unsigned int messageSize;
- quint8 status;
- QByteArray payloadToCheck;
- unsigned int requestId;
- memcpy((&messageSize), payload.data(), sizeof(unsigned int));
- memcpy((&status), payload.data() + sizeof(unsigned int), sizeof(unsigned char));
- payloadToCheck.resize(messageSize);
- memcpy((payloadToCheck.data()), payload.data() + sizeof(unsigned int) + sizeof(unsigned char), messageSize);
- memcpy((&requestId), payload.data() + sizeof(unsigned int) + sizeof(unsigned char) + messageSize, sizeof(unsigned int));
- QString outgoingTestString = "Test string validated";
- if (QString::compare(QString(payloadToCheck), outgoingTestString, Qt::CaseSensitive) != 0)
- {
- Q_EMIT UnitTestFailed("Unit Test for Good Shader Compiler Failed");
- return;
- }
- Q_EMIT StartUnitTestForFirstBadShaderCompiler();
-}
-
-void ShaderCompilerUnitTest::VerifyPayloadForFirstBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
-{
- (void) connId;
- (void) type;
- (void) serial;
- m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
- QString error = "Remote IP is taking too long to respond: 127.0.0.1";
- if ((payload.size() != 4) || (QString::compare(m_lastShaderCompilerErrorMessage, error, Qt::CaseSensitive) != 0))
- {
- Q_EMIT UnitTestFailed("Unit Test for First Bad Shader Compiler Failed");
- return;
- }
- m_lastShaderCompilerErrorMessage.clear();
- Q_EMIT StartUnitTestForSecondBadShaderCompiler();
-}
-
-void ShaderCompilerUnitTest::VerifyPayloadForSecondBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
-{
- (void) connId;
- (void) type;
- (void) serial;
- m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
- QString error = "Remote IP is taking too long to respond: 127.0.0.1";
- if ((payload.size() != 4) || (QString::compare(m_lastShaderCompilerErrorMessage, error, Qt::CaseSensitive) != 0))
- {
- Q_EMIT UnitTestFailed("Unit Test for Second Bad Shader Compiler Failed");
- return;
- }
- m_lastShaderCompilerErrorMessage.clear();
- Q_EMIT StartUnitTestForThirdBadShaderCompiler();
-}
-
-void ShaderCompilerUnitTest::VerifyPayloadForThirdBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload)
-{
- (void) connId;
- (void) type;
- (void) serial;
- m_shaderCompilerManager.m_sendResponseCallbackFn = nullptr;
- QString error = "Remote IP is taking too long to respond: 127.0.0.1";
- if ((payload.size() != 4) || (QString::compare(m_lastShaderCompilerErrorMessage, error, Qt::CaseSensitive) != 0))
- {
- Q_EMIT UnitTestFailed("Unit Test for Third Bad Shader Compiler Failed");
- return;
- }
- m_lastShaderCompilerErrorMessage.clear();
- Q_EMIT UnitTestPassed();
-}
-
-void ShaderCompilerUnitTest::ReceiveShaderCompilerErrorMessage(QString error, QString server, QString timestamp, QString payload)
-{
- (void) server;
- (void) timestamp;
- (void) payload;
- m_lastShaderCompilerErrorMessage = error;
-}
-
-
-REGISTER_UNIT_TEST(ShaderCompilerUnitTest)
-
diff --git a/Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.h b/Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.h
deleted file mode 100644
index edcff7e949..0000000000
--- a/Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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
- *
- */
-#ifndef SHADERCOMPILERUNITTEST_H
-#define SHADERCOMPILERUNITTEST_H
-
-#if !defined(Q_MOC_RUN)
-#include "UnitTestRunner.h"
-
-#include
-
-#include "native/shadercompiler/shadercompilerManager.h"
-//#include "native/shadercompiler/shadercompilerMessages.h"
-#include "native/utilities/UnitTestShaderCompilerServer.h"
-#include
-#include
-#endif
-
-class ConnectionManager;
-
-class ShaderCompilerManagerForUnitTest : public ShaderCompilerManager
-{
-public:
- explicit ShaderCompilerManagerForUnitTest(QObject* parent = 0) : ShaderCompilerManager(parent) {};
-
- // for this test, we override sendResponse and make it so that it just calls a callback instead of actually sending it to the connection manager.
- void sendResponse(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload) override
- {
- if (m_sendResponseCallbackFn)
- {
- m_sendResponseCallbackFn(connId, type, serial, payload);
- }
- }
- AZStd::function m_sendResponseCallbackFn;
-};
-
-class ShaderCompilerUnitTest
- : public UnitTestRun
-{
- Q_OBJECT
-public:
- ShaderCompilerUnitTest();
- ~ShaderCompilerUnitTest();
- virtual void StartTest() override;
- virtual int UnitTestPriority() const override;
- void ContructPayloadForShaderCompilerServer(QByteArray& payload);
-
-Q_SIGNALS:
- void StartUnitTestForGoodShaderCompiler();
- void StartUnitTestForFirstBadShaderCompiler();
- void StartUnitTestForSecondBadShaderCompiler();
- void StartUnitTestForThirdBadShaderCompiler();
-
-public Q_SLOTS:
- void UnitTestForGoodShaderCompiler();
- void UnitTestForFirstBadShaderCompiler();
- void UnitTestForSecondBadShaderCompiler();
- void UnitTestForThirdBadShaderCompiler();
- void VerifyPayloadForGoodShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
- void VerifyPayloadForFirstBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
- void VerifyPayloadForSecondBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
- void VerifyPayloadForThirdBadShaderCompiler(unsigned int connId, unsigned int type, unsigned int serial, QByteArray payload);
- void ReceiveShaderCompilerErrorMessage(QString error, QString server, QString timestamp, QString payload);
-
-
-private:
- UnitTestShaderCompilerServer m_server;
- ShaderCompilerManagerForUnitTest m_shaderCompilerManager;
- ConnectionManager* m_connectionManager;
- QByteArray m_testPayload;
- QString m_lastShaderCompilerErrorMessage;
- unsigned int m_connectionId = 0;
-};
-
-#endif // SHADERCOMPILERUNITTEST_H
-
-
diff --git a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp
index c3ff22a39e..7652b67db0 100644
--- a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp
+++ b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.cpp
@@ -12,8 +12,6 @@
#include "native/resourcecompiler/rccontroller.h"
#include "native/FileServer/fileServer.h"
#include "native/AssetManager/assetScanner.h"
-#include "native/shadercompiler/shadercompilerManager.h"
-#include "native/shadercompiler/shadercompilerModel.h"
#include
#include
@@ -55,7 +53,7 @@ namespace
{
moduleFileInfo.setFile(executableDirectory);
}
-
+
QDir binaryDir = moduleFileInfo.absoluteDir();
// strip extension
QString applicationBase = moduleFileInfo.completeBaseName();
@@ -70,7 +68,7 @@ namespace
binaryDir.remove(tempFile);
}
}
-
+
}
@@ -145,8 +143,6 @@ void GUIApplicationManager::Destroy()
DestroyIniConfiguration();
DestroyFileServer();
- DestroyShaderCompilerManager();
- DestroyShaderCompilerModel();
}
@@ -192,7 +188,7 @@ bool GUIApplicationManager::Run()
wrapper->enableSaveRestoreGeometry(GetOrganizationName(), GetApplicationName(), "MainWindow", restoreOnFirstShow);
AzQtComponents::StyleManager::setStyleSheet(m_mainWindow, QStringLiteral("style:AssetProcessor.qss"));
-
+
auto refreshStyleSheets = [styleManager]()
{
styleManager->Refresh();
@@ -334,7 +330,7 @@ bool GUIApplicationManager::Run()
m_duringStartup = false;
int resultCode = qApp->exec(); // this blocks until the last window is closed.
-
+
if(!InitiatedShutdown())
{
// if we are here it implies that AP did not stop the Qt event loop and is shutting down prematurely
@@ -427,7 +423,7 @@ bool GUIApplicationManager::OnError(const char* /*window*/, const char* message)
return true;
}
- // If we're the main thread, then consider showing the message box directly.
+ // If we're the main thread, then consider showing the message box directly.
// note that all other threads will PAUSE if they emit a message while the main thread is showing this box
// due to the way the trace system EBUS is mutex-protected.
Qt::ConnectionType connection = Qt::DirectConnection;
@@ -470,7 +466,7 @@ bool GUIApplicationManager::Activate()
AssetUtilities::ComputeProjectCacheRoot(projectCacheRoot);
m_localUserSettings.Load(projectCacheRoot.filePath("AssetProcessorUserSettings.xml").toUtf8().data(), context);
m_localUserSettings.Activate(AZ::UserSettings::CT_LOCAL);
-
+
InitIniConfiguration();
InitFileServer();
@@ -479,9 +475,6 @@ bool GUIApplicationManager::Activate()
{
return false;
}
-
- InitShaderCompilerModel();
- InitShaderCompilerManager();
return true;
}
@@ -606,7 +599,7 @@ void GUIApplicationManager::InitConnectionManager()
QObject::connect(m_fileServer, SIGNAL(AddRenameRequest(unsigned int, bool)), m_connectionManager, SLOT(AddRenameRequest(unsigned int, bool)));
QObject::connect(m_fileServer, SIGNAL(AddFindFileNamesRequest(unsigned int, bool)), m_connectionManager, SLOT(AddFindFileNamesRequest(unsigned int, bool)));
QObject::connect(m_fileServer, SIGNAL(UpdateConnectionMetrics()), m_connectionManager, SLOT(UpdateConnectionMetrics()));
-
+
m_connectionManager->RegisterService(ShowAssetProcessorRequest::MessageType,
std::bind([this](unsigned int /*connId*/, unsigned int /*type*/, unsigned int /*serial*/, QByteArray /*payload*/)
{
@@ -661,40 +654,6 @@ void GUIApplicationManager::DestroyFileServer()
}
}
-void GUIApplicationManager::InitShaderCompilerManager()
-{
- m_shaderCompilerManager = new ShaderCompilerManager();
-
- //Shader compiler stuff
- m_connectionManager->RegisterService(AssetUtilities::ComputeCRC32Lowercase("ShaderCompilerProxyRequest"), std::bind(&ShaderCompilerManager::process, m_shaderCompilerManager, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
- QObject::connect(m_shaderCompilerManager, SIGNAL(sendErrorMessageFromShaderJob(QString, QString, QString, QString)), m_shaderCompilerModel, SLOT(addShaderErrorInfoEntry(QString, QString, QString, QString)));
-
-
-}
-
-void GUIApplicationManager::DestroyShaderCompilerManager()
-{
- if (m_shaderCompilerManager)
- {
- delete m_shaderCompilerManager;
- m_shaderCompilerManager = nullptr;
- }
-}
-
-void GUIApplicationManager::InitShaderCompilerModel()
-{
- m_shaderCompilerModel = new ShaderCompilerModel();
-}
-
-void GUIApplicationManager::DestroyShaderCompilerModel()
-{
- if (m_shaderCompilerModel)
- {
- delete m_shaderCompilerModel;
- m_shaderCompilerModel = nullptr;
- }
-}
-
IniConfiguration* GUIApplicationManager::GetIniConfiguration() const
{
return m_iniConfiguration;
@@ -704,14 +663,6 @@ FileServer* GUIApplicationManager::GetFileServer() const
{
return m_fileServer;
}
-ShaderCompilerManager* GUIApplicationManager::GetShaderCompilerManager() const
-{
- return m_shaderCompilerManager;
-}
-ShaderCompilerModel* GUIApplicationManager::GetShaderCompilerModel() const
-{
- return m_shaderCompilerModel;
-}
void GUIApplicationManager::ShowTrayIconErrorMessage(QString msg)
{
diff --git a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.h b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.h
index 3cf58231f2..c10d841daf 100644
--- a/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.h
+++ b/Code/Tools/AssetProcessor/native/utilities/GUIApplicationManager.h
@@ -24,8 +24,6 @@ class ConnectionManager;
class IniConfiguration;
class ApplicationServer;
class FileServer;
-class ShaderCompilerManager;
-class ShaderCompilerModel;
namespace AssetProcessor
{
@@ -47,8 +45,6 @@ public:
ApplicationManager::BeforeRunStatus BeforeRun() override;
IniConfiguration* GetIniConfiguration() const;
FileServer* GetFileServer() const;
- ShaderCompilerManager* GetShaderCompilerManager() const;
- ShaderCompilerModel* GetShaderCompilerModel() const;
bool Run() override;
////////////////////////////////////////////////////
@@ -72,10 +68,6 @@ private:
void DestroyIniConfiguration();
void InitFileServer();
void DestroyFileServer();
- void InitShaderCompilerManager();
- void DestroyShaderCompilerManager();
- void InitShaderCompilerModel();
- void DestroyShaderCompilerModel();
void Destroy() override;
Q_SIGNALS:
@@ -99,8 +91,7 @@ private:
IniConfiguration* m_iniConfiguration = nullptr;
FileServer* m_fileServer = nullptr;
- ShaderCompilerManager* m_shaderCompilerManager = nullptr;
- ShaderCompilerModel* m_shaderCompilerModel = nullptr;
+
QFileSystemWatcher m_qtFileWatcher;
AZ::UserSettingsProvider m_localUserSettings;
bool m_messageBoxIsVisible = false;
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli
index 421178bb01..4480524468 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/SceneSrgAll.azsli
@@ -13,4 +13,5 @@
#ifdef AZ_COLLECTING_PARTIAL_SRGS
#include
#include
+#include // Temporary until gem partial view srgs can be included automatically.
#endif
diff --git a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli
index 9253885cfc..3e90e1a441 100644
--- a/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli
+++ b/Gems/Atom/Feature/Common/Assets/ShaderResourceGroups/ViewSrgAll.azsli
@@ -12,5 +12,4 @@
#ifdef AZ_COLLECTING_PARTIAL_SRGS
#include
-#include // Temporary until gem partial view srgs can be included automatically.
#endif
diff --git a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp
index b92d538fb5..553133aef7 100644
--- a/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp
@@ -101,6 +101,13 @@ namespace AZ
void EsmShadowmapsPass::UpdateChildren()
{
const RPI::PassAttachmentBinding& inputBinding = GetInputBinding(0);
+
+ if (!inputBinding.m_attachment)
+ {
+ AZ_Assert(false, "[EsmShadowmapsPass %s] requires an input attachment", GetPathName().GetCStr());
+ return;
+ }
+
AZ_Assert(inputBinding.m_attachment->m_descriptor.m_type == RHI::AttachmentType::Image, "[EsmShadowmapsPass %s] input attachment requires an image attachment", GetPathName().GetCStr());
m_shadowmapImageSize = inputBinding.m_attachment->m_descriptor.m_image.m_size;
m_shadowmapArraySize = inputBinding.m_attachment->m_descriptor.m_image.m_arraySize;
diff --git a/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp b/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp
index 7a2827bebe..65bdf655a7 100644
--- a/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp
+++ b/Gems/Atom/Feature/Common/Code/Source/Utils/GpuBufferHandler.cpp
@@ -24,14 +24,14 @@ namespace AZ
{
m_elementSize = descriptor.m_elementSize;
m_elementCount = 0;
-
+
m_bufferIndex = descriptor.m_srgLayout->FindShaderInputBufferIndex(Name(descriptor.m_bufferSrgName));
- AZ_Error(ClassName, m_bufferIndex.IsValid(), "Unable to find %s in view shader resource group.", descriptor.m_bufferSrgName.c_str());
+ AZ_Error(ClassName, m_bufferIndex.IsValid(), "Unable to find %s in %s shader resource group.", descriptor.m_bufferSrgName.c_str(), descriptor.m_srgLayout->GetName().GetCStr());
if (!descriptor.m_elementCountSrgName.empty())
{
m_elementCountIndex = descriptor.m_srgLayout->FindShaderInputConstantIndex(Name(descriptor.m_elementCountSrgName));
- AZ_Error(ClassName, m_elementCountIndex.IsValid(), "Unable to find %s in view shader resource group.", descriptor.m_elementCountSrgName.c_str());
+ AZ_Error(ClassName, m_elementCountIndex.IsValid(), "Unable to find %s in %s shader resource group.", descriptor.m_elementCountSrgName.c_str(), descriptor.m_srgLayout->GetName().GetCStr());
}
if (m_bufferIndex.IsValid())
diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui
index 549e746de6..b375f5e174 100644
--- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui
+++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui
@@ -28,101 +28,105 @@
-
-
-
-
- 1
- 0
-
+
+
+ Qt::Horizontal
-
- QDockWidget::NoDockWidgetFeatures
-
-
- ATL Controls
-
-
-
-
- 4
-
-
- 9
-
-
- 4
-
-
- 9
-
-
-
-
-
- -
-
-
-
- 2
- 1
-
-
-
- QDockWidget::NoDockWidgetFeatures
-
-
- Inspector
-
-
-
-
- 4
-
-
- 9
-
-
- 4
-
-
- 9
-
-
-
-
-
- -
-
-
-
- 1
- 0
-
-
-
+
false
-
- QDockWidget::NoDockWidgetFeatures
-
-
- Audio Middleware Controls
-
-
-
-
- 4
-
-
- 9
-
-
- 4
-
-
- 9
-
-
+
+
+
+ 1
+ 0
+
+
+
+ QDockWidget::NoDockWidgetFeatures
+
+
+ ATL Controls
+
+
+
+
+ 4
+
+
+ 9
+
+
+ 4
+
+
+ 9
+
+
+
+
+
+
+
+ 2
+ 1
+
+
+
+ QDockWidget::NoDockWidgetFeatures
+
+
+ Inspector
+
+
+
+
+ 4
+
+
+ 9
+
+
+ 4
+
+
+ 9
+
+
+
+
+
+
+
+ 1
+ 0
+
+
+
+ false
+
+
+ QDockWidget::NoDockWidgetFeatures
+
+
+ Audio Middleware Controls
+
+
+
+
+ 4
+
+
+ 9
+
+
+ 4
+
+
+ 9
+
+
+
@@ -134,7 +138,7 @@
0
0
972
- 21
+ 26
@@ -166,6 +171,17 @@
Reload
+
+ Ctrl+R
+
+
+
+
+ Refresh Audio System
+
+
+ Ctrl+Shift+R
+
@@ -204,6 +220,22 @@
+
+ actionRefreshAudioSystem
+ triggered()
+ MainWindow
+ RefreshAudioSystem()
+
+
+ -1
+ -1
+
+
+ 485
+ 336
+
+
+
CurrentControlNameChanged(QString)
diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp
index 5f870d06d4..35b1f3f2c8 100644
--- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp
+++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.cpp
@@ -198,6 +198,20 @@ namespace AudioControls
}
}
+ //-------------------------------------------------------------------------------------------//
+ void CAudioControlsEditorWindow::RefreshAudioSystem()
+ {
+ QString sLevelName = GetIEditor()->GetLevelName();
+
+ if (QString::compare(sLevelName, "Untitled", Qt::CaseInsensitive) == 0)
+ {
+ // Rather pass empty QString to indicate that no level is loaded!
+ sLevelName = QString();
+ }
+
+ Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::RefreshAudioSystem, sLevelName.toUtf8().data());
+ }
+
//-------------------------------------------------------------------------------------------//
void CAudioControlsEditorWindow::Save()
{
@@ -215,15 +229,7 @@ namespace AudioControls
messageBox.setWindowTitle("Audio Controls Editor");
if (messageBox.exec() == QMessageBox::Yes)
{
- QString sLevelName = GetIEditor()->GetLevelName();
-
- if (QString::compare(sLevelName, "Untitled", Qt::CaseInsensitive) == 0)
- {
- // Rather pass empty QString to indicate that no level is loaded!
- sLevelName = QString();
- }
-
- Audio::AudioSystemRequestBus::Broadcast(&Audio::AudioSystemRequestBus::Events::RefreshAudioSystem, sLevelName.toUtf8().data());
+ RefreshAudioSystem();
}
}
m_pATLModel->ClearDirtyFlags();
diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h
index 00ffabfbe9..43d0ed8c8e 100644
--- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h
+++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorWindow.h
@@ -61,6 +61,7 @@ namespace AudioControls
void UpdateInspector();
void FilterControlType(EACEControlType type, bool bShow);
void Update();
+ void RefreshAudioSystem();
protected:
void closeEvent(QCloseEvent* pEvent) override;
diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorInstanceCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorInstanceCommands.cpp
index ba25318944..328c905ded 100644
--- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorInstanceCommands.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/ActorInstanceCommands.cpp
@@ -439,6 +439,12 @@ namespace CommandSystem
return false;
}
+ if (actorInstance->GetEntity())
+ {
+ outResult = AZStd::string::format("Cannot remove actor instance. Actor instance %i belongs to an entity.", actorInstanceID);
+ return false;
+ }
+
// store the old values before removing the instance
m_oldPosition = actorInstance->GetLocalSpaceTransform().m_position;
m_oldRotation = actorInstance->GetLocalSpaceTransform().m_rotation;
@@ -618,7 +624,7 @@ namespace CommandSystem
MCore::CommandGroup commandGroup("Remove actor instances", numActorInstances);
AZStd::string tempString;
- // iterate over the selected instances and clone them
+ // iterate over the selected instances and remove them
for (size_t i = 0; i < numActorInstances; ++i)
{
// get the current actor instance
@@ -628,6 +634,18 @@ namespace CommandSystem
continue;
}
+ // Do not remove any runtime instance from the manager using the commands.
+ if (actorInstance->GetIsOwnedByRuntime())
+ {
+ continue;
+ }
+
+ // Do not remove the any instances owned by an entity from the manager using the commands.
+ if (actorInstance->GetEntity())
+ {
+ continue;
+ }
+
tempString = AZStd::string::format("RemoveActorInstance -actorInstanceID %i", actorInstance->GetID());
commandGroup.AddCommandString(tempString.c_str());
}
diff --git a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp
index bd5a14f772..b1a830c838 100644
--- a/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/CommandSystem/Source/AnimGraphCommands.cpp
@@ -455,8 +455,17 @@ namespace CommandSystem
EMotionFX::ActorInstance* actorInstance = nullptr;
if (parameters.CheckIfHasParameter("actorInstanceID"))
{
- const uint32 actorInstanceID = parameters.GetValueAsInt("actorInstanceID", this);
- actorInstance = EMotionFX::GetActorManager().FindActorInstanceByID(actorInstanceID);
+ const int actorInstanceID = parameters.GetValueAsInt("actorInstanceID", this);
+ if (actorInstanceID == -1)
+ {
+ // If there isn't an actorInstanceId, grab the first actor instance.
+ actorInstance = EMotionFX::GetActorManager().GetFirstEditorActorInstance();
+ }
+ else
+ {
+ actorInstance = EMotionFX::GetActorManager().FindActorInstanceByID(actorInstanceID);
+ }
+
if (!actorInstance)
{
outResult = AZStd::string::format("Cannot activate anim graph. Actor instance id '%i' is not valid.", actorInstanceID);
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp
index 184aac6e2c..252fccdb94 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.cpp
@@ -434,6 +434,20 @@ namespace EMotionFX
}
+ ActorInstance* ActorManager::GetFirstEditorActorInstance() const
+ {
+ const size_t numActorInstances = m_actorInstances.size();
+ for (size_t i = 0; i < numActorInstances; ++i)
+ {
+ if (!m_actorInstances[i]->GetIsOwnedByRuntime())
+ {
+ return m_actorInstances[i];
+ }
+ }
+ return nullptr;
+ }
+
+
const AZStd::vector& ActorManager::GetActorInstanceArray() const
{
return m_actorInstances;
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h
index 003153b36c..016edb302e 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/ActorManager.h
@@ -136,6 +136,12 @@ namespace EMotionFX
*/
MCORE_INLINE ActorInstance* GetActorInstance(size_t nr) const { return m_actorInstances[nr]; }
+ /**
+ * Get a given registered actor instance owned by editor (not owned by runtime).
+ * @result A pointer to the actor instance.
+ */
+ ActorInstance* GetFirstEditorActorInstance() const;
+
/**
* Get the array of actor instances.
* @result The const reference to the actor instance array.
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Velocity.cpp b/Gems/EMotionFX/Code/EMotionFX/Source/Velocity.cpp
new file mode 100644
index 0000000000..e02262fa8e
--- /dev/null
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/Velocity.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include
+#include
+
+namespace EMotionFX
+{
+ AZ::Vector3 CalculateLinearVelocity(const AZ::Vector3& lastPosition,
+ const AZ::Vector3& currentPosition,
+ float timeDelta)
+ {
+ if (timeDelta <= AZ::Constants::FloatEpsilon)
+ {
+ return AZ::Vector3::CreateZero();
+ }
+
+ const AZ::Vector3 deltaPosition = currentPosition - lastPosition;
+ const AZ::Vector3 velocity = deltaPosition / timeDelta;
+
+ if (velocity.GetLength() > AZ::Constants::FloatEpsilon)
+ {
+ return velocity;
+ }
+
+ return AZ::Vector3::CreateZero();
+ }
+
+ AZ::Vector3 CalculateAngularVelocity(const AZ::Quaternion& lastRotation,
+ const AZ::Quaternion& currentRotation,
+ float timeDelta)
+ {
+ if (timeDelta <= AZ::Constants::FloatEpsilon)
+ {
+ return AZ::Vector3::CreateZero();
+ }
+
+ const AZ::Quaternion deltaRotation = currentRotation * lastRotation.GetInverseFull();
+ const AZ::Quaternion shortestEquivalent = deltaRotation.GetShortestEquivalent().GetNormalized();
+ const AZ::Vector3 scaledAxisAngle = shortestEquivalent.ConvertToScaledAxisAngle();
+ const AZ::Vector3 angularVelocity = scaledAxisAngle / timeDelta;
+
+ if (angularVelocity.GetLength() > AZ::Constants::FloatEpsilon)
+ {
+ return angularVelocity;
+ }
+
+ return AZ::Vector3::CreateZero();
+ }
+
+ void DebugDrawVelocity(AzFramework::DebugDisplayRequests& debugDisplay,
+ const AZ::Vector3& position, const AZ::Vector3& velocity, const AZ::Color& color)
+ {
+ // Don't visualize joints that remain motionless (zero velocity).
+ if (velocity.GetLength() < AZ::Constants::FloatEpsilon)
+ {
+ return;
+ }
+
+ const float scale = 0.15f;
+ const AZ::Vector3 arrowPosition = position + velocity;
+
+ debugDisplay.DepthTestOff();
+ debugDisplay.SetColor(color);
+
+ debugDisplay.DrawSolidCylinder(/*center=*/(arrowPosition + position) * 0.5f,
+ /*direction=*/(arrowPosition - position).GetNormalizedSafe(),
+ /*radius=*/0.003f,
+ /*height=*/(arrowPosition - position).GetLength(),
+ /*drawShaded=*/false);
+
+ debugDisplay.DrawSolidCone(position + velocity,
+ velocity,
+ 0.1f * scale,
+ scale * 0.5f,
+ /*drawShaded=*/false);
+ }
+} // namespace EMotionFX
diff --git a/Gems/EMotionFX/Code/EMotionFX/Source/Velocity.h b/Gems/EMotionFX/Code/EMotionFX/Source/Velocity.h
new file mode 100644
index 0000000000..a37a888024
--- /dev/null
+++ b/Gems/EMotionFX/Code/EMotionFX/Source/Velocity.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace EMotionFX
+{
+ AZ::Vector3 EMFX_API CalculateLinearVelocity(const AZ::Vector3& lastPosition, const AZ::Vector3& currentPosition, float timeDelta);
+ AZ::Vector3 EMFX_API CalculateAngularVelocity(const AZ::Quaternion& lastRotation, const AZ::Quaternion& currentRotation, float timeDelta);
+
+ void EMFX_API DebugDrawVelocity(AzFramework::DebugDisplayRequests& debugDisplay,
+ const AZ::Vector3& position, const AZ::Vector3& velocity, const AZ::Color& color);
+} // namespace EMotionFX
diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp
index b75fb71060..083d6a9be1 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/MainWindow.cpp
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
@@ -1340,8 +1341,15 @@ namespace EMStudio
// add the load and the create instance commands
commandGroup.AddCommandString(loadActorCommand.c_str());
- commandGroup.AddCommandString("CreateActorInstance -actorID %LASTRESULT%");
+ // Temp solution after we refactor / remove the actor manager.
+ // We only need to create the actor instance by ourselves when openGLRenderPlugin is present.
+ // Atom render viewport will create actor instance along with the actor component.
+ PluginManager* pluginManager = GetPluginManager();
+ if (pluginManager->FindActivePlugin(static_cast(OpenGLRenderPlugin::CLASS_ID)))
+ {
+ commandGroup.AddCommandString("CreateActorInstance -actorID %LASTRESULT%");
+ }
// execute the group command
if (GetCommandManager()->ExecuteCommandGroup(commandGroup, outResult) == false)
diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp
index 2c9e84c9b9..3451855c86 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/RenderPlugin/RenderPlugin.cpp
@@ -429,6 +429,7 @@ namespace EMStudio
// 3. Relink the actor instances with the emstudio actors
const size_t numActorInstances = EMotionFX::GetActorManager().GetNumActorInstances();
+ size_t numActorInstancesInRenderPlugin = 0;
for (size_t i = 0; i < numActorInstances; ++i)
{
EMotionFX::ActorInstance* actorInstance = EMotionFX::GetActorManager().GetActorInstance(i);
@@ -440,6 +441,12 @@ namespace EMStudio
continue;
}
+ if (actorInstance->GetEntity())
+ {
+ continue;
+ }
+
+ numActorInstancesInRenderPlugin++;
if (!emstudioActor)
{
for (EMStudioRenderActor* currentEMStudioActor : m_actors)
@@ -485,6 +492,7 @@ namespace EMStudio
if (found == false)
{
emstudioActor->m_actorInstances.erase(AZStd::next(begin(emstudioActor->m_actorInstances), j));
+ numActorInstancesInRenderPlugin--;
}
else
{
@@ -497,7 +505,7 @@ namespace EMStudio
m_reinitRequested = false;
// zoom the camera to the available character only in case we're dealing with a single instance
- if (resetViewCloseup && numActorInstances == 1)
+ if (resetViewCloseup && numActorInstancesInRenderPlugin == 1)
{
ViewCloseup(false);
}
diff --git a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp
index cada8c7f59..c8ee9be995 100644
--- a/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp
+++ b/Gems/EMotionFX/Code/EMotionFX/Tools/EMotionStudio/EMStudioSDK/Source/Workspace.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
@@ -430,6 +431,18 @@ namespace EMStudio
continue;
}
+ // Temp solution after we refactor / remove the actor manager.
+ // We only need to create the actor instance by ourselves when openGLRenderPlugin is present.
+ // Atom render viewport will create actor instance along with the actor component.
+ PluginManager* pluginManager = GetPluginManager();
+ if (!pluginManager->FindActivePlugin(static_cast(OpenGLRenderPlugin::CLASS_ID)))
+ {
+ if (commands[i].find("CreateActorInstance") == 0)
+ {
+ continue;
+ }
+ }
+
AzFramework::StringFunc::Replace(commands[i], "@products@", assetCacheFolder.c_str());
AzFramework::StringFunc::Replace(commands[i], "@assets@", assetCacheFolder.c_str());
AzFramework::StringFunc::Replace(commands[i], "@root@", assetCacheFolder.c_str());
diff --git a/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake b/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake
index 8135f14f12..52c9d6aa15 100644
--- a/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake
+++ b/Gems/EMotionFX/Code/EMotionFX/emotionfx_files.cmake
@@ -144,6 +144,8 @@ set(FILES
Source/TransformData.h
Source/TriggerActionSetup.cpp
Source/TriggerActionSetup.h
+ Source/Velocity.cpp
+ Source/Velocity.h
Source/VertexAttributeLayer.cpp
Source/VertexAttributeLayer.h
Source/VertexAttributeLayerAbstractData.cpp
diff --git a/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.cpp b/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.cpp
index b59f2b69a0..4b21cde9f6 100644
--- a/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.cpp
+++ b/Gems/EMotionFX/Code/MCore/Source/MCoreCommandManager.cpp
@@ -547,7 +547,12 @@ namespace MCore
}
tmpStr = commandString.substr(lastResultIndex, rightPercentagePos - lastResultIndex + 1);
- AzFramework::StringFunc::Replace(commandString, tmpStr.c_str(), intermediateCommandResults[i - relativeIndex].c_str());
+ AZStd::string replaceStr = intermediateCommandResults[i - relativeIndex];
+ if (replaceStr.empty())
+ {
+ replaceStr = "-1";
+ }
+ AzFramework::StringFunc::Replace(commandString, tmpStr.c_str(), replaceStr.c_str());
replaceHappen = true;
// Search again in case the command group is referring to other results
diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp
index c673e14bce..c12a7ee6bc 100644
--- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp
+++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp
@@ -234,6 +234,10 @@ namespace EMotionFX
const float updateRateInSeconds = animGraphSampleRate > 0.0f ? 1.0f / animGraphSampleRate : 0.0f;
actorInstance->SetMotionSamplingRate(updateRateInSeconds);
}
+ else if (actorInstance->GetMotionSamplingRate() != 0)
+ {
+ actorInstance->SetMotionSamplingRate(0);
+ }
// Disable the automatic mesh LOD level adjustment based on screen space in case a simple LOD component is present.
// The simple LOD component overrides the mesh LOD level and syncs the skeleton with the mesh LOD level.
diff --git a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp
index 0f8ce4c5da..4cea2efcc8 100644
--- a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp
+++ b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp
@@ -249,6 +249,9 @@ namespace FastNoiseGem
void FastNoiseGradientComponent::Activate()
{
+ // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform.
+ GradientSignal::GradientTransformNotificationBus::Handler::BusConnect(GetEntityId());
+
// Some platforms require random seeds to be > 0. Clamp to a positive range to ensure we're always safe.
m_generator.SetSeed(AZ::GetMax(m_configuration.m_seed, 1));
m_generator.SetFrequency(m_configuration.m_frequency);
@@ -272,6 +275,7 @@ namespace FastNoiseGem
{
GradientSignal::GradientRequestBus::Handler::BusDisconnect();
FastNoiseGradientRequestBus::Handler::BusDisconnect();
+ GradientSignal::GradientTransformNotificationBus::Handler::BusDisconnect();
}
bool FastNoiseGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
@@ -294,14 +298,21 @@ namespace FastNoiseGem
return false;
}
+ void FastNoiseGradientComponent::OnGradientTransformChanged(const GradientSignal::GradientTransform& newTransform)
+ {
+ AZStd::unique_lock lock(m_transformMutex);
+ m_gradientTransform = newTransform;
+ }
+
float FastNoiseGradientComponent::GetValue(const GradientSignal::GradientSampleParams& sampleParams) const
{
AZ::Vector3 uvw = sampleParams.m_position;
-
bool wasPointRejected = false;
- const bool shouldNormalizeOutput = false;
- GradientSignal::GradientTransformRequestBus::Event(
- GetEntityId(), &GradientSignal::GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, shouldNormalizeOutput, wasPointRejected);
+
+ {
+ AZStd::shared_lock lock(m_transformMutex);
+ m_gradientTransform.TransformPositionToUVW(sampleParams.m_position, uvw, wasPointRejected);
+ }
if (!wasPointRejected)
{
diff --git a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.h b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.h
index da972743ec..dd19049ee6 100644
--- a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.h
+++ b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.h
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
@@ -67,6 +68,7 @@ namespace FastNoiseGem
: public AZ::Component
, private GradientSignal::GradientRequestBus::Handler
, private FastNoiseGradientRequestBus::Handler
+ , private GradientSignal::GradientTransformNotificationBus::Handler
{
public:
friend class EditorFastNoiseGradientComponent;
@@ -80,23 +82,25 @@ namespace FastNoiseGem
FastNoiseGradientComponent(const FastNoiseGradientConfig& configuration);
FastNoiseGradientComponent() = default;
- //////////////////////////////////////////////////////////////////////////
- // AZ::Component interface implementation
+ // AZ::Component overrides...
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
- //////////////////////////////////////////////////////////////////////////
- // GradientRequestBus
+ // GradientRequestBus overrides...
float GetValue(const GradientSignal::GradientSampleParams& sampleParams) const override;
protected:
FastNoiseGradientConfig m_configuration;
FastNoise m_generator;
+ GradientSignal::GradientTransform m_gradientTransform;
+ mutable AZStd::shared_mutex m_transformMutex;
- /////////////////////////////////////////////////////////////////////////
- // FastNoiseGradientRequest overrides
+ // GradientTransformNotificationBus overrides...
+ void OnGradientTransformChanged(const GradientSignal::GradientTransform& newTransform) override;
+
+ // FastNoiseGradientRequest overrides...
int GetRandomSeed() const override;
void SetRandomSeed(int seed) override;
diff --git a/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp b/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp
index bae1d424c7..f0d5a9d1bd 100644
--- a/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp
+++ b/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp
@@ -46,9 +46,10 @@ public:
////////////////////////////////////////////////////////////////////////////
//// GradientTransformRequestBus
- void TransformPositionToUVW([[maybe_unused]] const AZ::Vector3& inPosition, [[maybe_unused]] AZ::Vector3& outUVW, [[maybe_unused]] const bool shouldNormalizeOutput, [[maybe_unused]] bool& wasPointRejected) const override {}
- void GetGradientLocalBounds([[maybe_unused]] AZ::Aabb& bounds) const override {}
- void GetGradientEncompassingBounds([[maybe_unused]] AZ::Aabb& bounds) const override {}
+ const GradientSignal::GradientTransform& GetGradientTransform() const override
+ {
+ return m_gradientTransform;
+ }
//////////////////////////////////////////////////////////////////////////
// GradientTransformModifierRequestBus
@@ -96,6 +97,8 @@ public:
bool GetAdvancedMode() const override { return false; }
void SetAdvancedMode([[maybe_unused]] bool value) override {}
+
+ GradientSignal::GradientTransform m_gradientTransform;
};
TEST(FastNoiseTest, ComponentsWithComponentApplication)
diff --git a/Gems/GradientSignal/Code/CMakeLists.txt b/Gems/GradientSignal/Code/CMakeLists.txt
index c0b36f4b12..d1ac8cdacf 100644
--- a/Gems/GradientSignal/Code/CMakeLists.txt
+++ b/Gems/GradientSignal/Code/CMakeLists.txt
@@ -136,6 +136,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
+ AZ::AzTestShared
Gem::GradientSignal.Static
Gem::LmbrCentral
Gem::GradientSignal.Mocks
@@ -144,6 +145,12 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
NAME Gem::GradientSignal.Tests
)
+ ly_add_googlebenchmark(
+ NAME Gem::GradientSignal.Benchmarks
+ TARGET Gem::GradientSignal.Tests
+ )
+
+
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME GradientSignal.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
@@ -157,6 +164,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
+ AZ::AzTestShared
Gem::GradientSignal.Static
Gem::GradientSignal.Editor.Static
Gem::LmbrCentral.Editor
diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformModifierRequestBus.h b/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformModifierRequestBus.h
index 34cc8ef685..eba6aad12c 100644
--- a/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformModifierRequestBus.h
+++ b/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformModifierRequestBus.h
@@ -13,14 +13,30 @@
namespace GradientSignal
{
+ //! TransformType describes where the gradient's origin is mapped to.
+ enum class TransformType : AZ::u8
+ {
+ //! The gradient's origin is the world position of this entity.
+ World_ThisEntity = 0,
+ //! The gradient's origin is the local position of this entity, but in world space.
+ //! i.e. If the parent is at (2, 2), and the gradient is at (3,3) in local space, the gradient entity itself will be at (5,5) in
+ //! world space but its origin will frozen at (3,3) in world space, no matter how much the parent moves around.
+ Local_ThisEntity,
+ //! The gradient's origin is the world position of the reference entity.
+ World_ReferenceEntity,
+ //! The gradient's origin is the local position of the reference entity, but in world space.
+ Local_ReferenceEntity,
+ //! The gradient's origin is at (0,0,0) in world space.
+ World_Origin,
+ //! The gradient's origin is in translated world space relative to the reference entity.
+ Relative,
+ };
+
class GradientTransformModifierRequests
: public AZ::ComponentBus
{
public:
- /**
- * Overrides the default AZ::EBusTraits handler policy to allow one
- * listener only.
- */
+ //! Overrides the default AZ::EBusTraits handler policy to allow only one listener.
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual bool GetAllowReference() const = 0;
diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h b/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h
index 426255efe7..3b674d0bd5 100644
--- a/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h
+++ b/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
namespace GradientSignal
{
@@ -22,15 +23,56 @@ namespace GradientSignal
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::EntityId;
- //! allows multiple threads to call shape requests
+ //! allows multiple threads to call gradient transform requests
using MutexType = AZStd::recursive_mutex;
virtual ~GradientTransformRequests() = default;
- virtual void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, const bool shouldNormalizeOutput, bool& wasPointRejected) const = 0;
- virtual void GetGradientLocalBounds(AZ::Aabb& bounds) const = 0;
- virtual void GetGradientEncompassingBounds(AZ::Aabb& bounds) const = 0;
+ //! Get the GradientTransform that's been configured by the bus listener.
+ //! \return the GradientTransform instance that can be used to transform world points into gradient lookup space.
+ virtual const GradientTransform& GetGradientTransform() const = 0;
};
using GradientTransformRequestBus = AZ::EBus;
+
+ /**
+ * Notifies about changes to the GradientTransform configuration
+ */
+ class GradientTransformNotifications
+ : public AZ::EBusTraits
+ {
+ public:
+ ////////////////////////////////////////////////////////////////////////
+ // EBusTraits
+ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+ using BusIdType = AZ::EntityId;
+ using MutexType = AZStd::recursive_mutex;
+ ////////////////////////////////////////////////////////////////////////
+
+ //! Notify listeners that the GradientTransform configuration has changed.
+ //! \return the GradientTransform instance that can be used to transform world points into gradient lookup space.
+ virtual void OnGradientTransformChanged(const GradientTransform& newTransform) = 0;
+
+ //! Connection policy that auto-calls OnGradientTransformChanged on connection with the current GradientTransform data.
+ template
+ struct ConnectionPolicy : public AZ::EBusConnectionPolicy
+ {
+ static void Connect(
+ typename Bus::BusPtr& busPtr,
+ typename Bus::Context& context,
+ typename Bus::HandlerNode& handler,
+ typename Bus::Context::ConnectLockGuard& connectLock,
+ const typename Bus::BusIdType& id = 0)
+ {
+ AZ::EBusConnectionPolicy::Connect(busPtr, context, handler, connectLock, id);
+
+ GradientTransform transform;
+ GradientTransformRequestBus::EventResult(transform, id, &GradientTransformRequests::GetGradientTransform);
+ handler->OnGradientTransformChanged(transform);
+ }
+ };
+ };
+
+ using GradientTransformNotificationBus = AZ::EBus;
+
} //namespace GradientSignal
diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h b/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h
new file mode 100644
index 0000000000..b191ea245a
--- /dev/null
+++ b/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace GradientSignal
+{
+ //! Controls how the gradient repeats itself when queried outside the bounds of the shape.
+ enum class WrappingType : AZ::u8
+ {
+ None = 0, //! Unbounded - the gradient ignores the shape bounds.
+ ClampToEdge, //! The values on the edge of the shape will be extended outward in each direction.
+ Mirror, //! The gradient signal will be repeated but mirrored on every repeat.
+ Repeat, //! The gradient signal will be repeated in every direction.
+ ClampToZero, //! The value will always be 0 outside of the shape.
+ };
+
+ class GradientTransform
+ {
+ public:
+ GradientTransform() = default;
+
+ /**
+ * Create a GradientTransform with the given parameters.
+ * GradientTransform is a utility class that converts world space positions to gradient space UVW values which can be used
+ * to look up deterministic gradient values for the input spatial locations.
+ * \param shapeBounds The bounds of the shape associated with the gradient, in local space.
+ * \param transform The transform to use to convert from world space to gradient space.
+ * \param use3d True for 3D gradient lookup outputs, false for 2D gradient lookup outputs. (i.e. output W will be nonzero or zero)
+ * \param frequencyZoom Amount to scale the UVW results after wrapping is applied.
+ * \param wrappingType The way in which the gradient repeats itself outside the shape bounds.
+ */
+ GradientTransform(
+ const AZ::Aabb& shapeBounds,
+ const AZ::Matrix3x4& transform,
+ bool use3d,
+ float frequencyZoom,
+ GradientSignal::WrappingType wrappingType);
+
+ /**
+ * Checks to see if two GradientTransform instances are equivalent.
+ * Useful for being able to send out notifications when a GradientTransform has changed.
+ * \param rhs The second GradientTranform to compare against.
+ * \return True if they're equal, False if they aren't.
+ */
+ bool operator==(const GradientTransform& rhs) const
+ {
+ return (
+ (m_shapeBounds == rhs.m_shapeBounds) &&
+ (m_inverseTransform == rhs.m_inverseTransform) &&
+ (m_alwaysAcceptPoint == rhs.m_alwaysAcceptPoint) &&
+ (m_frequencyZoom == rhs.m_frequencyZoom) &&
+ (m_wrappingType == rhs.m_wrappingType) &&
+ (m_normalizeExtentsReciprocal == rhs.m_normalizeExtentsReciprocal));
+ }
+
+ /**
+ * Checks to see if two GradientTransform instances aren't equivalent.
+ * Useful for being able to send out notifications when a GradientTransform has changed.
+ * \param rhs The second GradientTranform to compare against.
+ * \return True if they're not equal, False if they are.
+ */
+ bool operator!=(const GradientTransform& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+
+ /**
+ * Transform the given world space position to a gradient space UVW lookup value.
+ * \param inPosition The input world space position to transform.
+ * \param outUVW [out] The UVW value that can be used to look up a deterministic gradient value.
+ * \param wasPointRejected [out] True if the input position doesn't have a gradient value, false if it does.
+ * Most gradients have values mapped to infinite world space, so wasPointRejected will almost always be false.
+ * It will only be true when using ClampToZero and the world space position falls outside the shape bounds.
+ */
+ void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const;
+
+ /**
+ * Transform the given world space position to a gradient space UVW lookup value and normalize to the shape bounds.
+ * "Normalizing" in this context means that regardless of the world space coordinates, (0,0,0) represents the minimum
+ * shape bounds corner, and (1,1,1) represents the maximum shape bounds corner. Depending on the wrapping type, it's possible
+ * (and even likely) to get values outside the 0-1 range.
+ * \param inPosition The input world space position to transform.
+ * \param outUVW [out] The UVW value that can be used to look up a deterministic gradient value.
+ * \param wasPointRejected [out] True if the input position doesn't have a gradient value, false if it does.
+ * Most gradients have values mapped to infinite world space, so wasPointRejected will almost always be false.
+ * It will only be true when using ClampToZero and the world space position falls outside the shape bounds.
+ */
+ void TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const;
+
+ /**
+ * Epsilon value to allow our UVW range to go to [min, max) by using the range [min, max - epsilon].
+ * To keep things behaving consistently between clamped and unbounded uv ranges, we want our clamped uvs to use a
+ * range of [min, max), so we'll actually clamp to [min, max - epsilon]. Since our floating-point numbers are likely in the
+ * -16384 to 16384 range, an epsilon of 0.001 will work without rounding to 0.
+ * (This constant is public so that it can be used from unit tests for validating transformation results)
+ */
+ static constexpr float UvEpsilon = 0.001f;
+
+ private:
+
+ //! These are the various transformations that will be performed, based on wrapping type.
+ static AZ::Vector3 NoTransform(const AZ::Vector3& point, const AZ::Aabb& bounds);
+ static AZ::Vector3 GetUnboundedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
+ static AZ::Vector3 GetClampedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
+ static AZ::Vector3 GetMirroredPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
+ static AZ::Vector3 GetRelativePointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
+ static AZ::Vector3 GetWrappedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
+
+ //! The shape bounds are used for determining the wrapping bounds, and to normalize the UVW results into if requested.
+ AZ::Aabb m_shapeBounds = AZ::Aabb::CreateNull();
+
+ /**
+ * The relative transform to use for converting from world space to gradient space, stored as an inverse transform.
+ * We only ever need to use the inverse transform, so we compute it once and store it instead of keeping the original
+ * transform around. Note that the GradientTransformComponent has many options for choosing which relative space to use
+ * for the transform, so the transform passed in to this class might already have many modifications applied to it.
+ * The inverse transform will also get its 3rd row cleared out if "use3d" is false and we're only performing 2D gradient
+ * transformations, so that the W component of the UVW output will always be 0.
+ */
+ AZ::Matrix3x4 m_inverseTransform = AZ::Matrix3x4::CreateIdentity();
+
+ /**
+ * Whether or not to always accept the input point as a valid output point.
+ * Most of the time, the gradient exists everywhere in world space, so we always accept the input point.
+ * The one exception is ClampToZero, which will return that the point is rejected if it falls outside the shape bounds.
+ */
+ bool m_alwaysAcceptPoint = true;
+
+ //! Apply a scale to the point *after* the wrapping is applied.
+ float m_frequencyZoom = 1.0f;
+
+ //! How the gradient should repeat itself outside of the shape bounds.
+ WrappingType m_wrappingType = WrappingType::None;
+
+ /**
+ * Cached reciprocal for performing an inverse lerp back to shape bounds.
+ * When normalizing the output UVW back into the shape bounds, we perform an inverse lerp. The inverse lerp
+ * equation is (point - min) * (1 / (max-min)), so we save off the (1 / (max-min)) term to avoid recalculating it on every point.
+ */
+ AZ::Vector3 m_normalizeExtentsReciprocal = AZ::Vector3(1.0f);
+ };
+
+} // namespace GradientSignal
diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h b/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h
index f65702c36c..4ffab0b4b4 100644
--- a/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h
+++ b/Gems/GradientSignal/Code/Include/GradientSignal/ImageAsset.h
@@ -35,6 +35,13 @@ namespace GradientSignal
static bool VersionConverter(AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement);
+ ImageAsset() = default;
+
+ ImageAsset(const AZ::Data::AssetId& assetId, AZ::Data::AssetData::AssetStatus status)
+ : AssetData(assetId, status)
+ {
+ }
+
AZ::u32 m_imageWidth = 0;
AZ::u32 m_imageHeight = 0;
AZ::u8 m_bytesPerPixel = 0;
diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/Util.h b/Gems/GradientSignal/Code/Include/GradientSignal/Util.h
index a6dbe118af..fa1791c053 100644
--- a/Gems/GradientSignal/Code/Include/GradientSignal/Util.h
+++ b/Gems/GradientSignal/Code/Include/GradientSignal/Util.h
@@ -13,49 +13,10 @@
#include
#include
#include
+#include
namespace GradientSignal
{
- enum class WrappingType : AZ::u8
- {
- None = 0,
- ClampToEdge,
- Mirror,
- Repeat,
- ClampToZero,
- };
-
- enum class TransformType : AZ::u8
- {
- World_ThisEntity = 0,
- Local_ThisEntity,
- World_ReferenceEntity,
- Local_ReferenceEntity,
- World_Origin,
- Relative,
- };
-
- AZ::Vector3 GetUnboundedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
- AZ::Vector3 GetClampedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
- AZ::Vector3 GetMirroredPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
- AZ::Vector3 GetRelativePointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds);
-
- inline AZ::Vector3 GetWrappedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
- {
- return AZ::Vector3(
- AZ::Wrap(point.GetX(), bounds.GetMin().GetX(), bounds.GetMax().GetX()),
- AZ::Wrap(point.GetY(), bounds.GetMin().GetY(), bounds.GetMax().GetY()),
- AZ::Wrap(point.GetZ(), bounds.GetMin().GetZ(), bounds.GetMax().GetZ()));
- }
-
- inline AZ::Vector3 GetNormalizedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
- {
- return AZ::Vector3(
- AZ::LerpInverse(bounds.GetMin().GetX(), bounds.GetMax().GetX(), point.GetX()),
- AZ::LerpInverse(bounds.GetMin().GetY(), bounds.GetMax().GetY(), point.GetY()),
- AZ::LerpInverse(bounds.GetMin().GetZ(), bounds.GetMax().GetZ(), point.GetZ()));
- }
-
inline void GetObbParamsFromShape(const AZ::EntityId& entity, AZ::Aabb& bounds, AZ::Matrix3x4& worldToBoundsTransform)
{
//get bound and transform data for associated shape
@@ -115,4 +76,5 @@ namespace GradientSignal
return AZ::Lerp(outputMin, outputMax, inputCorrected);
}
+
} // namespace GradientSignal
diff --git a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp
index 57de6f776a..5b2ce4d4cf 100644
--- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp
+++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp
@@ -276,24 +276,29 @@ namespace GradientSignal
void GradientTransformComponent::Activate()
{
+ m_dirty = false;
+ m_gradientTransform = GradientTransform();
+
+ // Update our GradientTransform to be configured correctly. We don't need to notify dependents of the change though.
+ // If anyone is listening, they're already getting notified below.
+ const bool notifyDependentsOfChange = false;
+ UpdateFromShape(notifyDependentsOfChange);
+
GradientTransformRequestBus::Handler::BusConnect(GetEntityId());
LmbrCentral::DependencyNotificationBus::Handler::BusConnect(GetEntityId());
AZ::TickBus::Handler::BusConnect();
GradientTransformModifierRequestBus::Handler::BusConnect(GetEntityId());
- m_dirty = false;
-
m_dependencyMonitor.Reset();
m_dependencyMonitor.ConnectOwner(GetEntityId());
m_dependencyMonitor.ConnectDependency(GetEntityId());
m_dependencyMonitor.ConnectDependency(GetShapeEntityId());
-
- UpdateFromShape();
}
void GradientTransformComponent::Deactivate()
{
m_dirty = false;
+ m_gradientTransform = GradientTransform();
m_dependencyMonitor.Reset();
GradientTransformRequestBus::Handler::BusDisconnect();
@@ -322,66 +327,10 @@ namespace GradientSignal
return false;
}
- void GradientTransformComponent::TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, const bool shouldNormalizeOutput, bool& wasPointRejected) const
+ const GradientTransform& GradientTransformComponent::GetGradientTransform() const
{
AZStd::lock_guard lock(m_cacheMutex);
-
- //transforming coordinate into "local" relative space of shape bounds
- outUVW = m_shapeTransformInverse * inPosition;
-
- if (!m_configuration.m_advancedMode || !m_configuration.m_is3d)
- {
- outUVW.SetZ(0.0f);
- }
-
- wasPointRejected = false;
- if (m_shapeBounds.IsValid())
- {
- //all wrap types and transformations are applied after the coordinate is transformed into shape relative space
- //this allows all calculations to be simplified and done using the shapes untransformed aabb
- //outputting a value that can be used to sample a gradient in its local space
- switch (m_configuration.m_wrappingType)
- {
- default:
- case WrappingType::None:
- outUVW = GetUnboundedPointInAabb(outUVW, m_shapeBounds);
- break;
- case WrappingType::ClampToEdge:
- outUVW = GetClampedPointInAabb(outUVW, m_shapeBounds);
- break;
- case WrappingType::ClampToZero:
- // We don't want to use m_shapeBounds.Contains() here because Contains() is inclusive on all edges.
- // For uv consistency between clamped and unclamped states, we only want to accept uv ranges of [min, max),
- // so we specifically need to exclude the max edges here.
- wasPointRejected = !(outUVW.IsGreaterEqualThan(m_shapeBounds.GetMin()) && outUVW.IsLessThan(m_shapeBounds.GetMax()));
- outUVW = GetClampedPointInAabb(outUVW, m_shapeBounds);
- break;
- case WrappingType::Mirror:
- outUVW = GetMirroredPointInAabb(outUVW, m_shapeBounds);
- break;
- case WrappingType::Repeat:
- outUVW = GetWrappedPointInAabb(outUVW, m_shapeBounds);
- break;
- }
- }
-
- outUVW *= m_configuration.m_frequencyZoom;
-
- if (shouldNormalizeOutput)
- {
- outUVW = GetNormalizedPointInAabb(outUVW, m_shapeBounds);
- }
- }
-
- void GradientTransformComponent::GetGradientLocalBounds(AZ::Aabb& bounds) const
- {
- bounds = m_shapeBounds;
- }
-
- void GradientTransformComponent::GetGradientEncompassingBounds(AZ::Aabb& bounds) const
- {
- bounds = m_shapeBounds;
- bounds.ApplyMatrix3x4(m_shapeTransformInverse.GetInverseFull());
+ return m_gradientTransform;
}
void GradientTransformComponent::OnCompositionChanged()
@@ -393,25 +342,16 @@ namespace GradientSignal
{
if (m_dirty)
{
- const auto configurationOld = m_configuration;
- const auto shapeBoundsOld = m_shapeBounds;
- const auto shapeTransformInverseOld = m_shapeTransformInverse;
+ // Updating on tick to query transform bus on main thread.
+ // Also, if the GradientTransform configuration changes, notify listeners so they can refresh themselves.
+ const bool notifyDependentsOfChange = true;
+ UpdateFromShape(notifyDependentsOfChange);
- //updating on tick to query transform bus on main thread
- UpdateFromShape();
-
- //notify observers if content has changed
- if (configurationOld != m_configuration ||
- shapeBoundsOld != m_shapeBounds ||
- shapeTransformInverseOld != m_shapeTransformInverse)
- {
- LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
- }
m_dirty = false;
}
}
- void GradientTransformComponent::UpdateFromShape()
+ void GradientTransformComponent::UpdateFromShape(bool notifyDependentsOfChange)
{
AZ_PROFILE_FUNCTION(Entity);
@@ -423,6 +363,10 @@ namespace GradientSignal
return;
}
+ const GradientTransform oldGradientTransform = m_gradientTransform;
+ AZ::Aabb shapeBounds = AZ::Aabb::CreateNull();
+ AZ::Matrix3x4 shapeTransformInverse = AZ::Matrix3x4::CreateIdentity();
+
AZ::Transform shapeTransform = AZ::Transform::CreateIdentity();
switch (m_configuration.m_transformType)
{
@@ -466,10 +410,10 @@ namespace GradientSignal
if (!m_configuration.m_advancedMode || !m_configuration.m_overrideBounds)
{
// If we have a shape reference, grab its local space bounds and (inverse) transform into that local space
- GetObbParamsFromShape(shapeReference, m_shapeBounds, m_shapeTransformInverse);
- if (m_shapeBounds.IsValid())
+ GetObbParamsFromShape(shapeReference, shapeBounds, shapeTransformInverse);
+ if (shapeBounds.IsValid())
{
- m_configuration.m_bounds = m_shapeBounds.GetExtents();
+ m_configuration.m_bounds = shapeBounds.GetExtents();
}
}
@@ -491,14 +435,34 @@ namespace GradientSignal
//rebuild bounds from parameters
m_configuration.m_bounds = m_configuration.m_bounds.GetAbs();
- m_shapeBounds = AZ::Aabb::CreateFromMinMax(-m_configuration.m_bounds * 0.5f, m_configuration.m_bounds * 0.5f);
+ shapeBounds = AZ::Aabb::CreateFromMinMax(-m_configuration.m_bounds * 0.5f, m_configuration.m_bounds * 0.5f);
//rebuild transform from parameters
AZ::Matrix3x4 shapeTransformFinal;
shapeTransformFinal.SetFromEulerDegrees(m_configuration.m_rotate);
shapeTransformFinal.SetTranslation(m_configuration.m_translate);
shapeTransformFinal.MultiplyByScale(m_configuration.m_scale);
- m_shapeTransformInverse = shapeTransformFinal.GetInverseFull();
+ shapeTransformInverse = shapeTransformFinal.GetInverseFull();
+
+ // Set everything up on the Gradient Transform
+ const bool use3dGradients = m_configuration.m_advancedMode && m_configuration.m_is3d;
+ m_gradientTransform = GradientTransform(
+ shapeBounds, shapeTransformFinal, use3dGradients, m_configuration.m_frequencyZoom, m_configuration.m_wrappingType);
+
+ // If the transform has changed, send out notifications.
+ if (oldGradientTransform != m_gradientTransform)
+ {
+ // Always notify on the GradientTransformNotificationBus.
+ GradientTransformNotificationBus::Event(
+ GetEntityId(), &GradientTransformNotificationBus::Events::OnGradientTransformChanged, m_gradientTransform);
+
+ // Only notify the DependencyNotificationBus when requested by the caller.
+ if (notifyDependentsOfChange)
+ {
+ LmbrCentral::DependencyNotificationBus::Event(
+ GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
+ }
+ }
}
AZ::EntityId GradientTransformComponent::GetShapeEntityId() const
diff --git a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h
index abaa245f15..805fba9e74 100644
--- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h
+++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h
@@ -101,9 +101,7 @@ namespace GradientSignal
//////////////////////////////////////////////////////////////////////////
// GradientTransformRequestBus
- void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, const bool shouldNormalizeOutput, bool& wasPointRejected) const override;
- void GetGradientLocalBounds(AZ::Aabb& bounds) const override;
- void GetGradientEncompassingBounds(AZ::Aabb& bounds) const override;
+ const GradientTransform& GetGradientTransform() const override;
//////////////////////////////////////////////////////////////////////////
// DependencyNotificationBus
@@ -113,7 +111,7 @@ namespace GradientSignal
// AZ::TickBus::Handler
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
- void UpdateFromShape();
+ void UpdateFromShape(bool notifyDependentsOfChange);
AZ::EntityId GetShapeEntityId() const;
@@ -168,9 +166,8 @@ namespace GradientSignal
private:
mutable AZStd::recursive_mutex m_cacheMutex;
GradientTransformConfig m_configuration;
- AZ::Aabb m_shapeBounds = AZ::Aabb::CreateNull();
- AZ::Matrix3x4 m_shapeTransformInverse = AZ::Matrix3x4::CreateIdentity();
LmbrCentral::DependencyMonitor m_dependencyMonitor;
AZStd::atomic_bool m_dirty{ false };
+ GradientTransform m_gradientTransform;
};
} //namespace GradientSignal
diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp
index 065f0ace0f..4bf709755b 100644
--- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp
+++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp
@@ -129,6 +129,9 @@ namespace GradientSignal
void ImageGradientComponent::Activate()
{
+ // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform.
+ GradientTransformNotificationBus::Handler::BusConnect(GetEntityId());
+
SetupDependencies();
ImageGradientRequestBus::Handler::BusConnect(GetEntityId());
@@ -144,6 +147,7 @@ namespace GradientSignal
AZ::Data::AssetBus::Handler::BusDisconnect();
GradientRequestBus::Handler::BusDisconnect();
ImageGradientRequestBus::Handler::BusDisconnect();
+ GradientTransformNotificationBus::Handler::BusDisconnect();
m_dependencyMonitor.Reset();
@@ -189,19 +193,27 @@ namespace GradientSignal
m_configuration.m_imageAsset = asset;
}
+ void ImageGradientComponent::OnGradientTransformChanged(const GradientTransform& newTransform)
+ {
+ AZStd::unique_lock lock(m_imageMutex);
+ m_gradientTransform = newTransform;
+ }
+
float ImageGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
{
AZ::Vector3 uvw = sampleParams.m_position;
-
bool wasPointRejected = false;
- const bool shouldNormalizeOutput = true;
- GradientTransformRequestBus::Event(
- GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, shouldNormalizeOutput, wasPointRejected);
- if (!wasPointRejected)
{
AZStd::shared_lock imageLock(m_imageMutex);
- return GetValueFromImageAsset(m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
+
+ m_gradientTransform.TransformPositionToUVWNormalized(sampleParams.m_position, uvw, wasPointRejected);
+
+ if (!wasPointRejected)
+ {
+ return GetValueFromImageAsset(
+ m_configuration.m_imageAsset, uvw, m_configuration.m_tilingX, m_configuration.m_tilingY, 0.0f);
+ }
}
return 0.0f;
diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.h b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.h
index e73c8d0c4a..8214436f83 100644
--- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.h
+++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -46,6 +47,7 @@ namespace GradientSignal
, private AZ::Data::AssetBus::Handler
, private GradientRequestBus::Handler
, private ImageGradientRequestBus::Handler
+ , private GradientTransformNotificationBus::Handler
{
public:
template friend class LmbrCentral::EditorWrappedComponentBase;
@@ -59,29 +61,27 @@ namespace GradientSignal
ImageGradientComponent() = default;
~ImageGradientComponent() = default;
- //////////////////////////////////////////////////////////////////////////
- // AZ::Component interface implementation
+ // AZ::Component overrides...
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
- //////////////////////////////////////////////////////////////////////////
- // GradientRequestBus
+ // GradientRequestBus overrides...
float GetValue(const GradientSampleParams& sampleParams) const override;
- //////////////////////////////////////////////////////////////////////////
- // AZ::Data::AssetBus::Handler
+ // AZ::Data::AssetBus overrides...
void OnAssetReady(AZ::Data::Asset asset) override;
void OnAssetMoved(AZ::Data::Asset asset, void* oldDataPointer) override;
void OnAssetReloaded(AZ::Data::Asset asset) override;
protected:
+ // GradientTransformNotificationBus overrides...
+ void OnGradientTransformChanged(const GradientTransform& newTransform) override;
void SetupDependencies();
- //////////////////////////////////////////////////////////////////////////
- // ImageGradientRequestBus
+ // ImageGradientRequestBus overrides...
AZStd::string GetImageAssetPath() const override;
void SetImageAssetPath(const AZStd::string& assetPath) override;
@@ -95,5 +95,6 @@ namespace GradientSignal
ImageGradientConfig m_configuration;
LmbrCentral::DependencyMonitor m_dependencyMonitor;
mutable AZStd::shared_mutex m_imageMutex;
+ GradientTransform m_gradientTransform;
};
}
diff --git a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp
index e150ff4305..8f9d40387b 100644
--- a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp
+++ b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp
@@ -138,6 +138,9 @@ namespace GradientSignal
void PerlinGradientComponent::Activate()
{
+ // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform.
+ GradientTransformNotificationBus::Handler::BusConnect(GetEntityId());
+
m_perlinImprovedNoise.reset(aznew PerlinImprovedNoise(AZ::GetMax(m_configuration.m_randomSeed, 1)));
GradientRequestBus::Handler::BusConnect(GetEntityId());
PerlinGradientRequestBus::Handler::BusConnect(GetEntityId());
@@ -148,6 +151,7 @@ namespace GradientSignal
m_perlinImprovedNoise.reset();
GradientRequestBus::Handler::BusDisconnect();
PerlinGradientRequestBus::Handler::BusDisconnect();
+ GradientTransformNotificationBus::Handler::BusDisconnect();
}
bool PerlinGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
@@ -170,22 +174,29 @@ namespace GradientSignal
return false;
}
+ void PerlinGradientComponent::OnGradientTransformChanged(const GradientTransform& newTransform)
+ {
+ AZStd::unique_lock lock(m_transformMutex);
+ m_gradientTransform = newTransform;
+ }
+
float PerlinGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
{
- AZ_PROFILE_FUNCTION(Entity);
-
if (m_perlinImprovedNoise)
{
AZ::Vector3 uvw = sampleParams.m_position;
-
bool wasPointRejected = false;
- const bool shouldNormalizeOutput = false;
- GradientTransformRequestBus::Event(
- GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, shouldNormalizeOutput, wasPointRejected);
+
+ {
+ AZStd::shared_lock lock(m_transformMutex);
+ m_gradientTransform.TransformPositionToUVW(sampleParams.m_position, uvw, wasPointRejected);
+ }
if (!wasPointRejected)
{
- return m_perlinImprovedNoise->GenerateOctaveNoise(uvw.GetX(), uvw.GetY(), uvw.GetZ(), m_configuration.m_octave, m_configuration.m_amplitude, m_configuration.m_frequency);
+ return m_perlinImprovedNoise->GenerateOctaveNoise(
+ uvw.GetX(), uvw.GetY(), uvw.GetZ(), m_configuration.m_octave, m_configuration.m_amplitude,
+ m_configuration.m_frequency);
}
}
diff --git a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.h b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.h
index 9fda45c716..ef171f5319 100644
--- a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.h
+++ b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.h
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
#include
@@ -47,6 +48,7 @@ namespace GradientSignal
: public AZ::Component
, private GradientRequestBus::Handler
, private PerlinGradientRequestBus::Handler
+ , private GradientTransformNotificationBus::Handler
{
public:
template friend class LmbrCentral::EditorWrappedComponentBase;
@@ -60,23 +62,25 @@ namespace GradientSignal
PerlinGradientComponent() = default;
~PerlinGradientComponent() = default;
- //////////////////////////////////////////////////////////////////////////
- // AZ::Component interface implementation
+ // AZ::Component overrides...
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
- //////////////////////////////////////////////////////////////////////////
- // GradientRequestBus
+ // GradientRequestBus overrides...
float GetValue(const GradientSampleParams& sampleParams) const override;
private:
PerlinGradientConfig m_configuration;
AZStd::unique_ptr m_perlinImprovedNoise;
+ GradientTransform m_gradientTransform;
+ mutable AZStd::shared_mutex m_transformMutex;
- /////////////////////////////////////////////////////////////////////////
- //PerlinGradientRequest overrides
+ // GradientTransformNotificationBus overrides...
+ void OnGradientTransformChanged(const GradientTransform& newTransform) override;
+
+ // PerlinGradientRequestBus overrides...
int GetRandomSeed() const override;
void SetRandomSeed(int seed) override;
diff --git a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp
index d28fe13aff..fc2ccbef70 100644
--- a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp
+++ b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp
@@ -105,6 +105,9 @@ namespace GradientSignal
void RandomGradientComponent::Activate()
{
+ // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform.
+ GradientTransformNotificationBus::Handler::BusConnect(GetEntityId());
+
GradientRequestBus::Handler::BusConnect(GetEntityId());
RandomGradientRequestBus::Handler::BusConnect(GetEntityId());
}
@@ -113,6 +116,7 @@ namespace GradientSignal
{
GradientRequestBus::Handler::BusDisconnect();
RandomGradientRequestBus::Handler::BusDisconnect();
+ GradientTransformNotificationBus::Handler::BusDisconnect();
}
bool RandomGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
@@ -135,16 +139,22 @@ namespace GradientSignal
return false;
}
+ void RandomGradientComponent::OnGradientTransformChanged(const GradientTransform& newTransform)
+ {
+ AZStd::unique_lock lock(m_transformMutex);
+ m_gradientTransform = newTransform;
+ }
+
float RandomGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
{
- AZ_PROFILE_FUNCTION(Entity);
AZ::Vector3 uvw = sampleParams.m_position;
-
bool wasPointRejected = false;
- const bool shouldNormalizeOutput = false;
- GradientTransformRequestBus::Event(
- GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, shouldNormalizeOutput, wasPointRejected);
+
+ {
+ AZStd::shared_lock lock(m_transformMutex);
+ m_gradientTransform.TransformPositionToUVW(sampleParams.m_position, uvw, wasPointRejected);
+ }
if (!wasPointRejected)
{
diff --git a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.h b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.h
index 299b9dadfe..b0dbd964a0 100644
--- a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.h
+++ b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.h
@@ -10,6 +10,7 @@
#include
#include
+#include
#include
namespace LmbrCentral
@@ -38,6 +39,7 @@ namespace GradientSignal
: public AZ::Component
, private GradientRequestBus::Handler
, private RandomGradientRequestBus::Handler
+ , private GradientTransformNotificationBus::Handler
{
public:
template friend class LmbrCentral::EditorWrappedComponentBase;
@@ -51,22 +53,24 @@ namespace GradientSignal
RandomGradientComponent() = default;
~RandomGradientComponent() = default;
- //////////////////////////////////////////////////////////////////////////
- // AZ::Component interface implementation
+ // AZ::Component overrides...
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
- //////////////////////////////////////////////////////////////////////////
- // GradientRequestBus
+ // GradientRequestBus overrides...
float GetValue(const GradientSampleParams& sampleParams) const override;
private:
RandomGradientConfig m_configuration;
+ GradientTransform m_gradientTransform;
+ mutable AZStd::shared_mutex m_transformMutex;
- /////////////////////////////////////////////////////////////////////////
- // RandomGradientRequest overrides
+ // GradientTransformNotificationBus overrides...
+ void OnGradientTransformChanged(const GradientTransform& newTransform) override;
+
+ // RandomGradientRequestBus overrides...
int GetRandomSeed() const override;
void SetRandomSeed(int seed) override;
};
diff --git a/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.cpp b/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.cpp
index 4517c30711..850faf19df 100644
--- a/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.cpp
+++ b/Gems/GradientSignal/Code/Source/Editor/EditorGradientTransformComponent.cpp
@@ -55,9 +55,13 @@ namespace GradientSignal
void EditorGradientTransformComponent::UpdateFromShape()
{
- // Update config from shape on game component, copy that back to our config
- m_component.UpdateFromShape();
- m_component.WriteOutConfig(&m_configuration);
- SetDirty();
+ if (m_runtimeComponentActive)
+ {
+ // Update config from shape on game component, copy that back to our config.
+ bool notifyDependentsOfChange = true;
+ m_component.UpdateFromShape(notifyDependentsOfChange);
+ m_component.WriteOutConfig(&m_configuration);
+ SetDirty();
+ }
}
} //namespace GradientSignal
diff --git a/Gems/GradientSignal/Code/Source/GradientTransform.cpp b/Gems/GradientSignal/Code/Source/GradientTransform.cpp
new file mode 100644
index 0000000000..ffaa8f7e6a
--- /dev/null
+++ b/Gems/GradientSignal/Code/Source/GradientTransform.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include
+#include
+
+
+namespace GradientSignal
+{
+ GradientTransform::GradientTransform(
+ const AZ::Aabb& shapeBounds, const AZ::Matrix3x4& transform, bool use3d,
+ float frequencyZoom, GradientSignal::WrappingType wrappingType)
+ : m_shapeBounds(shapeBounds)
+ , m_inverseTransform(transform.GetInverseFull())
+ , m_frequencyZoom(frequencyZoom)
+ , m_wrappingType(wrappingType)
+ , m_alwaysAcceptPoint(true)
+ {
+ // If we want this to be a 2D gradient lookup, we always want to set the W result in the output to 0.
+ // The easiest / cheapest way to make this happen is just to clear out the third row in the inverseTransform.
+ if (!use3d)
+ {
+ m_inverseTransform.SetRow(2, AZ::Vector4::CreateZero());
+ }
+
+ // If we have invalid shape bounds, reset the wrapping type back to None. Wrapping won't work without valid bounds.
+ if (!m_shapeBounds.IsValid())
+ {
+ m_wrappingType = WrappingType::None;
+ }
+
+ // ClampToZero is the only wrapping type that allows us to return a "pointIsRejected" result for points that fall
+ // outside the shape bounds.
+ if (m_wrappingType == WrappingType::ClampToZero)
+ {
+ m_alwaysAcceptPoint = false;
+ }
+
+ m_normalizeExtentsReciprocal = AZ::Vector3(
+ AZ::IsClose(0.0f, m_shapeBounds.GetXExtent()) ? 0.0f : (1.0f / m_shapeBounds.GetXExtent()),
+ AZ::IsClose(0.0f, m_shapeBounds.GetYExtent()) ? 0.0f : (1.0f / m_shapeBounds.GetYExtent()),
+ AZ::IsClose(0.0f, m_shapeBounds.GetZExtent()) ? 0.0f : (1.0f / m_shapeBounds.GetZExtent()));
+ }
+
+ void GradientTransform::TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const
+ {
+ // Transform coordinate into "local" relative space of shape bounds, and set W to 0 if this is a 2D gradient.
+ outUVW = m_inverseTransform * inPosition;
+
+ // For most wrapping types, we always accept the point, but for ClampToZero we only accept it if it's within
+ // the shape bounds. We don't use m_shapeBounds.Contains() here because Contains() is inclusive on all edges.
+ // For uv consistency between clamped and unclamped states, we only want to accept uv ranges of [min, max),
+ // so we specifically need to exclude the max edges here.
+ bool wasPointAccepted = m_alwaysAcceptPoint ||
+ (outUVW.IsGreaterEqualThan(m_shapeBounds.GetMin()) && outUVW.IsLessThan(m_shapeBounds.GetMax()));
+ wasPointRejected = !wasPointAccepted;
+
+ switch (m_wrappingType)
+ {
+ default:
+ case WrappingType::None:
+ outUVW = GetUnboundedPointInAabb(outUVW, m_shapeBounds);
+ break;
+ case WrappingType::ClampToEdge:
+ outUVW = GetClampedPointInAabb(outUVW, m_shapeBounds);
+ break;
+ case WrappingType::ClampToZero:
+ outUVW = GetClampedPointInAabb(outUVW, m_shapeBounds);
+ break;
+ case WrappingType::Mirror:
+ outUVW = GetMirroredPointInAabb(outUVW, m_shapeBounds);
+ break;
+ case WrappingType::Repeat:
+ outUVW = GetWrappedPointInAabb(outUVW, m_shapeBounds);
+ break;
+ }
+
+ outUVW *= m_frequencyZoom;
+ }
+
+ void GradientTransform::TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const
+ {
+ TransformPositionToUVW(inPosition, outUVW, wasPointRejected);
+
+ // This effectively does AZ::LerpInverse(bounds.GetMin(), bounds.GetMax(), point) if shouldNormalize is true,
+ // and just returns outUVW if shouldNormalize is false.
+ outUVW = m_normalizeExtentsReciprocal * (outUVW - m_shapeBounds.GetMin());
+ }
+
+ AZ::Vector3 GradientTransform::NoTransform(const AZ::Vector3& point, const AZ::Aabb& /*bounds*/)
+ {
+ return point;
+ }
+
+ AZ::Vector3 GradientTransform::GetUnboundedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& /*bounds*/)
+ {
+ return point;
+ }
+
+ AZ::Vector3 GradientTransform::GetClampedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
+ {
+ // We want the clamped sampling states to clamp uvs to the [min, max) range.
+ return point.GetClamp(bounds.GetMin(), bounds.GetMax() - AZ::Vector3(UvEpsilon));
+ }
+
+ AZ::Vector3 GradientTransform::GetWrappedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
+ {
+ return AZ::Vector3(
+ AZ::Wrap(point.GetX(), bounds.GetMin().GetX(), bounds.GetMax().GetX()),
+ AZ::Wrap(point.GetY(), bounds.GetMin().GetY(), bounds.GetMax().GetY()),
+ AZ::Wrap(point.GetZ(), bounds.GetMin().GetZ(), bounds.GetMax().GetZ()));
+ }
+
+ AZ::Vector3 GradientTransform::GetMirroredPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
+ {
+ /* For mirroring, we want to produce the following pattern:
+ * [min, max) : value
+ * [max, min) : max - value - epsilon
+ * [min, max) : value
+ * [max, min) : max - value - epsilon
+ * ...
+ * The epsilon is because we always want to keep our output values in the [min, max) range. We apply the epsilon to all
+ * the mirrored values so that we get consistent spacing between the values.
+ */
+
+ auto GetMirror = [](float value, float min, float max) -> float
+ {
+ // To calculate the mirror value, we move our value into relative space of [0, rangeX2), then use
+ // the first half of the range for our "[min, max)" range, and the second half for our "[max, min)" mirrored range.
+
+ float relativeValue = value - min;
+ float range = max - min;
+ float rangeX2 = range * 2.0f;
+
+ // A positive relativeValue will produce a value of [0, rangeX2) from a single mod, but a negative relativeValue
+ // will produce a value of (-rangeX2, 0]. Adding rangeX2 to the result and taking the mod again puts us back in
+ // the range of [0, rangeX2) for both negative and positive values. This keeps our mirroring pattern consistent and
+ // unbroken across both negative and positive coordinate space.
+ relativeValue = AZ::Mod(AZ::Mod(relativeValue, rangeX2) + rangeX2, rangeX2);
+
+ // [range, rangeX2) is our mirrored range, so flip the value when we're in this range and apply the epsilon so that
+ // we never return the max value, and so that our mirrored values have consistent spacing in the results.
+ if (relativeValue >= range)
+ {
+ relativeValue = rangeX2 - (relativeValue + UvEpsilon);
+ }
+
+ return relativeValue + min;
+ };
+
+ return AZ::Vector3(
+ GetMirror(point.GetX(), bounds.GetMin().GetX(), bounds.GetMax().GetX()),
+ GetMirror(point.GetY(), bounds.GetMin().GetY(), bounds.GetMax().GetY()),
+ GetMirror(point.GetZ(), bounds.GetMin().GetZ(), bounds.GetMax().GetZ()));
+ }
+
+ AZ::Vector3 GradientTransform::GetRelativePointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
+ {
+ return point - bounds.GetMin();
+ }
+}
diff --git a/Gems/GradientSignal/Code/Source/Util.cpp b/Gems/GradientSignal/Code/Source/Util.cpp
deleted file mode 100644
index d9ebf36aec..0000000000
--- a/Gems/GradientSignal/Code/Source/Util.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
-
-
-#include
-#include
-#include
-#include
-#include
-
-
-namespace GradientSignal
-{
- // To keep things behaving consistently between clamped and unbounded uv ranges, we
- // we want our clamped uvs to use a range of [min, max), so we'll actually clamp to
- // [min, max - epsilon]. Since our floating-point numbers are likely in the
- // -16384 to 16384 range, an epsilon of 0.001 will work without rounding to 0.
- static const float uvEpsilon = 0.001f;
-
- AZ::Vector3 GetUnboundedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& /*bounds*/)
- {
- return point;
- }
-
- AZ::Vector3 GetClampedPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
- {
- // We want the clamped sampling states to clamp uvs to the [min, max) range.
- return AZ::Vector3(
- AZ::GetClamp(point.GetX(), bounds.GetMin().GetX(), bounds.GetMax().GetX() - uvEpsilon),
- AZ::GetClamp(point.GetY(), bounds.GetMin().GetY(), bounds.GetMax().GetY() - uvEpsilon),
- AZ::GetClamp(point.GetZ(), bounds.GetMin().GetZ(), bounds.GetMax().GetZ() - uvEpsilon));
- }
-
- float GetMirror(float value, float min, float max)
- {
- float relativeValue = value - min;
- float range = max - min;
- float rangeX2 = range * 2.0f;
- if (relativeValue < 0.0)
- {
- relativeValue = rangeX2 - fmod(-relativeValue, rangeX2);
- }
- else
- {
- relativeValue = fmod(relativeValue, rangeX2);
- }
- if (relativeValue >= range)
- {
- // Since we want our uv range to stay in the [min, max) range,
- // it means that for mirroring, we want both the "forward" values
- // and the "mirrored" values to be in [0, range). We don't want
- // relativeValue == range, so we shift relativeValue by a small epsilon
- // in the mirrored case.
- relativeValue = rangeX2 - (relativeValue + uvEpsilon);
- }
-
- return relativeValue + min;
- }
-
- AZ::Vector3 GetMirroredPointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
- {
- return AZ::Vector3(
- GetMirror(point.GetX(), bounds.GetMin().GetX(), bounds.GetMax().GetX()),
- GetMirror(point.GetY(), bounds.GetMin().GetY(), bounds.GetMax().GetY()),
- GetMirror(point.GetZ(), bounds.GetMin().GetZ(), bounds.GetMax().GetZ()));
- }
-
- AZ::Vector3 GetRelativePointInAabb(const AZ::Vector3& point, const AZ::Aabb& bounds)
- {
- return point - bounds.GetMin();
- }
-}
diff --git a/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp b/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp
index 7e98aa1361..e8bd9f2dff 100644
--- a/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp
+++ b/Gems/GradientSignal/Code/Tests/EditorGradientSignalPreviewTests.cpp
@@ -6,7 +6,7 @@
*
*/
-#include "Tests/GradientSignalTestMocks.h"
+#include
#include
#include
@@ -28,7 +28,6 @@ namespace UnitTest
void SetUp() override
{
GradientSignalTest::SetUp();
- AZ::AllocatorInstance::Create();
// Set up job manager with two threads so that we can run and test the preview job logic.
AZ::JobManagerDesc desc;
@@ -46,7 +45,6 @@ namespace UnitTest
delete m_jobContext;
delete m_jobManager;
- AZ::AllocatorInstance::Destroy();
GradientSignalTest::TearDown();
}
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalBenchmarks.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalBenchmarks.cpp
new file mode 100644
index 0000000000..e780a5d893
--- /dev/null
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalBenchmarks.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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
+ *
+ */
+
+#ifdef HAVE_BENCHMARK
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace UnitTest
+{
+ BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_ImageGradientGetValue)(benchmark::State& state)
+ {
+ // Create the Image Gradient Component with some default sizes and parameters.
+ GradientSignal::ImageGradientConfig config;
+ const uint32_t imageSize = 4096;
+ const int32_t imageSeed = 12345;
+ config.m_imageAsset = ImageAssetMockAssetHandler::CreateImageAsset(imageSize, imageSize, imageSeed);
+ config.m_tilingX = 1.0f;
+ config.m_tilingY = 1.0f;
+ CreateComponent(m_testEntity.get(), config);
+
+ // Create the Gradient Transform Component with some default parameters.
+ GradientSignal::GradientTransformConfig gradientTransformConfig;
+ gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
+ CreateComponent(m_testEntity.get(), gradientTransformConfig);
+
+ // Run the benchmark
+ RunGetValueBenchmark(state);
+ }
+
+ BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_ImageGradientGetValue)
+ ->Args({ 1024, 1024 })
+ ->Args({ 2048, 2048 })
+ ->Args({ 4096, 4096 })
+ ->Unit(::benchmark::kMillisecond);
+
+ BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_PerlinGradientGetValue)(benchmark::State& state)
+ {
+ // Create the Perlin Gradient Component with some default sizes and parameters.
+ GradientSignal::PerlinGradientConfig config;
+ config.m_amplitude = 1.0f;
+ config.m_frequency = 1.1f;
+ config.m_octave = 4;
+ config.m_randomSeed = 12345;
+ CreateComponent(m_testEntity.get(), config);
+
+ // Create the Gradient Transform Component with some default parameters.
+ GradientSignal::GradientTransformConfig gradientTransformConfig;
+ gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
+ CreateComponent(m_testEntity.get(), gradientTransformConfig);
+
+ // Run the benchmark
+ RunGetValueBenchmark(state);
+ }
+
+ BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_PerlinGradientGetValue)
+ ->Args({ 1024, 1024 })
+ ->Args({ 2048, 2048 })
+ ->Args({ 4096, 4096 })
+ ->Unit(::benchmark::kMillisecond);
+
+ BENCHMARK_DEFINE_F(GradientSignalBenchmarkFixture, BM_RandomGradientGetValue)(benchmark::State& state)
+ {
+ // Create the Random Gradient Component with some default parameters.
+ GradientSignal::RandomGradientConfig config;
+ config.m_randomSeed = 12345;
+ CreateComponent(m_testEntity.get(), config);
+
+ // Create the Gradient Transform Component with some default parameters.
+ GradientSignal::GradientTransformConfig gradientTransformConfig;
+ gradientTransformConfig.m_wrappingType = GradientSignal::WrappingType::None;
+ CreateComponent(m_testEntity.get(), gradientTransformConfig);
+
+ // Run the benchmark
+ RunGetValueBenchmark(state);
+ }
+
+ BENCHMARK_REGISTER_F(GradientSignalBenchmarkFixture, BM_RandomGradientGetValue)
+ ->Args({ 1024, 1024 })
+ ->Args({ 2048, 2048 })
+ ->Args({ 4096, 4096 })
+ ->Unit(::benchmark::kMillisecond);
+
+#endif
+
+
+
+
+}
+
+
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp
index 4c22afac76..fa30132836 100644
--- a/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalImageTests.cpp
@@ -7,7 +7,7 @@
*/
-#include "Tests/GradientSignalTestMocks.h"
+#include
#include
#include
@@ -23,69 +23,6 @@ namespace UnitTest
struct GradientSignalImageTestsFixture
: public GradientSignalTest
{
- struct MockAssetHandler
- : public AZ::Data::AssetHandler
- {
- AZ::Data::AssetPtr CreateAsset([[maybe_unused]] const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
- {
- return AZ::Data::AssetPtr();
- }
-
- void DestroyAsset(AZ::Data::AssetPtr ptr) override
- {
- if (ptr)
- {
- delete ptr;
- }
- }
-
- void GetHandledAssetTypes([[maybe_unused]] AZStd::vector& assetTypes) override
- {
- }
-
- AZ::Data::AssetHandler::LoadResult LoadAssetData(
- [[maybe_unused]] const AZ::Data::Asset& asset,
- [[maybe_unused]] AZStd::shared_ptr stream,
- [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
- {
- return AZ::Data::AssetHandler::LoadResult::LoadComplete;
- }
-
- };
-
- MockAssetHandler* m_mockHandler = nullptr;
- GradientSignal::ImageAsset* m_imageData = nullptr;
-
- void SetUp() override
- {
- GradientSignalTest::SetUp();
- AZ::AllocatorInstance::Create();
- AZ::Data::AssetManager::Descriptor desc;
- AZ::Data::AssetManager::Create(desc);
- m_mockHandler = new MockAssetHandler();
- AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid());
- }
-
- void TearDown() override
- {
- AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler);
- delete m_mockHandler; // delete after removing from the asset manager
- AzFramework::LegacyAssetEventBus::ClearQueuedEvents();
- AZ::Data::AssetManager::Destroy();
- AZ::AllocatorInstance::Destroy();
- GradientSignalTest::TearDown();
- }
-
- struct AssignIdToAsset
- : public AZ::Data::AssetData
- {
- void MakeReady()
- {
- m_assetId = AZ::Data::AssetId(AZ::Uuid::CreateRandom());
- m_status.store(AZ::Data::AssetData::AssetStatus::Ready);
- }
- };
-
struct PixelTestSetup
{
// How to create the source image
@@ -105,61 +42,6 @@ namespace UnitTest
static const AZ::Vector2 EndOfList;
};
- AZ::Data::Asset CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
- {
- m_imageData = aznew GradientSignal::ImageAsset();
- m_imageData->m_imageWidth = width;
- m_imageData->m_imageHeight = height;
- m_imageData->m_bytesPerPixel = 1;
- m_imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
-
- size_t value = 0;
- AZStd::hash_combine(value, seed);
-
- for (AZ::u32 x = 0; x < width; ++x)
- {
- for (AZ::u32 y = 0; y < height; ++y)
- {
- AZStd::hash_combine(value, x);
- AZStd::hash_combine(value, y);
- m_imageData->m_imageData.push_back(static_cast(value));
- }
- }
-
- reinterpret_cast(m_imageData)->MakeReady();
- return AZ::Data::Asset(m_imageData, AZ::Data::AssetLoadBehavior::Default);
- }
-
- AZ::Data::Asset CreateSpecificPixelImageAsset(AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
- {
- m_imageData = aznew GradientSignal::ImageAsset();
- m_imageData->m_imageWidth = width;
- m_imageData->m_imageHeight = height;
- m_imageData->m_bytesPerPixel = 1;
- m_imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
-
- const AZ::u8 pixelValue = 255;
-
- // Image data should be stored inverted on the y axis relative to our engine, so loop backwards through y.
- for (int y = static_cast(height) - 1; y >= 0; --y)
- {
- for (AZ::u32 x = 0; x < width; ++x)
- {
- if ((x == static_cast(pixelX)) && (y == static_cast(pixelY)))
- {
- m_imageData->m_imageData.push_back(pixelValue);
- }
- else
- {
- m_imageData->m_imageData.push_back(0);
- }
- }
- }
-
- reinterpret_cast(m_imageData)->MakeReady();
- return AZ::Data::Asset(m_imageData, AZ::Data::AssetLoadBehavior::Default);
- }
-
void TestPixels(GradientSignal::GradientSampler& sampler, AZ::u32 width, AZ::u32 height, float stepSize, const AZStd::vector& expectedPoints)
{
AZStd::vector foundPoints;
@@ -203,7 +85,8 @@ namespace UnitTest
// Create the Image Gradient Component.
GradientSignal::ImageGradientConfig config;
- config.m_imageAsset = CreateSpecificPixelImageAsset(test.m_imageSize, test.m_imageSize, static_cast(test.m_pixel.GetX()), static_cast(test.m_pixel.GetY()));
+ config.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
+ test.m_imageSize, test.m_imageSize, static_cast(test.m_pixel.GetX()), static_cast(test.m_pixel.GetY()));
config.m_tilingX = test.m_tiling;
config.m_tilingY = test.m_tiling;
CreateComponent(entity.get(), config);
@@ -495,7 +378,7 @@ namespace UnitTest
// Create an ImageGradient with a 3x3 asset with the center pixel set.
GradientSignal::ImageGradientConfig gradientConfig;
- gradientConfig.m_imageAsset = CreateSpecificPixelImageAsset(3, 3, 1, 1);
+ gradientConfig.m_imageAsset = ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(3, 3, 1, 1);
CreateComponent(entity.get(), gradientConfig);
// Create the test GradientTransform
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp
index 25a1aa28bb..ec22fbc1ec 100644
--- a/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalReferencesTests.cpp
@@ -10,7 +10,7 @@
#include
#include
#include
-#include "Tests/GradientSignalTestMocks.h"
+#include
#include
#include
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp
index b8c0f85cf3..dc11d21b11 100644
--- a/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalServicesTests.cpp
@@ -6,7 +6,7 @@
*
*/
-#include "Tests/GradientSignalTestMocks.h"
+#include
#include
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp
index 809bbf2fe6..a0c8f5cf50 100644
--- a/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalSurfaceTests.cpp
@@ -9,7 +9,7 @@
#include
#include
#include
-#include "Tests/GradientSignalTestMocks.h"
+#include
#include
#include
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp
index 78aa4f1ac5..84b2daab84 100644
--- a/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalTest.cpp
@@ -7,7 +7,7 @@
*/
-#include "Tests/GradientSignalTestMocks.h"
+#include
#include
#include
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp
new file mode 100644
index 0000000000..bc9b39aae8
--- /dev/null
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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
+
+namespace UnitTest
+{
+ void GradientSignalBaseFixture::SetupCoreSystems()
+ {
+ m_app = AZStd::make_unique();
+ ASSERT_TRUE(m_app != nullptr);
+
+ AZ::ComponentApplication::Descriptor componentAppDesc;
+
+ m_systemEntity = m_app->Create(componentAppDesc);
+ ASSERT_TRUE(m_systemEntity != nullptr);
+ m_app->AddEntity(m_systemEntity);
+
+ AZ::AllocatorInstance::Create();
+ AZ::Data::AssetManager::Descriptor desc;
+ AZ::Data::AssetManager::Create(desc);
+ m_mockHandler = new ImageAssetMockAssetHandler();
+ AZ::Data::AssetManager::Instance().RegisterHandler(m_mockHandler, azrtti_typeid());
+ }
+
+ void GradientSignalBaseFixture::TearDownCoreSystems()
+ {
+ AZ::Data::AssetManager::Instance().UnregisterHandler(m_mockHandler);
+ delete m_mockHandler; // delete after removing from the asset manager
+ AzFramework::LegacyAssetEventBus::ClearQueuedEvents();
+ AZ::Data::AssetManager::Destroy();
+ AZ::AllocatorInstance::Destroy();
+
+ m_app->Destroy();
+ m_app.reset();
+ m_systemEntity = nullptr;
+ }
+
+ void GradientSignalTest::TestFixedDataSampler(const AZStd::vector& expectedOutput, int size, AZ::EntityId gradientEntityId)
+ {
+ GradientSignal::GradientSampler gradientSampler;
+ gradientSampler.m_gradientId = gradientEntityId;
+
+ for (int y = 0; y < size; ++y)
+ {
+ for (int x = 0; x < size; ++x)
+ {
+ GradientSignal::GradientSampleParams params;
+ params.m_position = AZ::Vector3(static_cast(x), static_cast(y), 0.0f);
+
+ const int index = y * size + x;
+ float actualValue = gradientSampler.GetValue(params);
+ float expectedValue = expectedOutput[index];
+
+ EXPECT_NEAR(actualValue, expectedValue, 0.01f);
+ }
+ }
+ }
+
+#ifdef HAVE_BENCHMARK
+ void GradientSignalBenchmarkFixture::CreateTestEntity(float shapeHalfBounds)
+ {
+ // Create the base entity
+ m_testEntity = CreateEntity();
+
+ // Create a mock Shape component that describes the bounds that we're using to map our gradient into world space.
+ CreateComponent(m_testEntity.get());
+ MockShapeComponentHandler mockShapeHandler(m_testEntity->GetId());
+ mockShapeHandler.m_GetLocalBounds = AZ::Aabb::CreateCenterRadius(AZ::Vector3(shapeHalfBounds), shapeHalfBounds);
+
+ // Create a mock Transform component that locates our gradient in the center of our desired mock Shape.
+ MockTransformHandler mockTransformHandler;
+ mockTransformHandler.m_GetLocalTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds));
+ mockTransformHandler.m_GetWorldTMOutput = AZ::Transform::CreateTranslation(AZ::Vector3(shapeHalfBounds));
+ mockTransformHandler.BusConnect(m_testEntity->GetId());
+ }
+
+ void GradientSignalBenchmarkFixture::DestroyTestEntity()
+ {
+ m_testEntity.reset();
+ }
+
+ void GradientSignalBenchmarkFixture::RunGetValueBenchmark(benchmark::State& state)
+ {
+ // All components are created, so activate the entity
+ ActivateEntity(m_testEntity.get());
+
+ // Create a gradient sampler and run through a series of points to see if they match expectations.
+ GradientSignal::GradientSampler gradientSampler;
+ gradientSampler.m_gradientId = m_testEntity->GetId();
+
+ // Get the height and width ranges for querying from our benchmark parameters
+ float height = aznumeric_cast(state.range(0));
+ float width = aznumeric_cast(state.range(1));
+
+ // Call GetValue() for every height and width in our ranges.
+ for (auto _ : state)
+ {
+ for (float y = 0.0f; y < height; y += 1.0f)
+ {
+ for (float x = 0.0f; x < width; x += 1.0f)
+ {
+ GradientSignal::GradientSampleParams params;
+ params.m_position = AZ::Vector3(x, y, 0.0f);
+ float value = gradientSampler.GetValue(params);
+ benchmark::DoNotOptimize(value);
+ }
+ }
+ }
+ }
+
+#endif
+
+}
+
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h
new file mode 100644
index 0000000000..5e05cba374
--- /dev/null
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include
+
+namespace UnitTest
+{
+ // Base test fixture used for GradientSignal unit tests and benchmark tests
+ class GradientSignalBaseFixture
+ {
+ public:
+ void SetupCoreSystems();
+ void TearDownCoreSystems();
+
+ AZStd::unique_ptr CreateEntity()
+ {
+ return AZStd::make_unique();
+ }
+
+ void ActivateEntity(AZ::Entity* entity)
+ {
+ entity->Init();
+ entity->Activate();
+ }
+
+ template
+ AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
+ {
+ m_app->RegisterComponentDescriptor(Component::CreateDescriptor());
+ return entity->CreateComponent(config);
+ }
+
+ template
+ AZ::Component* CreateComponent(AZ::Entity* entity)
+ {
+ m_app->RegisterComponentDescriptor(Component::CreateDescriptor());
+ return entity->CreateComponent();
+ }
+
+ AZStd::unique_ptr m_app;
+ AZ::Entity* m_systemEntity = nullptr;
+ ImageAssetMockAssetHandler* m_mockHandler = nullptr;
+ };
+
+ struct GradientSignalTest
+ : public GradientSignalBaseFixture
+ , public UnitTest::AllocatorsTestFixture
+ {
+ protected:
+ void SetUp() override
+ {
+ UnitTest::AllocatorsTestFixture::SetUp();
+ SetupCoreSystems();
+ }
+
+ void TearDown() override
+ {
+ TearDownCoreSystems();
+ UnitTest::AllocatorsTestFixture::TearDown();
+ }
+
+ void TestFixedDataSampler(const AZStd::vector& expectedOutput, int size, AZ::EntityId gradientEntityId);
+ };
+
+#ifdef HAVE_BENCHMARK
+ class GradientSignalBenchmarkFixture
+ : public GradientSignalBaseFixture
+ , public UnitTest::AllocatorsBenchmarkFixture
+ , public UnitTest::TraceBusRedirector
+ {
+ public:
+ void internalSetUp(const benchmark::State& state)
+ {
+ AZ::Debug::TraceMessageBus::Handler::BusConnect();
+ UnitTest::AllocatorsBenchmarkFixture::SetUp(state);
+ SetupCoreSystems();
+
+ // Create a default test entity with bounds of 256 m x 256 m x 256 m.
+ const float shapeHalfBounds = 128.0f;
+ CreateTestEntity(shapeHalfBounds);
+ }
+
+ void internalTearDown(const benchmark::State& state)
+ {
+ DestroyTestEntity();
+ TearDownCoreSystems();
+ UnitTest::AllocatorsBenchmarkFixture::TearDown(state);
+ AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
+ }
+
+ void CreateTestEntity(float shapeHalfBounds);
+ void DestroyTestEntity();
+
+ void RunGetValueBenchmark(benchmark::State& state);
+
+ protected:
+ void SetUp(const benchmark::State& state) override
+ {
+ internalSetUp(state);
+ }
+ void SetUp(benchmark::State& state) override
+ {
+ internalSetUp(state);
+ }
+
+ void TearDown(const benchmark::State& state) override
+ {
+ internalTearDown(state);
+ }
+ void TearDown(benchmark::State& state) override
+ {
+ internalTearDown(state);
+ }
+
+ AZStd::unique_ptr m_testEntity;
+ };
+#endif
+}
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp
new file mode 100644
index 0000000000..ccbe17dee8
--- /dev/null
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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
+
+namespace UnitTest
+{
+ AZ::Data::Asset ImageAssetMockAssetHandler::CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed)
+ {
+ GradientSignal::ImageAsset* imageData =
+ aznew GradientSignal::ImageAsset(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetData::AssetStatus::Ready);
+ imageData->m_imageWidth = width;
+ imageData->m_imageHeight = height;
+ imageData->m_bytesPerPixel = 1;
+ imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
+ imageData->m_imageData.reserve(width * height);
+
+ size_t value = 0;
+ AZStd::hash_combine(value, seed);
+
+ for (AZ::u32 x = 0; x < width; ++x)
+ {
+ for (AZ::u32 y = 0; y < height; ++y)
+ {
+ AZStd::hash_combine(value, x);
+ AZStd::hash_combine(value, y);
+ imageData->m_imageData.push_back(static_cast(value));
+ }
+ }
+
+ return AZ::Data::Asset(imageData, AZ::Data::AssetLoadBehavior::Default);
+ }
+
+ AZ::Data::Asset ImageAssetMockAssetHandler::CreateSpecificPixelImageAsset(
+ AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY)
+ {
+ GradientSignal::ImageAsset* imageData =
+ aznew GradientSignal::ImageAsset(AZ::Data::AssetId(AZ::Uuid::CreateRandom()), AZ::Data::AssetData::AssetStatus::Ready);
+ imageData->m_imageWidth = width;
+ imageData->m_imageHeight = height;
+ imageData->m_bytesPerPixel = 1;
+ imageData->m_imageFormat = ImageProcessingAtom::EPixelFormat::ePixelFormat_R8;
+ imageData->m_imageData.reserve(width * height);
+
+ const AZ::u8 pixelValue = 255;
+
+ // Image data should be stored inverted on the y axis relative to our engine, so loop backwards through y.
+ for (int y = static_cast(height) - 1; y >= 0; --y)
+ {
+ for (AZ::u32 x = 0; x < width; ++x)
+ {
+ if ((x == static_cast(pixelX)) && (y == static_cast(pixelY)))
+ {
+ imageData->m_imageData.push_back(pixelValue);
+ }
+ else
+ {
+ imageData->m_imageData.push_back(0);
+ }
+ }
+ }
+
+ return AZ::Data::Asset(imageData, AZ::Data::AssetLoadBehavior::Default);
+ }
+}
+
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h
index 9c0d8be588..1d83a5eb55 100644
--- a/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.h
@@ -8,90 +8,69 @@
#pragma once
#include
-#include
+#include
+#include
#include
#include
#include
+#include
+#include
+#include
#include
#include
#include
+#include
#include
#include
-#include
-
#include
namespace UnitTest
{
- struct GradientSignalTest
- : public ::testing::Test
+ // Mock asset handler for GradientSignal::ImageAsset that we can use in unit tests to pretend to load an image asset with.
+ // Also includes utility functions for creating image assets with specific testable patterns.
+ struct ImageAssetMockAssetHandler : public AZ::Data::AssetHandler
{
- protected:
- AZ::ComponentApplication m_app;
- AZ::Entity* m_systemEntity = nullptr;
+ //! Creates a deterministically random set of pixel data as an ImageAsset.
+ //! \param width The width of the ImageAsset
+ //! \param height The height of the ImageAsset
+ //! \param seed The random seed to use for generating the random data
+ //! \return The ImageAsset in a loaded ready state
+ static AZ::Data::Asset CreateImageAsset(AZ::u32 width, AZ::u32 height, AZ::s32 seed);
- void SetUp() override
+ //! Creates an ImageAsset where all the pixels are 0 except for the one pixel at the given coordinates, which is set to 1.
+ //! \param width The width of the ImageAsset
+ //! \param height The height of the ImageAsset
+ //! \param pixelX The X coordinate of the pixel to set to 1
+ //! \param pixelY The Y coordinate of the pixel to set to 1
+ //! \return The ImageAsset in a loaded ready state
+ static AZ::Data::Asset CreateSpecificPixelImageAsset(
+ AZ::u32 width, AZ::u32 height, AZ::u32 pixelX, AZ::u32 pixelY);
+
+ AZ::Data::AssetPtr CreateAsset(
+ [[maybe_unused]] const AZ::Data::AssetId& id, [[maybe_unused]] const AZ::Data::AssetType& type) override
{
- AZ::ComponentApplication::Descriptor appDesc;
- appDesc.m_memoryBlocksByteSize = 128 * 1024 * 1024;
- m_systemEntity = m_app.Create(appDesc);
- m_app.AddEntity(m_systemEntity);
+ return AZ::Data::AssetPtr();
}
- void TearDown() override
+ void DestroyAsset(AZ::Data::AssetPtr ptr) override
{
- m_app.Destroy();
- m_systemEntity = nullptr;
- }
-
- void TestFixedDataSampler(const AZStd::vector& expectedOutput, int size, AZ::EntityId gradientEntityId)
- {
- GradientSignal::GradientSampler gradientSampler;
- gradientSampler.m_gradientId = gradientEntityId;
-
- for(int y = 0; y < size; ++y)
+ if (ptr)
{
- for (int x = 0; x < size; ++x)
- {
- GradientSignal::GradientSampleParams params;
- params.m_position = AZ::Vector3(static_cast(x), static_cast(y), 0.0f);
-
- const int index = y * size + x;
- float actualValue = gradientSampler.GetValue(params);
- float expectedValue = expectedOutput[index];
-
- EXPECT_NEAR(actualValue, expectedValue, 0.01f);
- }
+ delete ptr;
}
}
- AZStd::unique_ptr CreateEntity()
+ void GetHandledAssetTypes([[maybe_unused]] AZStd::vector& assetTypes) override
{
- return AZStd::make_unique();
}
- void ActivateEntity(AZ::Entity* entity)
+ AZ::Data::AssetHandler::LoadResult LoadAssetData(
+ [[maybe_unused]] const AZ::Data::Asset& asset,
+ [[maybe_unused]] AZStd::shared_ptr stream,
+ [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB) override
{
- entity->Init();
- EXPECT_EQ(AZ::Entity::State::Init, entity->GetState());
-
- entity->Activate();
- EXPECT_EQ(AZ::Entity::State::Active, entity->GetState());
- }
-
- template
- AZ::Component* CreateComponent(AZ::Entity* entity, const Configuration& config)
- {
- m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
- return entity->CreateComponent(config);
- }
-
- template
- AZ::Component* CreateComponent(AZ::Entity* entity)
- {
- m_app.RegisterComponentDescriptor(Component::CreateDescriptor());
- return entity->CreateComponent();
+ return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
};
diff --git a/Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp
new file mode 100644
index 0000000000..ddebc0ce1f
--- /dev/null
+++ b/Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace UnitTest
+{
+ struct GradientSignalTransformTestsFixture : public GradientSignalTest
+ {
+ // By default, we'll use a shape half extents of (5, 10, 20) for every test, and a world translation of (100, 200, 300).
+ struct GradientTransformSetupData
+ {
+ GradientSignal::WrappingType m_wrappingType{ GradientSignal::WrappingType::None };
+ AZ::Vector3 m_shapeHalfExtents{ 5.0f, 10.0f, 20.0f };
+ AZ::Vector3 m_worldTranslation{ 100.0f, 200.0f, 300.0f };
+ float m_frequencyZoom{ 1.0f };
+ };
+
+ struct GradientTransformTestData
+ {
+ AZ::Vector3 m_positionToTest;
+ AZ::Vector3 m_expectedOutputUVW;
+ bool m_expectedOutputRejectionResult;
+ };
+
+ static constexpr float UvEpsilon = GradientSignal::GradientTransform::UvEpsilon;
+
+ void TestGradientTransform(const GradientTransformSetupData& setup, const GradientTransformTestData& test)
+ {
+ AZ::Aabb shapeBounds = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3::CreateZero(), setup.m_shapeHalfExtents);
+ AZ::Matrix3x4 transform = AZ::Matrix3x4::CreateTranslation(setup.m_worldTranslation);
+ float frequencyZoom = setup.m_frequencyZoom;
+ GradientSignal::WrappingType wrappingType = setup.m_wrappingType;
+
+ AZ::Vector3 outUVW;
+ bool wasPointRejected;
+
+ // Perform the query with a 3D gradient and verify that the results match expectations.
+ GradientSignal::GradientTransform gradientTransform3d(shapeBounds, transform, true, frequencyZoom, wrappingType);
+ gradientTransform3d.TransformPositionToUVW(test.m_positionToTest, outUVW, wasPointRejected);
+ EXPECT_THAT(outUVW, IsClose(test.m_expectedOutputUVW));
+ EXPECT_EQ(wasPointRejected, test.m_expectedOutputRejectionResult);
+
+ // Perform the query with a 2D gradient and verify that the results match, but always returns a W value of 0.
+ GradientSignal::GradientTransform gradientTransform2d(shapeBounds, transform, false, frequencyZoom, wrappingType);
+ gradientTransform2d.TransformPositionToUVW(test.m_positionToTest, outUVW, wasPointRejected);
+ EXPECT_THAT(outUVW, IsClose(AZ::Vector3(test.m_expectedOutputUVW.GetX(), test.m_expectedOutputUVW.GetY(), 0.0f)));
+ EXPECT_EQ(wasPointRejected, test.m_expectedOutputRejectionResult);
+ }
+ };
+
+ TEST_F(GradientSignalTransformTestsFixture, UnboundedWrappingReturnsTranslatedInput)
+ {
+ GradientTransformSetupData setup = { GradientSignal::WrappingType::None };
+ GradientTransformTestData test = {
+ // Input position to query
+ { 0.0f, 0.0f, 0.0f },
+
+ // Output: For no wrapping, the output is just the input position offset by the world translation.
+ { -100.0f, -200.0f, -300.0f }, false
+ };
+
+ TestGradientTransform(setup, test);
+ }
+
+ TEST_F(GradientSignalTransformTestsFixture, ClampToEdgeReturnsValuesClampedToShapeBounds)
+ {
+ GradientTransformSetupData setup = { GradientSignal::WrappingType::ClampToEdge };
+ GradientTransformTestData tests[] = {
+ // Test: Input point far below minimum shape bounds
+ // Our input point is below the minimum of shape bounds, so the result should be the minimum corner of the shape.
+ { { 0.0f, 0.0f, 0.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input point directly on minimum shape bounds
+ // Our input point is directly on the minimum of shape bounds, so the result should be the minimum corner of the shape.
+ { { 95.0f, 190.0f, 280.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input point inside shape bounds
+ // Our input point is inside the shape bounds, so the result is just input - translation.
+ { { 101.0f, 202.0f, 303.0f }, { 1.0f, 2.0f, 3.0f }, false },
+
+ // Test: Input point directly on maximum shape bounds
+ // On the maximum side, GradientTransform clamps to "max - epsilon" for consistency with other wrapping types, so our
+ // expected results are the max shape corner - epsilon.
+ { { 105.0f, 210.0f, 320.0f }, { 5.0f - UvEpsilon, 10.0f - UvEpsilon, 20.0f - UvEpsilon }, false },
+
+ // Test: Input point far above maximum shape bounds
+ // On the maximum side, GradientTransform clamps to "max - epsilon" for consistency with other wrapping types, so our
+ // expected results are the max shape corner - epsilon.
+ { { 1000.0f, 1000.0f, 1000.0f }, { 5.0f - UvEpsilon, 10.0f - UvEpsilon, 20.0f - UvEpsilon }, false },
+ };
+
+ for (auto& test : tests)
+ {
+ TestGradientTransform(setup, test);
+ }
+ }
+
+ TEST_F(GradientSignalTransformTestsFixture, MirrorReturnsValuesMirroredBasedOnShapeBounds)
+ {
+ /* Here's how the results are expected to work for various inputs when using Mirror wrapping.
+ * This assumes shape half extents of (5, 10, 20), and a center translation of (100, 200, 300):
+ * Inputs: Outputs:
+ * ... ...
+ * (75, 150, 200) - (85, 170, 240) (-5, -10, -20) to (5, 10, 20) // forward mirror
+ * (85, 170, 240) - (95, 190, 280) (5, 10, 20) to (-5, -10, -20) // back mirror
+ * (95, 190, 280) - (105, 210, 320) (-5, -10, -20) to (5, 10, 20) // starting point
+ * (105, 210, 320) - (115, 230, 360) (5, 10, 20) to (-5, -10, -20) // back mirror
+ * (115, 230, 360) - (125, 250, 400) (-5, -10, -20) to (5, 10, 20) // forward mirror
+ * ... ...
+ * When below the starting point, both forward and back mirrors will be adjusted by UvEpsilon except for points that fall on the
+ * shape minimums.
+ * When above the starting point, only back mirrors will be adjusted by UvEpsilon.
+ */
+
+ GradientTransformSetupData setup = { GradientSignal::WrappingType::Mirror };
+ GradientTransformTestData tests[] = {
+ // Test: Input exactly 2x below minimum bounds
+ // When landing exactly on the 2x boundary, we return the minumum shape bounds. There is no adjustment by epsilon
+ // on the minimum side of the bounds, even when we're in a mirror below the shape bounds.
+ { { 75.0f, 150.0f, 200.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input within 2nd mirror repeat below minimum bounds
+ // The second mirror repeat should go forward in values, but will be adjusted by UvEpsilon since we're below the
+ // minimum bounds.
+ { { 84.0f, 168.0f, 237.0f }, { 4.0f - UvEpsilon, 8.0f - UvEpsilon, 17.0f - UvEpsilon }, false },
+
+ // Test: Input exactly 1x below minimum bounds.
+ // When landing exactly on the 1x boundary, we return the maximum shape bounds minus epsilon.
+ { { 85.0f, 170.0f, 240.0f }, { 5.0f - UvEpsilon, 10.0f - UvEpsilon, 20.0f - UvEpsilon }, false },
+
+ // Test: Input within 1st mirror repeat below minimum bounds
+ // The first mirror repeat should go backwards in values, but will be adjusted by UvEpsilon since we're below the
+ // minimum bounds.
+ { { 94.0f, 188.0f, 277.0f }, { -4.0f - UvEpsilon, -8.0f - UvEpsilon, -17.0f - UvEpsilon }, false },
+
+ // Test: Input inside shape bounds
+ // The translated input position is (1, 2, 3) is inside the shape bounds, so we should just get the translated
+ // position back as output.
+ { { 101.0f, 202.0f, 303.0f }, { 1.0f, 2.0f, 3.0f }, false },
+
+ // Test: Input within 1st mirror repeat above maximum bounds
+ // The first mirror repeat should go backwards in values. We're above the maximum bounds, so the expected result
+ // is (4, 8, 17) minus an epsilon.
+ { { 106.0f, 212.0f, 323.0f }, { 4.0f - UvEpsilon, 8.0f - UvEpsilon, 17.0f - UvEpsilon }, false },
+
+ // Test: Input exactly 2x above minimum bounds.
+ // When landing exactly on the 2x boundary, we return the exact minimum value again.
+ { { 115.0f, 230.0f, 360.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input within 2nd mirror repeat above maximum bounds
+ // The second mirror repeat should go forwards in values. We're above the maximum bounds, so the expected result
+ // is (-4, -8, -17) with no epsilon.
+ { { 116.0f, 232.0f, 363.0f }, { -4.0f, -8.0f, -17.0f }, false },
+
+ // Test: Input exactly 2x above maximum bounds
+ // When landing exactly on the 2x boundary, we return the maximum adjusted by the epsilon again.
+ { { 125.0f, 250.0f, 400.0f }, { 5.0f - UvEpsilon, 10.0f - UvEpsilon, 20.0f - UvEpsilon }, false }
+ };
+
+ for (auto& test : tests)
+ {
+ TestGradientTransform(setup, test);
+ }
+ }
+
+ TEST_F(GradientSignalTransformTestsFixture, RepeatReturnsRepeatingValuesBasedOnShapeBounds)
+ {
+ /* Here's how the results are expected to work for various inputs when using Repeat wrapping.
+ * This assumes shape half extents of (5, 10, 20), and a center translation of (100, 200, 300):
+ * Inputs: Outputs:
+ * ... ...
+ * (75, 150, 200) - (85, 170, 240) (-5, -10, -20) to (5, 10, 20)
+ * (85, 170, 240) - (95, 190, 280) (-5, -10, -20) to (5, 10, 20)
+ * (95, 190, 280) - (105, 210, 320) (-5, -10, -20) to (5, 10, 20) // starting point
+ * (105, 210, 320) - (115, 230, 360) (-5, -10, -20) to (5, 10, 20)
+ * (115, 230, 360) - (125, 250, 400) (-5, -10, -20) to (5, 10, 20)
+ * ... ...
+ * Every shape min/max boundary point below the starting point will have the max shape value.
+ * Every shape min/max boundary point above the starting point with have the min shape value.
+ */
+
+
+ GradientTransformSetupData setup = { GradientSignal::WrappingType::Repeat };
+ GradientTransformTestData tests[] = {
+ // Test: 2x below minimum shape bounds
+ // We're on a shape boundary below the minimum bounds, so it should return the maximum.
+ { { 75.0f, 150.0f, 200.0f }, { 5.0f, 10.0f, 20.0f }, false },
+
+ // Test: Input within 2nd repeat below minimum shape bounds
+ // Every repeat should go forwards in values.
+ { { 76.0f, 152.0f, 203.0f }, { -4.0f, -8.0f, -17.0f }, false },
+
+ // Test: 1x below minimum shape bounds
+ // We're on a shape boundary below the minimum bounds, so it should return the maximum.
+ { { 85.0f, 170.0f, 240.0f }, { 5.0f, 10.0f, 20.0f }, false },
+
+ // Test: Input within 1st repeat below minimum shape bounds
+ // Every repeat should go forwards in values.
+ { { 86.0f, 172.0f, 243.0f }, { -4.0f, -8.0f, -17.0f }, false },
+
+ // Test: Input exactly on minimum shape bounds
+ // This should return the actual minimum bounds.
+ { { 95.0f, 190.0f, 280.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input inside shape bounds
+ // This should return the mapped value.
+ { { 101.0f, 202.0f, 303.0f }, { 1.0f, 2.0f, 3.0f }, false },
+
+ // Test: Input exactly on maximum shape bounds
+ // We're on a shape boundary above the minimum bounds, so it should return the minimum.
+ { { 105.0f, 210.0f, 320.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input within 1st repeat above maximum shape bounds
+ // Every repeat should go forwards in values.
+ { { 106.0f, 212.0f, 323.0f }, { -4.0f, -8.0f, -17.0f }, false },
+
+ // Test: 1x above maximum shape bounds
+ // We're on a shape boundary above the minimum bounds, so it should return the minimum.
+ { { 105.0f, 210.0f, 320.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input within 2nd repeat above maximum shape bounds
+ // Every repeat should go forwards in values.
+ { { 106.0f, 212.0f, 323.0f }, { -4.0f, -8.0f, -17.0f }, false },
+ };
+
+ for (auto& test : tests)
+ {
+ TestGradientTransform(setup, test);
+ }
+ }
+
+ TEST_F(GradientSignalTransformTestsFixture, ClampToZeroReturnsClampedValuesBasedOnShapeBounds)
+ {
+ GradientTransformSetupData setup = { GradientSignal::WrappingType::ClampToZero };
+ GradientTransformTestData tests[] = {
+ // Test: Input point far below minimum shape bounds
+ // Our input point is below the minimum of shape bounds, so the result should be the minimum corner of the shape.
+ // Points outside the shape bounds should return "true" for rejected.
+ { { 0.0f, 0.0f, 0.0f }, { -5.0f, -10.0f, -20.0f }, true },
+
+ // Test: Input point directly on minimum shape bounds
+ // Our input point is directly on the minimum of shape bounds, so the result should be the minimum corner of the shape.
+ { { 95.0f, 190.0f, 280.0f }, { -5.0f, -10.0f, -20.0f }, false },
+
+ // Test: Input point inside shape bounds
+ // Our input point is inside the shape bounds, so the result is just input - translation.
+ { { 101.0f, 202.0f, 303.0f }, { 1.0f, 2.0f, 3.0f }, false },
+
+ // Test: Input point directly on maximum shape bounds
+ // On the maximum side, GradientTransform clamps to "max - epsilon" for consistency with other wrapping types, so our
+ // expected results are the max shape corner - epsilon.
+ // Points outside the shape bounds (which includes the maximum edge of the shape bounds) should return "true" for rejected.
+ { { 105.0f, 210.0f, 320.0f }, { 5.0f - UvEpsilon, 10.0f - UvEpsilon, 20.0f - UvEpsilon }, true },
+
+ // Test: Input point far above maximum shape bounds
+ // On the maximum side, GradientTransform clamps to "max - epsilon" for consistency with other wrapping types, so our
+ // expected results are the max shape corner - epsilon.
+ // Points outside the shape bounds should return "true" for rejected.
+ { { 1000.0f, 1000.0f, 1000.0f }, { 5.0f - UvEpsilon, 10.0f - UvEpsilon, 20.0f - UvEpsilon }, true },
+ };
+
+ for (auto& test : tests)
+ {
+ TestGradientTransform(setup, test);
+ }
+ }
+}
+
+
diff --git a/Gems/GradientSignal/Code/gradientsignal_editor_tests_files.cmake b/Gems/GradientSignal/Code/gradientsignal_editor_tests_files.cmake
index 5a3a75602b..8172591afa 100644
--- a/Gems/GradientSignal/Code/gradientsignal_editor_tests_files.cmake
+++ b/Gems/GradientSignal/Code/gradientsignal_editor_tests_files.cmake
@@ -7,5 +7,6 @@
#
set(FILES
+ Tests/GradientSignalTestFixtures.cpp
Tests/EditorGradientSignalPreviewTests.cpp
)
diff --git a/Gems/GradientSignal/Code/gradientsignal_files.cmake b/Gems/GradientSignal/Code/gradientsignal_files.cmake
index 5b28150eb4..7318f18739 100644
--- a/Gems/GradientSignal/Code/gradientsignal_files.cmake
+++ b/Gems/GradientSignal/Code/gradientsignal_files.cmake
@@ -8,6 +8,7 @@
set(FILES
Include/GradientSignal/GradientSampler.h
+ Include/GradientSignal/GradientTransform.h
Include/GradientSignal/SmoothStep.h
Include/GradientSignal/ImageAsset.h
Include/GradientSignal/ImageSettings.h
@@ -77,10 +78,10 @@ set(FILES
Source/GradientSampler.cpp
Source/GradientSignalSystemComponent.cpp
Source/GradientSignalSystemComponent.h
+ Source/GradientTransform.cpp
Source/SmoothStep.cpp
Source/ImageAsset.cpp
Source/ImageSettings.cpp
Source/PerlinImprovedNoise.cpp
- Source/Util.cpp
Source/GradientImageConversion.cpp
)
diff --git a/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake b/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake
index 8c8a4c25e1..8e081e5711 100644
--- a/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake
+++ b/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake
@@ -7,10 +7,15 @@
#
set(FILES
+ Tests/GradientSignalBenchmarks.cpp
Tests/GradientSignalImageTests.cpp
Tests/GradientSignalReferencesTests.cpp
Tests/GradientSignalServicesTests.cpp
Tests/GradientSignalSurfaceTests.cpp
+ Tests/GradientSignalTransformTests.cpp
+ Tests/GradientSignalTestFixtures.cpp
+ Tests/GradientSignalTestFixtures.h
+ Tests/GradientSignalTestMocks.cpp
Tests/GradientSignalTestMocks.h
Tests/GradientSignalTest.cpp
Tests/ImageAssetTests.cpp
diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/AssetEditorToolbar/AssetEditorToolbar.ui b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/AssetEditorToolbar/AssetEditorToolbar.ui
index 688b3724e4..cf7a8e31d1 100644
--- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/AssetEditorToolbar/AssetEditorToolbar.ui
+++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Widgets/AssetEditorToolbar/AssetEditorToolbar.ui
@@ -56,7 +56,7 @@
-
- Groups the current selection in the active graph [Ctrl+Shift+G]
+ Groups the current selection in the active graph [Ctrl+Alt+O]
...
@@ -66,14 +66,14 @@
:/GraphCanvasEditorResources/group.svg:/GraphCanvasEditorResources/group.svg
- Ctrl+Shift+G
+ Ctrl+Alt+O
-
- <html><head/><body><p>Ungroups the selected element in the active graph [Ctrl+Shift+H]</p></body></html>
+ Ungroups the selected element in the active graph [Ctrl+Alt+P]
...
@@ -83,7 +83,7 @@
:/GraphCanvasEditorResources/ungroup.svg:/GraphCanvasEditorResources/ungroup.svg
- Ctrl+Shift+H
+ Ctrl+Alt+P
diff --git a/Gems/ImGui/Code/Source/Platform/Windows/imgui_windows.cmake b/Gems/ImGui/Code/Source/Platform/Windows/imgui_windows.cmake
index 419c652a38..bea2cfc38b 100644
--- a/Gems/ImGui/Code/Source/Platform/Windows/imgui_windows.cmake
+++ b/Gems/ImGui/Code/Source/Platform/Windows/imgui_windows.cmake
@@ -8,6 +8,5 @@
set(LY_COMPILE_DEFINITIONS
PRIVATE
- IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
)
diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja
index d0b77159f8..01bed7d52d 100644
--- a/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja
+++ b/Gems/Multiplayer/Code/Include/Multiplayer/AutoGen/AutoComponent_Source.jinja
@@ -309,11 +309,11 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop
{#
#}
-{% macro PrintRpcParameters(printPrefix, paramDefines) %}
-{% if paramDefines|count > 0 %}
+{% macro PrintRpcParameters(printPrefix, paramDefines) -%}
+{% if paramDefines|count > 0 -%}
{{ printPrefix }}{{ ', '.join(paramDefines) }}
-{% endif %}
-{% endmacro %}
+{%- endif %}
+{%- endmacro -%}
{#
#}
@@ -366,47 +366,45 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ PrintRpcParam
{% set paramTypes = [] %}
{% set paramDefines = [] %}
{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }}
- ->Method("{{ UpperFirst(Property.attrib['Name']) }}", []({{ ClassName }}* self{{ PrintRpcParameters(', ', paramDefines) }}) {
-{% if (InvokeFrom == 'Server') %}
- self->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
-{% elif (InvokeFrom == 'Authority') or (InvokeFrom == 'Autonomous') %}
- if (self->m_controller)
- {
- self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
- }
- else
- {
- AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This remote-procedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", self->GetEntity()->GetName().c_str(), self->GetEntityId().ToString().c_str())
- }
-{% endif %}
- })
+ ->Method("{{ UpperFirst(Property.attrib['Name']) }}", []({{ ClassName }}* self{{ PrintRpcParameters(', ', paramDefines) }}){
+{% if (InvokeFrom == 'Server') %}
+ self->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
+{% elif (InvokeFrom == 'Authority') or (InvokeFrom == 'Autonomous') %}
+ if (self->m_controller)
+ {
+ self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
+ }
+ else
+ {
+ AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This remote-procedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", self->GetEntity()->GetName().c_str(), self->GetEntityId().ToString().c_str())
+ }
+{% endif %}
+ })
->Method("{{ UpperFirst(Property.attrib['Name']) }}ByEntityId", [](AZ::EntityId id{{ PrintRpcParameters(', ', paramDefines) }}) {
-
- AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id);
- if (!entity)
- {
- AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str())
- return;
- }
-
- {{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>();
- if (!networkComponent)
- {
- AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str())
- return;
- }
-{% if (InvokeFrom == 'Server') %}
- networkComponent->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
-{% elif (InvokeFrom == 'Authority') or (InvokeFrom == 'Autonomous') %}
- {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController());
- if (!controller)
- {
- AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str())
- return;
- }
- controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
-{% endif %}
- }, { { { "Source", "The Source containing the {{ ClassName }}Controller" }{% for paramName in paramNames %}, {"{{ paramName }}"}{% endfor %}}})
+ AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id);
+ if (!entity)
+ {
+ AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str())
+ return;
+ }
+ {{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>();
+ if (!networkComponent)
+ {
+ AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str())
+ return;
+ }
+{% if (InvokeFrom == 'Server') %}
+ networkComponent->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
+{% elif (InvokeFrom == 'Authority') or (InvokeFrom == 'Autonomous') %}
+ {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController());
+ if (!controller)
+ {
+ AZ_Warning("Network RPC", false, "{{ ClassName }} {{ UpperFirst(Property.attrib['Name']) }}ByEntityId method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str())
+ return;
+ }
+ controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }});
+{% endif %}
+ }, { { { "Source", "The Source containing the {{ ClassName }}Controller" }{% for paramName in paramNames %}, {"{{ paramName }}"}{% endfor %}}})
->Attribute(AZ::Script::Attributes::ToolTip, "{{Property.attrib['Description']}}")
{% endif %}
{% endcall %}
diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp
index e7cf297f1e..beb17ed9f6 100644
--- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp
+++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp
@@ -39,8 +39,8 @@ namespace Multiplayer
"Network Binding", "The Network Binding component marks an entity as able to be replicated across the network")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Multiplayer")
- ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NetBind.png")
- ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/NetBind.png")
+ ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/NetBinding.svg")
+ ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/NetBinding.svg")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
}
}
diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp
index 66b14579fa..f112098eae 100644
--- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp
+++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp
@@ -41,6 +41,12 @@ namespace Multiplayer
// Automated testing listens for these logs
if (editorsv_isDedicated)
{
+ // Server logs will be piped to the editor so turn off buffering,
+ // otherwise it'll take a lot of logs to fill up the buffer before stdout is finally flushed.
+ // This isn't optimal, but will only affect editor-servers (used when testing multiplayer levels in Editor gameplay mode) and not production servers.
+ // Note: _IOLBF (flush on newlines) won't work for Automated Testing which uses a headless server app and will fall back to _IOFBF (full buffering)
+ setvbuf(stdout, NULL, _IONBF, 0);
+
// If the settings registry is not available at this point,
// then something catastrophic has happened in the application startup.
// That should have been caught and messaged out earlier in startup.
@@ -235,5 +241,4 @@ namespace Multiplayer
{
return MultiplayerEditorPackets::DispatchPacket(connection, packetHeader, serializer, *this);
}
-
}
diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h
index 0c892c847f..721c7e0a0f 100644
--- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h
+++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.h
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
namespace AzNetworking
{
diff --git a/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp b/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp
index 0a0dbfc160..750097b428 100644
--- a/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp
+++ b/Gems/ScriptCanvas/Code/Editor/Components/EditorScriptCanvasComponent.cpp
@@ -242,7 +242,7 @@ namespace ScriptCanvasEditor
SetName(m_sourceHandle.Path().Filename().Native());
}
- void EditorScriptCanvasComponent::OpenEditor(const AZ::Data::AssetId&, const AZ::Data::AssetType&)
+ void EditorScriptCanvasComponent::OpenEditor([[maybe_unused]] const AZ::Data::AssetId& assetId, const AZ::Data::AssetType&)
{
AzToolsFramework::OpenViewPane(LyViewPane::ScriptCanvas);
diff --git a/Gems/ScriptCanvas/Code/Editor/View/Widgets/GraphTabBar.cpp b/Gems/ScriptCanvas/Code/Editor/View/Widgets/GraphTabBar.cpp
index c5f808d5f4..0213765737 100644
--- a/Gems/ScriptCanvas/Code/Editor/View/Widgets/GraphTabBar.cpp
+++ b/Gems/ScriptCanvas/Code/Editor/View/Widgets/GraphTabBar.cpp
@@ -430,7 +430,7 @@ namespace ScriptCanvasEditor
void GraphTabBar::UpdateFileState(const ScriptCanvasEditor::SourceHandle& assetId, Tracker::ScriptCanvasFileState fileState)
{
auto tabData = GetTabData(assetId);
- if (tabData && tabData->m_fileState != fileState)
+ if (tabData && tabData->m_fileState != Tracker::ScriptCanvasFileState::NEW && tabData->m_fileState != fileState)
{
int index = FindTab(assetId);
tabData->m_fileState = fileState;
diff --git a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp
index f0bee2171a..6222d902da 100644
--- a/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp
+++ b/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp
@@ -1136,23 +1136,20 @@ namespace ScriptCanvasEditor
return AZ::Success(outTabIndex);
}
- auto loadedGraph = LoadFromFile(fileAssetId.Path().c_str());
- if (!loadedGraph.IsSuccess())
+ auto loadedGraphOutcome = LoadFromFile(fileAssetId.Path().c_str());
+ if (!loadedGraphOutcome.IsSuccess())
{
return AZ::Failure(AZStd::string("Failed to load graph at %s", fileAssetId.Path().c_str()));
}
- outTabIndex = CreateAssetTab(loadedGraph.GetValue(), fileState);
-
- if (!m_isRestoringWorkspace)
- {
- SetActiveAsset(loadedGraph.GetValue());
- }
+ auto loadedGraph = loadedGraphOutcome.TakeValue();
+ CompleteDescriptionInPlace(loadedGraph);
+ outTabIndex = CreateAssetTab(loadedGraph, fileState);
if (outTabIndex >= 0)
{
- AddRecentFile(loadedGraph.GetValue().Path().c_str());
- OpenScriptCanvasAssetImplementation(loadedGraph.GetValue(), fileState);
+ AddRecentFile(loadedGraph.Path().c_str());
+ OpenScriptCanvasAssetImplementation(loadedGraph, fileState);
return AZ::Success(outTabIndex);
}
else
@@ -1169,6 +1166,11 @@ namespace ScriptCanvasEditor
return AZ::Failure(AZStd::string("Unable to open asset with invalid asset id"));
}
+ if (!m_isRestoringWorkspace)
+ {
+ SetActiveAsset(scriptCanvasAsset);
+ }
+
if (!scriptCanvasAsset.IsDescriptionValid())
{
if (!m_isRestoringWorkspace)
@@ -1345,9 +1347,11 @@ namespace ScriptCanvasEditor
AZ::Outcome outcome = LoadFromFile(fullPath);
if (!outcome.IsSuccess())
{
- QMessageBox::warning(this, "Invalid Source File", QString("'%1' failed to load properly.").arg(fullPath), QMessageBox::Ok);
+ QMessageBox::warning(this, "Invalid Source File"
+ , QString("'%1' failed to load properly.\nFailure: %2").arg(fullPath).arg(outcome.GetError().c_str()), QMessageBox::Ok);
m_errorFilePath = fullPath;
- AZ_Warning("ScriptCanvas", false, "Unable to open file as a ScriptCanvas graph: %s", fullPath);
+ AZ_Warning("ScriptCanvas", false, "Unable to open file as a ScriptCanvas graph: %s. Failure: %s"
+ , fullPath, outcome.GetError().c_str());
return;
}
@@ -1596,7 +1600,14 @@ namespace ScriptCanvasEditor
bool MainWindow::OnFileSave()
{
- return SaveAssetImpl(m_activeGraph, Save::InPlace);
+ if (auto metaData = m_tabBar->GetTabData(m_activeGraph); metaData && metaData->m_fileState == Tracker::ScriptCanvasFileState::NEW)
+ {
+ return SaveAssetImpl(m_activeGraph, Save::As);
+ }
+ else
+ {
+ return SaveAssetImpl(m_activeGraph, Save::InPlace);
+ }
}
bool MainWindow::OnFileSaveAs()
diff --git a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material
index 53f6fd5b9e..da2fc95293 100644
--- a/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material
+++ b/Gems/Terrain/Assets/Materials/Terrain/DefaultPbrTerrain.material
@@ -5,7 +5,7 @@
"propertyLayoutVersion": 1,
"properties": {
"baseColor": {
- "color": [ 0.18, 0.18, 0.18 ]
+ "color": [ 0.18, 0.18, 0.18 ]
}
}
}
diff --git a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype
index 1e7305cc11..836e85661d 100644
--- a/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype
+++ b/Gems/Terrain/Assets/Materials/Terrain/PbrTerrain.materialtype
@@ -107,7 +107,7 @@
"displayName": "Detail Texture UV Multiplier",
"description": "How many times to repeat the detail texture per sector",
"type": "Float",
- "defaultValue": 8.0,
+ "defaultValue": 0.5,
"connection": {
"type": "ShaderInput",
"id": "m_detailTextureMultiplier"
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/SceneSrg.azsli b/Gems/Terrain/Assets/Shaders/Terrain/SceneSrg.azsli
new file mode 100644
index 0000000000..2545db7c93
--- /dev/null
+++ b/Gems/Terrain/Assets/Shaders/Terrain/SceneSrg.azsli
@@ -0,0 +1,35 @@
+/*
+ * 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
+ *
+ */
+
+#ifndef AZ_COLLECTING_PARTIAL_SRGS
+#error Do not include this file directly. Include the main .srgi file instead.
+#endif
+
+partial ShaderResourceGroup SceneSrg
+{
+ Sampler HeightmapSampler
+ {
+ MinFilter = Linear;
+ MagFilter = Linear;
+ MipFilter = Point;
+ AddressU = Clamp;
+ AddressV = Clamp;
+ AddressW = Clamp;
+ };
+
+ struct TerrainWorldData
+ {
+ float3 m_min;
+ float m_padding1;
+ float3 m_max;
+ float m_padding2;
+ };
+
+ Texture2D m_heightmapImage;
+ TerrainWorldData m_terrainWorldData;
+}
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
index 770f877ea8..ee3e9b2a5f 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainCommon.azsli
@@ -15,34 +15,13 @@
ShaderResourceGroup ObjectSrg : SRG_PerObject
{
- struct TerrainData
+ struct PatchData
{
- float2 m_uvMin;
- float2 m_uvMax;
- float2 m_uvStep;
- float m_sampleSpacing;
- float m_heightScale;
+ float2 m_xyTranslation;
+ float m_xyScale;
};
- struct MacroMaterialData
- {
- float2 m_uvMin;
- float2 m_uvMax;
- float m_normalFactor;
- bool m_flipNormalX;
- bool m_flipNormalY;
- uint m_mapsInUse;
- };
-
- row_major float3x4 m_modelToWorld;
-
- TerrainData m_terrainData;
-
- MacroMaterialData m_macroMaterialData[4];
- uint m_macroMaterialCount;
-
- Texture2D m_macroColorMap[4];
- Texture2D m_macroNormalMap[4];
+ PatchData m_patchData;
// The below shouldn't be in this SRG but needs to be for now because the lighting functions depend on them.
@@ -117,109 +96,137 @@ option bool o_useTerrainSmoothing = false;
struct VertexInput
{
float2 m_position : POSITION;
- float2 m_uv : UV;
};
-// Sample a texture with a 5 tap B-Spline. Consider ripping this out and putting in a more general location.
-// This function samples a 4x4 neighborhood around the uv. Normally this would take 16 samples, but by taking
-// advantage of bilinear filtering this can be done with 9 taps on the edges between pixels. The cost is further
-// reduced by dropping the diagonals.
-float SampleBSpline5Tap(Texture2D texture, SamplerState textureSampler, float2 uv, float2 textureSize, float2 rcpTextureSize)
+// This class is used to calculate heights and normals for terrain. Using a class for this was the easiest way to
+// de-duplicate code between the forward and depth shaders.
+class HeightContext
{
- // Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and
- // a bottom right coordinate of 3,3.
+ float3 m_worldMin;
+ float3 m_worldMax;
+ float2 m_xyPosition;
- // Find the position in texture space then round it to get the center of the 1,1 pixel (tc1)
- float2 texelPos = uv * textureSize;
- float2 tc1= floor(texelPos - 0.5) + 0.5;
+ float2 m_textureSize;
+ float2 m_rcpTextureSize;
+ float2 m_sampleSpacing;
+ float2 m_rcpSampleSpacing;
- // Offset from center position to texel
- float2 f = texelPos - tc1;
+ float m_heightScale;
+ int2 m_heightmapCoord;
- // Compute B-Spline weights based on the offset
- float2 OneMinusF = (1.0 - f);
- float2 OneMinusF2 = OneMinusF * OneMinusF;
- float2 OneMinusF3 = OneMinusF2 * OneMinusF;
- float2 w0 = OneMinusF3;
- float2 w1 = 4.0 + 3.0 * f * f * f - 6.0 * f * f;
- float2 w2 = 4.0 + 3.0 * OneMinusF3 - 6.0 * OneMinusF2;
- float2 w3 = f * f * f;
-
- float2 w12 = w1 + w2;
-
- // Compute uv coordinates for sampling the texture
- float2 tc0 = (tc1 - 1.0f) * rcpTextureSize;
- float2 tc3 = (tc1 + 2.0f) * rcpTextureSize;
- float2 tc12 = (tc1 + w2 / w12) * rcpTextureSize;
-
- // Compute sample weights
- float sw0 = w12.x * w12.y; // middle
- float sw1 = w12.x * w0.y; // top
- float sw2 = w0.x * w12.y; // left
- float sw3 = w12.x * w3.y; // bottom
- float sw4 = w3.x * w12.y; // right
-
- // total weight of samples to normalize result.
- float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4;
-
- float result = 0.0f;
- result += texture.SampleLevel(textureSampler, float2(tc12.x, tc12.y), 0.0).r * sw0;
- result += texture.SampleLevel(textureSampler, float2(tc12.x, tc0.y), 0.0).r * sw1;
- result += texture.SampleLevel(textureSampler, float2( tc0.x, tc12.y), 0.0).r * sw2;
- result += texture.SampleLevel(textureSampler, float2(tc12.x, tc3.y), 0.0).r * sw3;
- result += texture.SampleLevel(textureSampler, float2( tc3.x, tc12.y), 0.0).r * sw4;
-
- return result / totalWeight;
-}
-
-float4x4 GetObject_WorldMatrix()
-{
- float4x4 modelToWorld = float4x4(
- float4(1, 0, 0, 0),
- float4(0, 1, 0, 0),
- float4(0, 0, 1, 0),
- float4(0, 0, 0, 1));
-
- modelToWorld[0] = ObjectSrg::m_modelToWorld[0];
- modelToWorld[1] = ObjectSrg::m_modelToWorld[1];
- modelToWorld[2] = ObjectSrg::m_modelToWorld[2];
- return modelToWorld;
-}
-
-float GetHeight(float2 origUv)
-{
- float2 halfStep = ObjectSrg::m_terrainData.m_uvStep * 0.5;
- float2 uv = origUv * (1.0 - ObjectSrg::m_terrainData.m_uvStep) + halfStep;
-
- float height = 0.0f;
- if (o_useTerrainSmoothing)
+
+ // Sample a texture with a 5 tap B-Spline. Consider ripping this out and putting in a more general location.
+ // This function samples a 4x4 neighborhood around the uv. Normally this would take 16 samples, but by taking
+ // advantage of bilinear filtering this can be done with 9 taps on the edges between pixels. The cost is further
+ // reduced by dropping the diagonals.
+ float SampleBSpline5Tap(Texture2D texture, SamplerState textureSampler, float2 uv, float2 textureSize, float2 rcpTextureSize)
{
- float2 textureSize;
- ViewSrg::m_heightmapImage.GetDimensions(textureSize.x, textureSize.y);
- height = SampleBSpline5Tap(ViewSrg::m_heightmapImage, ViewSrg::HeightmapSampler, uv, textureSize, rcp(textureSize));
- }
- else
- {
- height = ViewSrg::m_heightmapImage.SampleLevel(ViewSrg::HeightmapSampler, uv, 0).r;
+ // Think of sample locations in the 4x4 neighborhood as having a top left coordinate of 0,0 and
+ // a bottom right coordinate of 3,3.
+
+ // Find the position in texture space then round it to get the center of the 1,1 pixel (tc1)
+ float2 texelPos = uv * textureSize;
+ float2 tc1= floor(texelPos - 0.5) + 0.5;
+
+ // Offset from center position to texel
+ float2 f = texelPos - tc1;
+
+ // Compute B-Spline weights based on the offset
+ float2 OneMinusF = (1.0 - f);
+ float2 OneMinusF2 = OneMinusF * OneMinusF;
+ float2 OneMinusF3 = OneMinusF2 * OneMinusF;
+ float2 w0 = OneMinusF3;
+ float2 w1 = 4.0 + 3.0 * f * f * f - 6.0 * f * f;
+ float2 w2 = 4.0 + 3.0 * OneMinusF3 - 6.0 * OneMinusF2;
+ float2 w3 = f * f * f;
+
+ float2 w12 = w1 + w2;
+
+ // Compute uv coordinates for sampling the texture
+ float2 tc0 = (tc1 - 1.0f) * rcpTextureSize;
+ float2 tc3 = (tc1 + 2.0f) * rcpTextureSize;
+ float2 tc12 = (tc1 + w2 / w12) * rcpTextureSize;
+
+ // Compute sample weights
+ float sw0 = w12.x * w12.y; // middle
+ float sw1 = w12.x * w0.y; // top
+ float sw2 = w0.x * w12.y; // left
+ float sw3 = w12.x * w3.y; // bottom
+ float sw4 = w3.x * w12.y; // right
+
+ // total weight of samples to normalize result.
+ float totalWeight = sw0 + sw1 + sw2 + sw3 + sw4;
+
+ float result = 0.0f;
+ result += texture.SampleLevel(textureSampler, float2(tc12.x, tc12.y), 0.0).r * sw0;
+ result += texture.SampleLevel(textureSampler, float2(tc12.x, tc0.y), 0.0).r * sw1;
+ result += texture.SampleLevel(textureSampler, float2( tc0.x, tc12.y), 0.0).r * sw2;
+ result += texture.SampleLevel(textureSampler, float2(tc12.x, tc3.y), 0.0).r * sw3;
+ result += texture.SampleLevel(textureSampler, float2( tc3.x, tc12.y), 0.0).r * sw4;
+
+ return result / totalWeight;
}
- return ObjectSrg::m_terrainData.m_heightScale * (height - 0.5f);
-}
-
-float3 GetTerrainWorldPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv)
-{
- // Remove all vertices outside our bounds by turning them into NaN positions.
- if (any(uv > 1.0) || any (uv < 0.0))
+ float2 GetWorldXYPosition(in ObjectSrg::PatchData patchData, in float2 vertexPosition)
{
- return asfloat(0x7fc00000); // NaN
+ return float2(patchData.m_xyTranslation + vertexPosition * patchData.m_xyScale);
}
- // Loop up the height and calculate our final position.
- float height = GetHeight(uv);
- return mul(GetObject_WorldMatrix(), float4(vertexPosition, height, 1.0f)).xyz;
-}
+ float2 GetHeightmapUv(in float2 position, in float2 worldMin, in float2 worldMax)
+ {
+ return (position - worldMin) / (worldMax - worldMin);
+ }
-float4 GetTerrainProjectedPosition(ObjectSrg::TerrainData terrainData, float2 vertexPosition, float2 uv)
-{
- return mul(ViewSrg::m_viewProjectionMatrix, float4(GetTerrainWorldPosition(terrainData, vertexPosition, uv), 1.0));
-}
+ int2 GetHeightmapCoord(in float2 position, in float2 rcpSampleSpacing, in float2 worldMin)
+ {
+ return int2((position - worldMin) * rcpSampleSpacing);
+ }
+
+ float GetHeight(Texture2D heightmapImage, int2 offset = int2(0, 0))
+ {
+ float height = heightmapImage.Load(int3(m_heightmapCoord + offset, 0)).r;
+ return m_worldMin.z + height * m_heightScale;
+ }
+
+ float GetSmoothedHeight(Texture2D heightmapImage, SamplerState heightmapSampler)
+ {
+ float2 uv = GetHeightmapUv(m_xyPosition, m_worldMin.xy, m_worldMax.xy);
+ float2 halfStep = m_rcpTextureSize * 0.5;
+ uv = uv * (1.0 - m_rcpTextureSize) + halfStep;
+ float height = SampleBSpline5Tap(heightmapImage, heightmapSampler, uv, m_textureSize, m_rcpTextureSize);
+ return m_worldMin.z + height * (m_worldMax.z - m_worldMin.z);
+ }
+
+ float3 CalculateNormal(Texture2D heightmapImage)
+ {
+ float up = GetHeight(heightmapImage, int2( 0, -1));
+ float right = GetHeight(heightmapImage, int2( 1, 0));
+ float down = GetHeight(heightmapImage, int2( 0, 1));
+ float left = GetHeight(heightmapImage, int2(-1, 0));
+
+ float3 bitangent = normalize(float3(0.0, m_sampleSpacing.y * 2.0f, down - up));
+ float3 tangent = normalize(float3(m_sampleSpacing.x * 2.0f, 0.0, right - left));
+ return normalize(cross(tangent, bitangent));
+ }
+
+ bool IsVertexOutsideOfTerrainBounds()
+ {
+ return (any(m_xyPosition < m_worldMin.xy) ||
+ any(m_xyPosition > m_worldMax.xy));
+ }
+
+ void Initialize(Texture2D heightmapImage, float2 vertexPosition, ObjectSrg::PatchData patchData, float3 worldMin, float3 worldMax)
+ {
+ m_worldMin = worldMin;
+ m_worldMax = worldMax;
+ m_xyPosition = GetWorldXYPosition(patchData, vertexPosition);
+
+ heightmapImage.GetDimensions(m_textureSize.x, m_textureSize.y);
+ m_rcpTextureSize = rcp(m_textureSize);
+ m_sampleSpacing = (worldMax.xy - worldMin.xy) * m_rcpTextureSize;
+ m_rcpSampleSpacing = rcp(m_sampleSpacing);
+
+ m_heightScale = worldMax.z - worldMin.z;
+ m_heightmapCoord = GetHeightmapCoord(m_xyPosition, m_rcpSampleSpacing, worldMin.xy);
+ }
+};
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli
index 33c817c0f9..b18f2885db 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli
@@ -123,7 +123,7 @@ float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv, fl
float3 color = materialData.m_baseColor;
if ((materialData.m_flags & DetailTextureFlags::UseTextureBaseColor) > 0)
{
- color = TerrainSrg::m_detailTextures[GetDetailColorIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rgb;
+ color = TerrainSrg::m_textures[GetDetailColorIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rgb;
}
return color * materialData.m_baseColorFactor;
}
@@ -133,7 +133,7 @@ float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv, f
float2 normal = float2(0.0, 0.0);
if ((materialData.m_flags & DetailTextureFlags::UseTextureNormal) > 0)
{
- normal = TerrainSrg::m_detailTextures[GetDetailNormalIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rg;
+ normal = TerrainSrg::m_textures[GetDetailNormalIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rg;
}
// X and Y are inverted here to be consistent with SampleNormalXY in NormalInput.azsli.
@@ -153,7 +153,7 @@ float GetDetailRoughness(TerrainSrg::DetailMaterialData materialData, float2 uv,
float roughness = materialData.m_roughnessScale;
if ((materialData.m_flags & DetailTextureFlags::UseTextureRoughness) > 0)
{
- roughness = TerrainSrg::m_detailTextures[GetDetailRoughnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
+ roughness = TerrainSrg::m_textures[GetDetailRoughnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
roughness = materialData.m_roughnessBias + roughness * materialData.m_roughnessScale;
}
return roughness;
@@ -164,7 +164,7 @@ float GetDetailMetalness(TerrainSrg::DetailMaterialData materialData, float2 uv,
float metalness = 1.0;
if ((materialData.m_flags & DetailTextureFlags::UseTextureMetallic) > 0)
{
- metalness = TerrainSrg::m_detailTextures[GetDetailMetalnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
+ metalness = TerrainSrg::m_textures[GetDetailMetalnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
}
return metalness * materialData.m_metalFactor;
}
@@ -174,7 +174,7 @@ float GetDetailSpecularF0(TerrainSrg::DetailMaterialData materialData, float2 uv
float specularF0 = 1.0;
if ((materialData.m_flags & DetailTextureFlags::UseTextureSpecularF0) > 0)
{
- specularF0 = TerrainSrg::m_detailTextures[GetDetailSpecularF0Index(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
+ specularF0 = TerrainSrg::m_textures[GetDetailSpecularF0Index(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
}
return specularF0 * materialData.m_specularF0Factor;
}
@@ -184,7 +184,7 @@ float GetDetailOcclusion(TerrainSrg::DetailMaterialData materialData, float2 uv,
float occlusion = 1.0;
if ((materialData.m_flags & DetailTextureFlags::UseTextureOcclusion) > 0)
{
- occlusion = TerrainSrg::m_detailTextures[GetDetailOcclusionIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
+ occlusion = TerrainSrg::m_textures[GetDetailOcclusionIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
}
return occlusion * materialData.m_occlusionFactor;
}
@@ -194,7 +194,7 @@ float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv, fl
float height = materialData.m_heightFactor;
if ((materialData.m_flags & DetailTextureFlags::UseTextureHeight) > 0)
{
- height = TerrainSrg::m_detailTextures[GetDetailHeightIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
+ height = TerrainSrg::m_textures[GetDetailHeightIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
height = materialData.m_heightOffset + height * materialData.m_heightFactor;
}
return height;
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl
index ab9064e740..741f45d775 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl
@@ -8,6 +8,7 @@
#include
+#include
#include
#include
#include
@@ -26,7 +27,6 @@ struct VSOutput
float4 m_position : SV_Position;
float3 m_normal: NORMAL;
float3 m_worldPosition : UV0;
- float2 m_uv : UV1;
float3 m_shadowCoords[ViewSrg::MaxCascadeCount] : UV2;
};
@@ -34,24 +34,30 @@ VSOutput TerrainPBR_MainPassVS(VertexInput IN)
{
VSOutput OUT;
- ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData;
+ HeightContext heightContext;
+ heightContext.Initialize(SceneSrg::m_heightmapImage, IN.m_position, ObjectSrg::m_patchData, SceneSrg::m_terrainWorldData.m_min, SceneSrg::m_terrainWorldData.m_max);
- float2 uv = IN.m_uv;
- float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, uv);
- float3 worldPosition = GetTerrainWorldPosition(terrainData, IN.m_position, origUv);
- OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
- OUT.m_worldPosition = worldPosition;
+ if (heightContext.IsVertexOutsideOfTerrainBounds())
+ {
+ // Output a NaN to remove this vertex.
+ OUT.m_position = 1.0 / 0.0;
+ return OUT;
+ }
- // Calculate normal
- float up = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, -1.0f));
- float right = GetHeight(origUv + terrainData.m_uvStep * float2( 1.0f, 0.0f));
- float down = GetHeight(origUv + terrainData.m_uvStep * float2( 0.0f, 1.0f));
- float left = GetHeight(origUv + terrainData.m_uvStep * float2(-1.0f, 0.0f));
+ float height = 0.0;
- float3 bitangent = normalize(float3(0.0, terrainData.m_sampleSpacing * 2.0f, down - up));
- float3 tangent = normalize(float3(terrainData.m_sampleSpacing * 2.0f, 0.0, right - left));
- OUT.m_normal = normalize(cross(tangent, bitangent));
- OUT.m_uv = uv;
+ if (o_useTerrainSmoothing)
+ {
+ height = heightContext.GetSmoothedHeight(SceneSrg::m_heightmapImage, SceneSrg::HeightmapSampler);
+ }
+ else
+ {
+ height = heightContext.GetHeight(SceneSrg::m_heightmapImage);
+ }
+
+ OUT.m_worldPosition = float3(heightContext.m_xyPosition, height);
+ OUT.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(OUT.m_worldPosition, 1.0));
+ OUT.m_normal = heightContext.CalculateNormal(SceneSrg::m_heightmapImage);
// directional light shadow
const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
@@ -59,7 +65,7 @@ VSOutput TerrainPBR_MainPassVS(VertexInput IN)
{
DirectionalLightShadow::GetShadowCoords(
shadowIndex,
- worldPosition,
+ OUT.m_worldPosition,
OUT.m_normal,
OUT.m_shadowCoords);
}
@@ -76,7 +82,7 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
float viewDistance = length(ViewSrg::m_worldPosition - surface.position);
float detailFactor = saturate((viewDistance - TerrainMaterialSrg::m_detailFadeDistance) / max(TerrainMaterialSrg::m_detailFadeLength, EPSILON));
- float2 detailUv = IN.m_uv * TerrainMaterialSrg::m_detailTextureMultiplier;
+ float2 detailUv = IN.m_worldPosition.xy * TerrainMaterialSrg::m_detailTextureMultiplier;
// ------- Normal -------
float3 macroNormal = normalize(IN.m_normal);
@@ -84,39 +90,54 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
// ------- Macro Color / Normal -------
float3 macroColor = TerrainMaterialSrg::m_baseColor.rgb;
- // There's a bug that shows up with an NVidia GTX 1660 Super card happening on driver versions as recent as 496.49 (10/26/21) in which
- // the IN.m_uv values will intermittently "flicker" to 0.0 after entering and exiting game mode.
- // (See https://github.com/o3de/o3de/issues/5014)
- // This bug has only shown up on PCs when using the DX12 RHI. It doesn't show up with Vulkan or when capturing frames with PIX or
- // RenderDoc. Our best guess is that it is a driver bug. The workaround is to use the IN.m_uv values in a calculation prior to the
- // point that we actually use them for macroUv below. The "if(any(!isnan(IN.m_uv)))" seems to be sufficient for the workaround. The
- // if statement will always be true, but just the act of reading these values in the if statement makes the values stable. Removing
- // the if statement causes the flickering to occur using the steps documented in the bug.
- if (any(!isnan(IN.m_uv)))
+ uint2 macroGridResolution = uint2(TerrainSrg::m_macroMaterialGrid.m_resolution >> 16, TerrainSrg::m_macroMaterialGrid.m_resolution & 0xFFFF);
+ float macroTileSize = TerrainSrg::m_macroMaterialGrid.m_tileSize;
+ float2 macroGridOffset = TerrainSrg::m_macroMaterialGrid.m_offset;
+ uint2 macroGridPosition = (surface.position.xy - macroGridOffset) / macroTileSize;
+
+ uint macroTileIndex = macroGridResolution.x * macroGridPosition.y + macroGridPosition.x;
+ static const uint NumMacroMaterialsPerTile = 4;
+ macroTileIndex *= NumMacroMaterialsPerTile;
+
+ [unroll] for (uint i = 0; i < NumMacroMaterialsPerTile; ++i)
{
- [unroll] for (uint i = 0; i < 4 && (i < ObjectSrg::m_macroMaterialCount); ++i)
+ TerrainSrg::MacroMaterialData macroMaterialData = TerrainSrg::m_macroMaterialData[macroTileIndex + i];
+ if ((macroMaterialData.m_flags & 1) == 0)
{
- float2 macroUvMin = ObjectSrg::m_macroMaterialData[i].m_uvMin;
- float2 macroUvMax = ObjectSrg::m_macroMaterialData[i].m_uvMax;
- float2 macroUv = lerp(macroUvMin, macroUvMax, IN.m_uv);
- if (macroUv.x >= 0.0 && macroUv.x <= 1.0 && macroUv.y >= 0.0 && macroUv.y <= 1.0)
- {
- if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 1) > 0)
- {
- macroColor = GetBaseColorInput(ObjectSrg::m_macroColorMap[i], TerrainMaterialSrg::m_sampler, macroUv, macroColor, true);
- }
- if ((ObjectSrg::m_macroMaterialData[i].m_mapsInUse & 2) > 0)
- {
- bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX;
- bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY;
- float factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor;
-
- float2 sampledValue = SampleNormalXY(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, macroUv, flipX, flipY);
- macroNormal = normalize(GetTangentSpaceNormal_Unnormalized(sampledValue.xy, factor));
- }
- break;
- }
+ break; // No more macro materials for this tile
}
+
+ if (any(surface.position.xy < macroMaterialData.m_boundsMin) || any (surface.position.xy > macroMaterialData.m_boundsMax))
+ {
+ continue; // Macro material exists for this tile but is out of the bounds of this particular position
+ }
+
+ float2 macroUvSize = macroMaterialData.m_boundsMax - macroMaterialData.m_boundsMin;
+ macroUvSize.x = -macroUvSize.x;
+ float2 macroUv = (macroMaterialData.m_boundsMin - surface.position.xy) / macroUvSize;
+
+ // The macro uv gradient can vary massively over the quad because different pixels may choose different macro materials with different UVs.
+ // To fix, we use the world position scaled by the macro uv scale which should be fairly uniform across macro materials.
+ float2 macroUvScale = IN.m_worldPosition.xy / macroUvSize;
+ float2 ddx_macroUv = ddx(macroUvScale);
+ float2 ddy_macroUv = ddy(macroUvScale);
+
+ if (macroMaterialData.m_colorMapId != 0xFFFF)
+ {
+ macroColor = TerrainSrg::m_textures[macroMaterialData.m_colorMapId].SampleGrad(TerrainMaterialSrg::m_sampler, macroUv, ddx_macroUv, ddy_macroUv).rgb;
+ macroColor = TransformColor(macroColor, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg);
+ }
+
+ if (macroMaterialData.m_normalMapId != 0xFFFF)
+ {
+ bool flipX = macroMaterialData.m_flags & 2;
+ bool flipY = macroMaterialData.m_flags & 4;
+ float factor = macroMaterialData.m_normalFactor;
+
+ float2 sampledValue = SampleNormalXY(TerrainSrg::m_textures[macroMaterialData.m_normalMapId], TerrainMaterialSrg::m_sampler, macroUv, flipX, flipY);
+ macroNormal = normalize(GetTangentSpaceNormal_Unnormalized(sampledValue, factor));
+ }
+ break;
}
// ------- Base Color -------
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli
index f980e02b9d..3fcf8d75ca 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli
+++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainSrg.azsli
@@ -53,12 +53,38 @@ ShaderResourceGroup TerrainSrg : SRG_Terrain
uint2 m_padding;
};
+ struct MacroMaterialData
+ {
+ // bit 1 : Is this macro material used.
+ // bit 2 : flip normal x
+ // bit 3 : flip normal y
+ uint m_flags;
+
+ uint m_colorMapId;
+ uint m_normalMapId;
+ float m_normalFactor;
+ float2 m_boundsMin;
+ float2 m_boundsMax;
+ };
+
+ struct MacroMaterialGrid
+ {
+ uint m_resolution; // How many x/y tiles in grid. x & y stored in 16 bits each. Total number of entries in m_macroMaterialData will be x * y
+ float m_tileSize; // Size of a tile in meters.
+ float2 m_offset; // x/y offset of min x/y corner of grid.
+ };
+
Texture2D m_detailMaterialIdImage;
StructuredBuffer m_detailMaterialData;
- Texture2D m_detailTextures[]; // bindless array of all textures for detail materials
+ StructuredBuffer m_macroMaterialData;
+ MacroMaterialGrid m_macroMaterialGrid;
+
+ Texture2D m_textures[]; // bindless array of all textures for detail and macro materials
float2 m_detailMaterialIdImageCenter;
float m_detailHalfPixelUv;
float4 m_detailAabb;
}
+
+static const float MacroMaterialsPerTile = 4;
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl
index fd855a1bcd..c74a0646ee 100644
--- a/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl
+++ b/Gems/Terrain/Assets/Shaders/Terrain/Terrain_DepthPass.azsl
@@ -6,6 +6,7 @@
*/
#include
+#include
#include
#include "TerrainCommon.azsli"
#include
@@ -18,9 +19,30 @@ struct VSDepthOutput
VSDepthOutput MainVS(in VertexInput input)
{
VSDepthOutput output;
- ObjectSrg::TerrainData terrainData = ObjectSrg::m_terrainData;
- float2 origUv = lerp(terrainData.m_uvMin, terrainData.m_uvMax, input.m_uv);
- output.m_position = GetTerrainProjectedPosition(terrainData, input.m_position, origUv);
+ HeightContext heightContext;
+ heightContext.Initialize(SceneSrg::m_heightmapImage, input.m_position, ObjectSrg::m_patchData, SceneSrg::m_terrainWorldData.m_min, SceneSrg::m_terrainWorldData.m_max);
+
+ if (heightContext.IsVertexOutsideOfTerrainBounds())
+ {
+ // Output a NaN to remove this vertex.
+ output.m_position = 1.0 / 0.0;
+ return output;
+ }
+
+ float height = 0.0;
+
+ if (o_useTerrainSmoothing)
+ {
+ height = heightContext.GetSmoothedHeight(SceneSrg::m_heightmapImage, SceneSrg::HeightmapSampler);
+ }
+ else
+ {
+ height = heightContext.GetHeight(SceneSrg::m_heightmapImage);
+ }
+
+ float3 worldPosition = float3(heightContext.m_xyPosition, height);
+ output.m_position = mul(ViewSrg::m_viewProjectionMatrix, float4(worldPosition, 1.0));
+
return output;
}
diff --git a/Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli b/Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli
deleted file mode 100644
index 144f2abf6b..0000000000
--- a/Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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
- *
- */
-
-#ifndef AZ_COLLECTING_PARTIAL_SRGS
-#error Do not include this file directly. Include the main .srgi file instead.
-#endif
-
-partial ShaderResourceGroup ViewSrg
-{
- Sampler HeightmapSampler
- {
- MinFilter = Linear;
- MagFilter = Linear;
- MipFilter = Point;
- AddressU = Clamp;
- AddressV = Clamp;
- AddressW = Clamp;
- };
-
- Sampler DetailSampler
- {
- AddressU = Wrap;
- AddressV = Wrap;
- MinFilter = Point;
- MagFilter = Point;
- MipFilter = Point;
- };
-
- struct DetailMaterialData
- {
- // Uv
- row_major float3x4 m_uvTransform;
-
- float3 m_baseColor;
-
- // Factor / Scale / Bias for input textures
- float m_baseColorFactor;
-
- float m_normalFactor;
- float m_metalFactor;
- float m_roughnessScale;
- float m_roughnessBias;
-
- float m_specularF0Factor;
- float m_occlusionFactor;
- float m_heightFactor;
- float m_heightOffset;
-
- float m_heightBlendFactor;
-
- // Flags
- uint m_flags; // see DetailTextureFlags
-
- // Image indices
- uint m_colorNormalImageIndices;
- uint m_roughnessMetalnessImageIndices;
-
- uint m_specularF0OcclusionImageIndices;
- uint m_heightImageIndex; // only first 16 bits used
-
- // 16 byte aligned
- uint2 m_padding;
- };
-
- Texture2D m_heightmapImage;
- Texture2D m_detailMaterialIdImage;
- StructuredBuffer m_detailMaterialData;
-
- Texture2D m_detailTextures[]; // bindless array of all textures for detail materials
-
- float2 m_detailMaterialIdImageCenter;
- float m_detailHalfPixelUv;
-
- float4 m_detailAabb;
-}
diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp
index 1299403659..8c2c0e80b6 100644
--- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp
+++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp
@@ -255,6 +255,8 @@ namespace Terrain
void TerrainPhysicsColliderComponent::GenerateHeightsInBounds(AZStd::vector& heights) const
{
+ AZ_PROFILE_FUNCTION(Entity);
+
const AZ::Vector2 gridResolution = GetHeightfieldGridSpacing();
AZ::Aabb worldSize = GetHeightfieldAabb();
@@ -315,6 +317,8 @@ namespace Terrain
void TerrainPhysicsColliderComponent::GenerateHeightsAndMaterialsInBounds(
AZStd::vector& heightMaterials) const
{
+ AZ_PROFILE_FUNCTION(Entity);
+
const AZ::Vector2 gridResolution = GetHeightfieldGridSpacing();
AZ::Aabb worldSize = GetHeightfieldAabb();
diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp
index 79fd5509f2..8daccd7d61 100644
--- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp
+++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.cpp
@@ -90,13 +90,28 @@ namespace Terrain
void TerrainWorldDebuggerComponent::Activate()
{
- m_wireframeBounds = AZ::Aabb::CreateNull();
+ // Given the AuxGeom vertex limits, MaxSectorsToDraw is the max number of wireframe sectors we can draw without exceeding the
+ // limits. Since we want an N x N sector grid, take the square root to get the number of sectors in each direction.
+ m_sectorGridSize = aznumeric_cast(sqrtf(MaxSectorsToDraw));
+
+ // We're always going to keep the camera in the center square, so "round" downwards to an odd number of sectors if we currently
+ // have an even number. (If we added a sector, we'll go above the max sectors that we can draw with our vertex limits)
+ m_sectorGridSize = (m_sectorGridSize & 0x01) ? m_sectorGridSize : m_sectorGridSize - 1;
+
+ // Create our fixed set of sectors that we'll draw. By default, they'll all be constructed as dirty, so they'll get refreshed
+ // the first time we try to draw them. (If wireframe drawing is disabled, we'll never refresh them)
+ m_wireframeSectors.clear();
+ m_wireframeSectors.resize(m_sectorGridSize * m_sectorGridSize);
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect();
- RefreshCachedWireframeGrid(AZ::Aabb::CreateNull());
+ // Any time the world bounds potentially changes, notify that the terrain debugger's visibility bounds also changed.
+ // Otherwise, DisplayEntityViewport() won't get called at the appropriate times, since the visibility could get incorrectly
+ // culled out.
+ AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
+ &AzFramework::IEntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId());
}
void TerrainWorldDebuggerComponent::Deactivate()
@@ -105,7 +120,6 @@ namespace Terrain
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
- m_wireframeBounds = AZ::Aabb::CreateNull();
m_wireframeSectors.clear();
}
@@ -144,178 +158,254 @@ namespace Terrain
return GetWorldBounds();
}
- void TerrainWorldDebuggerComponent::DisplayEntityViewport(
- const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
+ void TerrainWorldDebuggerComponent::MarkDirtySectors(const AZ::Aabb& dirtyRegion)
{
- // Draw a wireframe box around the entire terrain world bounds
- if (m_configuration.m_drawWorldBounds)
+ // Create a 2D version of dirtyRegion that has Z set to min/max float values, so that we can just check for XY overlap with
+ // each sector.
+ const AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues(
+ dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), AZStd::numeric_limits::lowest(),
+ dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), AZStd::numeric_limits::max());
+
+ // For each sector that overlaps the dirty region (or all of them if the region is invalid), mark them as dirty so that
+ // they'll get refreshed the next time we need to draw them.
+ for (auto& sector : m_wireframeSectors)
{
- AZ::Color outlineColor(1.0f, 0.0f, 0.0f, 1.0f);
- AZ::Aabb aabb = GetWorldBounds();
-
- debugDisplay.SetColor(outlineColor);
- debugDisplay.DrawWireBox(aabb.GetMin(), aabb.GetMax());
- }
-
- // Draw a wireframe representation of the terrain surface
- if (m_configuration.m_drawWireframe && !m_wireframeSectors.empty())
- {
- // Start by assuming we'll draw the entire world.
- AZ::Aabb drawingAabb = GetWorldBounds();
-
- // Assuming we can get the camera, reduce the drawing bounds to a fixed distance around the camera.
- if (auto viewportContextRequests = AZ::RPI::ViewportContextRequests::Get(); viewportContextRequests)
+ if (!dirtyRegion2D.IsValid() || dirtyRegion2D.Overlaps(sector.m_aabb))
{
- // Get the current camera position.
- AZ::RPI::ViewportContextPtr viewportContext = viewportContextRequests->GetViewportContextById(viewportInfo.m_viewportId);
- AZ::Vector3 cameraPos = viewportContext->GetCameraTransform().GetTranslation();
-
- // Determine how far to draw in each direction in world space based on our MaxSectorsToDraw
- AZ::Vector2 queryResolution = AZ::Vector2(1.0f);
- AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
- queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
- AZ::Vector3 viewDistance(
- queryResolution.GetX() * SectorSizeInGridPoints * sqrtf(MaxSectorsToDraw),
- queryResolution.GetY() * SectorSizeInGridPoints * sqrtf(MaxSectorsToDraw),
- 0.0f);
-
- // Create an AABB around the camera based on how far we want to be able to draw in each direction and clamp the
- // drawing AABB to it.
- AZ::Aabb cameraAabb = AZ::Aabb::CreateFromMinMax(
- AZ::Vector3(
- cameraPos.GetX() - viewDistance.GetX(), cameraPos.GetY() - viewDistance.GetY(), drawingAabb.GetMin().GetZ()),
- AZ::Vector3(
- cameraPos.GetX() + viewDistance.GetX(), cameraPos.GetY() + viewDistance.GetY(), drawingAabb.GetMin().GetZ()));
- drawingAabb.Clamp(cameraAabb);
- }
-
- // For each sector, if it appears within our view distance, draw it.
- for (auto& sector : m_wireframeSectors)
- {
- if (drawingAabb.Overlaps(sector.m_aabb))
- {
- if (!sector.m_lineVertices.empty())
- {
- const AZ::Color primaryColor = AZ::Color(0.25f, 0.25f, 0.25f, 1.0f);
- debugDisplay.DrawLines(sector.m_lineVertices, primaryColor);
- }
- else
- {
- AZ_Warning("Debug", false, "empty sector!");
- }
- }
+ sector.m_isDirty = true;
}
}
}
- void TerrainWorldDebuggerComponent::RefreshCachedWireframeGrid(const AZ::Aabb& dirtyRegion)
+ void TerrainWorldDebuggerComponent::DrawWorldBounds(AzFramework::DebugDisplayRequests& debugDisplay)
{
- // Get the terrain world bounds and grid resolution.
-
- AZ::Aabb worldBounds = GetWorldBounds();
-
- AZ::Vector2 queryResolution = AZ::Vector2(1.0f);
- AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
- queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
-
- // Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current
- // world bounds, we still update the wireframe accordingly.
- AZ::Aabb dirtyRegion2D = AZ::Aabb::CreateFromMinMaxValues(
- dirtyRegion.GetMin().GetX(), dirtyRegion.GetMin().GetY(), worldBounds.GetMin().GetZ(),
- dirtyRegion.GetMax().GetX(), dirtyRegion.GetMax().GetY(), worldBounds.GetMax().GetZ());
-
- // Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square.
- // So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--).
- const float xSectorSize = (queryResolution.GetX() * SectorSizeInGridPoints);
- const float ySectorSize = (queryResolution.GetY() * SectorSizeInGridPoints);
-
- // Calculate the total number of sectors to cache. The world bounds might not be evenly divisible by sector bounds, so we add
- // an extra sector's worth of size in each direction so that clamping down to an integer still accounts for that fractional sector.
- const int32_t numSectorsX = aznumeric_cast((worldBounds.GetXExtent() + xSectorSize) / xSectorSize);
- const int32_t numSectorsY = aznumeric_cast((worldBounds.GetYExtent() + ySectorSize) / ySectorSize);
-
- // If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it
- // with WireframeSector entries with the proper AABB sizes.
- if (!m_wireframeBounds.IsValid() || !dirtyRegion2D.IsValid() || !m_wireframeBounds.IsClose(worldBounds))
+ if (!m_configuration.m_drawWorldBounds)
{
- m_wireframeBounds = worldBounds;
-
- m_wireframeSectors.clear();
- m_wireframeSectors.reserve(numSectorsX * numSectorsY);
-
- for (int32_t ySector = 0; ySector < numSectorsY; ySector++)
- {
- for (int32_t xSector = 0; xSector < numSectorsX; xSector++)
- {
- // For each sector, set up the AABB for the sector and reserve memory for the line vertices.
- WireframeSector sector;
- sector.m_lineVertices.reserve(VerticesPerSector);
- sector.m_aabb = AZ::Aabb::CreateFromMinMax(
- AZ::Vector3(
- worldBounds.GetMin().GetX() + (xSector * xSectorSize), worldBounds.GetMin().GetY() + (ySector * ySectorSize),
- worldBounds.GetMin().GetZ()),
- AZ::Vector3(
- worldBounds.GetMin().GetX() + ((xSector + 1) * xSectorSize),
- worldBounds.GetMin().GetY() + ((ySector + 1) * ySectorSize), worldBounds.GetMax().GetZ()));
-
- sector.m_aabb.Clamp(worldBounds);
-
- m_wireframeSectors.push_back(AZStd::move(sector));
- }
- }
-
- // Notify the visibility system that our bounds have changed.
- AzFramework::IEntityBoundsUnionRequestBus::Broadcast(
- &AzFramework::IEntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId());
+ return;
}
- // For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data.
- for (auto& sector : m_wireframeSectors)
+ // Draw a wireframe box around the entire terrain world bounds
+ AZ::Color outlineColor(1.0f, 0.0f, 0.0f, 1.0f);
+ AZ::Aabb aabb = GetWorldBounds();
+
+ debugDisplay.SetColor(outlineColor);
+ debugDisplay.DrawWireBox(aabb.GetMin(), aabb.GetMax());
+ }
+
+ void TerrainWorldDebuggerComponent::DrawWireframe(
+ const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
+ {
+ AZ_PROFILE_FUNCTION(Entity);
+
+ if (!m_configuration.m_drawWireframe)
{
- if (dirtyRegion2D.IsValid() && !dirtyRegion2D.Overlaps(sector.m_aabb))
- {
- continue;
- }
+ return;
+ }
- sector.m_lineVertices.clear();
+ /* This draws a wireframe centered on the camera that extends out to a certain distance at all times. To reduce the amount of
+ * recalculations we need to do on each camera movement, we divide the world into a conceptual grid of sectors, where each sector
+ * contains a fixed number of terrain height points. So for example, if the terrain has height data at 1 m spacing, the sectors
+ * might be 10 m x 10 m in size. If the height data is spaced at 0.5 m, the sectors might be 5 m x 5 m in size. The wireframe
+ * draws N x N sectors centered around the camera, as determined by m_sectorGridSize. So a gridSize of 7 with a sector size of
+ * 10 m means that we'll be drawing 7 x 7 sectors, or 70 m x 70 m, centered around the camera. Each time the camera moves into
+ * a new sector, we refresh the changed sectors before drawing them.
+ *
+ * The only tricky bit to this design is the way the sectors are stored and indexed. They're stored in a single vector as NxN
+ * entries, so they would normally be indexed as (y * N) + x. Since we want this to be centered on the camera, the easy answer
+ * would be to take the camera position - (N / 2) (since we're centering) as the relative offset to the first entry. But this
+ * would mean that the entire set of entries would change every time we move the camera. For example, if we had 5 entries,
+ * they might map to 0-4, 1-5, 2-6, 3-7, etc as the camera moves.
+ *
+ * Instead, we use mod (%) to rotate our indices around, so it would go (0 1 2 3 4), (5 1 2 3 4), (5 6 2 3 4), (5 6 7 3 4), etc
+ * as the camera moves. For negative entries, we rotate the indices in reverse, so that we get results like (0 1 2 3 4),
+ * (0 1 2 3 -1), (0 1 2 -2 -1), (0 1 -3 -2 -1), etc. This way we always have the correct range of sectors, and sectors that have
+ * remained visible are left alone and don't need to be updated again.
+ */
- for (float y = sector.m_aabb.GetMin().GetY(); y < sector.m_aabb.GetMax().GetY(); y += queryResolution.GetY())
+ // Get the terrain world bounds
+ AZ::Aabb worldBounds = GetWorldBounds();
+ float worldMinZ = worldBounds.GetMin().GetZ();
+
+ // Get the terrain height data resolution
+ AZ::Vector2 heightDataResolution = AZ::Vector2(1.0f);
+ AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
+ heightDataResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
+
+ // Get the size of a wireframe sector in world space
+ const AZ::Vector2 sectorSize = heightDataResolution * SectorSizeInGridPoints;
+
+ // Try to get the current camera position, or default to (0,0) if we can't.
+ AZ::Vector3 cameraPos = AZ::Vector3::CreateZero();
+ if (auto viewportContextRequests = AZ::RPI::ViewportContextRequests::Get(); viewportContextRequests)
+ {
+ AZ::RPI::ViewportContextPtr viewportContext = viewportContextRequests->GetViewportContextById(viewportInfo.m_viewportId);
+ cameraPos = viewportContext->GetCameraTransform().GetTranslation();
+ }
+
+ // Convert our camera position to a wireframe grid sector. We first convert from world space to sector space by dividing by
+ // sectorSize, so that integer values are sectors, and fractional values are the distance within the sector. Then we get the
+ // floor, so that we consistently get the next lowest integer - i.e. 2.3 -> 2, and -2.3 -> -3. This gives us consistent behavior
+ // across both positive and negative positions.
+ AZ::Vector2 gridPosition = AZ::Vector2(cameraPos.GetX(), cameraPos.GetY()) / sectorSize;
+ int32_t cameraSectorX = aznumeric_cast(gridPosition.GetFloor().GetX());
+ int32_t cameraSectorY = aznumeric_cast