From c00d7ff6585c3e1240241c8d7af9dbc556f16a06 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Fri, 3 Dec 2021 00:56:40 -0800 Subject: [PATCH 01/39] Small formatting fix to jinja for autocomponent's behavior context Signed-off-by: Gene Walters --- .../AutoGen/AutoComponent_Source.jinja | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) 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 %} From 308a2e4ce3771326832828c7379d3d0c6922552a Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Fri, 3 Dec 2021 15:29:55 -0800 Subject: [PATCH 02/39] Add RPC script for Authority->Autonomous message; the authority is telling each player their player id (in the order than they joined) Signed-off-by: Gene Walters --- ...tworkTestPlayerComponent.AutoComponent.xml | 4 +- .../AutoComponent_RPC.prefab | 627 ++++++ .../AutoComponent_RPC.scriptcanvas | 1958 +++++++++++++++++ .../GlobalGameData.scriptcanvas | 662 ++++++ .../GlobalGameData.scriptevents | 106 + .../AutoComponent_RPC/Player.prefab | 195 ++ .../Multiplayer/AutoComponent_RPC/tags.txt | 12 + 7 files changed, 3562 insertions(+), 2 deletions(-) create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptcanvas create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/GlobalGameData.scriptevents create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/Player.prefab create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/tags.txt diff --git a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml index 46d6191835..07004c4f1e 100644 --- a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml +++ b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml @@ -19,8 +19,8 @@ - - + + 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..a9bec3251a --- /dev/null +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab @@ -0,0 +1,627 @@ +{ + "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]" + ] + }, + "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": 15661114386016447348 + } + } + }, + "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 + } + } + } + } +} \ 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..474266e7a0 --- /dev/null +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas @@ -0,0 +1,1958 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "ScriptCanvasData", + "ClassData": { + "m_scriptCanvas": { + "Id": { + "id": 45584036346395 + }, + "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": 45592626280987 + }, + "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": 45605511182875 + } + } + ], + "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": 45605511182875 + } + } + ], + "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_parameterNames": [ + { + "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": 45609806150171 + }, + "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": "EntityId: 0" + } + ], + "methodType": 2, + "methodName": "IsNetEntityRoleAuthority", + "className": "NetBindComponent", + "resultSlotIDs": [ + {} + ], + "inputSlots": [ + { + "m_id": "{4EAB8D16-C0B4-44E1-885D-8E6754CD1A55}" + } + ], + "prettyClassName": "NetBindComponent" + } + } + }, + { + "Id": { + "id": 45588331313691 + }, + "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_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": 45605511182875 + }, + "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": 45618396084763 + }, + "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": 45631280986651 + }, + "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": 45601216215579 + }, + "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_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": 45626986019355 + }, + "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": 45596921248283 + }, + "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": 45622691052059 + }, + "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": 45614101117467 + }, + "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": 45635575953947 + }, + "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Out), destEndpoint=(If: In)", + "Components": { + "Component_[3645153988172561571]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3645153988172561571, + "sourceEndpoint": { + "nodeId": { + "id": 45609806150171 + }, + "slotId": { + "m_id": "{1071F455-D5A0-4C7A-AF91-648A0F197885}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45614101117467 + }, + "slotId": { + "m_id": "{8AE7F430-C4A9-444E-B42A-BDDFE96C387A}" + } + } + } + } + }, + { + "Id": { + "id": 45639870921243 + }, + "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Is Role Authority), destEndpoint=(If: Condition)", + "Components": { + "Component_[17942926291787646743]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17942926291787646743, + "sourceEndpoint": { + "nodeId": { + "id": 45609806150171 + }, + "slotId": { + "m_id": "{BA87B4ED-9A52-4A0D-8920-CD0ED4E839EA}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45614101117467 + }, + "slotId": { + "m_id": "{3E89B67B-1EF2-4DAB-8028-59A97527F3DF}" + } + } + } + } + }, + { + "Id": { + "id": 45644165888539 + }, + "Name": "srcEndpoint=(If: True), destEndpoint=(Send Script Event: In)", + "Components": { + "Component_[3298020356088639785]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 3298020356088639785, + "sourceEndpoint": { + "nodeId": { + "id": 45614101117467 + }, + "slotId": { + "m_id": "{78C92586-C0E1-449F-8EB2-B1CE3BFC8AE7}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45618396084763 + }, + "slotId": { + "m_id": "{2FF1A0A1-59C6-4DCF-AE1D-296BD5964D4E}" + } + } + } + } + }, + { + "Id": { + "id": 45648460855835 + }, + "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": 45618396084763 + }, + "slotId": { + "m_id": "{F2955681-A901-432D-99A3-53818F46CDE7}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45631280986651 + }, + "slotId": { + "m_id": "{23DBACD8-7784-4E18-A51C-258B25DF4C19}" + } + } + } + } + }, + { + "Id": { + "id": 45652755823131 + }, + "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": 45631280986651 + }, + "slotId": { + "m_id": "{19C850D7-7F1E-4D41-AFD9-5A4FA03D54F0}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45596921248283 + }, + "slotId": { + "m_id": "{1FDB4A9A-D46A-49E8-B734-475230EB7C06}" + } + } + } + } + }, + { + "Id": { + "id": 45657050790427 + }, + "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: Out), destEndpoint=(Print: In)", + "Components": { + "Component_[17367899649716276273]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17367899649716276273, + "sourceEndpoint": { + "nodeId": { + "id": 45596921248283 + }, + "slotId": { + "m_id": "{964EAD23-8DF5-47A9-BF07-1E5C7CD0CC6C}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45588331313691 + }, + "slotId": { + "m_id": "{F3ED8C08-D751-492A-A39A-7B7734727A5A}" + } + } + } + } + }, + { + "Id": { + "id": 45661345757723 + }, + "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(TimeDelay: Start)", + "Components": { + "Component_[13463448697016307967]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 13463448697016307967, + "sourceEndpoint": { + "nodeId": { + "id": 45622691052059 + }, + "slotId": { + "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45626986019355 + }, + "slotId": { + "m_id": "{D2337DC6-F126-4254-B296-2DE6BA03993F}" + } + } + } + } + }, + { + "Id": { + "id": 45665640725019 + }, + "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": 45605511182875 + }, + "slotId": { + "m_id": "{7262E6CD-BADF-4DD0-8FCE-3A02C3F31CC8}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45592626280987 + }, + "slotId": { + "m_id": "{8C7E88EA-8FEE-45D0-9A2E-78BC4A18F508}" + } + } + } + } + }, + { + "Id": { + "id": 45669935692315 + }, + "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": 45605511182875 + }, + "slotId": { + "m_id": "{1A6BCB27-F99F-4B6B-89CC-F2BE2A698331}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45592626280987 + }, + "slotId": { + "m_id": "{46E6B649-CAA4-4318-960D-5E4854E21BB2}" + } + } + } + } + }, + { + "Id": { + "id": 45674230659611 + }, + "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": 45592626280987 + }, + "slotId": { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45601216215579 + }, + "slotId": { + "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}" + } + } + } + } + }, + { + "Id": { + "id": 45678525626907 + }, + "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": 45592626280987 + }, + "slotId": { + "m_id": "{586BE222-2DEA-4E08-968F-07CA4EB96C54}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45601216215579 + }, + "slotId": { + "m_id": "{D206E5BC-3746-4A18-80C7-DBD335FD05FE}" + } + } + } + } + }, + { + "Id": { + "id": 45682820594203 + }, + "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": 45622691052059 + }, + "slotId": { + "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45605511182875 + }, + "slotId": { + "m_id": "{211BD78B-E01E-44F8-B44E-2FD988047BE9}" + } + } + } + } + }, + { + "Id": { + "id": 45687115561499 + }, + "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(IsNetEntityRoleAuthority: In)", + "Components": { + "Component_[5772191552099194657]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 5772191552099194657, + "sourceEndpoint": { + "nodeId": { + "id": 45626986019355 + }, + "slotId": { + "m_id": "{25568B15-5372-4C2D-8280-2B433251EACA}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 45609806150171 + }, + "slotId": { + "m_id": "{B6C4BE5E-CDE4-4EC7-98D0-A019CD80041C}" + } + } + } + } + } + ], + "m_scriptEventAssets": [ + [ + { + "id": 45631280986651 + }, + {} + ], + [ + { + "id": 45618396084763 + }, + {} + ] + ] + }, + "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}", + "versionData": { + "_grammarVersion": 1, + "_runtimeVersion": 1, + "_fileVersion": 1 + }, + "m_variableCounter": 1, + "GraphCanvasData": [ + { + "Key": { + "id": 45584036346395 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "Scale": 0.9670775714052148, + "AnchorX": 180.9575653076172, + "AnchorY": 127.18731689453125 + } + } + } + } + }, + { + "Key": { + "id": 45588331313691 + }, + "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": 45592626280987 + }, + "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": [ + 980.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": 45596921248283 + }, + "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": 45601216215579 + }, + "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": [ + 1160.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": 45605511182875 + }, + "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": 45609806150171 + }, + "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": 45614101117467 + }, + "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": 45618396084763 + }, + "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": 45622691052059 + }, + "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": 45626986019355 + }, + "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": 45631280986651 + }, + "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}" + } + } + } + } + ], + "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/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 From 58872581c5fe794c81db20711eb1b9ce30fd544a Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Mon, 6 Dec 2021 08:52:54 -0800 Subject: [PATCH 03/39] Wip.RPC testing Authority->Client and Server->Authory. Ran into a blocker so checking in now to save work Signed-off-by: Gene Walters --- ...tworkTestPlayerComponent.AutoComponent.xml | 6 +- .../AutoComponent_RPC.prefab | 125 +++++- .../AutoComponent_RPC.scriptcanvas | 394 ++++++++++-------- 3 files changed, 341 insertions(+), 184 deletions(-) diff --git a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml index 07004c4f1e..9037db43ee 100644 --- a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml +++ b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml @@ -29,10 +29,10 @@ - + - - + + diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab index a9bec3251a..74dbcd993f 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab @@ -20,7 +20,9 @@ "Id": 14126657869720434043, "Child Entity Order": [ "Entity_[1176639161715]", - "Entity_[12685882829720]" + "Entity_[12685882829720]", + "Entity_[31145534614197]", + "Entity_[31145534614197]" ] }, "Component_[15230859088967841193]": { @@ -205,7 +207,7 @@ "Controller": { "Configuration": { "Field of View": 55.0, - "EditorEntityId": 15661114386016447348 + "EditorEntityId": 11050384689878106598 } } }, @@ -622,6 +624,125 @@ "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_[14300469789192169678]": { + "$type": "GenericComponentWrapper", + "Id": 14300469789192169678, + "m_template": { + "$type": "AutomatedTesting::NetworkTestPlayerComponent" + } + }, + "Component_[14607413934411389854]": { + "$type": "EditorInspectorComponent", + "Id": 14607413934411389854 + }, + "Component_[15494977028055234270]": { + "$type": "EditorDisabledCompositionComponent", + "Id": 15494977028055234270 + }, + "Component_[16325088972532345964]": { + "$type": "EditorLockComponent", + "Id": 16325088972532345964 + }, + "Component_[1639017114849961416]": { + "$type": "GenericComponentWrapper", + "Id": 1639017114849961416, + "m_template": { + "$type": "Multiplayer::LocalPredictionPlayerInputComponent" + } + }, + "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_[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 index 474266e7a0..54bb46e379 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 45584036346395 + "id": 49433505360565 }, "Name": "AutoComponent_RPC", "Components": { @@ -43,7 +43,7 @@ "m_nodes": [ { "Id": { - "id": 45592626280987 + "id": 49442095295157 }, "Name": "SC-EventNode(AuthorityToAutonomous_PlayerNumber Notify Event)", "Components": { @@ -66,7 +66,7 @@ { "$type": "RestrictedNodeContract", "m_nodeId": { - "id": 45605511182875 + "id": 49472160066229 } } ], @@ -176,7 +176,7 @@ { "$type": "RestrictedNodeContract", "m_nodeId": { - "id": 45605511182875 + "id": 49472160066229 } } ], @@ -201,6 +201,24 @@ "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}" }, @@ -209,6 +227,24 @@ } ], "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}" }, @@ -225,7 +261,7 @@ }, { "Id": { - "id": 45609806150171 + "id": 49467865098933 }, "Name": "SC-Node(IsNetEntityRoleAuthority)", "Components": { @@ -309,7 +345,7 @@ "value": { "id": 2901262558 }, - "label": "EntityId: 0" + "label": "Entity Id" } ], "methodType": 2, @@ -329,7 +365,7 @@ }, { "Id": { - "id": 45588331313691 + "id": 49446390262453 }, "Name": "SC-Node(Print)", "Components": { @@ -432,7 +468,7 @@ }, { "Id": { - "id": 45605511182875 + "id": 49472160066229 }, "Name": "SC-Node(GetAuthorityToAutonomous_PlayerNumberEventByEntityId)", "Components": { @@ -537,7 +573,7 @@ }, { "Id": { - "id": 45618396084763 + "id": 49476455033525 }, "Name": "SendScriptEvent", "Components": { @@ -599,7 +635,7 @@ }, { "Id": { - "id": 45631280986651 + "id": 49437800327861 }, "Name": "SendScriptEvent", "Components": { @@ -689,7 +725,7 @@ }, { "Id": { - "id": 45601216215579 + "id": 49454980197045 }, "Name": "SC-Node(Print)", "Components": { @@ -788,7 +824,7 @@ }, { "Id": { - "id": 45626986019355 + "id": 49459275164341 }, "Name": "SC-Node(TimeDelayNodeableNode)", "Components": { @@ -927,7 +963,7 @@ }, { "Id": { - "id": 45596921248283 + "id": 49480750000821 }, "Name": "SC-Node(AuthorityToAutonomous_PlayerNumberByEntityId)", "Components": { @@ -1045,7 +1081,7 @@ }, { "Id": { - "id": 45622691052059 + "id": 49450685229749 }, "Name": "SC-Node(Start)", "Components": { @@ -1075,7 +1111,7 @@ }, { "Id": { - "id": 45614101117467 + "id": 49463570131637 }, "Name": "SC-Node(Gate)", "Components": { @@ -1167,7 +1203,7 @@ "m_connections": [ { "Id": { - "id": 45635575953947 + "id": 49485044968117 }, "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Out), destEndpoint=(If: In)", "Components": { @@ -1176,7 +1212,7 @@ "Id": 3645153988172561571, "sourceEndpoint": { "nodeId": { - "id": 45609806150171 + "id": 49467865098933 }, "slotId": { "m_id": "{1071F455-D5A0-4C7A-AF91-648A0F197885}" @@ -1184,7 +1220,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45614101117467 + "id": 49463570131637 }, "slotId": { "m_id": "{8AE7F430-C4A9-444E-B42A-BDDFE96C387A}" @@ -1195,7 +1231,7 @@ }, { "Id": { - "id": 45639870921243 + "id": 49489339935413 }, "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Is Role Authority), destEndpoint=(If: Condition)", "Components": { @@ -1204,7 +1240,7 @@ "Id": 17942926291787646743, "sourceEndpoint": { "nodeId": { - "id": 45609806150171 + "id": 49467865098933 }, "slotId": { "m_id": "{BA87B4ED-9A52-4A0D-8920-CD0ED4E839EA}" @@ -1212,7 +1248,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45614101117467 + "id": 49463570131637 }, "slotId": { "m_id": "{3E89B67B-1EF2-4DAB-8028-59A97527F3DF}" @@ -1223,7 +1259,7 @@ }, { "Id": { - "id": 45644165888539 + "id": 49493634902709 }, "Name": "srcEndpoint=(If: True), destEndpoint=(Send Script Event: In)", "Components": { @@ -1232,7 +1268,7 @@ "Id": 3298020356088639785, "sourceEndpoint": { "nodeId": { - "id": 45614101117467 + "id": 49463570131637 }, "slotId": { "m_id": "{78C92586-C0E1-449F-8EB2-B1CE3BFC8AE7}" @@ -1240,7 +1276,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45618396084763 + "id": 49476455033525 }, "slotId": { "m_id": "{2FF1A0A1-59C6-4DCF-AE1D-296BD5964D4E}" @@ -1251,7 +1287,7 @@ }, { "Id": { - "id": 45648460855835 + "id": 49497929870005 }, "Name": "srcEndpoint=(Send Script Event: Out), destEndpoint=(Send Script Event: In)", "Components": { @@ -1260,7 +1296,7 @@ "Id": 5482500452520221078, "sourceEndpoint": { "nodeId": { - "id": 45618396084763 + "id": 49476455033525 }, "slotId": { "m_id": "{F2955681-A901-432D-99A3-53818F46CDE7}" @@ -1268,7 +1304,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45631280986651 + "id": 49437800327861 }, "slotId": { "m_id": "{23DBACD8-7784-4E18-A51C-258B25DF4C19}" @@ -1279,7 +1315,7 @@ }, { "Id": { - "id": 45652755823131 + "id": 49502224837301 }, "Name": "srcEndpoint=(Send Script Event: Out), destEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: In)", "Components": { @@ -1288,7 +1324,7 @@ "Id": 16156808606878296902, "sourceEndpoint": { "nodeId": { - "id": 45631280986651 + "id": 49437800327861 }, "slotId": { "m_id": "{19C850D7-7F1E-4D41-AFD9-5A4FA03D54F0}" @@ -1296,7 +1332,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45596921248283 + "id": 49480750000821 }, "slotId": { "m_id": "{1FDB4A9A-D46A-49E8-B734-475230EB7C06}" @@ -1307,7 +1343,7 @@ }, { "Id": { - "id": 45657050790427 + "id": 49506519804597 }, "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: Out), destEndpoint=(Print: In)", "Components": { @@ -1316,7 +1352,7 @@ "Id": 17367899649716276273, "sourceEndpoint": { "nodeId": { - "id": 45596921248283 + "id": 49480750000821 }, "slotId": { "m_id": "{964EAD23-8DF5-47A9-BF07-1E5C7CD0CC6C}" @@ -1324,7 +1360,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45588331313691 + "id": 49446390262453 }, "slotId": { "m_id": "{F3ED8C08-D751-492A-A39A-7B7734727A5A}" @@ -1335,7 +1371,7 @@ }, { "Id": { - "id": 45661345757723 + "id": 49510814771893 }, "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(TimeDelay: Start)", "Components": { @@ -1344,7 +1380,7 @@ "Id": 13463448697016307967, "sourceEndpoint": { "nodeId": { - "id": 45622691052059 + "id": 49450685229749 }, "slotId": { "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}" @@ -1352,7 +1388,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45626986019355 + "id": 49459275164341 }, "slotId": { "m_id": "{D2337DC6-F126-4254-B296-2DE6BA03993F}" @@ -1363,7 +1399,7 @@ }, { "Id": { - "id": 45665640725019 + "id": 49515109739189 }, "Name": "srcEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: Event), destEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: AuthorityToAutonomous_PlayerNumber Notify Event)", "Components": { @@ -1372,7 +1408,7 @@ "Id": 9477060643694737434, "sourceEndpoint": { "nodeId": { - "id": 45605511182875 + "id": 49472160066229 }, "slotId": { "m_id": "{7262E6CD-BADF-4DD0-8FCE-3A02C3F31CC8}" @@ -1380,7 +1416,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45592626280987 + "id": 49442095295157 }, "slotId": { "m_id": "{8C7E88EA-8FEE-45D0-9A2E-78BC4A18F508}" @@ -1391,7 +1427,7 @@ }, { "Id": { - "id": 45669935692315 + "id": 49519404706485 }, "Name": "srcEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: Out), destEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: Connect)", "Components": { @@ -1400,7 +1436,7 @@ "Id": 16543380658701699472, "sourceEndpoint": { "nodeId": { - "id": 45605511182875 + "id": 49472160066229 }, "slotId": { "m_id": "{1A6BCB27-F99F-4B6B-89CC-F2BE2A698331}" @@ -1408,7 +1444,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45592626280987 + "id": 49442095295157 }, "slotId": { "m_id": "{46E6B649-CAA4-4318-960D-5E4854E21BB2}" @@ -1419,7 +1455,7 @@ }, { "Id": { - "id": 45674230659611 + "id": 49523699673781 }, "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: player_number), destEndpoint=(Print: Value)", "Components": { @@ -1428,7 +1464,7 @@ "Id": 864863482692400383, "sourceEndpoint": { "nodeId": { - "id": 45592626280987 + "id": 49442095295157 }, "slotId": { "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" @@ -1436,7 +1472,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45601216215579 + "id": 49454980197045 }, "slotId": { "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}" @@ -1447,7 +1483,7 @@ }, { "Id": { - "id": 45678525626907 + "id": 49527994641077 }, "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: OnEvent), destEndpoint=(Print: In)", "Components": { @@ -1456,7 +1492,7 @@ "Id": 2451057837093425972, "sourceEndpoint": { "nodeId": { - "id": 45592626280987 + "id": 49442095295157 }, "slotId": { "m_id": "{586BE222-2DEA-4E08-968F-07CA4EB96C54}" @@ -1464,7 +1500,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45601216215579 + "id": 49454980197045 }, "slotId": { "m_id": "{D206E5BC-3746-4A18-80C7-DBD335FD05FE}" @@ -1475,7 +1511,7 @@ }, { "Id": { - "id": 45682820594203 + "id": 49532289608373 }, "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: In)", "Components": { @@ -1484,7 +1520,7 @@ "Id": 12180981889720748145, "sourceEndpoint": { "nodeId": { - "id": 45622691052059 + "id": 49450685229749 }, "slotId": { "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}" @@ -1492,7 +1528,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45605511182875 + "id": 49472160066229 }, "slotId": { "m_id": "{211BD78B-E01E-44F8-B44E-2FD988047BE9}" @@ -1503,7 +1539,7 @@ }, { "Id": { - "id": 45687115561499 + "id": 49536584575669 }, "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(IsNetEntityRoleAuthority: In)", "Components": { @@ -1512,7 +1548,7 @@ "Id": 5772191552099194657, "sourceEndpoint": { "nodeId": { - "id": 45626986019355 + "id": 49459275164341 }, "slotId": { "m_id": "{25568B15-5372-4C2D-8280-2B433251EACA}" @@ -1520,7 +1556,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 45609806150171 + "id": 49467865098933 }, "slotId": { "m_id": "{B6C4BE5E-CDE4-4EC7-98D0-A019CD80041C}" @@ -1533,13 +1569,13 @@ "m_scriptEventAssets": [ [ { - "id": 45631280986651 + "id": 49437800327861 }, {} ], [ { - "id": 45618396084763 + "id": 49476455033525 }, {} ] @@ -1555,16 +1591,16 @@ "GraphCanvasData": [ { "Key": { - "id": 45584036346395 + "id": 49433505360565 }, "Value": { "ComponentData": { "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { "$type": "SceneComponentSaveData", "ViewParams": { - "Scale": 0.9670775714052148, - "AnchorX": 180.9575653076172, - "AnchorY": 127.18731689453125 + "Scale": 0.8220159356944325, + "AnchorX": 72.99128723144531, + "AnchorY": 74.20780944824219 } } } @@ -1572,7 +1608,69 @@ }, { "Key": { - "id": 45588331313691 + "id": 49437800327861 + }, + "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": 49442095295157 + }, + "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": 49446390262453 }, "Value": { "ComponentData": { @@ -1602,7 +1700,7 @@ }, { "Key": { - "id": 45592626280987 + "id": 49450685229749 }, "Value": { "ComponentData": { @@ -1611,60 +1709,28 @@ }, "{328FF15C-C302-458F-A43D-E1794DE0904E}": { "$type": "GeneralNodeTitleComponentSaveData", - "PaletteOverride": "HandlerNodeTitlePalette" + "PaletteOverride": "TimeNodeTitlePalette" }, "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 980.0, - 620.0 + 40.0, + 280.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".azeventhandler" + "$type": "StylingComponentSaveData" }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{F112D217-FAC8-4577-B67A-DFDF47842F7D}" + "PersistentId": "{B4CB62D9-9965-42E2-81FF-BDADDBD14CBE}" } } } }, { "Key": { - "id": 45596921248283 - }, - "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": 45601216215579 + "id": 49454980197045 }, "Value": { "ComponentData": { @@ -1678,7 +1744,7 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 1160.0, + 1400.0, 620.0 ] }, @@ -1694,7 +1760,7 @@ }, { "Key": { - "id": 45605511182875 + "id": 49459275164341 }, "Value": { "ComponentData": { @@ -1703,29 +1769,58 @@ }, "{328FF15C-C302-458F-A43D-E1794DE0904E}": { "$type": "GeneralNodeTitleComponentSaveData", - "PaletteOverride": "MethodNodeTitlePalette" + "PaletteOverride": "TimeNodeTitlePalette" }, "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 380.0, - 620.0 + 200.0, + 260.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".method" + "$type": "StylingComponentSaveData" }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{BBBE60FC-0215-4A5A-BEBE-4FC869E5EECE}" + "PersistentId": "{5DF902FE-B0C4-4563-B2BA-17996B24E211}" } } } }, { "Key": { - "id": 45609806150171 + "id": 49463570131637 + }, + "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": 49467865098933 }, "Value": { "ComponentData": { @@ -1756,7 +1851,7 @@ }, { "Key": { - "id": 45614101117467 + "id": 49472160066229 }, "Value": { "ComponentData": { @@ -1765,28 +1860,29 @@ }, "{328FF15C-C302-458F-A43D-E1794DE0904E}": { "$type": "GeneralNodeTitleComponentSaveData", - "PaletteOverride": "LogicNodeTitlePalette" + "PaletteOverride": "MethodNodeTitlePalette" }, "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 940.0, - 260.0 + 380.0, + 620.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData" + "$type": "StylingComponentSaveData", + "SubStyle": ".method" }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{53C8DAF1-FFF3-4AF8-A7FB-BC0B4E492B37}" + "PersistentId": "{BBBE60FC-0215-4A5A-BEBE-4FC869E5EECE}" } } } }, { "Key": { - "id": 45618396084763 + "id": 49476455033525 }, "Value": { "ComponentData": { @@ -1817,67 +1913,7 @@ }, { "Key": { - "id": 45622691052059 - }, - "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": 45626986019355 - }, - "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": 45631280986651 + "id": 49480750000821 }, "Value": { "ComponentData": { @@ -1891,7 +1927,7 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 1540.0, + 1840.0, 260.0 ] }, @@ -1901,7 +1937,7 @@ }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{FD844BFD-72DA-44BA-B92A-AFDB58263B91}" + "PersistentId": "{59FFF3C1-2D7B-47EA-AE35-ED15DCB95CC8}" } } } From b0be54fa16a6257dd0b14f3e5df5fc9dc7b5e675 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Tue, 7 Dec 2021 14:46:58 -0800 Subject: [PATCH 04/39] Small script changes. WiP Signed-off-by: Gene Walters --- .../AutoComponent_RPC.prefab | 29 +- .../AutoComponent_RPC.scriptcanvas | 1332 +++++++++++++---- 2 files changed, 1041 insertions(+), 320 deletions(-) diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab index 74dbcd993f..758cb4fca8 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.prefab @@ -21,7 +21,6 @@ "Child Entity Order": [ "Entity_[1176639161715]", "Entity_[12685882829720]", - "Entity_[31145534614197]", "Entity_[31145534614197]" ] }, @@ -641,17 +640,17 @@ "$type": "EditorVisibilityComponent", "Id": 14169903623243423134 }, - "Component_[14300469789192169678]": { - "$type": "GenericComponentWrapper", - "Id": 14300469789192169678, - "m_template": { - "$type": "AutomatedTesting::NetworkTestPlayerComponent" - } - }, "Component_[14607413934411389854]": { "$type": "EditorInspectorComponent", "Id": 14607413934411389854 }, + "Component_[15396284312416541768]": { + "$type": "GenericComponentWrapper", + "Id": 15396284312416541768, + "m_template": { + "$type": "Multiplayer::LocalPredictionPlayerInputComponent" + } + }, "Component_[15494977028055234270]": { "$type": "EditorDisabledCompositionComponent", "Id": 15494977028055234270 @@ -660,13 +659,6 @@ "$type": "EditorLockComponent", "Id": 16325088972532345964 }, - "Component_[1639017114849961416]": { - "$type": "GenericComponentWrapper", - "Id": 1639017114849961416, - "m_template": { - "$type": "Multiplayer::LocalPredictionPlayerInputComponent" - } - }, "Component_[1986030426392465743]": { "$type": "EditorEntitySortComponent", "Id": 1986030426392465743 @@ -738,6 +730,13 @@ "$type": "NetBindComponent" } }, + "Component_[9816897251206708579]": { + "$type": "GenericComponentWrapper", + "Id": 9816897251206708579, + "m_template": { + "$type": "AutomatedTesting::NetworkTestPlayerComponent" + } + }, "Component_[9880860858035405475]": { "$type": "EditorOnlyEntityComponent", "Id": 9880860858035405475 diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas index 54bb46e379..72cc980550 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 49433505360565 + "id": 8702689999760 }, "Name": "AutoComponent_RPC", "Components": { @@ -43,7 +43,7 @@ "m_nodes": [ { "Id": { - "id": 49442095295157 + "id": 8706984967056 }, "Name": "SC-EventNode(AuthorityToAutonomous_PlayerNumber Notify Event)", "Components": { @@ -66,7 +66,7 @@ { "$type": "RestrictedNodeContract", "m_nodeId": { - "id": 49472160066229 + "id": 8719869868944 } } ], @@ -176,7 +176,7 @@ { "$type": "RestrictedNodeContract", "m_nodeId": { - "id": 49472160066229 + "id": 8719869868944 } } ], @@ -201,6 +201,366 @@ "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}" }, @@ -227,6 +587,366 @@ } ], "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}" }, @@ -261,7 +981,7 @@ }, { "Id": { - "id": 49467865098933 + "id": 8724164836240 }, "Name": "SC-Node(IsNetEntityRoleAuthority)", "Components": { @@ -365,7 +1085,7 @@ }, { "Id": { - "id": 49446390262453 + "id": 8737049738128 }, "Name": "SC-Node(Print)", "Components": { @@ -445,6 +1165,7 @@ } ], "m_format": "AutoComponent_RPC: Sending client PlayerNumber {Value}\n", + "m_numericPrecision": 0, "m_arrayBindingMap": [ { "Key": 1, @@ -468,7 +1189,7 @@ }, { "Id": { - "id": 49472160066229 + "id": 8719869868944 }, "Name": "SC-Node(GetAuthorityToAutonomous_PlayerNumberEventByEntityId)", "Components": { @@ -573,7 +1294,7 @@ }, { "Id": { - "id": 49476455033525 + "id": 8711279934352 }, "Name": "SendScriptEvent", "Components": { @@ -635,7 +1356,7 @@ }, { "Id": { - "id": 49437800327861 + "id": 8749934640016 }, "Name": "SendScriptEvent", "Components": { @@ -725,7 +1446,7 @@ }, { "Id": { - "id": 49454980197045 + "id": 8732754770832 }, "Name": "SC-Node(Print)", "Components": { @@ -801,6 +1522,7 @@ } ], "m_format": "AutoComponent_RPC: I'm Player #{Value}\n", + "m_numericPrecision": 0, "m_arrayBindingMap": [ { "Key": 1, @@ -824,7 +1546,7 @@ }, { "Id": { - "id": 49459275164341 + "id": 8715574901648 }, "Name": "SC-Node(TimeDelayNodeableNode)", "Components": { @@ -963,7 +1685,7 @@ }, { "Id": { - "id": 49480750000821 + "id": 8741344705424 }, "Name": "SC-Node(AuthorityToAutonomous_PlayerNumberByEntityId)", "Components": { @@ -1081,7 +1803,7 @@ }, { "Id": { - "id": 49450685229749 + "id": 8745639672720 }, "Name": "SC-Node(Start)", "Components": { @@ -1111,7 +1833,7 @@ }, { "Id": { - "id": 49463570131637 + "id": 8728459803536 }, "Name": "SC-Node(Gate)", "Components": { @@ -1203,7 +1925,7 @@ "m_connections": [ { "Id": { - "id": 49485044968117 + "id": 8754229607312 }, "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Out), destEndpoint=(If: In)", "Components": { @@ -1212,7 +1934,7 @@ "Id": 3645153988172561571, "sourceEndpoint": { "nodeId": { - "id": 49467865098933 + "id": 8724164836240 }, "slotId": { "m_id": "{1071F455-D5A0-4C7A-AF91-648A0F197885}" @@ -1220,7 +1942,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49463570131637 + "id": 8728459803536 }, "slotId": { "m_id": "{8AE7F430-C4A9-444E-B42A-BDDFE96C387A}" @@ -1231,7 +1953,7 @@ }, { "Id": { - "id": 49489339935413 + "id": 8758524574608 }, "Name": "srcEndpoint=(IsNetEntityRoleAuthority: Is Role Authority), destEndpoint=(If: Condition)", "Components": { @@ -1240,7 +1962,7 @@ "Id": 17942926291787646743, "sourceEndpoint": { "nodeId": { - "id": 49467865098933 + "id": 8724164836240 }, "slotId": { "m_id": "{BA87B4ED-9A52-4A0D-8920-CD0ED4E839EA}" @@ -1248,7 +1970,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49463570131637 + "id": 8728459803536 }, "slotId": { "m_id": "{3E89B67B-1EF2-4DAB-8028-59A97527F3DF}" @@ -1259,7 +1981,7 @@ }, { "Id": { - "id": 49493634902709 + "id": 8762819541904 }, "Name": "srcEndpoint=(If: True), destEndpoint=(Send Script Event: In)", "Components": { @@ -1268,7 +1990,7 @@ "Id": 3298020356088639785, "sourceEndpoint": { "nodeId": { - "id": 49463570131637 + "id": 8728459803536 }, "slotId": { "m_id": "{78C92586-C0E1-449F-8EB2-B1CE3BFC8AE7}" @@ -1276,7 +1998,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49476455033525 + "id": 8711279934352 }, "slotId": { "m_id": "{2FF1A0A1-59C6-4DCF-AE1D-296BD5964D4E}" @@ -1287,7 +2009,7 @@ }, { "Id": { - "id": 49497929870005 + "id": 8767114509200 }, "Name": "srcEndpoint=(Send Script Event: Out), destEndpoint=(Send Script Event: In)", "Components": { @@ -1296,7 +2018,7 @@ "Id": 5482500452520221078, "sourceEndpoint": { "nodeId": { - "id": 49476455033525 + "id": 8711279934352 }, "slotId": { "m_id": "{F2955681-A901-432D-99A3-53818F46CDE7}" @@ -1304,7 +2026,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49437800327861 + "id": 8749934640016 }, "slotId": { "m_id": "{23DBACD8-7784-4E18-A51C-258B25DF4C19}" @@ -1315,7 +2037,7 @@ }, { "Id": { - "id": 49502224837301 + "id": 8771409476496 }, "Name": "srcEndpoint=(Send Script Event: Out), destEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: In)", "Components": { @@ -1324,7 +2046,7 @@ "Id": 16156808606878296902, "sourceEndpoint": { "nodeId": { - "id": 49437800327861 + "id": 8749934640016 }, "slotId": { "m_id": "{19C850D7-7F1E-4D41-AFD9-5A4FA03D54F0}" @@ -1332,7 +2054,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49480750000821 + "id": 8741344705424 }, "slotId": { "m_id": "{1FDB4A9A-D46A-49E8-B734-475230EB7C06}" @@ -1343,7 +2065,7 @@ }, { "Id": { - "id": 49506519804597 + "id": 8775704443792 }, "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumberByEntityId: Out), destEndpoint=(Print: In)", "Components": { @@ -1352,7 +2074,7 @@ "Id": 17367899649716276273, "sourceEndpoint": { "nodeId": { - "id": 49480750000821 + "id": 8741344705424 }, "slotId": { "m_id": "{964EAD23-8DF5-47A9-BF07-1E5C7CD0CC6C}" @@ -1360,7 +2082,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49446390262453 + "id": 8737049738128 }, "slotId": { "m_id": "{F3ED8C08-D751-492A-A39A-7B7734727A5A}" @@ -1371,7 +2093,7 @@ }, { "Id": { - "id": 49510814771893 + "id": 8779999411088 }, "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(TimeDelay: Start)", "Components": { @@ -1380,7 +2102,7 @@ "Id": 13463448697016307967, "sourceEndpoint": { "nodeId": { - "id": 49450685229749 + "id": 8745639672720 }, "slotId": { "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}" @@ -1388,7 +2110,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49459275164341 + "id": 8715574901648 }, "slotId": { "m_id": "{D2337DC6-F126-4254-B296-2DE6BA03993F}" @@ -1399,7 +2121,7 @@ }, { "Id": { - "id": 49515109739189 + "id": 8784294378384 }, "Name": "srcEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: Event), destEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: AuthorityToAutonomous_PlayerNumber Notify Event)", "Components": { @@ -1408,7 +2130,7 @@ "Id": 9477060643694737434, "sourceEndpoint": { "nodeId": { - "id": 49472160066229 + "id": 8719869868944 }, "slotId": { "m_id": "{7262E6CD-BADF-4DD0-8FCE-3A02C3F31CC8}" @@ -1416,7 +2138,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49442095295157 + "id": 8706984967056 }, "slotId": { "m_id": "{8C7E88EA-8FEE-45D0-9A2E-78BC4A18F508}" @@ -1427,7 +2149,7 @@ }, { "Id": { - "id": 49519404706485 + "id": 8788589345680 }, "Name": "srcEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: Out), destEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: Connect)", "Components": { @@ -1436,7 +2158,7 @@ "Id": 16543380658701699472, "sourceEndpoint": { "nodeId": { - "id": 49472160066229 + "id": 8719869868944 }, "slotId": { "m_id": "{1A6BCB27-F99F-4B6B-89CC-F2BE2A698331}" @@ -1444,7 +2166,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49442095295157 + "id": 8706984967056 }, "slotId": { "m_id": "{46E6B649-CAA4-4318-960D-5E4854E21BB2}" @@ -1455,7 +2177,7 @@ }, { "Id": { - "id": 49523699673781 + "id": 8792884312976 }, "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: player_number), destEndpoint=(Print: Value)", "Components": { @@ -1464,7 +2186,7 @@ "Id": 864863482692400383, "sourceEndpoint": { "nodeId": { - "id": 49442095295157 + "id": 8706984967056 }, "slotId": { "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" @@ -1472,7 +2194,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49454980197045 + "id": 8732754770832 }, "slotId": { "m_id": "{BA146872-FBFF-4662-A569-8F1B4C8774AC}" @@ -1483,7 +2205,7 @@ }, { "Id": { - "id": 49527994641077 + "id": 8797179280272 }, "Name": "srcEndpoint=(AuthorityToAutonomous_PlayerNumber Notify Event: OnEvent), destEndpoint=(Print: In)", "Components": { @@ -1492,7 +2214,7 @@ "Id": 2451057837093425972, "sourceEndpoint": { "nodeId": { - "id": 49442095295157 + "id": 8706984967056 }, "slotId": { "m_id": "{586BE222-2DEA-4E08-968F-07CA4EB96C54}" @@ -1500,7 +2222,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49454980197045 + "id": 8732754770832 }, "slotId": { "m_id": "{D206E5BC-3746-4A18-80C7-DBD335FD05FE}" @@ -1511,7 +2233,7 @@ }, { "Id": { - "id": 49532289608373 + "id": 8801474247568 }, "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(GetAuthorityToAutonomous_PlayerNumberEventByEntityId: In)", "Components": { @@ -1520,7 +2242,7 @@ "Id": 12180981889720748145, "sourceEndpoint": { "nodeId": { - "id": 49450685229749 + "id": 8745639672720 }, "slotId": { "m_id": "{A8EA9EF2-A3A6-46B5-8D43-08EF6B6B5B32}" @@ -1528,7 +2250,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49472160066229 + "id": 8719869868944 }, "slotId": { "m_id": "{211BD78B-E01E-44F8-B44E-2FD988047BE9}" @@ -1539,7 +2261,7 @@ }, { "Id": { - "id": 49536584575669 + "id": 8805769214864 }, "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(IsNetEntityRoleAuthority: In)", "Components": { @@ -1548,7 +2270,7 @@ "Id": 5772191552099194657, "sourceEndpoint": { "nodeId": { - "id": 49459275164341 + "id": 8715574901648 }, "slotId": { "m_id": "{25568B15-5372-4C2D-8280-2B433251EACA}" @@ -1556,7 +2278,7 @@ }, "targetEndpoint": { "nodeId": { - "id": 49467865098933 + "id": 8724164836240 }, "slotId": { "m_id": "{B6C4BE5E-CDE4-4EC7-98D0-A019CD80041C}" @@ -1569,13 +2291,13 @@ "m_scriptEventAssets": [ [ { - "id": 49437800327861 + "id": 8749934640016 }, {} ], [ { - "id": 49476455033525 + "id": 8711279934352 }, {} ] @@ -1591,16 +2313,16 @@ "GraphCanvasData": [ { "Key": { - "id": 49433505360565 + "id": 8702689999760 }, "Value": { "ComponentData": { "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { "$type": "SceneComponentSaveData", "ViewParams": { - "Scale": 0.8220159356944325, - "AnchorX": 72.99128723144531, - "AnchorY": 74.20780944824219 + "Scale": 0.9240486637125038, + "AnchorX": 1429.578369140625, + "AnchorY": -71.42481231689453 } } } @@ -1608,38 +2330,7 @@ }, { "Key": { - "id": 49437800327861 - }, - "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": 49442095295157 + "id": 8706984967056 }, "Value": { "ComponentData": { @@ -1670,219 +2361,7 @@ }, { "Key": { - "id": 49446390262453 - }, - "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": 49450685229749 - }, - "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": 49454980197045 - }, - "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": [ - 1400.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": 49459275164341 - }, - "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": 49463570131637 - }, - "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": 49467865098933 - }, - "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": 49472160066229 - }, - "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": 49476455033525 + "id": 8711279934352 }, "Value": { "ComponentData": { @@ -1913,7 +2392,189 @@ }, { "Key": { - "id": 49480750000821 + "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": { @@ -1941,6 +2602,67 @@ } } } + }, + { + "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}" + } + } + } } ], "StatisticsHelper": { From 80a216e079dac034d958d32f0e221858947f9f87 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Wed, 8 Dec 2021 00:21:02 -0800 Subject: [PATCH 05/39] Adding RPC test. Update editor-servers to flush stdout Signed-off-by: Gene Walters --- .../Multiplayer/TestSuite_Sandbox.py | 3 + .../tests/Multiplayer_AutoComponent_RPC.py | 91 + ...oComponent_RPC_NetLevelEntity.scriptcanvas | 1702 +++++++++++++++++ .../Editor/MultiplayerEditorConnection.cpp | 4 +- .../Editor/MultiplayerEditorConnection.h | 1 + 5 files changed, 1800 insertions(+), 1 deletion(-) create mode 100644 AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py create mode 100644 AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas 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..3f971e4abf --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py @@ -0,0 +1,91 @@ +""" +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 Tests(): + 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") + found_lines = ("Expected log lines were found", "Expected log lines were not found") + found_unexpected_lines = ("Unexpected log lines were not found", "Unexpected log lines were found") +# fmt: on + + +def Multiplayer_AutoComponent_RPC(): + r""" + Summary: + Runs a test to make sure that network input can be sent from the autonomous player, received by the authority, and processed + + Level Description: + - Dynamic + 1. Although the level is 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 will listen for the CreateInput and ProcessInput events. + Print logs occur upon triggering the CreateInput and ProcessInput events along with their values; we are testing to make sure the expected events are values are recieved. + - Static + 1. This is an empty level. All the logic occurs on the Player.network.spawnable (see the above Dynamic description) + + + Expected Outcome: + We should see editor logs stating that network input has been created and processed. + 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 + + + # 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): + helper.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)) + + 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(Tests.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(Tests.find_network_player, player_id.IsValid()) + + # 4) Check the editor logs for expected and unexpected log output + EXPECTEDLINE_WAIT_TIME_SECONDS = 1.0 + wait_for_critical_expected_line('Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) + wait_for_critical_expected_line("AutoComponent_RPC: I'm Player #1", section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) + + + # Exit game mode + helper.exit_game_mode(Tests.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_NetLevelEntity.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas new file mode 100644 index 0000000000..aa5244a7cd --- /dev/null +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas @@ -0,0 +1,1702 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "ScriptCanvasData", + "ClassData": { + "m_scriptCanvas": { + "Id": { + "id": 56982432064952 + }, + "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": 10.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": 57021086770616 + }, + "Name": "SC-Node(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId)", + "Components": { + "Component_[1410603071340071190]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 1410603071340071190, + "Slots": [ + { + "id": { + "m_id": "{406DA66F-95FA-406B-8A5B-F2C5E292EC21}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "EntityId: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{5CDED810-9455-424A-9A1D-40B9366D1F94}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{2FC162C0-D717-4053-9842-39DAD3E8EA86}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{C18E637D-BFD1-42C8-A30D-C2B52D157142}" + }, + "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_PlaySomeSuperficialFxEventByEntityId", + "className": "NetworkTestPlayerComponent", + "resultSlotIDs": [ + {} + ], + "inputSlots": [ + { + "m_id": "{406DA66F-95FA-406B-8A5B-F2C5E292EC21}" + } + ], + "prettyClassName": "NetworkTestPlayerComponent" + } + } + }, + { + "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 superficial fx.\n", + "m_unresolvedString": [ + "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some superficial 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 superficial fx.\n", + "m_unresolvedString": [ + "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx.\n" + ] + } + } + }, + { + "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": "{E9B979F9-D682-4B58-8E88-6102387FE70B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "m_format": "AutoComponent_RPC_NetLevelEntity Activated!\n", + "m_unresolvedString": [ + "AutoComponent_RPC_NetLevelEntity Activated!\n" + ] + } + } + }, + { + "Id": { + "id": 57016791803320 + }, + "Name": "SC-EventNode(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event)", + "Components": { + "Component_[17598011665473695286]": { + "$type": "AzEventHandler", + "Id": 17598011665473695286, + "Slots": [ + { + "id": { + "m_id": "{6F8A1DAB-76A6-445F-B532-6B71CD5DB48F}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "ConnectionLimitContract", + "limit": 1 + }, + { + "$type": "RestrictedNodeContract", + "m_nodeId": { + "id": 57021086770616 + } + } + ], + "slotName": "Connect", + "toolTip": "Connect the AZ Event to this AZ Event Handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{D986DF89-49E8-4CFF-907E-7A46BC118208}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Disconnect", + "toolTip": "Disconnect current AZ Event from this AZ Event Handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{277D7B39-E1B0-4700-946B-FFA024EEDF89}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "On Connected", + "toolTip": "Signaled when a connection has taken place.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{7673B52D-3BEC-4BFF-B29A-D027400B16B1}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "On Disconnected", + "toolTip": "Signaled when this event handler is disconnected.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{A53312FA-7FAB-4C8C-A105-478C634ECD33}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnEvent", + "toolTip": "Triggered when the AZ Event invokes Signal() function.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + }, + "IsLatent": true + }, + { + "id": { + "m_id": "{F5D0C2B0-F557-4D94-B747-6F3EF01AF54B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "ConnectionLimitContract", + "limit": 1 + }, + { + "$type": "RestrictedNodeContract", + "m_nodeId": { + "id": 57021086770616 + } + } + ], + "slotName": "AuthorityToClientNoParams_PlaySomeSuperficialFx 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_PlaySomeSuperficialFx Notify Event" + } + ], + "m_azEventEntry": { + "m_eventName": "AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event", + "m_eventSlotId": { + "m_id": "{F5D0C2B0-F557-4D94-B747-6F3EF01AF54B}" + } + } + } + } + }, + { + "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": 57008201868728 + }, + "Name": "SC-Node(AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId)", + "Components": { + "Component_[5156950796673122600]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 5156950796673122600, + "Slots": [ + { + "id": { + "m_id": "{AF45BF64-D202-411F-9CE1-A7139ABC6E65}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Source", + "toolTip": "The Source containing the NetworkTestPlayerComponentController", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{A6449574-6CD4-4986-ABED-2E10136D1B2C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F8CE0394-770E-4F0D-B908-3E01D0E04AE1}" + }, + "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_PlaySomeSuperficialFxByEntityId", + "className": "NetworkTestPlayerComponent", + "resultSlotIDs": [ + {} + ], + "inputSlots": [ + { + "m_id": "{AF45BF64-D202-411F-9CE1-A7139ABC6E65}" + } + ], + "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}" + } + ] + } + } + } + } + ], + "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": 57033971672504 + }, + "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId: Event<>), destEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event)", + "Components": { + "Component_[9588403027578693736]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 9588403027578693736, + "sourceEndpoint": { + "nodeId": { + "id": 57021086770616 + }, + "slotId": { + "m_id": "{C18E637D-BFD1-42C8-A30D-C2B52D157142}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 57016791803320 + }, + "slotId": { + "m_id": "{F5D0C2B0-F557-4D94-B747-6F3EF01AF54B}" + } + } + } + } + }, + { + "Id": { + "id": 57038266639800 + }, + "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId: Out), destEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: Connect)", + "Components": { + "Component_[1452138089363094396]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 1452138089363094396, + "sourceEndpoint": { + "nodeId": { + "id": 57021086770616 + }, + "slotId": { + "m_id": "{2FC162C0-D717-4053-9842-39DAD3E8EA86}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 57016791803320 + }, + "slotId": { + "m_id": "{6F8A1DAB-76A6-445F-B532-6B71CD5DB48F}" + } + } + } + } + }, + { + "Id": { + "id": 57042561607096 + }, + "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId: In)", + "Components": { + "Component_[17563947682404363417]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17563947682404363417, + "sourceEndpoint": { + "nodeId": { + "id": 57025381737912 + }, + "slotId": { + "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 57021086770616 + }, + "slotId": { + "m_id": "{5CDED810-9455-424A-9A1D-40B9366D1F94}" + } + } + } + } + }, + { + "Id": { + "id": 57046856574392 + }, + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: OnEvent), destEndpoint=(Print: In)", + "Components": { + "Component_[12226462283795741406]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 12226462283795741406, + "sourceEndpoint": { + "nodeId": { + "id": 57016791803320 + }, + "slotId": { + "m_id": "{A53312FA-7FAB-4C8C-A105-478C634ECD33}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 56995316966840 + }, + "slotId": { + "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}" + } + } + } + } + }, + { + "Id": { + "id": 57051151541688 + }, + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: OnEvent), destEndpoint=(DrawTextOnEntity: In)", + "Components": { + "Component_[7209285242155620531]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 7209285242155620531, + "sourceEndpoint": { + "nodeId": { + "id": 57016791803320 + }, + "slotId": { + "m_id": "{A53312FA-7FAB-4C8C-A105-478C634ECD33}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 57003906901432 + }, + "slotId": { + "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}" + } + } + } + } + }, + { + "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": 57059741476280 + }, + "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId: In)", + "Components": { + "Component_[8833903103579494596]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 8833903103579494596, + "sourceEndpoint": { + "nodeId": { + "id": 56986727032248 + }, + "slotId": { + "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 57008201868728 + }, + "slotId": { + "m_id": "{A6449574-6CD4-4986-ABED-2E10136D1B2C}" + } + } + } + } + }, + { + "Id": { + "id": 57064036443576 + }, + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId: Out), destEndpoint=(Print: In)", + "Components": { + "Component_[8297971328953001309]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 8297971328953001309, + "sourceEndpoint": { + "nodeId": { + "id": 57008201868728 + }, + "slotId": { + "m_id": "{F8CE0394-770E-4F0D-B908-3E01D0E04AE1}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 56999611934136 + }, + "slotId": { + "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" + } + } + } + } + }, + { + "Id": { + "id": 57068331410872 + }, + "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(Print: In)", + "Components": { + "Component_[11504712829988319988]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 11504712829988319988, + "sourceEndpoint": { + "nodeId": { + "id": 57025381737912 + }, + "slotId": { + "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 56991021999544 + }, + "slotId": { + "m_id": "{8E1B9705-148A-42E4-831E-D7B2877358E3}" + } + } + } + } + } + ] + }, + "m_assetType": "{3E2AC8CD-713F-453E-967F-29517F331784}", + "versionData": { + "_grammarVersion": 1, + "_runtimeVersion": 1, + "_fileVersion": 1 + }, + "GraphCanvasData": [ + { + "Key": { + "id": 56982432064952 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "Scale": 1.0440124999999998, + "AnchorX": 173.36956787109375, + "AnchorY": -153.25486755371094 + } + } + } + } + }, + { + "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": [ + -240.0, + -300.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": [ + 960.0, + -40.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": 57008201868728 + }, + "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, + -40.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{FD25E23C-E976-4F9A-8C16-80B125D80485}" + } + } + } + }, + { + "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": 57016791803320 + }, + "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": [ + 220.0, + 260.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".azeventhandler" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{58CEEB80-152A-4B8E-8698-8B8EE5F31A41}" + } + } + } + }, + { + "Key": { + "id": 57021086770616 + }, + "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": [ + -220.0, + 260.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{30443D97-4B57-482D-BA92-005DB39A9FA7}" + } + } + } + }, + { + "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}" + } + } + } + } + ], + "StatisticsHelper": { + "InstanceCounter": [ + { + "Key": 4199610336680704683, + "Value": 1 + }, + { + "Key": 4847610523576971761, + "Value": 1 + }, + { + "Key": 6462358712820489356, + "Value": 1 + }, + { + "Key": 10684225535275896474, + "Value": 3 + }, + { + "Key": 11941188735314642437, + "Value": 1 + }, + { + "Key": 11983076003173356132, + "Value": 1 + }, + { + "Key": 13774516226790665785, + "Value": 1 + }, + { + "Key": 17705307213431043531, + "Value": 1 + } + ] + } + }, + "Component_[630514155173445772]": { + "$type": "EditorGraphVariableManagerComponent", + "Id": 630514155173445772 + } + } + } + } +} \ No newline at end of file diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index 66b14579fa..11615137ca 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -41,6 +41,9 @@ namespace Multiplayer // Automated testing listens for these logs if (editorsv_isDedicated) { + // Server logs piped to the editor. Change the buffering policy to ensure every write to stdout is flushed. + 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 +238,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 { From 8b4527c64bca858bb72f2e9e1faf126e6d5b4a5e Mon Sep 17 00:00:00 2001 From: Tobias Alexander Franke Date: Thu, 2 Sep 2021 19:10:38 +0800 Subject: [PATCH 06/39] Fix: illegal exe name and version no. when opening Project Settings Tool Signed-off-by: T.J. McGrath-Daly --- AutomatedTesting/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AutomatedTesting/project.json b/AutomatedTesting/project.json index 5ee4ee68f8..5759f860c4 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" From ce0c62268a106cb278335d1e38ac5979f8e028bc Mon Sep 17 00:00:00 2001 From: Tobias Alexander Franke Date: Fri, 15 Oct 2021 14:26:37 +0800 Subject: [PATCH 07/39] Parameters of the Simple LOD distance component still take effect after Enable anim graph is disabled. Signed-off-by: T.J. McGrath-Daly --- .../Code/Source/Integration/Components/SimpleLODComponent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp index fdc6426e4c..ec94a9a15d 100644 --- a/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp +++ b/Gems/EMotionFX/Code/Source/Integration/Components/SimpleLODComponent.cpp @@ -232,6 +232,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. From 7276ad0dbfdb06914a8966a9b0a1c4d37075ada1 Mon Sep 17 00:00:00 2001 From: "T.J. McGrath-Daly" Date: Thu, 18 Nov 2021 15:11:26 +0800 Subject: [PATCH 08/39] The Groups and Ungroups shortcut keys in Script Canvas Editor do not take effect Signed-off-by: T.J. McGrath-Daly --- Code/Editor/EditorPanelUtils.cpp | 4 ++-- .../Widgets/AssetEditorToolbar/AssetEditorToolbar.ui | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) 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/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 From 0a7e9388ad71f79b192fc8dd781856004e05e238 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Thu, 9 Dec 2021 19:23:48 -0800 Subject: [PATCH 09/39] Cleanup: Adding wait_for_critical_expected_line as a public method to utils.py and updated the RPC test to use it. Signed-off-by: Gene Walters --- .../editor_python_test_tools/utils.py | 54 +++++++++++-------- .../tests/Multiplayer_AutoComponent_RPC.py | 30 +++-------- 2 files changed, 39 insertions(+), 45 deletions(-) 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..92ac279627 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,31 @@ 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_expected_line(window, expected_message, 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 expected_message: The log message to search. + :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 message is found, otherwise false. + """ + for printInfo in print_infos: + if printInfo.window == window.strip() and printInfo.message.strip() == expected_message: + return True + return False + + @staticmethod + def wait_for_critical_expected_line(window, expected_message, print_infos, time_out): + TestHelper.wait_for_condition(lambda : TestHelper.find_expected_line(window, expected_message, print_infos), time_out) + Report.critical_result(("Found expected line: " + expected_message, "Failed to find expected line: " + expected_message), TestHelper.find_expected_line(window, expected_message, print_infos)) + + @staticmethod + def wait_for_critical_unexpected_line(window, unexpected_line, print_infos, time_out): + TestHelper.wait_for_condition(lambda : TestHelper.find_expected_line(window, unexpected_line, print_infos), time_out) + Report.critical_result(("Unexpected line not found: " + unexpected_line, "Unexpected line found: " + unexpected_line), not TestHelper.find_expected_line(window, unexpected_line, print_infos)) + @staticmethod def multiplayer_enter_game_mode(msgtuple_success_fail: Tuple[str, str], sv_default_player_spawn_asset: str) -> None: """ @@ -108,23 +133,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 +143,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.wait_for_critical_unexpected_line("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.wait_for_critical_expected_line("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.wait_for_critical_expected_line("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.wait_for_critical_expected_line("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.wait_for_critical_expected_line("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.wait_for_critical_unexpected_line("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/tests/Multiplayer_AutoComponent_RPC.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py index 3f971e4abf..387de66acd 100644 --- a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py +++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py @@ -14,27 +14,25 @@ class Tests(): 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") - found_lines = ("Expected log lines were found", "Expected log lines were not found") - found_unexpected_lines = ("Unexpected log lines were not found", "Unexpected log lines were found") # fmt: on def Multiplayer_AutoComponent_RPC(): r""" Summary: - Runs a test to make sure that network input can be sent from the autonomous player, received by the authority, and processed + Runs a test to make sure that RPCs can be sent and received via script canvas Level Description: - Dynamic - 1. Although the level is 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 will listen for the CreateInput and ProcessInput events. - Print logs occur upon triggering the CreateInput and ProcessInput events along with their values; we are testing to make sure the expected events are values are recieved. + 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. This is an empty level. All the logic occurs on the Player.network.spawnable (see the above Dynamic description) + 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 network input has been created and processed. + 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: @@ -46,18 +44,6 @@ def Multiplayer_AutoComponent_RPC(): from editor_python_test_tools.utils import TestHelper as helper from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole - - # 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): - helper.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)) - level_name = "AutoComponent_RPC" player_prefab_name = "Player" player_prefab_path = f"levels/multiplayer/{level_name}/{player_prefab_name}.network.spawnable" @@ -77,8 +63,8 @@ def Multiplayer_AutoComponent_RPC(): # 4) Check the editor logs for expected and unexpected log output EXPECTEDLINE_WAIT_TIME_SECONDS = 1.0 - wait_for_critical_expected_line('Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) - wait_for_critical_expected_line("AutoComponent_RPC: I'm Player #1", section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) + helper.wait_for_critical_expected_line('EditorServer', 'Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) + helper.wait_for_critical_expected_line('Script', "AutoComponent_RPC: I'm Player #1", section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) # Exit game mode From bf8422152ba9ca3fc951bfb3ef10ed1b00aa5274 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Fri, 17 Dec 2021 00:12:21 -0800 Subject: [PATCH 10/39] Turn off stdout buffering when connecting to editor-server so that the editor properly gets all the server-logging without having to first fill up a buffer. Allows the logs to pipe instantly for easier debugging, and also fixes a problem where the logs wont reach the editor because the buffer doesn't fill Signed-off-by: Gene Walters --- .../AutoComponent_RPC.scriptcanvas | 2339 ++++++++++++++++- .../Editor/MultiplayerEditorConnection.cpp | 5 +- 2 files changed, 2325 insertions(+), 19 deletions(-) diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas index 72cc980550..c1c77b14fe 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 8702689999760 + "id": 2816238339133127497 }, "Name": "AutoComponent_RPC", "Components": { @@ -582,6 +582,1158 @@ { "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, { "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" } @@ -968,6 +2120,1158 @@ { "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, + { + "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" + }, { "m_id": "{82D3E2FD-ADFA-47F7-A889-D1172BF2EC49}" } @@ -2311,23 +4615,6 @@ }, "m_variableCounter": 1, "GraphCanvasData": [ - { - "Key": { - "id": 8702689999760 - }, - "Value": { - "ComponentData": { - "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { - "$type": "SceneComponentSaveData", - "ViewParams": { - "Scale": 0.9240486637125038, - "AnchorX": 1429.578369140625, - "AnchorY": -71.42481231689453 - } - } - } - } - }, { "Key": { "id": 8706984967056 @@ -2663,6 +4950,22 @@ } } } + }, + { + "Key": { + "id": 2816238339133127497 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "AnchorX": 260.0, + "AnchorY": 479.0 + } + } + } + } } ], "StatisticsHelper": { diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index 11615137ca..f7882d7bf9 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -41,7 +41,10 @@ namespace Multiplayer // Automated testing listens for these logs if (editorsv_isDedicated) { - // Server logs piped to the editor. Change the buffering policy to ensure every write to stdout is flushed. + // 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 + // 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, From 1be475c81a8e98c2afe128676cad2f8d16845df3 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 17 Dec 2021 14:53:54 +0300 Subject: [PATCH 11/39] [AudioSystem] ACE: Added Ctrl-R shortcut for the "File->Reload" action. Signed-off-by: Maxim Ivanov --- .../Code/Source/Editor/AudioControlsEditorMainWindow.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui index 549e746de6..352e1bdf78 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui @@ -166,6 +166,9 @@ Reload + + Ctrl+R + From d9e1e59af703eb6c4d852bf62542c38afd8a0dba Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 17 Dec 2021 15:42:05 +0300 Subject: [PATCH 12/39] [AudioSystem] ACE: Added "File -> Refresh Audio System" action (same as "Game -> Refresh Audio") with "Ctrl-Shift-R" shortcut. Signed-off-by: Maxim Ivanov --- .../Editor/AudioControlsEditorMainWindow.ui | 25 +++++++++++++++++++ .../Editor/AudioControlsEditorWindow.cpp | 24 +++++++++++------- .../Source/Editor/AudioControlsEditorWindow.h | 1 + 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui index 352e1bdf78..7a1f4793b8 100644 --- a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui +++ b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui @@ -143,6 +143,7 @@ + @@ -170,6 +171,14 @@ Ctrl+R + + + Refresh Audio System + + + Ctrl+Shift+R + + @@ -207,6 +216,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; From 3ecf622804474af37cab528eba371a57f605ee00 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 17 Dec 2021 16:06:44 +0300 Subject: [PATCH 13/39] [AudioSystem] ACE: Panels now can be resized horizontally by dragging the boundary between them. Signed-off-by: Maxim Ivanov --- .../Editor/AudioControlsEditorMainWindow.ui | 190 +++++++++--------- 1 file changed, 97 insertions(+), 93 deletions(-) diff --git a/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui b/Gems/AudioSystem/Code/Source/Editor/AudioControlsEditorMainWindow.ui index 7a1f4793b8..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 From d508d5c4c2a57eecdc4a7efcb990b803393f373d Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Fri, 17 Dec 2021 17:49:36 -0800 Subject: [PATCH 14/39] Updating pytest code comments and RPC name based on review feedback Signed-off-by: Gene Walters --- ...tworkTestPlayerComponent.AutoComponent.xml | 2 +- .../editor_python_test_tools/utils.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml index 9037db43ee..251d2b25af 100644 --- a/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml +++ b/AutomatedTesting/Gem/Code/Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml @@ -29,7 +29,7 @@ - + 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 92ac279627..2ba692d89b 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -117,11 +117,31 @@ class TestHelper: @staticmethod def wait_for_critical_expected_line(window, expected_message, print_infos, time_out): + """ + Looks for an expected line in a list of tracer log lines for a period of time. + Reports a critical result based on if the expected line was found. The result is successful if the line is found. + :param window: The log's window name. For example, logs printed via script-canvas use the "Script" window. + :param expected_message: The log message we're expecting 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 expected message is found, a successful critical result is reported; otherwise failure. + """ TestHelper.wait_for_condition(lambda : TestHelper.find_expected_line(window, expected_message, print_infos), time_out) Report.critical_result(("Found expected line: " + expected_message, "Failed to find expected line: " + expected_message), TestHelper.find_expected_line(window, expected_message, print_infos)) @staticmethod def wait_for_critical_unexpected_line(window, unexpected_line, print_infos, time_out): + """ + Looks for an unexpected line in a list of tracer log lines over a period of time. + Reports a critical result based on if the unexpected line was found. The result is successful if the line is not found. + :param window: The log's window name. For example, logs printed via script-canvas use the "Script" window. + :param unexpected_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. + + :return: No return value, but if the unexpected message is found, a failed critical result is reported; otherwise success. + """ TestHelper.wait_for_condition(lambda : TestHelper.find_expected_line(window, unexpected_line, print_infos), time_out) Report.critical_result(("Unexpected line not found: " + unexpected_line, "Unexpected line found: " + unexpected_line), not TestHelper.find_expected_line(window, unexpected_line, print_infos)) From fd078e3c29a5d39f96cb4965f65ede334b55e338 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Sat, 18 Dec 2021 10:11:20 -0800 Subject: [PATCH 15/39] Small py var name change based on review feedback. Updating script graphs from previous commit to use new RPC name Signed-off-by: Gene Walters --- .../tests/Multiplayer_AutoComponent_RPC.py | 8 +- ...oComponent_RPC_NetLevelEntity.scriptcanvas | 932 ++++++++++-------- 2 files changed, 511 insertions(+), 429 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py index 387de66acd..3b7ae89368 100644 --- a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py +++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py @@ -10,7 +10,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT # fmt: off -class Tests(): +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") @@ -55,11 +55,11 @@ def Multiplayer_AutoComponent_RPC(): with Tracer() as section_tracer: # 2) Enter game mode - helper.multiplayer_enter_game_mode(Tests.enter_game_mode, player_prefab_path.lower()) + 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(Tests.find_network_player, player_id.IsValid()) + Report.critical_result(TestSuccessFailTuples.find_network_player, player_id.IsValid()) # 4) Check the editor logs for expected and unexpected log output EXPECTEDLINE_WAIT_TIME_SECONDS = 1.0 @@ -68,7 +68,7 @@ def Multiplayer_AutoComponent_RPC(): # Exit game mode - helper.exit_game_mode(Tests.exit_game_mode) + helper.exit_game_mode(TestSuccessFailTuples.exit_game_mode) diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas index aa5244a7cd..8e5a6b374c 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 56982432064952 + "id": 5286394689911028851 }, "Name": "AutoComponent_RPC_NetLevelEntity", "Components": { @@ -216,40 +216,35 @@ }, { "Id": { - "id": 57021086770616 + "id": 28956289730909 }, - "Name": "SC-Node(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId)", + "Name": "SC-EventNode(AuthorityToClientNoParams_PlayFx Notify Event)", "Components": { - "Component_[1410603071340071190]": { - "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", - "Id": 1410603071340071190, + "Component_[12478098247313937239]": { + "$type": "AzEventHandler", + "Id": 12478098247313937239, "Slots": [ { "id": { - "m_id": "{406DA66F-95FA-406B-8A5B-F2C5E292EC21}" + "m_id": "{E97830A5-323D-4CD1-85CF-BAFB1735AC34}" }, "contracts": [ { "$type": "SlotTypeContract" - } - ], - "slotName": "EntityId: 0", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 2 - }, - "DataType": 1 - }, - { - "id": { - "m_id": "{5CDED810-9455-424A-9A1D-40B9366D1F94}" - }, - "contracts": [ + }, { - "$type": "SlotTypeContract" + "$type": "ConnectionLimitContract", + "limit": 1 + }, + { + "$type": "RestrictedNodeContract", + "m_nodeId": { + "id": 28466663459165 + } } ], - "slotName": "In", + "slotName": "Connect", + "toolTip": "Connect the AZ Event to this AZ Event Handler.", "Descriptor": { "ConnectionType": 1, "SlotType": 1 @@ -257,14 +252,31 @@ }, { "id": { - "m_id": "{2FC162C0-D717-4053-9842-39DAD3E8EA86}" + "m_id": "{2A7ABD07-368B-44F3-BB16-946539B76A16}" }, "contracts": [ { "$type": "SlotTypeContract" } ], - "slotName": "Out", + "slotName": "Disconnect", + "toolTip": "Disconnect current AZ Event from this AZ Event Handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{E1C7B2B6-B5ED-4BA6-8484-A74F336CD53F}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "On Connected", + "toolTip": "Signaled when a connection has taken place.", "Descriptor": { "ConnectionType": 2, "SlotType": 1 @@ -272,20 +284,59 @@ }, { "id": { - "m_id": "{C18E637D-BFD1-42C8-A30D-C2B52D157142}" + "m_id": "{2731EE09-9E48-4EDB-9548-024EAAC23A03}" }, "contracts": [ { "$type": "SlotTypeContract" } ], - "slotName": "Event<>", - "DisplayDataType": { - "m_type": 4, - "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" - }, + "slotName": "On Disconnected", + "toolTip": "Signaled when this event handler is disconnected.", "Descriptor": { "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{1F11AAA9-2A99-41A2-AABE-7B57463E3664}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "OnEvent", + "toolTip": "Triggered when the AZ Event invokes Signal() function.", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + }, + "IsLatent": true + }, + { + "id": { + "m_id": "{7A4D75C3-C9C8-4CFB-A87C-C15B71AB3296}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "ConnectionLimitContract", + "limit": 1 + }, + { + "$type": "RestrictedNodeContract", + "m_nodeId": { + "id": 28466663459165 + } + } + ], + "slotName": "AuthorityToClientNoParams_PlayFx Notify Event", + "Descriptor": { + "ConnectionType": 1, "SlotType": 2 }, "DataType": 1 @@ -293,29 +344,21 @@ ], "Datums": [ { + "isOverloadedStorage": false, "scriptCanvasType": { - "m_type": 1 + "m_type": 4, + "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" }, - "isNullPointer": false, - "$type": "EntityId", - "value": { - "id": 2901262558 - }, - "label": "EntityId: 0" + "isNullPointer": true, + "label": "AuthorityToClientNoParams_PlayFx Notify Event" } ], - "methodType": 2, - "methodName": "GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId", - "className": "NetworkTestPlayerComponent", - "resultSlotIDs": [ - {} - ], - "inputSlots": [ - { - "m_id": "{406DA66F-95FA-406B-8A5B-F2C5E292EC21}" + "m_azEventEntry": { + "m_eventName": "AuthorityToClientNoParams_PlayFx Notify Event", + "m_eventSlotId": { + "m_id": "{7A4D75C3-C9C8-4CFB-A87C-C15B71AB3296}" } - ], - "prettyClassName": "NetworkTestPlayerComponent" + } } } }, @@ -640,153 +683,6 @@ } } }, - { - "Id": { - "id": 57016791803320 - }, - "Name": "SC-EventNode(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event)", - "Components": { - "Component_[17598011665473695286]": { - "$type": "AzEventHandler", - "Id": 17598011665473695286, - "Slots": [ - { - "id": { - "m_id": "{6F8A1DAB-76A6-445F-B532-6B71CD5DB48F}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - }, - { - "$type": "ConnectionLimitContract", - "limit": 1 - }, - { - "$type": "RestrictedNodeContract", - "m_nodeId": { - "id": 57021086770616 - } - } - ], - "slotName": "Connect", - "toolTip": "Connect the AZ Event to this AZ Event Handler.", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{D986DF89-49E8-4CFF-907E-7A46BC118208}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "Disconnect", - "toolTip": "Disconnect current AZ Event from this AZ Event Handler.", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{277D7B39-E1B0-4700-946B-FFA024EEDF89}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "On Connected", - "toolTip": "Signaled when a connection has taken place.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{7673B52D-3BEC-4BFF-B29A-D027400B16B1}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "On Disconnected", - "toolTip": "Signaled when this event handler is disconnected.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{A53312FA-7FAB-4C8C-A105-478C634ECD33}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "OnEvent", - "toolTip": "Triggered when the AZ Event invokes Signal() function.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - }, - "IsLatent": true - }, - { - "id": { - "m_id": "{F5D0C2B0-F557-4D94-B747-6F3EF01AF54B}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - }, - { - "$type": "ConnectionLimitContract", - "limit": 1 - }, - { - "$type": "RestrictedNodeContract", - "m_nodeId": { - "id": 57021086770616 - } - } - ], - "slotName": "AuthorityToClientNoParams_PlaySomeSuperficialFx 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_PlaySomeSuperficialFx Notify Event" - } - ], - "m_azEventEntry": { - "m_eventName": "AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event", - "m_eventSlotId": { - "m_id": "{F5D0C2B0-F557-4D94-B747-6F3EF01AF54B}" - } - } - } - } - }, { "Id": { "id": 57025381737912 @@ -819,17 +715,120 @@ }, { "Id": { - "id": 57008201868728 + "id": 28466663459165 }, - "Name": "SC-Node(AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId)", + "Name": "SC-Node(GetAuthorityToClientNoParams_PlayFxEventByEntityId)", "Components": { - "Component_[5156950796673122600]": { + "Component_[5890343372099746558]": { "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", - "Id": 5156950796673122600, + "Id": 5890343372099746558, "Slots": [ { "id": { - "m_id": "{AF45BF64-D202-411F-9CE1-A7139ABC6E65}" + "m_id": "{4E1A6903-3F02-4395-A4C9-202D1F13250E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "EntityId: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{26902930-1BC7-4BF6-8F41-C313BA819AA3}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{B6C92656-7593-4759-B657-98DB7CA30482}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{10D59D8F-E406-431B-87F9-FC1AF1EC65AF}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Event<>", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 2901262558 + }, + "label": "EntityId: 0" + } + ], + "methodType": 2, + "methodName": "GetAuthorityToClientNoParams_PlayFxEventByEntityId", + "className": "NetworkTestPlayerComponent", + "inputSlots": [ + { + "m_id": "{4E1A6903-3F02-4395-A4C9-202D1F13250E}" + } + ], + "prettyClassName": "NetworkTestPlayerComponent" + } + } + }, + { + "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": [ { @@ -846,7 +845,7 @@ }, { "id": { - "m_id": "{A6449574-6CD4-4986-ABED-2E10136D1B2C}" + "m_id": "{AB0D7C00-A334-449A-AC56-EA3167AB8900}" }, "contracts": [ { @@ -861,7 +860,7 @@ }, { "id": { - "m_id": "{F8CE0394-770E-4F0D-B908-3E01D0E04AE1}" + "m_id": "{A52302D6-9DF9-45C2-960D-19BF90A4A931}" }, "contracts": [ { @@ -889,14 +888,14 @@ } ], "methodType": 2, - "methodName": "AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId", + "methodName": "AuthorityToClientNoParams_PlayFxByEntityId", "className": "NetworkTestPlayerComponent", "resultSlotIDs": [ {} ], "inputSlots": [ { - "m_id": "{AF45BF64-D202-411F-9CE1-A7139ABC6E65}" + "m_id": "{87B7266B-D7B1-4CAD-9898-4D7F0274DAB0}" } ], "prettyClassName": "NetworkTestPlayerComponent" @@ -1072,62 +1071,6 @@ } } }, - { - "Id": { - "id": 57033971672504 - }, - "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId: Event<>), destEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event)", - "Components": { - "Component_[9588403027578693736]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 9588403027578693736, - "sourceEndpoint": { - "nodeId": { - "id": 57021086770616 - }, - "slotId": { - "m_id": "{C18E637D-BFD1-42C8-A30D-C2B52D157142}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 57016791803320 - }, - "slotId": { - "m_id": "{F5D0C2B0-F557-4D94-B747-6F3EF01AF54B}" - } - } - } - } - }, - { - "Id": { - "id": 57038266639800 - }, - "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId: Out), destEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: Connect)", - "Components": { - "Component_[1452138089363094396]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 1452138089363094396, - "sourceEndpoint": { - "nodeId": { - "id": 57021086770616 - }, - "slotId": { - "m_id": "{2FC162C0-D717-4053-9842-39DAD3E8EA86}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 57016791803320 - }, - "slotId": { - "m_id": "{6F8A1DAB-76A6-445F-B532-6B71CD5DB48F}" - } - } - } - } - }, { "Id": { "id": 57042561607096 @@ -1240,62 +1183,6 @@ } } }, - { - "Id": { - "id": 57059741476280 - }, - "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId: In)", - "Components": { - "Component_[8833903103579494596]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 8833903103579494596, - "sourceEndpoint": { - "nodeId": { - "id": 56986727032248 - }, - "slotId": { - "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 57008201868728 - }, - "slotId": { - "m_id": "{A6449574-6CD4-4986-ABED-2E10136D1B2C}" - } - } - } - } - }, - { - "Id": { - "id": 57064036443576 - }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFxByEntityId: Out), destEndpoint=(Print: In)", - "Components": { - "Component_[8297971328953001309]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 8297971328953001309, - "sourceEndpoint": { - "nodeId": { - "id": 57008201868728 - }, - "slotId": { - "m_id": "{F8CE0394-770E-4F0D-B908-3E01D0E04AE1}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 56999611934136 - }, - "slotId": { - "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" - } - } - } - } - }, { "Id": { "id": 57068331410872 @@ -1323,6 +1210,202 @@ } } } + }, + { + "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": 9732016114013 + }, + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFxByEntityId: Out), destEndpoint=(Print: In)", + "Components": { + "Component_[14133537125895802472]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 14133537125895802472, + "sourceEndpoint": { + "nodeId": { + "id": 8400576252253 + }, + "slotId": { + "m_id": "{A52302D6-9DF9-45C2-960D-19BF90A4A931}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 56999611934136 + }, + "slotId": { + "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" + } + } + } + } + }, + { + "Id": { + "id": 29660664367453 + }, + "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Event<>), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: AuthorityToClientNoParams_PlayFx Notify Event)", + "Components": { + "Component_[17084894170988218373]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 17084894170988218373, + "sourceEndpoint": { + "nodeId": { + "id": 28466663459165 + }, + "slotId": { + "m_id": "{10D59D8F-E406-431B-87F9-FC1AF1EC65AF}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 28956289730909 + }, + "slotId": { + "m_id": "{7A4D75C3-C9C8-4CFB-A87C-C15B71AB3296}" + } + } + } + } + }, + { + "Id": { + "id": 29716498942301 + }, + "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Out), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: Connect)", + "Components": { + "Component_[6504512854579046293]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 6504512854579046293, + "sourceEndpoint": { + "nodeId": { + "id": 28466663459165 + }, + "slotId": { + "m_id": "{B6C92656-7593-4759-B657-98DB7CA30482}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 28956289730909 + }, + "slotId": { + "m_id": "{E97830A5-323D-4CD1-85CF-BAFB1735AC34}" + } + } + } + } + }, + { + "Id": { + "id": 30240484952413 + }, + "Name": "srcEndpoint=(: ), destEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: In)", + "Components": { + "Component_[4329986353751309486]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 4329986353751309486, + "sourceEndpoint": { + "nodeId": { + "id": 57025381737912 + }, + "slotId": { + "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 28466663459165 + }, + "slotId": { + "m_id": "{26902930-1BC7-4BF6-8F41-C313BA819AA3}" + } + } + } + } + }, + { + "Id": { + "id": 30678571616605 + }, + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(: )", + "Components": { + "Component_[14658596010250057469]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 14658596010250057469, + "sourceEndpoint": { + "nodeId": { + "id": 28956289730909 + }, + "slotId": { + "m_id": "{1F11AAA9-2A99-41A2-AABE-7B57463E3664}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 57003906901432 + }, + "slotId": { + "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}" + } + } + } + } + }, + { + "Id": { + "id": 31060823705949 + }, + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(: )", + "Components": { + "Component_[16833378372852944435]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 16833378372852944435, + "sourceEndpoint": { + "nodeId": { + "id": 28956289730909 + }, + "slotId": { + "m_id": "{1F11AAA9-2A99-41A2-AABE-7B57463E3664}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 56995316966840 + }, + "slotId": { + "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}" + } + } + } + } } ] }, @@ -1335,17 +1418,93 @@ "GraphCanvasData": [ { "Key": { - "id": 56982432064952 + "id": 8400576252253 }, "Value": { "ComponentData": { - "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { - "$type": "SceneComponentSaveData", - "ViewParams": { - "Scale": 1.0440124999999998, - "AnchorX": 173.36956787109375, - "AnchorY": -153.25486755371094 - } + "{24CB38BB-1705-4EC5-8F63-B574571B4DCD}": { + "$type": "NodeSaveData" + }, + "{328FF15C-C302-458F-A43D-E1794DE0904E}": { + "$type": "GeneralNodeTitleComponentSaveData", + "PaletteOverride": "MethodNodeTitlePalette" + }, + "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { + "$type": "GeometrySaveData", + "Position": [ + 440.0, + -20.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": 28466663459165 + }, + "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": [ + -200.0, + 300.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".method" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{A84996D5-99BD-4F5D-859E-02BFAB3FA83A}" + } + } + } + }, + { + "Key": { + "id": 28956289730909 + }, + "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": [ + 260.0, + 300.0 + ] + }, + "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { + "$type": "StylingComponentSaveData", + "SubStyle": ".azeventhandler" + }, + "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { + "$type": "PersistentIdComponentSaveData", + "PersistentId": "{B237687E-F57E-4C04-9B46-103548751B5D}" } } } @@ -1501,37 +1660,6 @@ } } }, - { - "Key": { - "id": 57008201868728 - }, - "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, - -40.0 - ] - }, - "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".method" - }, - "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { - "$type": "PersistentIdComponentSaveData", - "PersistentId": "{FD25E23C-E976-4F9A-8C16-80B125D80485}" - } - } - } - }, { "Key": { "id": 57012496836024 @@ -1562,68 +1690,6 @@ } } }, - { - "Key": { - "id": 57016791803320 - }, - "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": [ - 220.0, - 260.0 - ] - }, - "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".azeventhandler" - }, - "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { - "$type": "PersistentIdComponentSaveData", - "PersistentId": "{58CEEB80-152A-4B8E-8698-8B8EE5F31A41}" - } - } - } - }, - { - "Key": { - "id": 57021086770616 - }, - "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": [ - -220.0, - 260.0 - ] - }, - "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".method" - }, - "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { - "$type": "PersistentIdComponentSaveData", - "PersistentId": "{30443D97-4B57-482D-BA92-005DB39A9FA7}" - } - } - } - }, { "Key": { "id": 57025381737912 @@ -1653,6 +1719,22 @@ } } } + }, + { + "Key": { + "id": 5286394689911028851 + }, + "Value": { + "ComponentData": { + "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { + "$type": "SceneComponentSaveData", + "ViewParams": { + "AnchorX": -579.0, + "AnchorY": -194.0 + } + } + } + } } ], "StatisticsHelper": { @@ -1670,13 +1752,17 @@ "Value": 1 }, { - "Key": 10684225535275896474, - "Value": 3 + "Key": 7087687843968394353, + "Value": 1 }, { - "Key": 11941188735314642437, + "Key": 8679770052035517025, "Value": 1 }, + { + "Key": 10684225535275896474, + "Value": 3 + }, { "Key": 11983076003173356132, "Value": 1 @@ -1684,10 +1770,6 @@ { "Key": 13774516226790665785, "Value": 1 - }, - { - "Key": 17705307213431043531, - "Value": 1 } ] } From 92d3df17fadb7ea5c872068f9d6df26a28664d9b Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Sat, 18 Dec 2021 10:16:02 -0800 Subject: [PATCH 16/39] Fix incomplete comment Signed-off-by: Gene Walters --- .../Code/Source/Editor/MultiplayerEditorConnection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index f7882d7bf9..f112098eae 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -43,7 +43,7 @@ namespace Multiplayer { // 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 + // 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); From 874bbbfca35e1f20a55a028453fa5ca5991f2f6d Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Sat, 18 Dec 2021 10:26:37 -0800 Subject: [PATCH 17/39] Better magic number naming based on review feedback Signed-off-by: Gene Walters --- .../Multiplayer/tests/Multiplayer_AutoComponent_RPC.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py index 3b7ae89368..8c13806a30 100644 --- a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py +++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py @@ -62,9 +62,9 @@ def Multiplayer_AutoComponent_RPC(): Report.critical_result(TestSuccessFailTuples.find_network_player, player_id.IsValid()) # 4) Check the editor logs for expected and unexpected log output - EXPECTEDLINE_WAIT_TIME_SECONDS = 1.0 - helper.wait_for_critical_expected_line('EditorServer', 'Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) - helper.wait_for_critical_expected_line('Script', "AutoComponent_RPC: I'm Player #1", section_tracer.prints, EXPECTEDLINE_WAIT_TIME_SECONDS) + 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.wait_for_critical_expected_line('EditorServer', 'Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS) + helper.wait_for_critical_expected_line('Script', "AutoComponent_RPC: I'm Player #1", section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS) # Exit game mode From e58d8815f105f0e28645801d79935685f3bdde2d Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sat, 18 Dec 2021 17:38:50 -0800 Subject: [PATCH 18/39] chore: add assertions to plane to ensure normalization Signed-off-by: Michael Pollind --- Code/Framework/AzCore/AzCore/Math/Plane.inl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Code/Framework/AzCore/AzCore/Math/Plane.inl b/Code/Framework/AzCore/AzCore/Math/Plane.inl index f33d356312..bade6f1775 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 a 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 a 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 not normalized"); 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 not normalized"); 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 not normalized"); m_plane.Set(a, b, c, d); } AZ_MATH_INLINE void Plane::SetNormal(const Vector3& normal) { + AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized"); m_plane.SetX(normal.GetX()); m_plane.SetY(normal.GetY()); m_plane.SetZ(normal.GetZ()); From 8aa54659741a4f67861c43b94dfbd3008006ce61 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Tue, 21 Dec 2021 17:53:59 -0800 Subject: [PATCH 19/39] Resaving script graph which was old and causing some deserialization errors on game start Signed-off-by: Gene Walters --- ...oComponent_RPC_NetLevelEntity.scriptcanvas | 966 ++++++++++-------- 1 file changed, 564 insertions(+), 402 deletions(-) diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas index 8e5a6b374c..3869dfdfcb 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas @@ -5,7 +5,7 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 5286394689911028851 + "id": 1685762441320719908 }, "Name": "AutoComponent_RPC_NetLevelEntity", "Components": { @@ -148,7 +148,7 @@ }, "isNullPointer": false, "$type": "double", - "value": 10.0, + "value": 99.0, "label": "Repetitions" }, { @@ -214,154 +214,6 @@ } } }, - { - "Id": { - "id": 28956289730909 - }, - "Name": "SC-EventNode(AuthorityToClientNoParams_PlayFx Notify Event)", - "Components": { - "Component_[12478098247313937239]": { - "$type": "AzEventHandler", - "Id": 12478098247313937239, - "Slots": [ - { - "id": { - "m_id": "{E97830A5-323D-4CD1-85CF-BAFB1735AC34}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - }, - { - "$type": "ConnectionLimitContract", - "limit": 1 - }, - { - "$type": "RestrictedNodeContract", - "m_nodeId": { - "id": 28466663459165 - } - } - ], - "slotName": "Connect", - "toolTip": "Connect the AZ Event to this AZ Event Handler.", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{2A7ABD07-368B-44F3-BB16-946539B76A16}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "Disconnect", - "toolTip": "Disconnect current AZ Event from this AZ Event Handler.", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{E1C7B2B6-B5ED-4BA6-8484-A74F336CD53F}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "On Connected", - "toolTip": "Signaled when a connection has taken place.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{2731EE09-9E48-4EDB-9548-024EAAC23A03}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "On Disconnected", - "toolTip": "Signaled when this event handler is disconnected.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{1F11AAA9-2A99-41A2-AABE-7B57463E3664}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "OnEvent", - "toolTip": "Triggered when the AZ Event invokes Signal() function.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - }, - "IsLatent": true - }, - { - "id": { - "m_id": "{7A4D75C3-C9C8-4CFB-A87C-C15B71AB3296}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - }, - { - "$type": "ConnectionLimitContract", - "limit": 1 - }, - { - "$type": "RestrictedNodeContract", - "m_nodeId": { - "id": 28466663459165 - } - } - ], - "slotName": "AuthorityToClientNoParams_PlayFx Notify Event", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 2 - }, - "DataType": 1 - } - ], - "Datums": [ - { - "isOverloadedStorage": false, - "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": "{7A4D75C3-C9C8-4CFB-A87C-C15B71AB3296}" - } - } - } - } - }, { "Id": { "id": 57003906901432 @@ -578,9 +430,9 @@ } } ], - "m_format": "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some superficial fx.\n", + "m_format": "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.\n", "m_unresolvedString": [ - "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some superficial fx.\n" + "AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.\n" ] } } @@ -627,13 +479,160 @@ } } ], - "m_format": "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx.\n", + "m_format": "AutoComponent_RPC_NetLevelEntity: I'm a client playing some fx.\n", "m_unresolvedString": [ - "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx.\n" + "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 @@ -660,6 +659,30 @@ "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}" @@ -676,10 +699,36 @@ } } ], - "m_format": "AutoComponent_RPC_NetLevelEntity Activated!\n", + "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!\n" - ] + "AutoComponent_RPC_NetLevelEntity Activated on entity: ", + {}, + "\n" + ], + "m_formatSlotMap": { + "Value": { + "m_id": "{CEBD2B9C-7DBA-486A-88DB-19F9C406B18E}" + } + } } } }, @@ -715,17 +764,17 @@ }, { "Id": { - "id": 28466663459165 + "id": 8310662318335 }, - "Name": "SC-Node(GetAuthorityToClientNoParams_PlayFxEventByEntityId)", + "Name": "SC-Node(GetEntityName)", "Components": { - "Component_[5890343372099746558]": { + "Component_[2232030369305027010]": { "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", - "Id": 5890343372099746558, + "Id": 2232030369305027010, "Slots": [ { "id": { - "m_id": "{4E1A6903-3F02-4395-A4C9-202D1F13250E}" + "m_id": "{F69C5762-0CAB-4A54-9C81-C99CDB73C834}" }, "contracts": [ { @@ -741,7 +790,7 @@ }, { "id": { - "m_id": "{26902930-1BC7-4BF6-8F41-C313BA819AA3}" + "m_id": "{03554C56-8237-4C19-B9F8-87DEA1AC3ED0}" }, "contracts": [ { @@ -756,7 +805,7 @@ }, { "id": { - "m_id": "{B6C92656-7593-4759-B657-98DB7CA30482}" + "m_id": "{6ECBD749-25FE-4813-B34E-EF46BC09E616}" }, "contracts": [ { @@ -771,17 +820,16 @@ }, { "id": { - "m_id": "{10D59D8F-E406-431B-87F9-FC1AF1EC65AF}" + "m_id": "{36D47097-8F0C-4CBD-8DE2-32F4754C569F}" }, "contracts": [ { "$type": "SlotTypeContract" } ], - "slotName": "Event<>", + "slotName": "String", "DisplayDataType": { - "m_type": 4, - "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" + "m_type": 5 }, "Descriptor": { "ConnectionType": 2, @@ -792,7 +840,6 @@ ], "Datums": [ { - "isOverloadedStorage": false, "scriptCanvasType": { "m_type": 1 }, @@ -801,18 +848,21 @@ "value": { "id": 2901262558 }, - "label": "EntityId: 0" + "label": "Entity Id" } ], - "methodType": 2, - "methodName": "GetAuthorityToClientNoParams_PlayFxEventByEntityId", - "className": "NetworkTestPlayerComponent", + "methodType": 0, + "methodName": "GetEntityName", + "className": "GameEntityContextRequestBus", + "resultSlotIDs": [ + {} + ], "inputSlots": [ { - "m_id": "{4E1A6903-3F02-4395-A4C9-202D1F13250E}" + "m_id": "{F69C5762-0CAB-4A54-9C81-C99CDB73C834}" } ], - "prettyClassName": "NetworkTestPlayerComponent" + "prettyClassName": "GameEntityContextRequestBus" } } }, @@ -1040,6 +1090,111 @@ } } } + }, + { + "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": [ @@ -1071,90 +1226,6 @@ } } }, - { - "Id": { - "id": 57042561607096 - }, - "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(GetAuthorityToClientNoParams_PlaySomeSuperficialFxEventByEntityId: In)", - "Components": { - "Component_[17563947682404363417]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 17563947682404363417, - "sourceEndpoint": { - "nodeId": { - "id": 57025381737912 - }, - "slotId": { - "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 57021086770616 - }, - "slotId": { - "m_id": "{5CDED810-9455-424A-9A1D-40B9366D1F94}" - } - } - } - } - }, - { - "Id": { - "id": 57046856574392 - }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: OnEvent), destEndpoint=(Print: In)", - "Components": { - "Component_[12226462283795741406]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 12226462283795741406, - "sourceEndpoint": { - "nodeId": { - "id": 57016791803320 - }, - "slotId": { - "m_id": "{A53312FA-7FAB-4C8C-A105-478C634ECD33}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 56995316966840 - }, - "slotId": { - "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}" - } - } - } - } - }, - { - "Id": { - "id": 57051151541688 - }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlaySomeSuperficialFx Notify Event: OnEvent), destEndpoint=(DrawTextOnEntity: In)", - "Components": { - "Component_[7209285242155620531]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 7209285242155620531, - "sourceEndpoint": { - "nodeId": { - "id": 57016791803320 - }, - "slotId": { - "m_id": "{A53312FA-7FAB-4C8C-A105-478C634ECD33}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 57003906901432 - }, - "slotId": { - "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}" - } - } - } - } - }, { "Id": { "id": 57055446508984 @@ -1183,34 +1254,6 @@ } } }, - { - "Id": { - "id": 57068331410872 - }, - "Name": "srcEndpoint=(On Graph Start: Out), destEndpoint=(Print: In)", - "Components": { - "Component_[11504712829988319988]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 11504712829988319988, - "sourceEndpoint": { - "nodeId": { - "id": 57025381737912 - }, - "slotId": { - "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 56991021999544 - }, - "slotId": { - "m_id": "{8E1B9705-148A-42E4-831E-D7B2877358E3}" - } - } - } - } - }, { "Id": { "id": 9392713697629 @@ -1241,27 +1284,27 @@ }, { "Id": { - "id": 9732016114013 + "id": 10269167405311 }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFxByEntityId: Out), destEndpoint=(Print: In)", + "Name": "srcEndpoint=(GetEntityName: String), destEndpoint=(Print: Value)", "Components": { - "Component_[14133537125895802472]": { + "Component_[11931728297561282182]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 14133537125895802472, + "Id": 11931728297561282182, "sourceEndpoint": { "nodeId": { - "id": 8400576252253 + "id": 8310662318335 }, "slotId": { - "m_id": "{A52302D6-9DF9-45C2-960D-19BF90A4A931}" + "m_id": "{36D47097-8F0C-4CBD-8DE2-32F4754C569F}" } }, "targetEndpoint": { "nodeId": { - "id": 56999611934136 + "id": 56991021999544 }, "slotId": { - "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" + "m_id": "{CEBD2B9C-7DBA-486A-88DB-19F9C406B18E}" } } } @@ -1269,27 +1312,83 @@ }, { "Id": { - "id": 29660664367453 + "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_[17084894170988218373]": { + "Component_[4910818715692868417]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 17084894170988218373, + "Id": 4910818715692868417, "sourceEndpoint": { "nodeId": { - "id": 28466663459165 + "id": 7820402811489 }, "slotId": { - "m_id": "{10D59D8F-E406-431B-87F9-FC1AF1EC65AF}" + "m_id": "{9C6DDF96-6BF0-45ED-B15F-2E6C2FF5F886}" } }, "targetEndpoint": { "nodeId": { - "id": 28956289730909 + "id": 8318619017825 }, "slotId": { - "m_id": "{7A4D75C3-C9C8-4CFB-A87C-C15B71AB3296}" + "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}" } } } @@ -1297,27 +1396,27 @@ }, { "Id": { - "id": 29716498942301 + "id": 9078828229217 }, "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Out), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: Connect)", "Components": { - "Component_[6504512854579046293]": { + "Component_[16758724763058723803]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 6504512854579046293, + "Id": 16758724763058723803, "sourceEndpoint": { "nodeId": { - "id": 28466663459165 + "id": 7820402811489 }, "slotId": { - "m_id": "{B6C92656-7593-4759-B657-98DB7CA30482}" + "m_id": "{94C2AF04-6BFA-4E5A-9490-C7479A7AF61E}" } }, "targetEndpoint": { "nodeId": { - "id": 28956289730909 + "id": 8318619017825 }, "slotId": { - "m_id": "{E97830A5-323D-4CD1-85CF-BAFB1735AC34}" + "m_id": "{2A42C379-8E3B-46EF-BC63-C1D5395CB583}" } } } @@ -1325,27 +1424,27 @@ }, { "Id": { - "id": 30240484952413 + "id": 9808972669537 }, - "Name": "srcEndpoint=(: ), destEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: In)", + "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: In)", "Components": { - "Component_[4329986353751309486]": { + "Component_[597205010205160938]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 4329986353751309486, + "Id": 597205010205160938, "sourceEndpoint": { "nodeId": { - "id": 57025381737912 + "id": 57012496836024 }, "slotId": { - "m_id": "{28664064-2483-465A-9BB1-4E1890048CDF}" + "m_id": "{158B30BE-BD39-40AE-A8A8-F0E5694F0180}" } }, "targetEndpoint": { "nodeId": { - "id": 28466663459165 + "id": 7820402811489 }, "slotId": { - "m_id": "{26902930-1BC7-4BF6-8F41-C313BA819AA3}" + "m_id": "{510F56FD-6778-4DB8-BDDE-258335431CC6}" } } } @@ -1353,19 +1452,47 @@ }, { "Id": { - "id": 30678571616605 + "id": 10148275085921 }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(: )", + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(Print: In)", "Components": { - "Component_[14658596010250057469]": { + "Component_[1594149632687531010]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 14658596010250057469, + "Id": 1594149632687531010, "sourceEndpoint": { "nodeId": { - "id": 28956289730909 + "id": 8318619017825 }, "slotId": { - "m_id": "{1F11AAA9-2A99-41A2-AABE-7B57463E3664}" + "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": { @@ -1381,27 +1508,27 @@ }, { "Id": { - "id": 31060823705949 + "id": 42045766425613 }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(: )", + "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(Print: In)", "Components": { - "Component_[16833378372852944435]": { + "Component_[1911118463107071864]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 16833378372852944435, + "Id": 1911118463107071864, "sourceEndpoint": { "nodeId": { - "id": 28956289730909 + "id": 56986727032248 }, "slotId": { - "m_id": "{1F11AAA9-2A99-41A2-AABE-7B57463E3664}" + "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}" } }, "targetEndpoint": { "nodeId": { - "id": 56995316966840 + "id": 56999611934136 }, "slotId": { - "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}" + "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" } } } @@ -1416,6 +1543,99 @@ "_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 @@ -1432,8 +1652,8 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 440.0, - -20.0 + 420.0, + -60.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1447,68 +1667,6 @@ } } }, - { - "Key": { - "id": 28466663459165 - }, - "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": [ - -200.0, - 300.0 - ] - }, - "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".method" - }, - "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { - "$type": "PersistentIdComponentSaveData", - "PersistentId": "{A84996D5-99BD-4F5D-859E-02BFAB3FA83A}" - } - } - } - }, - { - "Key": { - "id": 28956289730909 - }, - "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": [ - 260.0, - 300.0 - ] - }, - "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { - "$type": "StylingComponentSaveData", - "SubStyle": ".azeventhandler" - }, - "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { - "$type": "PersistentIdComponentSaveData", - "PersistentId": "{B237687E-F57E-4C04-9B46-103548751B5D}" - } - } - } - }, { "Key": { "id": 56986727032248 @@ -1555,8 +1713,8 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - -240.0, - -300.0 + 540.0, + -320.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1615,8 +1773,8 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 960.0, - -40.0 + 420.0, + 100.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1722,15 +1880,15 @@ }, { "Key": { - "id": 5286394689911028851 + "id": 1685762441320719908 }, "Value": { "ComponentData": { "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { "$type": "SceneComponentSaveData", "ViewParams": { - "AnchorX": -579.0, - "AnchorY": -194.0 + "AnchorX": -349.0, + "AnchorY": 10.0 } } } @@ -1767,6 +1925,10 @@ "Key": 11983076003173356132, "Value": 1 }, + { + "Key": 13774516196858047560, + "Value": 1 + }, { "Key": 13774516226790665785, "Value": 1 From 0b78c377f8baa7941aad3094b513d069690168a4 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Mon, 27 Dec 2021 09:39:04 -0800 Subject: [PATCH 20/39] Minor: py method comment added Signed-off-by: Gene Walters --- .../EditorPythonTestTools/editor_python_test_tools/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2ba692d89b..1674fba7be 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -138,7 +138,7 @@ class TestHelper: :param window: The log's window name. For example, logs printed via script-canvas use the "Script" window. :param unexpected_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. + :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 unexpected message is found, a failed critical result is reported; otherwise success. """ From 81b2841bd2b75137b6e7b7ce6618d3a4c3245e3e Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Tue, 28 Dec 2021 09:16:48 -0800 Subject: [PATCH 21/39] chore: correct text for assertion Signed-off-by: Michael Pollind --- Code/Framework/AzCore/AzCore/Math/Plane.inl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzCore/AzCore/Math/Plane.inl b/Code/Framework/AzCore/AzCore/Math/Plane.inl index bade6f1775..795ee8b4bc 100644 --- a/Code/Framework/AzCore/AzCore/Math/Plane.inl +++ b/Code/Framework/AzCore/AzCore/Math/Plane.inl @@ -19,14 +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 a normalized"); + 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 a normalized"); + AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized"); Plane result; result.Set(normal, dist); return result; @@ -35,7 +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 not normalized"); + AZ_MATH_ASSERT(Vector3(a, b, c).IsNormalized(), "This normal is notormalized"); Plane result; result.Set(a, b, c, d); return result; @@ -68,21 +68,21 @@ namespace AZ AZ_MATH_INLINE void Plane::Set(const Vector3& normal, float d) { - AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is not normalized"); + 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 not normalized"); + 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 not normalized"); + AZ_MATH_ASSERT(normal.IsNormalized(), "This normal is notormalized"); m_plane.SetX(normal.GetX()); m_plane.SetY(normal.GetY()); m_plane.SetZ(normal.GetZ()); From 49dd17f410a5ffefb55c1484e8a1bec9b257362e Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Wed, 29 Dec 2021 11:25:17 -0600 Subject: [PATCH 22/39] Optimized Terrain Debugger Wireframe rendering (#6572) * Optimized wireframe drawing. As a part of rearranging the code to make use of the upcoming ProcessHeightsFromRegion, the number of calls to GetHeight could be reduced, dropping the refresh time in my test case from 2550 ms to 1068 ms. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Change refresh to only happen on wireframe draws. This helps ensure that multiple data changes in a single frame don't cause multiple refreshes, and prevents us from taking a refresh penalty when the wireframe isn't being drawn at all. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Change debugger to update wireframes incrementally. This works by having a fixed NxN grid of sectors. The camera is always considered as being in the center of the grid, so anytime the camera's grid square changes, only a subset of sectors are updated to have the new wireframe data. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Bugfix - sector vertex count was 4x too high. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Bugfixes & comments. Fixed initial "mark dirty" refresh - the dirty region Z value needed to be ignored. Added copious comments for the math & logic, and simplified some of the math. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Small update to comment for better readability. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Addressed PR feedback. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../TerrainWorldDebuggerComponent.cpp | 390 +++++++++++------- .../TerrainWorldDebuggerComponent.h | 25 +- 2 files changed, 256 insertions(+), 159 deletions(-) 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(gridPosition.GetFloor().GetY()); + + // Loop through each sector that we *want* to draw, based on camera position. If the current sector at that index in + // m_wireframeSectors doesn't match the world position we want, update its world position and mark it as dirty. + // (We loop from -gridSize/2 to gridSize/2 so that the camera is always in the center sector.) + for (int32_t sectorY = cameraSectorY - (m_sectorGridSize / 2); sectorY <= cameraSectorY + (m_sectorGridSize / 2); sectorY++) + { + for (int32_t sectorX = cameraSectorX - (m_sectorGridSize / 2); sectorX <= cameraSectorX + (m_sectorGridSize / 2); sectorX++) { - for (float x = sector.m_aabb.GetMin().GetX(); x < sector.m_aabb.GetMax().GetX(); x += queryResolution.GetX()) + + // Calculate the index in m_wireframeSectors for this sector. Our indices should rotate through 0 - gridSize, but just + // using a single mod will produce a negative result for negative sector indices. Using abs() will give us incorrect + // "backwards" indices for negative numbers, so instead we add the grid size and mod a second time. + // Ex: For a grid size of 5, we want the indices to map like this: + // Index 0 1 2 3 4 + // Values -10 -9 -8 -7 -6 + // -5 -4 -3 -2 -1 + // 0 1 2 3 4 + // 5 6 7 8 9 + // For -9, (-9 % 5) = -4, then (-4 + 5) % 5 = 1. If we used abs(), we'd get 4, which is backwards from what we want. + int32_t sectorYIndex = ((sectorY % m_sectorGridSize) + m_sectorGridSize) % m_sectorGridSize; + int32_t sectorXIndex = ((sectorX % m_sectorGridSize) + m_sectorGridSize) % m_sectorGridSize; + int32_t sectorIndex = (sectorYIndex * m_sectorGridSize) + sectorXIndex; + + WireframeSector& sector = m_wireframeSectors[sectorIndex]; + + // Calculate the new world space box for this sector. + AZ::Aabb sectorAabb = AZ::Aabb::CreateFromMinMax( + AZ::Vector3(sectorX * sectorSize.GetX(), sectorY * sectorSize.GetY(), worldMinZ), + AZ::Vector3((sectorX + 1) * sectorSize.GetX(), (sectorY + 1) * sectorSize.GetY(), worldMinZ)); + + // Clamp it to the terrain world bounds. + sectorAabb.Clamp(worldBounds); + + // If the world space box for the sector doesn't match, set it and mark the sector as dirty so we refresh the height data. + if (sector.m_aabb != sectorAabb) { - float x1 = x + queryResolution.GetX(); - float y1 = y + queryResolution.GetY(); - - float z00 = 0.0f; - float z01 = 0.0f; - float z10 = 0.0f; - bool terrainExists; - - AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - z00, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y, - AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - z01, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y1, - AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( - z10, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x1, y, - AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - - sector.m_lineVertices.push_back(AZ::Vector3(x, y, z00)); - sector.m_lineVertices.push_back(AZ::Vector3(x1, y, z10)); - - sector.m_lineVertices.push_back(AZ::Vector3(x, y, z00)); - sector.m_lineVertices.push_back(AZ::Vector3(x, y1, z01)); + sector.m_aabb = sectorAabb; + sector.m_isDirty = true; } } } + + // Finally, for each sector, rebuild the data if it's dirty, then draw it assuming it has valid data. + // (Sectors that are outside the world bounds won't have any valid data, so they'll get skipped) + for (auto& sector : m_wireframeSectors) + { + if (sector.m_isDirty) + { + RebuildSectorWireframe(sector, heightDataResolution, worldMinZ); + } + + 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); + } + } + } + + + void TerrainWorldDebuggerComponent::DisplayEntityViewport( + const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) + { + DrawWorldBounds(debugDisplay); + DrawWireframe(viewportInfo, debugDisplay); + + } + + void TerrainWorldDebuggerComponent::RebuildSectorWireframe(WireframeSector& sector, const AZ::Vector2& gridResolution, float worldMinZ) + { + if (!sector.m_isDirty) + { + return; + } + + sector.m_isDirty = false; + + // To rebuild the wireframe, we walk through the sector by X, then by Y. For each point, we add two lines in a _| shape. + // To do that, we'll need to cache the height from the previous point to draw the _ line, and from the previous row to draw + // the | line. + + // When walking through the bounding box, the loops will be inclusive on one side, and exclusive on the other. However, since + // our box is exactly aligned with grid points, we want to get the grid points on both sides in each direction, so we need to + // expand our query region by one extra point. + // For example, if our AABB is 2 m and our grid resolution is 1 m, we'll want to query (*--*--*--), not (*--*--). + // Since we're processing lines based on the grid points and going backwards, this will give us (*--*--*). + + AZ::Aabb region = sector.m_aabb; + region.SetMax(region.GetMax() + AZ::Vector3(gridResolution.GetX(), gridResolution.GetY(), 0.0f)); + + // This keeps track of the height from the previous point for the _ line. + float previousHeight = 0.0f; + + // This keeps track of the heights from the previous row for the | line. + AZStd::vector rowHeights(aznumeric_cast(ceil(region.GetExtents().GetX() / gridResolution.GetX()))); + + // We need 4 vertices for each grid point in our sector to hold the _| shape. + const uint32_t numSamplesX = static_cast((region.GetMax().GetX() - region.GetMin().GetX()) / gridResolution.GetX()); + const uint32_t numSamplesY = static_cast((region.GetMax().GetY() - region.GetMin().GetY()) / gridResolution.GetY()); + sector.m_lineVertices.clear(); + sector.m_lineVertices.reserve(numSamplesX * numSamplesY * 4); + + // For each terrain height value in the region, create the _| grid lines for that point and cache off the height value + // for use with subsequent grid line calculations. + auto ProcessHeightValue = [gridResolution, &previousHeight, &rowHeights, §or] + (uint32_t xIndex, uint32_t yIndex, const AZ::Vector3& position, [[maybe_unused]] bool terrainExists) + { + // Don't add any vertices for the first column or first row. These grid lines will be handled by an adjacent sector, if + // there is one. + if ((xIndex > 0) && (yIndex > 0)) + { + float x = position.GetX() - gridResolution.GetX(); + float y = position.GetY() - gridResolution.GetY(); + + sector.m_lineVertices.emplace_back(AZ::Vector3(x, position.GetY(), previousHeight)); + sector.m_lineVertices.emplace_back(position); + + sector.m_lineVertices.emplace_back(AZ::Vector3(position.GetX(), y, rowHeights[xIndex])); + sector.m_lineVertices.emplace_back(position); + } + + // Save off the heights so that we can use them to draw subsequent columns and rows. + previousHeight = position.GetZ(); + rowHeights[xIndex] = position.GetZ(); + }; + + // This set of nested loops will get replaced with a call to ProcessHeightsFromRegion once the API exists. + uint32_t yIndex = 0; + for (float y = region.GetMin().GetY(); y < region.GetMax().GetY(); y += gridResolution.GetY()) + { + uint32_t xIndex = 0; + for (float x = region.GetMin().GetX(); x < region.GetMax().GetX(); x += gridResolution.GetX()) + { + float height = worldMinZ; + bool terrainExists = false; + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + height, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, x, y, + AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); + ProcessHeightValue(xIndex, yIndex, AZ::Vector3(x, y, height), terrainExists); + xIndex++; + } + yIndex++; + } } void TerrainWorldDebuggerComponent::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) { if (dataChangedMask & (TerrainDataChangedMask::Settings | TerrainDataChangedMask::HeightData)) { - RefreshCachedWireframeGrid(dirtyRegion); + MarkDirtySectors(dirtyRegion); + } + + if (dataChangedMask & TerrainDataChangedMask::Settings) + { + // Any time the world bounds potentially changes, notify that the terrain debugger's visibility bounds also changed. + AzFramework::IEntityBoundsUnionRequestBus::Broadcast( + &AzFramework::IEntityBoundsUnionRequestBus::Events::RefreshEntityLocalBoundsUnion, GetEntityId()); } } diff --git a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h index 5ec831c038..f3bcead8c3 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h @@ -82,33 +82,40 @@ namespace Terrain private: + TerrainWorldDebuggerConfig m_configuration; + // Cache our debug wireframe representation in "sectors" of data so that we can easily control how far out we draw // the wireframe representation in each direction. struct WireframeSector { - AZ::Aabb m_aabb; + AZ::Aabb m_aabb{ AZ::Aabb::CreateNull() }; AZStd::vector m_lineVertices; + bool m_isDirty{ true }; }; + void RebuildSectorWireframe(WireframeSector& sector, const AZ::Vector2& gridResolution, float worldMinZ); + void MarkDirtySectors(const AZ::Aabb& dirtyRegion); + void DrawWorldBounds(AzFramework::DebugDisplayRequests& debugDisplay); + void DrawWireframe(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay); + // Each sector contains an N x N grid of squares that it will draw. Since this is a count of the number of terrain grid points // in each direction, the actual world size will depend on the terrain grid resolution in each direction. static constexpr int32_t SectorSizeInGridPoints = 10; - // For each grid point we will draw half a square (left-right, top-down), so we need 4 vertices for the two lines. + // For each grid point we will draw half a square ( _| ), so we need 4 vertices for the two lines. static constexpr int32_t VerticesPerGridPoint = 4; - // Pre-calculate the total number of vertices per sector. - static constexpr int32_t VerticesPerSector = - (SectorSizeInGridPoints * VerticesPerGridPoint) * (SectorSizeInGridPoints * VerticesPerGridPoint); + // Pre-calculate the total number of vertices per sector (N x N grid points, with 4 vertices per grid point) + static constexpr int32_t VerticesPerSector = (SectorSizeInGridPoints * SectorSizeInGridPoints) * VerticesPerGridPoint; // AuxGeom has limits to the number of lines it can draw in a frame, so we'll cap how many total sectors to draw. static constexpr int32_t MaxVerticesToDraw = 500000; static constexpr int32_t MaxSectorsToDraw = MaxVerticesToDraw / VerticesPerSector; - void RefreshCachedWireframeGrid(const AZ::Aabb& dirtyRegion); - - TerrainWorldDebuggerConfig m_configuration; + // Structure to keep track of all our current wireframe sectors, so that we don't have to recalculate them every frame. AZStd::vector m_wireframeSectors; - AZ::Aabb m_wireframeBounds; + + // The size in sectors of our wireframe grid in each direction (i.e. a 5 x 5 sector grid has a sectorGridSize of 5) + int32_t m_sectorGridSize{ 0 }; }; } From 26a5128576e169df7984146617598f1798a9b30e Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Wed, 29 Dec 2021 10:02:32 -0800 Subject: [PATCH 23/39] Renaming py util methods for reporting log lines based on feedback Signed-off-by: Gene Walters --- .../editor_python_test_tools/utils.py | 46 +++++++++---------- .../tests/Multiplayer_AutoComponent_RPC.py | 10 +++- 2 files changed, 30 insertions(+), 26 deletions(-) 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 1674fba7be..35a50b6090 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/utils.py @@ -101,49 +101,47 @@ class TestHelper: Report.critical_result(msgtuple_success_fail, general.is_in_game_mode()) @staticmethod - def find_expected_line(window, expected_message, print_infos): + 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 expected_message: The log message to search. + :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 message is found, otherwise false. + :return: True if the line is found, otherwise false. """ for printInfo in print_infos: - if printInfo.window == window.strip() and printInfo.message.strip() == expected_message: + if printInfo.window == window.strip() and printInfo.message.strip() == line: return True return False @staticmethod - def wait_for_critical_expected_line(window, expected_message, print_infos, time_out): + def succeed_if_log_line_found(window, line, print_infos, time_out): """ - Looks for an expected line in a list of tracer log lines for a period of time. - Reports a critical result based on if the expected line was found. The result is successful if the line is found. + 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 expected_message: The log message we're expecting to find. + :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 expected message is found, a successful critical result is reported; otherwise failure. + :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_expected_line(window, expected_message, print_infos), time_out) - Report.critical_result(("Found expected line: " + expected_message, "Failed to find expected line: " + expected_message), TestHelper.find_expected_line(window, expected_message, print_infos)) + 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 wait_for_critical_unexpected_line(window, unexpected_line, print_infos, time_out): + def fail_if_log_line_found(window, line, print_infos, time_out): """ - Looks for an unexpected line in a list of tracer log lines over a period of time. - Reports a critical result based on if the unexpected line was found. The result is successful if the line is not found. + 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 unexpected_line: The log message we're hoping to not find. + :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 unexpected message is found, a failed critical result is reported; otherwise 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_expected_line(window, unexpected_line, print_infos), time_out) - Report.critical_result(("Unexpected line not found: " + unexpected_line, "Unexpected line found: " + unexpected_line), not TestHelper.find_expected_line(window, unexpected_line, print_infos)) + 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: @@ -163,20 +161,20 @@ class TestHelper: multiplayer.PythonEditorFuncs_enter_game_mode() # make sure the server launcher binary exists - TestHelper.wait_for_critical_unexpected_line("MultiplayerEditor", "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) - TestHelper.wait_for_critical_expected_line("EditorServer", "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) - TestHelper.wait_for_critical_expected_line("MultiplayerEditor", "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) - TestHelper.wait_for_critical_expected_line("EditorServer", "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) - TestHelper.wait_for_critical_expected_line("MultiplayerEditorConnection", "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) - TestHelper.wait_for_critical_unexpected_line("EditorServer", 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/tests/Multiplayer_AutoComponent_RPC.py b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py index 8c13806a30..827826cbcc 100644 --- a/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py +++ b/AutomatedTesting/Gem/PythonTests/Multiplayer/tests/Multiplayer_AutoComponent_RPC.py @@ -63,8 +63,14 @@ def Multiplayer_AutoComponent_RPC(): # 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.wait_for_critical_expected_line('EditorServer', 'Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS) - helper.wait_for_critical_expected_line('Script', "AutoComponent_RPC: I'm Player #1", section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS) + 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 From a10bf927393934821ea2df3986df6561e99e4a49 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Wed, 29 Dec 2021 13:44:03 -0600 Subject: [PATCH 24/39] Encapsulated gradient transform logic into separate class (#6586) * First version of GradientTransform class. The gradient transform logic is getting encapsulated into a class so that it can be cached and used by components in a much more optimal way than making ebus calls to the GradientTransform component on every transformed point. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Moved GradientTransform into its own source files. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Clean up and simplify GradientTransform logic. Added extensive commenting and split TransformPositionToUVW into a separate method for normalizing (TransformPositionToUVWNormalized) so that there doesn't need to be any conditional logic. There's no runtime variance as to which one needs to be called from a given call site. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Added unit tests for GradientTransform. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Add comparison operators to GradientTransform so we can easily tell when it has changed. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Updated comments to be more Doxygen-friendly. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Source/FastNoiseGradientComponent.cpp | 3 +- Gems/FastNoise/Code/Tests/FastNoiseTest.cpp | 12 +- Gems/GradientSignal/Code/CMakeLists.txt | 2 + .../GradientTransformModifierRequestBus.h | 24 +- .../Ebuses/GradientTransformRequestBus.h | 3 +- .../GradientSignal/GradientTransform.h | 157 ++++++++++ .../Code/Include/GradientSignal/Util.h | 42 +-- .../Components/GradientTransformComponent.cpp | 59 +--- .../Components/GradientTransformComponent.h | 4 +- .../Components/ImageGradientComponent.cpp | 3 +- .../Components/PerlinGradientComponent.cpp | 3 +- .../Components/RandomGradientComponent.cpp | 3 +- .../Code/Source/GradientTransform.cpp | 163 ++++++++++ Gems/GradientSignal/Code/Source/Util.cpp | 77 ----- .../Tests/GradientSignalTransformTests.cpp | 284 ++++++++++++++++++ .../Code/gradientsignal_files.cmake | 3 +- .../Code/gradientsignal_tests_files.cmake | 1 + 17 files changed, 663 insertions(+), 180 deletions(-) create mode 100644 Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h create mode 100644 Gems/GradientSignal/Code/Source/GradientTransform.cpp delete mode 100644 Gems/GradientSignal/Code/Source/Util.cpp create mode 100644 Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp diff --git a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp index 0f8ce4c5da..bb4c337299 100644 --- a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp +++ b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp @@ -299,9 +299,8 @@ namespace FastNoiseGem 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); + GetEntityId(), &GradientSignal::GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, wasPointRejected); if (!wasPointRejected) { diff --git a/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp b/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp index bae1d424c7..620f538c71 100644 --- a/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp +++ b/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp @@ -46,8 +46,16 @@ 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 TransformPositionToUVW([[maybe_unused]] const AZ::Vector3& inPosition, [[maybe_unused]] AZ::Vector3& outUVW, [[maybe_unused]] bool& wasPointRejected) const override {} + void TransformPositionToUVWNormalized( + [[maybe_unused]] const AZ::Vector3& inPosition, + [[maybe_unused]] AZ::Vector3& outUVW, + [[maybe_unused]] bool& wasPointRejected) const override + { + } + void GetGradientLocalBounds([[maybe_unused]] AZ::Aabb& bounds) const override + { + } void GetGradientEncompassingBounds([[maybe_unused]] AZ::Aabb& bounds) const override {} ////////////////////////////////////////////////////////////////////////// diff --git a/Gems/GradientSignal/Code/CMakeLists.txt b/Gems/GradientSignal/Code/CMakeLists.txt index c0b36f4b12..f5c0457c23 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 @@ -157,6 +158,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..ac2f1a9cb1 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h @@ -27,7 +27,8 @@ namespace GradientSignal virtual ~GradientTransformRequests() = default; - virtual void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, const bool shouldNormalizeOutput, bool& wasPointRejected) const = 0; + virtual void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const = 0; + virtual void TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const = 0; virtual void GetGradientLocalBounds(AZ::Aabb& bounds) const = 0; virtual void GetGradientEncompassingBounds(AZ::Aabb& bounds) const = 0; }; diff --git a/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h b/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h new file mode 100644 index 0000000000..c8073c2f4f --- /dev/null +++ b/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h @@ -0,0 +1,157 @@ +/* + * 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. + using WrappingTransformFunction = AZStd::function; + 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; + WrappingTransformFunction m_wrappingTransform = NoTransform; + + /** + * 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/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..8cd7dc56a1 100644 --- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp @@ -322,55 +322,17 @@ namespace GradientSignal return false; } - void GradientTransformComponent::TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, const bool shouldNormalizeOutput, bool& wasPointRejected) const + void GradientTransformComponent::TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const { AZStd::lock_guard lock(m_cacheMutex); + m_gradientTransform.TransformPositionToUVW(inPosition, outUVW, wasPointRejected); + } - //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::TransformPositionToUVWNormalized( + const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const + { + AZStd::lock_guard lock(m_cacheMutex); + m_gradientTransform.TransformPositionToUVWNormalized(inPosition, outUVW, wasPointRejected); } void GradientTransformComponent::GetGradientLocalBounds(AZ::Aabb& bounds) const @@ -499,6 +461,11 @@ namespace GradientSignal shapeTransformFinal.SetTranslation(m_configuration.m_translate); shapeTransformFinal.MultiplyByScale(m_configuration.m_scale); m_shapeTransformInverse = shapeTransformFinal.GetInverseFull(); + + // Set everything up on the Gradient Transform + const bool use3dGradients = m_configuration.m_advancedMode && m_configuration.m_is3d; + m_gradientTransform = GradientTransform( + m_shapeBounds, shapeTransformFinal, use3dGradients, m_configuration.m_frequencyZoom, m_configuration.m_wrappingType); } 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..50ae6fe475 100644 --- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h +++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h @@ -101,7 +101,8 @@ namespace GradientSignal ////////////////////////////////////////////////////////////////////////// // GradientTransformRequestBus - void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, const bool shouldNormalizeOutput, bool& wasPointRejected) const override; + void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const override; + void TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const override; void GetGradientLocalBounds(AZ::Aabb& bounds) const override; void GetGradientEncompassingBounds(AZ::Aabb& bounds) const override; @@ -172,5 +173,6 @@ namespace GradientSignal 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..2d594a201c 100644 --- a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp @@ -194,9 +194,8 @@ namespace GradientSignal 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); + GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVWNormalized, sampleParams.m_position, uvw, wasPointRejected); if (!wasPointRejected) { diff --git a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp index e150ff4305..d5e846b9ef 100644 --- a/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/PerlinGradientComponent.cpp @@ -179,9 +179,8 @@ namespace GradientSignal 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); + GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, wasPointRejected); if (!wasPointRejected) { diff --git a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp index d28fe13aff..d320dbeb3d 100644 --- a/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp +++ b/Gems/GradientSignal/Code/Source/Components/RandomGradientComponent.cpp @@ -142,9 +142,8 @@ namespace GradientSignal 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); + GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, wasPointRejected); if (!wasPointRejected) { diff --git a/Gems/GradientSignal/Code/Source/GradientTransform.cpp b/Gems/GradientSignal/Code/Source/GradientTransform.cpp new file mode 100644 index 0000000000..acf9e2f150 --- /dev/null +++ b/Gems/GradientSignal/Code/Source/GradientTransform.cpp @@ -0,0 +1,163 @@ +/* + * 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_wrappingTransform(NoTransform) + , 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()); + } + + // Set up the appropriate wrapping transform function for the the given wrapping type. + // Also note that ClampToZero is the only wrapping type that allows us to return a "pointIsRejected" result + // for points that fall outside the shape bounds. + if (m_shapeBounds.IsValid()) + { + switch (wrappingType) + { + default: + case WrappingType::None: + m_wrappingTransform = GetUnboundedPointInAabb; + break; + case WrappingType::ClampToEdge: + m_wrappingTransform = GetClampedPointInAabb; + break; + case WrappingType::ClampToZero: + m_alwaysAcceptPoint = false; + m_wrappingTransform = GetClampedPointInAabb; + break; + case WrappingType::Mirror: + m_wrappingTransform = GetMirroredPointInAabb; + break; + case WrappingType::Repeat: + m_wrappingTransform = GetWrappedPointInAabb; + break; + } + } + + 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; + + outUVW = m_wrappingTransform(outUVW, m_shapeBounds); + 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/GradientSignalTransformTests.cpp b/Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp new file mode 100644 index 0000000000..73dabf1d6a --- /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 "Tests/GradientSignalTestMocks.h" + +#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_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..5f8ffe2ebb 100644 --- a/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake +++ b/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake @@ -11,6 +11,7 @@ set(FILES Tests/GradientSignalReferencesTests.cpp Tests/GradientSignalServicesTests.cpp Tests/GradientSignalSurfaceTests.cpp + Tests/GradientSignalTransformTests.cpp Tests/GradientSignalTestMocks.h Tests/GradientSignalTest.cpp Tests/ImageAssetTests.cpp From 2f1346c719d5e6ed9c2c9a878bb13e9a75d34c69 Mon Sep 17 00:00:00 2001 From: Ken Pruiksma Date: Wed, 29 Dec 2021 16:38:28 -0600 Subject: [PATCH 25/39] Terrain Feature Processor separated into several classes. Macro materials abstracted from meshes. (#6350) * Breaking up terrain FP wip - macro materials decoupled from meshes. Mesh creation and rendering pulled out from MeshFeatureProcessor into TerrainMeshManager. Stubs added for macro and detail material managers. Signed-off-by: Ken Pruiksma * Separated macro material management from the terrain feature processor. Also separated bindless image array handling from terrain feature processor. Signed-off-by: Ken Pruiksma * Detail materials separated from terrain feature processor. Also pulled out Aabb2i and Vector2i into their own simple classes Signed-off-by: Ken Pruiksma * Changed some classes so that when the SRG changes the classes don't need to be completely reinitialized and can instead just update their indices and push data to the new srg. Fixed an issue where MacroMaterialData wasn't being exposed to ScriptCanvas. Terrain shader reloads should now work correctly. Also added some debug logging to help catch an issue where sometimes detail materials don't seem to load. Signed-off-by: Ken Pruiksma * Terrain PR reveiw updates Signed-off-by: Ken Pruiksma * Some small PR review fixes. More comments in TerrainDetailMaterialManager.h Signed-off-by: Ken Pruiksma * Fixing unused variable causing clang failure Signed-off-by: Ken Pruiksma * Fixing unused variable in release. Signed-off-by: Ken Pruiksma * Fixing a forward declare that oddly didn't work on linux. Signed-off-by: Ken Pruiksma * Fixing linux missing include... not sure why only linux failed on this. Signed-off-by: Ken Pruiksma * Adding missing include Signed-off-by: Ken Pruiksma --- .../ShaderResourceGroups/SceneSrgAll.azsli | 1 + .../ShaderResourceGroups/ViewSrgAll.azsli | 1 - .../Code/Source/Utils/GpuBufferHandler.cpp | 6 +- .../Terrain/DefaultPbrTerrain.material | 2 +- .../Materials/Terrain/PbrTerrain.materialtype | 2 +- .../Assets/Shaders/Terrain/SceneSrg.azsli | 35 + .../Shaders/Terrain/TerrainCommon.azsli | 243 +-- .../Terrain/TerrainDetailHelpers.azsli | 14 +- .../Terrain/TerrainPBR_ForwardPass.azsl | 117 +- .../Assets/Shaders/Terrain/TerrainSrg.azsli | 28 +- .../Shaders/Terrain/Terrain_DepthPass.azsl | 28 +- .../Assets/Shaders/Terrain/ViewSrg.azsli | 80 - .../Code/Source/TerrainRenderer/Aabb2i.cpp | 44 + .../Code/Source/TerrainRenderer/Aabb2i.h | 34 + .../BindlessImageArrayHandler.cpp | 101 ++ .../BindlessImageArrayHandler.h | 48 + .../TerrainDetailMaterialManager.cpp | 912 ++++++++++ .../TerrainDetailMaterialManager.h | 226 +++ .../TerrainFeatureProcessor.cpp | 1522 ++--------------- .../TerrainRenderer/TerrainFeatureProcessor.h | 314 +--- .../TerrainMacroMaterialBus.cpp | 19 + .../TerrainRenderer/TerrainMacroMaterialBus.h | 6 +- .../TerrainMacroMaterialManager.cpp | 412 +++++ .../TerrainMacroMaterialManager.h | 116 ++ .../TerrainRenderer/TerrainMeshManager.cpp | 354 ++++ .../TerrainRenderer/TerrainMeshManager.h | 117 ++ .../Code/Source/TerrainRenderer/Vector2i.cpp | 41 + .../Code/Source/TerrainRenderer/Vector2i.h | 29 + Gems/Terrain/Code/terrain_files.cmake | 12 + 29 files changed, 2922 insertions(+), 1942 deletions(-) create mode 100644 Gems/Terrain/Assets/Shaders/Terrain/SceneSrg.azsli delete mode 100644 Gems/Terrain/Assets/Shaders/Terrain/ViewSrg.azsli create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.cpp create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.h create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.cpp create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.h create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.cpp create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.h create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.h create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.cpp create mode 100644 Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.h 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/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/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/TerrainRenderer/Aabb2i.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.cpp new file mode 100644 index 0000000000..c38651f296 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.cpp @@ -0,0 +1,44 @@ +/* + * 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 Terrain +{ + Aabb2i::Aabb2i(const Vector2i& min, const Vector2i& max) + : m_min(min) + , m_max(max) + {} + + Aabb2i Aabb2i::operator+(const Vector2i& rhs) const + { + return { m_min + rhs, m_max + rhs }; + } + + Aabb2i Aabb2i::operator-(const Vector2i& rhs) const + { + return *this + -rhs; + } + + Aabb2i Aabb2i::GetClamped(Aabb2i rhs) const + { + Aabb2i ret; + ret.m_min.m_x = AZ::GetMax(m_min.m_x, rhs.m_min.m_x); + ret.m_min.m_y = AZ::GetMax(m_min.m_y, rhs.m_min.m_y); + ret.m_max.m_x = AZ::GetMin(m_max.m_x, rhs.m_max.m_x); + ret.m_max.m_y = AZ::GetMin(m_max.m_y, rhs.m_max.m_y); + return ret; + } + + bool Aabb2i::IsValid() const + { + // Intentionally strict, equal min/max not valid. + return m_min.m_x < m_max.m_x && m_min.m_y < m_max.m_y; + } +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.h b/Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.h new file mode 100644 index 0000000000..88f735564d --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Aabb2i.h @@ -0,0 +1,34 @@ +/* + * 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 + +namespace Terrain +{ + class Aabb2i + { + public: + + Aabb2i() = default; + Aabb2i(const Vector2i& min, const Vector2i& max); + + Aabb2i operator+(const Vector2i& offset) const; + Aabb2i operator-(const Vector2i& offset) const; + + Aabb2i GetClamped(Aabb2i rhs) const; + bool IsValid() const; + + + Vector2i m_min{AZStd::numeric_limits::min(), AZStd::numeric_limits::min()}; + Vector2i m_max{AZStd::numeric_limits::max(), AZStd::numeric_limits::max()}; + + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.cpp new file mode 100644 index 0000000000..595877c88b --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.cpp @@ -0,0 +1,101 @@ +/* + * 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 + +namespace AZ::Render +{ + namespace + { + [[maybe_unused]] const char* BindlessImageArrayHandlerName = "TerrainFeatureProcessor"; + } + + void BindlessImageArrayHandler::Initialize(AZ::Data::Instance& srg, const AZ::Name& propertyName) + { + if (!m_isInitialized) + { + m_isInitialized = UpdateSrgIndices(srg, propertyName); + } + else + { + AZ_Error(BindlessImageArrayHandlerName, false, "Already initialized."); + } + } + + void BindlessImageArrayHandler::Reset() + { + m_texturesIndex = {}; + m_isInitialized = false; + } + + bool BindlessImageArrayHandler::IsInitialized() const + { + return m_isInitialized; + } + + bool BindlessImageArrayHandler::UpdateSrgIndices(AZ::Data::Instance& srg, const AZ::Name& propertyName) + { + if (srg) + { + m_texturesIndex = srg->GetLayout()->FindShaderInputImageUnboundedArrayIndex(propertyName); + AZ_Error(BindlessImageArrayHandlerName, m_texturesIndex.IsValid(), "Failed to find srg input constant %s.", propertyName.GetCStr()); + } + else + { + AZ_Error(BindlessImageArrayHandlerName, false, "Cannot initialize using a null shader resource group."); + } + return m_texturesIndex.IsValid(); + } + + uint16_t BindlessImageArrayHandler::AppendBindlessImage(const AZ::RHI::ImageView* imageView) + { + uint16_t imageIndex = 0xFFFF; + + AZStd::unique_lock lock(m_updateMutex); + if (m_bindlessImageViewFreeList.size() > 0) + { + imageIndex = m_bindlessImageViewFreeList.back(); + m_bindlessImageViewFreeList.pop_back(); + m_bindlessImageViews.at(imageIndex) = imageView; + } + else + { + imageIndex = aznumeric_cast(m_bindlessImageViews.size()); + m_bindlessImageViews.push_back(imageView); + } + return imageIndex; + } + + void BindlessImageArrayHandler::UpdateBindlessImage(uint16_t index, const AZ::RHI::ImageView* imageView) + { + AZStd::shared_lock lock(m_updateMutex); + m_bindlessImageViews.at(index) = imageView; + } + + void BindlessImageArrayHandler::RemoveBindlessImage(uint16_t index) + { + AZStd::unique_lock lock(m_updateMutex); + m_bindlessImageViews.at(index) = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Magenta)->GetImageView(); + m_bindlessImageViewFreeList.push_back(index); + } + + bool BindlessImageArrayHandler::UpdateSrg(AZ::Data::Instance& srg) const + { + if (!m_isInitialized) + { + AZ_Error("BindlessImageArrayHandler", false, "BindlessImageArrayHandler not initialized") + return false; + } + + AZStd::array_view imageViews(m_bindlessImageViews.data(), m_bindlessImageViews.size()); + return srg->SetImageViewUnboundedArray(m_texturesIndex, imageViews); + } + +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.h b/Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.h new file mode 100644 index 0000000000..5315e8526d --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/BindlessImageArrayHandler.h @@ -0,0 +1,48 @@ +/* + * 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 AZ::Render +{ + class BindlessImageArrayHandler + { + public: + + static constexpr uint16_t InvalidImageIndex = 0xFFFF; + + BindlessImageArrayHandler() = default; + ~BindlessImageArrayHandler() = default; + + void Initialize(AZ::Data::Instance& srg, const AZ::Name& propertyName); + void Reset(); + bool IsInitialized() const; + bool UpdateSrgIndices(AZ::Data::Instance& srg, const AZ::Name& propertyName); + + uint16_t AppendBindlessImage(const RHI::ImageView* imageView); + void UpdateBindlessImage(uint16_t index, const RHI::ImageView* imageView); + void RemoveBindlessImage(uint16_t index); + + bool UpdateSrg(AZ::Data::Instance& srg) const; + + private: + + AZStd::vector m_bindlessImageViews; + AZStd::vector m_bindlessImageViewFreeList; + RHI::ShaderInputImageUnboundedArrayIndex m_texturesIndex; + AZStd::shared_mutex m_updateMutex; + bool m_isInitialized{ false }; + }; +} + diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp new file mode 100644 index 0000000000..2bc9e42bff --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp @@ -0,0 +1,912 @@ +/* + * 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 Terrain +{ + namespace + { + [[maybe_unused]] static const char* TerrainDetailMaterialManagerName = "TerrainDetailMaterialManager"; + static const char* TerrainDetailChars = "TerrainDetail"; + } + + namespace DetailMaterialInputs + { + static const char* const BaseColorColor("baseColor.color"); + static const char* const BaseColorMap("baseColor.textureMap"); + static const char* const BaseColorUseTexture("baseColor.useTexture"); + static const char* const BaseColorFactor("baseColor.factor"); + static const char* const BaseColorBlendMode("baseColor.textureBlendMode"); + static const char* const MetallicMap("metallic.textureMap"); + static const char* const MetallicUseTexture("metallic.useTexture"); + static const char* const MetallicFactor("metallic.factor"); + static const char* const RoughnessMap("roughness.textureMap"); + static const char* const RoughnessUseTexture("roughness.useTexture"); + static const char* const RoughnessFactor("roughness.factor"); + static const char* const RoughnessLowerBound("roughness.lowerBound"); + static const char* const RoughnessUpperBound("roughness.upperBound"); + static const char* const SpecularF0Map("specularF0.textureMap"); + static const char* const SpecularF0UseTexture("specularF0.useTexture"); + static const char* const SpecularF0Factor("specularF0.factor"); + static const char* const NormalMap("normal.textureMap"); + static const char* const NormalUseTexture("normal.useTexture"); + static const char* const NormalFactor("normal.factor"); + static const char* const NormalFlipX("normal.flipX"); + static const char* const NormalFlipY("normal.flipY"); + static const char* const DiffuseOcclusionMap("occlusion.diffuseTextureMap"); + static const char* const DiffuseOcclusionUseTexture("occlusion.diffuseUseTexture"); + static const char* const DiffuseOcclusionFactor("occlusion.diffuseFactor"); + static const char* const HeightMap("parallax.textureMap"); + static const char* const HeightUseTexture("parallax.useTexture"); + static const char* const HeightFactor("parallax.factor"); + static const char* const HeightOffset("parallax.offset"); + static const char* const HeightBlendFactor("parallax.blendFactor"); + } + + namespace TerrainSrgInputs + { + static const char* const DetailMaterialIdImage("m_detailMaterialIdImage"); + static const char* const DetailMaterialData("m_detailMaterialData"); + static const char* const DetailMaterialIdImageCenter("m_detailMaterialIdImageCenter"); + static const char* const DetailHalfPixelUv("m_detailHalfPixelUv"); + static const char* const DetailAabb("m_detailAabb"); + } + + AZ_CVAR(bool, + r_terrainDebugDetailMaterials, + false, + [](const bool& value) + { + AZ::RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(AZ::Name{ "o_debugDetailMaterialIds" }, AZ::RPI::ShaderOptionValue{ value }); + }, + AZ::ConsoleFunctorFlags::Null, + "Turns on debugging for detail material ids for terrain." + ); + + AZ_CVAR(bool, + r_terrainDebugDetailImageUpdates, + false, + nullptr, + AZ::ConsoleFunctorFlags::Null, + "Turns on debugging for detail material update regions for terrain." + ); + + void TerrainDetailMaterialManager::Initialize( + const AZStd::shared_ptr& bindlessImageHandler, + AZ::Data::Instance& terrainSrg) + { + AZ_Error(TerrainDetailMaterialManagerName, bindlessImageHandler, "bindlessImageHandler must not be null."); + AZ_Error(TerrainDetailMaterialManagerName, terrainSrg, "terrainSrg must not be null."); + AZ_Error(TerrainDetailMaterialManagerName, !m_isInitialized, "Already initialized."); + + if (!bindlessImageHandler || !terrainSrg || m_isInitialized) + { + return; + } + + if (UpdateSrgIndices(terrainSrg)) + { + m_bindlessImageHandler = bindlessImageHandler; + + // Find any detail material areas that have already been created. + TerrainAreaMaterialRequestBus::EnumerateHandlers( + [&](TerrainAreaMaterialRequests* handler) + { + const AZ::Aabb& bounds = handler->GetTerrainSurfaceMaterialRegion(); + const AZStd::vector materialMappings = handler->GetSurfaceMaterialMappings(); + AZ::EntityId entityId = *(Terrain::TerrainAreaMaterialRequestBus::GetCurrentBusId()); + + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + materialRegion.m_region = bounds; + + for (const auto& materialMapping : materialMappings) + { + if (materialMapping.m_materialInstance) + { + OnTerrainSurfaceMaterialMappingCreated(entityId, materialMapping.m_surfaceTag, materialMapping.m_materialInstance); + } + } + return true; + } + ); + TerrainAreaMaterialNotificationBus::Handler::BusConnect(); + + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + OnTerrainDataChanged(worldBounds, TerrainDataChangedMask::SurfaceData); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + m_isInitialized = true; + } + } + + bool TerrainDetailMaterialManager::UpdateSrgIndices(AZ::Data::Instance& terrainSrg) + { + const AZ::RHI::ShaderResourceGroupLayout* terrainSrgLayout = terrainSrg->GetLayout(); + + m_detailMaterialIdPropertyIndex = terrainSrgLayout->FindShaderInputImageIndex(AZ::Name(TerrainSrgInputs::DetailMaterialIdImage)); + AZ_Error(TerrainDetailMaterialManagerName, m_detailMaterialIdPropertyIndex.IsValid(), "Failed to find terrain srg input constant %s.", TerrainSrgInputs::DetailMaterialIdImage); + + m_detailCenterPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailMaterialIdImageCenter)); + AZ_Error(TerrainDetailMaterialManagerName, m_detailCenterPropertyIndex.IsValid(), "Failed to find terrain srg input constant %s.", TerrainSrgInputs::DetailMaterialIdImageCenter); + + m_detailHalfPixelUvPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailHalfPixelUv)); + AZ_Error(TerrainDetailMaterialManagerName, m_detailHalfPixelUvPropertyIndex.IsValid(), "Failed to find terrain srg input constant %s.", TerrainSrgInputs::DetailHalfPixelUv); + + m_detailAabbPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailAabb)); + AZ_Error(TerrainDetailMaterialManagerName, m_detailAabbPropertyIndex.IsValid(), "Failed to find terrain srg input constant %s.", TerrainSrgInputs::DetailAabb); + + // Set up the gpu buffer for detail material data + AZ::Render::GpuBufferHandler::Descriptor desc; + desc.m_bufferName = "Detail Material Data"; + desc.m_bufferSrgName = TerrainSrgInputs::DetailMaterialData; + desc.m_elementSize = sizeof(DetailMaterialShaderData); + desc.m_srgLayout = terrainSrgLayout; + m_detailMaterialDataBuffer = AZ::Render::GpuBufferHandler(desc); + + bool IndicesValid = + m_detailMaterialIdPropertyIndex.IsValid() && + m_detailCenterPropertyIndex.IsValid() && + m_detailHalfPixelUvPropertyIndex.IsValid() && + m_detailAabbPropertyIndex.IsValid(); + + m_detailImageNeedsUpdate = true; + m_detailMaterialBufferNeedsUpdate = true; + + return IndicesValid && m_detailMaterialDataBuffer.IsValid(); + } + + void TerrainDetailMaterialManager::RemoveAllImages() + { + for (const DetailMaterialData& materialData: m_detailMaterials.GetDataVector()) + { + DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(materialData.m_detailMaterialBufferIndex); + + auto checkRemoveImage = [&](uint16_t index) + { + if (index != 0xFFFF) + { + m_bindlessImageHandler->RemoveBindlessImage(index); + } + }; + + checkRemoveImage(shaderData.m_colorImageIndex); + checkRemoveImage(shaderData.m_normalImageIndex); + checkRemoveImage(shaderData.m_roughnessImageIndex); + checkRemoveImage(shaderData.m_metalnessImageIndex); + checkRemoveImage(shaderData.m_specularF0ImageIndex); + checkRemoveImage(shaderData.m_occlusionImageIndex); + checkRemoveImage(shaderData.m_heightImageIndex); + } + } + + bool TerrainDetailMaterialManager::IsInitialized() const + { + return m_isInitialized; + } + + void TerrainDetailMaterialManager::Reset() + { + RemoveAllImages(); + m_bindlessImageHandler.reset(); + + m_detailTextureImage = {}; + m_detailMaterials.Clear(); + m_detailMaterialRegions.Clear(); + m_detailMaterialShaderData.Clear(); + m_detailMaterialDataBuffer.Release(); + + m_dirtyDetailRegion = AZ::Aabb::CreateNull(); + m_previousCameraPosition = AZ::Vector3(AZStd::numeric_limits::max(), 0.0, 0.0); + m_detailTextureBounds = {}; + m_detailTextureCenter = {}; + + m_detailMaterialBufferNeedsUpdate = false; + m_detailImageNeedsUpdate = false; + + TerrainAreaMaterialNotificationBus::Handler::BusDisconnect(); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + + m_isInitialized = false; + } + + void TerrainDetailMaterialManager::Update(const AZ::Vector3& cameraPosition, AZ::Data::Instance& terrainSrg) + { + if (m_detailMaterialBufferNeedsUpdate) + { + m_detailMaterialBufferNeedsUpdate = false; + m_detailMaterialDataBuffer.UpdateBuffer(m_detailMaterialShaderData.GetRawData(), aznumeric_cast(m_detailMaterialShaderData.GetSize())); + } + + if (m_dirtyDetailRegion.IsValid() || !cameraPosition.IsClose(m_previousCameraPosition) || m_detailImageNeedsUpdate) + { + if (r_terrainDebugDetailImageUpdates) + { + AZ_Printf("TerrainDetailMaterialManager", "Previous Camera: (%f, %f, %f) New Cameara: (%f, %f, %f)", + m_previousCameraPosition.GetX(), m_previousCameraPosition.GetY(), m_previousCameraPosition.GetZ(), + cameraPosition.GetX(), cameraPosition.GetY(), cameraPosition.GetZ()); + } + int32_t newDetailTexturePosX = aznumeric_cast(AZStd::roundf(cameraPosition.GetX() / DetailTextureScale)); + int32_t newDetailTexturePosY = aznumeric_cast(AZStd::roundf(cameraPosition.GetY() / DetailTextureScale)); + + Aabb2i newBounds; + newBounds.m_min.m_x = newDetailTexturePosX - DetailTextureSizeHalf; + newBounds.m_min.m_y = newDetailTexturePosY - DetailTextureSizeHalf; + newBounds.m_max.m_x = newDetailTexturePosX + DetailTextureSizeHalf; + newBounds.m_max.m_y = newDetailTexturePosY + DetailTextureSizeHalf; + + // Use modulo to find the center point in texture space. Care must be taken so negative values are + // handled appropriately (ie, we want -1 % 1024 to equal 1023, not -1) + Vector2i newCenter; + newCenter.m_x = (DetailTextureSize + (newDetailTexturePosX % DetailTextureSize)) % DetailTextureSize; + newCenter.m_y = (DetailTextureSize + (newDetailTexturePosY % DetailTextureSize)) % DetailTextureSize; + + CheckUpdateDetailTexture(newBounds, newCenter); + + m_detailTextureBounds = newBounds; + m_dirtyDetailRegion = AZ::Aabb::CreateNull(); + + m_previousCameraPosition = cameraPosition; + + AZ::Vector4 detailAabb = AZ::Vector4( + m_detailTextureBounds.m_min.m_x * DetailTextureScale, + m_detailTextureBounds.m_min.m_y * DetailTextureScale, + m_detailTextureBounds.m_max.m_x * DetailTextureScale, + m_detailTextureBounds.m_max.m_y * DetailTextureScale + ); + AZ::Vector2 detailUvOffset = AZ::Vector2(float(newCenter.m_x) / DetailTextureSize, float(newCenter.m_y) / DetailTextureSize); + + terrainSrg->SetConstant(m_detailAabbPropertyIndex, detailAabb); + terrainSrg->SetConstant(m_detailHalfPixelUvPropertyIndex, 0.5f / DetailTextureSize); + terrainSrg->SetConstant(m_detailCenterPropertyIndex, detailUvOffset); + terrainSrg->SetImage(m_detailMaterialIdPropertyIndex, m_detailTextureImage); + + m_detailMaterialDataBuffer.UpdateSrg(terrainSrg.get()); + } + + m_detailImageNeedsUpdate = false; + } + + void TerrainDetailMaterialManager::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) + { + if ((dataChangedMask & TerrainDataChangedMask::SurfaceData) != 0) + { + m_dirtyDetailRegion.AddAabb(dirtyRegion); + } + } + + void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + + // Validate that the surface tag is new + for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) + { + if (surface.m_surfaceTag == surfaceTag) + { + AZ_Error(TerrainDetailMaterialManagerName, false, "Already have a surface material mapping for this surface tag."); + return; + } + } + + uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material); + materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); + m_detailMaterials.GetData(detailMaterialId).refCount++; + m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + } + + void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + + for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) + { + if (surface.m_surfaceTag == surfaceTag) + { + CheckDetailMaterialForDeletion(surface.m_detailMaterialId); + + if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag) + { + AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back()); + } + materialRegion.m_materialsForSurfaces.pop_back(); + m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + return; + } + } + AZ_Error(TerrainDetailMaterialManagerName, false, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed()."); + } + + void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + + bool found = false; + uint16_t materialId = CreateOrUpdateDetailMaterial(material); + for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) + { + if (surface.m_surfaceTag == surfaceTag) + { + found = true; + if (surface.m_detailMaterialId != materialId) + { + ++m_detailMaterials.GetData(materialId).refCount; + CheckDetailMaterialForDeletion(surface.m_detailMaterialId); + surface.m_detailMaterialId = materialId; + } + break; + } + } + + if (!found) + { + ++m_detailMaterials.GetData(materialId).refCount; + materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, materialId }); + } + m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + } + + void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + materialRegion.m_region = newRegion; + m_dirtyDetailRegion.AddAabb(oldRegion); + m_dirtyDetailRegion.AddAabb(newRegion); + } + + void TerrainDetailMaterialManager::CheckDetailMaterialForDeletion(uint16_t detailMaterialId) + { + auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); + if (--detailMaterialData.refCount == 0) + { + uint16_t bufferIndex = detailMaterialData.m_detailMaterialBufferIndex; + DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(bufferIndex); + + for (uint16_t imageIndex : + { + shaderData.m_colorImageIndex, + shaderData.m_normalImageIndex, + shaderData.m_roughnessImageIndex, + shaderData.m_metalnessImageIndex, + shaderData.m_specularF0ImageIndex, + shaderData.m_occlusionImageIndex, + shaderData.m_heightImageIndex + }) + { + if (imageIndex != InvalidImageIndex) + { + m_bindlessImageHandler->RemoveBindlessImage(imageIndex); + } + } + + m_detailMaterialShaderData.Release(bufferIndex); + m_detailMaterials.RemoveIndex(detailMaterialId); + + m_detailMaterialBufferNeedsUpdate = true; + } + } + + uint16_t TerrainDetailMaterialManager::CreateOrUpdateDetailMaterial(MaterialInstance material) + { + static constexpr uint16_t InvalidDetailMaterial = 0xFFFF; + uint16_t detailMaterialId = InvalidDetailMaterial; + + for (auto& detailMaterialData : m_detailMaterials.GetDataVector()) + { + if (detailMaterialData.m_assetId == material->GetAssetId()) + { + detailMaterialId = m_detailMaterials.GetIndexForData(&detailMaterialData); + UpdateDetailMaterialData(detailMaterialId, material); + break; + } + } + + AZ_Assert(m_detailMaterialShaderData.GetSize() < 0xFF, "Only 255 detail materials supported."); + + if (detailMaterialId == InvalidDetailMaterial && m_detailMaterialShaderData.GetSize() < 0xFF) + { + detailMaterialId = m_detailMaterials.GetFreeSlotIndex(); + auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); + detailMaterialData.m_detailMaterialBufferIndex = aznumeric_cast(m_detailMaterialShaderData.Reserve()); + UpdateDetailMaterialData(detailMaterialId, material); + } + return detailMaterialId; + } + + void TerrainDetailMaterialManager::UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material) + { + DetailMaterialData& materialData = m_detailMaterials.GetData(detailMaterialIndex); + DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(materialData.m_detailMaterialBufferIndex); + + if (materialData.m_materialChangeId == material->GetCurrentChangeId()) + { + return; // material hasn't changed, nothing to do + } + + materialData.m_materialChangeId = material->GetCurrentChangeId(); + materialData.m_assetId = material->GetAssetId(); + + DetailTextureFlags& flags = shaderData.m_flags; + + auto getIndex = [&](const char* const indexName) -> AZ::RPI::MaterialPropertyIndex + { + const AZ::RPI::MaterialPropertyIndex index = material->FindPropertyIndex(AZ::Name(indexName)); + AZ_Warning(TerrainDetailMaterialManagerName, index.IsValid(), "Failed to find shader input constant %s.", indexName); + return index; + }; + + auto applyProperty = [&](const char* const indexName, auto& ref) -> void + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + // GetValue() expects the actaul type, not a reference type, so the reference needs to be removed. + using TypeRefRemoved = AZStd::remove_cvref_t; + ref = material->GetPropertyValue(index).GetValue(); + } + }; + + auto applyImage = [&](const char* const indexName, AZ::Data::Instance& ref, const char* const usingFlagName, DetailTextureFlags flagToSet, uint16_t& imageIndex) -> void + { + // Determine if an image exists and if its using flag allows it to be used. + const auto index = getIndex(indexName); + const auto useTextureIndex = getIndex(usingFlagName); + bool useTextureValue = true; + if (useTextureIndex.IsValid()) + { + useTextureValue = material->GetPropertyValue(useTextureIndex).GetValue(); + } + if (index.IsValid() && useTextureValue) + { + ref = material->GetPropertyValue(index).GetValue>(); + } + useTextureValue = useTextureValue && ref; + flags = DetailTextureFlags(useTextureValue ? (flags | flagToSet) : (flags & ~flagToSet)); + + // Update queues to add/remove textures depending on if the image is used + if (ref) + { + if (imageIndex == InvalidImageIndex) + { + imageIndex = m_bindlessImageHandler->AppendBindlessImage(ref->GetImageView()); + } + else + { + m_bindlessImageHandler->UpdateBindlessImage(imageIndex, ref->GetImageView()); + } + } + else if (imageIndex != InvalidImageIndex) + { + m_bindlessImageHandler->RemoveBindlessImage(imageIndex); + imageIndex = InvalidImageIndex; + } + }; + + auto applyFlag = [&](const char* const indexName, DetailTextureFlags flagToSet) -> void + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + bool flagValue = material->GetPropertyValue(index).GetValue(); + flags = DetailTextureFlags(flagValue ? flags | flagToSet : flags); + } + }; + + auto getEnumName = [&](const char* const indexName) -> const AZStd::string_view + { + const auto index = getIndex(indexName); + if (index.IsValid()) + { + uint32_t enumIndex = material->GetPropertyValue(index).GetValue(); + const AZ::Name& enumName = material->GetMaterialPropertiesLayout()->GetPropertyDescriptor(index)->GetEnumName(enumIndex); + return enumName.GetStringView(); + } + return ""; + }; + + using namespace DetailMaterialInputs; + applyImage(BaseColorMap, materialData.m_colorImage, BaseColorUseTexture, DetailTextureFlags::UseTextureBaseColor, shaderData.m_colorImageIndex); + applyProperty(BaseColorFactor, shaderData.m_baseColorFactor); + + const auto index = getIndex(BaseColorColor); + if (index.IsValid()) + { + AZ::Color baseColor = material->GetPropertyValue(index).GetValue(); + shaderData.m_baseColorRed = baseColor.GetR(); + shaderData.m_baseColorGreen = baseColor.GetG(); + shaderData.m_baseColorBlue = baseColor.GetB(); + } + + const AZStd::string_view& blendModeString = getEnumName(BaseColorBlendMode); + if (blendModeString == "Multiply") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeMultiply); + } + else if (blendModeString == "LinearLight") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLinearLight); + } + else if (blendModeString == "Lerp") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLerp); + } + else if (blendModeString == "Overlay") + { + flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeOverlay); + } + + applyImage(MetallicMap, materialData.m_metalnessImage, MetallicUseTexture, DetailTextureFlags::UseTextureMetallic, shaderData.m_metalnessImageIndex); + applyProperty(MetallicFactor, shaderData.m_metalFactor); + + applyImage(RoughnessMap, materialData.m_roughnessImage, RoughnessUseTexture, DetailTextureFlags::UseTextureRoughness, shaderData.m_roughnessImageIndex); + + if ((flags & DetailTextureFlags::UseTextureRoughness) > 0) + { + float lowerBound = 0.0; + float upperBound = 1.0; + applyProperty(RoughnessLowerBound, lowerBound); + applyProperty(RoughnessUpperBound, upperBound); + shaderData.m_roughnessBias = lowerBound; + shaderData.m_roughnessScale = upperBound - lowerBound; + } + else + { + shaderData.m_roughnessBias = 0.0; + applyProperty(RoughnessFactor, shaderData.m_roughnessScale); + } + + applyImage(SpecularF0Map, materialData.m_specularF0Image, SpecularF0UseTexture, DetailTextureFlags::UseTextureSpecularF0, shaderData.m_specularF0ImageIndex); + applyProperty(SpecularF0Factor, shaderData.m_specularF0Factor); + + applyImage(NormalMap, materialData.m_normalImage, NormalUseTexture, DetailTextureFlags::UseTextureNormal, shaderData.m_normalImageIndex); + applyProperty(NormalFactor, shaderData.m_normalFactor); + applyFlag(NormalFlipX, DetailTextureFlags::FlipNormalX); + applyFlag(NormalFlipY, DetailTextureFlags::FlipNormalY); + + applyImage(DiffuseOcclusionMap, materialData.m_occlusionImage, DiffuseOcclusionUseTexture, DetailTextureFlags::UseTextureOcclusion, shaderData.m_occlusionImageIndex); + applyProperty(DiffuseOcclusionFactor, shaderData.m_occlusionFactor); + + applyImage(HeightMap, materialData.m_heightImage, HeightUseTexture, DetailTextureFlags::UseTextureHeight, shaderData.m_heightImageIndex); + applyProperty(HeightFactor, shaderData.m_heightFactor); + applyProperty(HeightOffset, shaderData.m_heightOffset); + applyProperty(HeightBlendFactor, shaderData.m_heightBlendFactor); + + m_detailMaterialBufferNeedsUpdate = true; + } + + void TerrainDetailMaterialManager::CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter) + { + if (r_terrainDebugDetailImageUpdates) + { + AZ_Printf("TerrainDetailMaterialManager", "Old Bounds: m(%i, %i)M(%i, %i) New Bounds: m(%i, %i)M(%i, %i)", + m_detailTextureBounds.m_min.m_x, m_detailTextureBounds.m_min.m_y, m_detailTextureBounds.m_max.m_x, m_detailTextureBounds.m_max.m_y, + newBounds.m_min.m_x, newBounds.m_min.m_y, newBounds.m_max.m_x, newBounds.m_max.m_y + ); + } + if (!m_detailTextureImage) + { + // If the m_detailTextureImage doesn't exist, create it and populate the entire texture + + const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); + AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( + AZ::RHI::ImageBindFlags::ShaderRead, DetailTextureSize, DetailTextureSize, AZ::RHI::Format::R8G8B8A8_UINT + ); + const AZ::Name TerrainDetailName = AZ::Name(TerrainDetailChars); + m_detailTextureImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainDetailName, nullptr, nullptr); + AZ_Error(TerrainDetailMaterialManagerName, m_detailTextureImage, "Failed to initialize the detail texture image."); + + UpdateDetailTexture(newBounds, newBounds, newCenter); + } + else + { + // If the new bounds of the detail texture are different than the old bounds, then the edges of the texture need to be updated. + + int32_t offsetX = m_detailTextureBounds.m_min.m_x - newBounds.m_min.m_x; + + // Horizontal edge update + if (newBounds.m_min.m_x != m_detailTextureBounds.m_min.m_x) + { + Aabb2i updateBounds; + if (newBounds.m_min.m_x < m_detailTextureBounds.m_min.m_x) + { + updateBounds.m_min.m_x = newBounds.m_min.m_x; + updateBounds.m_max.m_x = m_detailTextureBounds.m_min.m_x; + } + else + { + updateBounds.m_min.m_x = m_detailTextureBounds.m_max.m_x; + updateBounds.m_max.m_x = newBounds.m_max.m_x; + } + updateBounds.m_min.m_y = newBounds.m_min.m_y; + updateBounds.m_max.m_y = newBounds.m_max.m_y; + + if (r_terrainDebugDetailImageUpdates) + { + AZ_Printf("TerrainDetailMaterialManager", "Updating horizontal edge: m(%i, %i)M(%i, %i)", + updateBounds.m_min.m_x, updateBounds.m_min.m_y, updateBounds.m_max.m_x, updateBounds.m_max.m_y); + } + UpdateDetailTexture(updateBounds, newBounds, newCenter); + } + + // Vertical edge update + if (newBounds.m_min.m_y != m_detailTextureBounds.m_min.m_y) + { + Aabb2i updateBounds; + // Don't update areas that have already been updated in the horizontal update. + updateBounds.m_min.m_x = newBounds.m_min.m_x + AZ::GetMax(0, offsetX); + updateBounds.m_max.m_x = newBounds.m_max.m_x + AZ::GetMin(0, offsetX); + if (newBounds.m_min.m_y < m_detailTextureBounds.m_min.m_y) + { + updateBounds.m_min.m_y = newBounds.m_min.m_y; + updateBounds.m_max.m_y = m_detailTextureBounds.m_min.m_y; + } + else + { + updateBounds.m_min.m_y = m_detailTextureBounds.m_max.m_y; + updateBounds.m_max.m_y = newBounds.m_max.m_y; + } + + if (r_terrainDebugDetailImageUpdates) + { + AZ_Printf("TerrainDetailMaterialManager", "Updating vertical edge: m(%i, %i)M(%i, %i)", + updateBounds.m_min.m_x, updateBounds.m_min.m_y, updateBounds.m_max.m_x, updateBounds.m_max.m_y); + } + UpdateDetailTexture(updateBounds, newBounds, newCenter); + } + + if (m_dirtyDetailRegion.IsValid()) + { + if (r_terrainDebugDetailImageUpdates) + { + AZ_Printf("TerrainDetailMaterialManager", "m_dirtyDetailRegion: m(%f, %f)M(%f, %f)", + m_dirtyDetailRegion.GetMin().GetX(), m_dirtyDetailRegion.GetMin().GetY(), m_dirtyDetailRegion.GetMax().GetX(), m_dirtyDetailRegion.GetMax().GetY()); + } + // If any regions are marked as dirty, then they should be updated. + + AZ::Vector3 currentMin = AZ::Vector3(newBounds.m_min.m_x * DetailTextureScale, newBounds.m_min.m_y * DetailTextureScale, -0.5f); + AZ::Vector3 currentMax = AZ::Vector3(newBounds.m_max.m_x * DetailTextureScale, newBounds.m_max.m_y * DetailTextureScale, 0.5f); + AZ::Aabb detailTextureCoverage = AZ::Aabb::CreateFromMinMax(currentMin, currentMax); + AZ::Vector3 previousMin = AZ::Vector3(m_detailTextureBounds.m_min.m_x * DetailTextureScale, m_detailTextureBounds.m_min.m_y * DetailTextureScale, -0.5f); + AZ::Vector3 previousMax = AZ::Vector3(m_detailTextureBounds.m_max.m_x * DetailTextureScale, m_detailTextureBounds.m_max.m_y * DetailTextureScale, 0.5f); + AZ::Aabb previousCoverage = AZ::Aabb::CreateFromMinMax(previousMin, previousMax); + + // Area of texture not already updated by camera movement above. + AZ::Aabb clampedCoverage = previousCoverage.GetClamped(detailTextureCoverage); + + // Clamp the dirty region to the area of the detail texture that is visible and not already updated. + clampedCoverage.Clamp(m_dirtyDetailRegion); + + if (clampedCoverage.IsValid()) + { + Aabb2i updateBounds; + updateBounds.m_min.m_x = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMin().GetX() / DetailTextureScale)); + updateBounds.m_min.m_y = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMin().GetY() / DetailTextureScale)); + updateBounds.m_max.m_x = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMax().GetX() / DetailTextureScale)); + updateBounds.m_max.m_y = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMax().GetY() / DetailTextureScale)); + if (updateBounds.m_min.m_x < updateBounds.m_max.m_x && updateBounds.m_min.m_y < updateBounds.m_max.m_y) + { + + if (r_terrainDebugDetailImageUpdates) + { + AZ_Printf("TerrainDetailMaterialManager", "Updating dirty region: m(%i, %i)M(%i, %i)", + updateBounds.m_min.m_x, updateBounds.m_min.m_y, updateBounds.m_max.m_x, updateBounds.m_max.m_y); + } + UpdateDetailTexture(updateBounds, newBounds, newCenter); + } + } + } + } + } + + void TerrainDetailMaterialManager::UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel) + { + if (!m_detailTextureImage) + { + return; + } + + struct DetailMaterialPixel + { + uint8_t m_material1{ 255 }; + uint8_t m_material2{ 255 }; + uint8_t m_blend{ 0 }; // 0 = full weight on material1, 255 = full weight on material2 + uint8_t m_padding{ 0 }; + }; + + // Because the center of the detail texture may be offset, each update area may actually need to be split into + // up to 4 separate update areas in each sector of the quadrant. + AZStd::array textureSpaceAreas; + AZStd::array scaledWorldSpaceAreas; + uint8_t updateAreaCount = CalculateUpdateRegions(updateArea, textureBounds, centerPixel, textureSpaceAreas, scaledWorldSpaceAreas); + + if (updateAreaCount > 0) + { + m_detailImageNeedsUpdate = true; + } + + // Pull the data for each area updated and use it to construct an update for the detail material id texture. + for (uint8_t i = 0; i < updateAreaCount; ++i) + { + const Aabb2i& quadrantTextureArea = textureSpaceAreas[i]; + const Aabb2i& quadrantWorldArea = scaledWorldSpaceAreas[i]; + + AZStd::vector pixels; + pixels.resize((quadrantWorldArea.m_max.m_x - quadrantWorldArea.m_min.m_x) * (quadrantWorldArea.m_max.m_y - quadrantWorldArea.m_min.m_y)); + uint32_t index = 0; + + for (int yPos = quadrantWorldArea.m_min.m_y; yPos < quadrantWorldArea.m_max.m_y; ++yPos) + { + for (int xPos = quadrantWorldArea.m_min.m_x; xPos < quadrantWorldArea.m_max.m_x; ++xPos) + { + AZ::Vector2 position = AZ::Vector2(xPos * DetailTextureScale, yPos * DetailTextureScale); + AzFramework::SurfaceData::SurfaceTagWeightList surfaceWeights; + AzFramework::Terrain::TerrainDataRequestBus::Broadcast(&AzFramework::Terrain::TerrainDataRequests::GetSurfaceWeightsFromVector2, position, surfaceWeights, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, nullptr); + + // Store the top two surface weights in the texture with m_blend storing the relative weight. + bool isFirstMaterial = true; + float firstWeight = 0.0f; + for (const auto& surfaceTagWeight : surfaceWeights) + { + if (surfaceTagWeight.m_weight > 0.0f) + { + AZ::Crc32 surfaceType = surfaceTagWeight.m_surfaceType; + uint16_t materialId = GetDetailMaterialForSurfaceTypeAndPosition(surfaceType, position); + if (materialId != m_detailMaterials.NoFreeSlot && materialId < 255) + { + if (isFirstMaterial) + { + pixels.at(index).m_material1 = aznumeric_cast(materialId); + firstWeight = surfaceTagWeight.m_weight; + // m_blend only needs to be calculated is material 2 is found, otherwise the initial value of 0 is correct. + isFirstMaterial = false; + } + else + { + pixels.at(index).m_material2 = aznumeric_cast(materialId); + float totalWeight = firstWeight + surfaceTagWeight.m_weight; + float blendWeight = 1.0f - (firstWeight / totalWeight); + pixels.at(index).m_blend = aznumeric_cast(AZStd::round(blendWeight * 255.0f)); + break; + } + } + } + else + { + break; // since the list is ordered, no other materials are in the list with positive weights. + } + } + ++index; + } + } + + const int32_t left = quadrantTextureArea.m_min.m_x; + const int32_t top = quadrantTextureArea.m_min.m_y; + const int32_t width = quadrantTextureArea.m_max.m_x - quadrantTextureArea.m_min.m_x; + const int32_t height = quadrantTextureArea.m_max.m_y - quadrantTextureArea.m_min.m_y; + + AZ::RHI::ImageUpdateRequest imageUpdateRequest; + imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast(left); + imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast(top); + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(DetailMaterialPixel); + imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(DetailMaterialPixel); + imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height; + imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1; + imageUpdateRequest.m_sourceData = pixels.data(); + imageUpdateRequest.m_image = m_detailTextureImage->GetRHIImage(); + + m_detailTextureImage->UpdateImageContents(imageUpdateRequest); + } + } + + uint8_t TerrainDetailMaterialManager::CalculateUpdateRegions(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel, + AZStd::array& textureSpaceAreas, AZStd::array& scaledWorldSpaceAreas) + { + Vector2i centerOffset = { centerPixel.m_x - DetailTextureSizeHalf, centerPixel.m_y - DetailTextureSizeHalf }; + + int32_t quadrantXOffset = centerPixel.m_x < DetailTextureSizeHalf ? DetailTextureSize : -DetailTextureSize; + int32_t quadrantYOffset = centerPixel.m_y < DetailTextureSizeHalf ? DetailTextureSize : -DetailTextureSize; + + uint8_t numQuadrants = 0; + + // For each of the 4 quadrants: + auto calculateQuadrant = [&](Vector2i quadrantOffset) + { + Aabb2i offsetUpdateArea = updateArea + centerOffset + quadrantOffset; + Aabb2i updateSectionBounds = textureBounds.GetClamped(offsetUpdateArea); + if (updateSectionBounds.IsValid()) + { + textureSpaceAreas[numQuadrants] = updateSectionBounds - textureBounds.m_min; + scaledWorldSpaceAreas[numQuadrants] = updateSectionBounds - centerOffset - quadrantOffset; + ++numQuadrants; + } + }; + + calculateQuadrant({ 0, 0 }); + calculateQuadrant({ quadrantXOffset, 0 }); + calculateQuadrant({ 0, quadrantYOffset }); + calculateQuadrant({ quadrantXOffset, quadrantYOffset }); + + return numQuadrants; + } + + uint16_t TerrainDetailMaterialManager::GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position) + { + for (const auto& materialRegion : m_detailMaterialRegions.GetDataVector()) + { + if (materialRegion.m_region.Contains(AZ::Vector3(position.GetX(), position.GetY(), 0.0f))) + { + for (const auto& materialSurface : materialRegion.m_materialsForSurfaces) + { + if (materialSurface.m_surfaceTag == surfaceType) + { + return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex; + } + } + } + } + return m_detailMaterials.NoFreeSlot; + } + + auto TerrainDetailMaterialManager::FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) + -> DetailMaterialListRegion* + { + for (DetailMaterialListRegion& data : container.GetDataVector()) + { + if (data.m_entityId == entityId) + { + return &data; + } + } + return nullptr; + } + + auto TerrainDetailMaterialManager::FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) + -> DetailMaterialListRegion& + { + DetailMaterialListRegion* dataPtr = FindByEntityId(entityId, container); + if (dataPtr != nullptr) + { + return *dataPtr; + } + + const uint16_t slotId = container.GetFreeSlotIndex(); + AZ_Assert(slotId != AZ::Render::IndexedDataVector::NoFreeSlot, "Ran out of indices"); + + DetailMaterialListRegion& data = container.GetData(slotId); + data.m_entityId = entityId; + return data; + } + + void TerrainDetailMaterialManager::RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) + { + for (DetailMaterialListRegion& data : container.GetDataVector()) + { + if (data.m_entityId == entityId) + { + container.RemoveData(&data); + return; + } + } + AZ_Assert(false, "Entity Id not found in container.") + } + +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h new file mode 100644 index 0000000000..98f2c26dd8 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h @@ -0,0 +1,226 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Terrain +{ + class TerrainDetailMaterialManager + : private AzFramework::Terrain::TerrainDataNotificationBus::Handler + , private TerrainAreaMaterialNotificationBus::Handler + { + public: + + AZ_RTTI(TerrainDetailMaterialManager, "{3CBAF88F-E3B1-43B8-97A5-999133188BCC}"); + AZ_DISABLE_COPY_MOVE(TerrainDetailMaterialManager); + + TerrainDetailMaterialManager() = default; + ~TerrainDetailMaterialManager() = default; + + void Initialize( + const AZStd::shared_ptr& bindlessImageHandler, + AZ::Data::Instance& terrainSrg); + bool IsInitialized() const; + void Reset(); + bool UpdateSrgIndices(AZ::Data::Instance& srg); + + void Update(const AZ::Vector3& cameraPosition, AZ::Data::Instance& terrainSrg); + + private: + + using MaterialInstance = AZ::Data::Instance; + static constexpr auto InvalidImageIndex = AZ::Render::BindlessImageArrayHandler::InvalidImageIndex; + + enum DetailTextureFlags : uint32_t + { + UseTextureBaseColor = 0b0000'0000'0000'0000'0000'0000'0000'0001, + UseTextureNormal = 0b0000'0000'0000'0000'0000'0000'0000'0010, + UseTextureMetallic = 0b0000'0000'0000'0000'0000'0000'0000'0100, + UseTextureRoughness = 0b0000'0000'0000'0000'0000'0000'0000'1000, + UseTextureOcclusion = 0b0000'0000'0000'0000'0000'0000'0001'0000, + UseTextureHeight = 0b0000'0000'0000'0000'0000'0000'0010'0000, + UseTextureSpecularF0 = 0b0000'0000'0000'0000'0000'0000'0100'0000, + + FlipNormalX = 0b0000'0000'0000'0001'0000'0000'0000'0000, + FlipNormalY = 0b0000'0000'0000'0010'0000'0000'0000'0000, + + BlendModeMask = 0b0000'0000'0000'1100'0000'0000'0000'0000, + BlendModeLerp = 0b0000'0000'0000'0000'0000'0000'0000'0000, + BlendModeLinearLight = 0b0000'0000'0000'0100'0000'0000'0000'0000, + BlendModeMultiply = 0b0000'0000'0000'1000'0000'0000'0000'0000, + BlendModeOverlay = 0b0000'0000'0000'1100'0000'0000'0000'0000, + }; + + struct DetailMaterialShaderData + { + // Uv + AZStd::array m_uvTransform + { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + }; + + float m_baseColorRed{ 1.0f }; + float m_baseColorGreen{ 1.0f }; + float m_baseColorBlue{ 1.0f }; + + // Factor / Scale / Bias for input textures + float m_baseColorFactor{ 1.0f }; + + float m_normalFactor{ 1.0f }; + float m_metalFactor{ 1.0f }; + float m_roughnessScale{ 1.0f }; + float m_roughnessBias{ 0.0f }; + + float m_specularF0Factor{ 1.0f }; + float m_occlusionFactor{ 1.0f }; + float m_heightFactor{ 1.0f }; + float m_heightOffset{ 0.0f }; + + float m_heightBlendFactor{ 0.5f }; + + // Flags + DetailTextureFlags m_flags{ 0 }; + + // Image indices + uint16_t m_colorImageIndex{ InvalidImageIndex }; + uint16_t m_normalImageIndex{ InvalidImageIndex }; + uint16_t m_roughnessImageIndex{ InvalidImageIndex }; + uint16_t m_metalnessImageIndex{ InvalidImageIndex }; + + uint16_t m_specularF0ImageIndex{ InvalidImageIndex }; + uint16_t m_occlusionImageIndex{ InvalidImageIndex }; + uint16_t m_heightImageIndex{ InvalidImageIndex }; + + // 16 byte aligned + uint16_t m_padding1; + uint32_t m_padding2; + uint32_t m_padding3; + }; + static_assert(sizeof(DetailMaterialShaderData) % 16 == 0, "DetailMaterialShaderData must be 16 byte aligned."); + + struct DetailMaterialData + { + AZ::Data::AssetId m_assetId; + AZ::RPI::Material::ChangeId m_materialChangeId{AZ::RPI::Material::DEFAULT_CHANGE_ID}; + uint32_t refCount = 0; + uint16_t m_detailMaterialBufferIndex{ 0xFFFF }; + + AZ::Data::Instance m_colorImage; + AZ::Data::Instance m_normalImage; + AZ::Data::Instance m_roughnessImage; + AZ::Data::Instance m_metalnessImage; + AZ::Data::Instance m_specularF0Image; + AZ::Data::Instance m_occlusionImage; + AZ::Data::Instance m_heightImage; + }; + + struct DetailMaterialSurface + { + AZ::Crc32 m_surfaceTag; + uint16_t m_detailMaterialId; + }; + + struct DetailMaterialListRegion + { + AZ::EntityId m_entityId; + AZ::Aabb m_region{AZ::Aabb::CreateNull()}; + AZStd::vector m_materialsForSurfaces; + }; + + // System-level parameters + static constexpr int32_t DetailTextureSize{ 1024 }; + static constexpr int32_t DetailTextureSizeHalf{ DetailTextureSize / 2 }; + static constexpr float DetailTextureScale{ 0.5f }; + + // AzFramework::Terrain::TerrainDataNotificationBus overrides... + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + // TerrainAreaMaterialNotificationBus overrides... + void OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; + void OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) override; + void OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; + void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; + + //! Removes all images from all detail materials from the bindless image array + void RemoveAllImages(); + + //! Creates or updates an existing detail material with settings from a material instance + uint16_t CreateOrUpdateDetailMaterial(MaterialInstance material); + + //! Decrements the ref count on a detail material and removes it if it reaches 0 + void CheckDetailMaterialForDeletion(uint16_t detailMaterialId); + + //! Updates a specific detail material with settings from a material instance + void UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material); + + //! Checks to see if the detail material id texture needs to update based on new center and bounds. Any + //! required updates are then executed. + void CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter); + + //! Updates the detail texture in a given area + void UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel); + + //! Finds the detail material Id for a surface type and position + uint16_t GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position); + + //! Calculates which regions of the detail material id texture need to be updated based on the update area. Since + //! the "center" of the detail material id texture can move, a single update region in contiguous world space may + //! map to up to 4 different areas on teh detail material id texture. + uint8_t CalculateUpdateRegions(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel, + AZStd::array& textureSpaceAreas, AZStd::array& scaledWorldSpaceAreas); + + DetailMaterialListRegion* FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); + DetailMaterialListRegion& FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); + void RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); + + AZStd::shared_ptr m_bindlessImageHandler; + + AZ::Data::Instance m_detailTextureImage; + + AZ::Render::IndexedDataVector m_detailMaterials; + AZ::Render::IndexedDataVector m_detailMaterialRegions; + AZ::Render::SparseVector m_detailMaterialShaderData; + AZ::Render::GpuBufferHandler m_detailMaterialDataBuffer; + + AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() }; + AZ::Vector3 m_previousCameraPosition = AZ::Vector3(AZStd::numeric_limits::max(), 0.0, 0.0); + Aabb2i m_detailTextureBounds; + Vector2i m_detailTextureCenter; + + AZ::RHI::ShaderInputImageIndex m_detailMaterialIdPropertyIndex; + AZ::RHI::ShaderInputBufferIndex m_detailMaterialDataIndex; + AZ::RHI::ShaderInputConstantIndex m_detailCenterPropertyIndex; + AZ::RHI::ShaderInputConstantIndex m_detailAabbPropertyIndex; + AZ::RHI::ShaderInputConstantIndex m_detailHalfPixelUvPropertyIndex; + + bool m_isInitialized{ false }; + bool m_detailMaterialBufferNeedsUpdate{ false }; + bool m_detailImageNeedsUpdate{ false }; + + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp index 9f83844243..351c34308a 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.cpp @@ -7,38 +7,22 @@ */ #include -#include - -#include -#include -#include -#include -#include #include -#include -#include -#include #include #include #include #include -#include -#include #include #include -#include #include #include #include #include #include -#include -#include -#include #include @@ -50,79 +34,19 @@ namespace Terrain { [[maybe_unused]] const char* TerrainFPName = "TerrainFeatureProcessor"; const char* TerrainHeightmapChars = "TerrainHeightmap"; - const char* TerrainDetailChars = "TerrainDetail"; } - namespace ViewSrgInputs + namespace SceneSrgInputs { static const char* const HeightmapImage("m_heightmapImage"); + static const char* const TerrainWorldData("m_terrainWorldData"); } namespace TerrainSrgInputs { - static const char* const DetailMaterialIdImage("m_detailMaterialIdImage"); - static const char* const DetailMaterialData("m_detailMaterialData"); - static const char* const DetailMaterialIdImageCenter("m_detailMaterialIdImageCenter"); - static const char* const DetailHalfPixelUv("m_detailHalfPixelUv"); - static const char* const DetailAabb("m_detailAabb"); - static const char* const DetailTextures("m_detailTextures"); + static const char* const Textures("m_textures"); } - namespace DetailMaterialInputs - { - static const char* const BaseColorColor("baseColor.color"); - static const char* const BaseColorMap("baseColor.textureMap"); - static const char* const BaseColorUseTexture("baseColor.useTexture"); - static const char* const BaseColorFactor("baseColor.factor"); - static const char* const BaseColorBlendMode("baseColor.textureBlendMode"); - static const char* const MetallicMap("metallic.textureMap"); - static const char* const MetallicUseTexture("metallic.useTexture"); - static const char* const MetallicFactor("metallic.factor"); - static const char* const RoughnessMap("roughness.textureMap"); - static const char* const RoughnessUseTexture("roughness.useTexture"); - static const char* const RoughnessFactor("roughness.factor"); - static const char* const RoughnessLowerBound("roughness.lowerBound"); - static const char* const RoughnessUpperBound("roughness.upperBound"); - static const char* const SpecularF0Map("specularF0.textureMap"); - static const char* const SpecularF0UseTexture("specularF0.useTexture"); - static const char* const SpecularF0Factor("specularF0.factor"); - static const char* const NormalMap("normal.textureMap"); - static const char* const NormalUseTexture("normal.useTexture"); - static const char* const NormalFactor("normal.factor"); - static const char* const NormalFlipX("normal.flipX"); - static const char* const NormalFlipY("normal.flipY"); - static const char* const DiffuseOcclusionMap("occlusion.diffuseTextureMap"); - static const char* const DiffuseOcclusionUseTexture("occlusion.diffuseUseTexture"); - static const char* const DiffuseOcclusionFactor("occlusion.diffuseFactor"); - static const char* const HeightMap("parallax.textureMap"); - static const char* const HeightUseTexture("parallax.useTexture"); - static const char* const HeightFactor("parallax.factor"); - static const char* const HeightOffset("parallax.offset"); - static const char* const HeightBlendFactor("parallax.blendFactor"); - } - - namespace ShaderInputs - { - static const char* const ModelToWorld("m_modelToWorld"); - static const char* const TerrainData("m_terrainData"); - static const char* const MacroMaterialData("m_macroMaterialData"); - static const char* const MacroMaterialCount("m_macroMaterialCount"); - static const char* const MacroColorMap("m_macroColorMap"); - static const char* const MacroNormalMap("m_macroNormalMap"); - } - - AZ_CVAR(bool, - r_terrainDebugDetailMaterials, - false, - [](const bool& value) - { - AZ::RPI::ShaderSystemInterface::Get()->SetGlobalShaderOption(AZ::Name{ "o_debugDetailMaterialIds" }, AZ::RPI::ShaderOptionValue{ value }); - }, - AZ::ConsoleFunctorFlags::Null, - "Turns on debugging for detail material ids for terrain." - ); - - void TerrainFeatureProcessor::Reflect(AZ::ReflectContext* context) { if (AZ::SerializeContext* serialize = azrtti_cast(context)) @@ -150,13 +74,17 @@ namespace Terrain void TerrainFeatureProcessor::Initialize() { - // Load indices for the View Srg. + m_meshManager.Initialize(); + m_imageArrayHandler = AZStd::make_shared(); - auto viewSrgLayout = AZ::RPI::RPISystemInterface::Get()->GetViewSrgLayout(); + auto sceneSrgLayout = AZ::RPI::RPISystemInterface::Get()->GetSceneSrgLayout(); - m_heightmapPropertyIndex = viewSrgLayout->FindShaderInputImageIndex(AZ::Name(ViewSrgInputs::HeightmapImage)); - AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", ViewSrgInputs::HeightmapImage); + m_heightmapPropertyIndex = sceneSrgLayout->FindShaderInputImageIndex(AZ::Name(SceneSrgInputs::HeightmapImage)); + AZ_Error(TerrainFPName, m_heightmapPropertyIndex.IsValid(), "Failed to find scene srg input constant %s.", SceneSrgInputs::HeightmapImage); + m_worldDataIndex = sceneSrgLayout->FindShaderInputConstantIndex(AZ::Name(SceneSrgInputs::TerrainWorldData)); + AZ_Error(TerrainFPName, m_worldDataIndex.IsValid(), "Failed to find scene srg input constant %s.", SceneSrgInputs::TerrainWorldData); + // Load the terrain material asynchronously const AZStd::string materialFilePath = "Materials/Terrain/DefaultPbrTerrain.azmaterial"; m_materialAssetLoader = AZStd::make_unique(); @@ -179,31 +107,24 @@ namespace Terrain } } ); - if (!InitializePatchModel()) - { - AZ_Error(TerrainFPName, false, "Failed to create Terrain render buffers!"); - return; - } OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::HeightData); } void TerrainFeatureProcessor::Deactivate() { - TerrainMacroMaterialNotificationBus::Handler::BusDisconnect(); AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); AZ::RPI::MaterialReloadNotificationBus::Handler::BusDisconnect(); DisableSceneNotification(); + OnTerrainDataDestroyBegin(); - m_patchModel = {}; - m_areaData = {}; - m_dirtyRegion = AZ::Aabb::CreateNull(); - m_sectorData.clear(); - m_macroMaterials.Clear(); m_materialAssetLoader = {}; m_materialInstance = {}; + m_meshManager.Reset(); + m_macroMaterialManager.Reset(); + m_detailMaterialManager.Reset(); } void TerrainFeatureProcessor::Render(const AZ::RPI::FeatureProcessor::RenderPacket& packet) @@ -213,7 +134,10 @@ namespace Terrain void TerrainFeatureProcessor::OnTerrainDataDestroyBegin() { - m_areaData = {}; + m_heightmapImage = {}; + m_terrainBounds = AZ::Aabb::CreateNull(); + m_dirtyRegion = AZ::Aabb::CreateNull(); + m_heightmapNeedsUpdate = false; } void TerrainFeatureProcessor::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) @@ -222,10 +146,6 @@ namespace Terrain { TerrainHeightOrSettingsUpdated(dirtyRegion); } - if ((dataChangedMask & TerrainDataChangedMask::SurfaceData) != 0) - { - TerrainSurfaceDataUpdated(dirtyRegion); - } } void TerrainFeatureProcessor::TerrainHeightOrSettingsUpdated(const AZ::Aabb& dirtyRegion) @@ -239,131 +159,15 @@ namespace Terrain m_dirtyRegion.AddAabb(regionToUpdate); m_dirtyRegion.Clamp(worldBounds); - const AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter()); - AZ::Vector2 queryResolution2D = AZ::Vector2(1.0f); AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( queryResolution2D, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); // Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension. float queryResolution = queryResolution2D.GetX(); - // Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes. - m_areaData.m_rebuildSectors = m_areaData.m_rebuildSectors || - m_areaData.m_terrainBounds.GetMin().GetX() != worldBounds.GetMin().GetX() || - m_areaData.m_terrainBounds.GetMin().GetY() != worldBounds.GetMin().GetY() || - m_areaData.m_terrainBounds.GetMax().GetX() != worldBounds.GetMax().GetX() || - m_areaData.m_terrainBounds.GetMax().GetY() != worldBounds.GetMax().GetY() || - m_areaData.m_sampleSpacing != queryResolution; - - m_areaData.m_transform = transform; - m_areaData.m_terrainBounds = worldBounds; - m_areaData.m_sampleSpacing = queryResolution; - m_areaData.m_heightmapUpdated = true; - } - - void TerrainFeatureProcessor::TerrainSurfaceDataUpdated(const AZ::Aabb& dirtyRegion) - { - m_dirtyDetailRegion.AddAabb(dirtyRegion); - } - - void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) - { - MacroMaterialData& materialData = FindOrCreateByEntityId(entityId, m_macroMaterials); - - UpdateMacroMaterialData(materialData, newMaterialData); - - // Update all sectors in region. - ForOverlappingSectors(materialData.m_bounds, - [&](SectorData& sectorData) { - if (sectorData.m_macroMaterials.size() < sectorData.m_macroMaterials.max_size()) - { - sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(&materialData)); - } - } - ); - } - - void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) - { - MacroMaterialData& data = FindOrCreateByEntityId(entityId, m_macroMaterials); - UpdateMacroMaterialData(data, newMaterialData); - } - - void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged( - AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) - { - MacroMaterialData& materialData = FindOrCreateByEntityId(entityId, m_macroMaterials); - for (SectorData& sectorData : m_sectorData) - { - bool overlapsOld = sectorData.m_aabb.Overlaps(materialData.m_bounds); - bool overlapsNew = sectorData.m_aabb.Overlaps(newRegion); - if (overlapsOld && !overlapsNew) - { - // Remove the macro material from this sector - for (uint16_t& idx : sectorData.m_macroMaterials) - { - if (m_macroMaterials.GetData(idx).m_entityId == entityId) - { - idx = sectorData.m_macroMaterials.back(); - sectorData.m_macroMaterials.pop_back(); - } - } - } - else if (overlapsNew && !overlapsOld) - { - // Add the macro material to this sector - if (sectorData.m_macroMaterials.size() < MaxMaterialsPerSector) - { - sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(&materialData)); - } - } - } - m_areaData.m_macroMaterialsUpdated = true; - materialData.m_bounds = newRegion; - } - - void TerrainFeatureProcessor::OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) - { - const MacroMaterialData* materialData = FindByEntityId(entityId, m_macroMaterials); - - if (materialData) - { - uint16_t destroyedMaterialIndex = m_macroMaterials.GetIndexForData(materialData); - ForOverlappingSectors(materialData->m_bounds, - [&](SectorData& sectorData) { - for (uint16_t& idx : sectorData.m_macroMaterials) - { - if (idx == destroyedMaterialIndex) - { - idx = sectorData.m_macroMaterials.back(); - sectorData.m_macroMaterials.pop_back(); - } - } - }); - } - - m_areaData.m_macroMaterialsUpdated = true; - RemoveByEntityId(entityId, m_macroMaterials); - } - - void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) - { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - - // Validate that the surface tag is new - for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) - { - if (surface.m_surfaceTag == surfaceTag) - { - AZ_Error(TerrainFPName, false, "Already have a surface material mapping for this surface tag."); - return; - } - } - - uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material); - materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); - m_detailMaterials.GetData(detailMaterialId).refCount++; - m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + m_terrainBounds = worldBounds; + m_sampleSpacing = queryResolution; + m_heightmapNeedsUpdate = true; } void TerrainFeatureProcessor::OnRenderPipelinePassesChanged([[maybe_unused]] AZ::RPI::RenderPipeline* renderPipeline) @@ -371,551 +175,18 @@ namespace Terrain CacheForwardPass(); } - void TerrainFeatureProcessor::CheckDetailMaterialForDeletion(uint16_t detailMaterialId) + void TerrainFeatureProcessor::UpdateHeightmapImage() { - auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); - if (--detailMaterialData.refCount == 0) - { - uint16_t bufferIndex = detailMaterialData.m_detailMaterialBufferIndex; - DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(bufferIndex); - - for (uint16_t imageIndex : - { - shaderData.m_colorImageIndex, - shaderData.m_normalImageIndex, - shaderData.m_roughnessImageIndex, - shaderData.m_metalnessImageIndex, - shaderData.m_specularF0ImageIndex, - shaderData.m_occlusionImageIndex, - shaderData.m_heightImageIndex - }) - { - if (imageIndex != InvalidDetailImageIndex) - { - m_detailImageViews.at(imageIndex) = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Magenta)->GetImageView(); - m_detailImageViewFreeList.push_back(imageIndex); - m_detailImagesNeedUpdate = true; - } - } - - m_detailMaterialShaderData.Release(bufferIndex); - m_detailMaterials.RemoveIndex(detailMaterialId); - } - } - - void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) - { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - - for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) - { - if (surface.m_surfaceTag == surfaceTag) - { - CheckDetailMaterialForDeletion(surface.m_detailMaterialId); - - if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag) - { - AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back()); - } - materialRegion.m_materialsForSurfaces.pop_back(); - m_dirtyDetailRegion.AddAabb(materialRegion.m_region); - return; - } - } - AZ_Error(TerrainFPName, false, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed()."); - } - - void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) - { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - - bool found = false; - uint16_t materialId = CreateOrUpdateDetailMaterial(material); - for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) - { - if (surface.m_surfaceTag == surfaceTag) - { - found = true; - if (surface.m_detailMaterialId != materialId) - { - ++m_detailMaterials.GetData(materialId).refCount; - CheckDetailMaterialForDeletion(surface.m_detailMaterialId); - surface.m_detailMaterialId = materialId; - } - break; - } - } - - if (!found) - { - ++m_detailMaterials.GetData(materialId).refCount; - materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, materialId }); - } - m_dirtyDetailRegion.AddAabb(materialRegion.m_region); - } - - void TerrainFeatureProcessor::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) - { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - materialRegion.m_region = newRegion; - m_dirtyDetailRegion.AddAabb(oldRegion); - m_dirtyDetailRegion.AddAabb(newRegion); - } - - uint16_t TerrainFeatureProcessor::CreateOrUpdateDetailMaterial(MaterialInstance material) - { - static constexpr uint16_t InvalidDetailMaterial = 0xFFFF; - uint16_t detailMaterialId = InvalidDetailMaterial; - - for (auto& detailMaterialData : m_detailMaterials.GetDataVector()) - { - if (detailMaterialData.m_assetId == material->GetAssetId()) - { - detailMaterialId = m_detailMaterials.GetIndexForData(&detailMaterialData); - UpdateDetailMaterialData(detailMaterialId, material); - break; - } - } - - AZ_Assert(m_detailMaterialShaderData.GetSize() < 0xFF, "Only 255 detail materials supported."); - - if (detailMaterialId == InvalidDetailMaterial && m_detailMaterialShaderData.GetSize() < 0xFF) - { - detailMaterialId = m_detailMaterials.GetFreeSlotIndex(); - auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); - detailMaterialData.m_detailMaterialBufferIndex = aznumeric_cast(m_detailMaterialShaderData.Reserve()); - UpdateDetailMaterialData(detailMaterialId, material); - } - return detailMaterialId; - } - - void TerrainFeatureProcessor::UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material) - { - DetailMaterialData& materialData = m_detailMaterials.GetData(detailMaterialIndex); - DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(materialData.m_detailMaterialBufferIndex); - - if (materialData.m_materialChangeId == material->GetCurrentChangeId()) - { - return; // material hasn't changed, nothing to do - } - - materialData.m_materialChangeId = material->GetCurrentChangeId(); - materialData.m_assetId = material->GetAssetId(); - - DetailTextureFlags& flags = shaderData.m_flags; - - auto getIndex = [&](const char* const indexName) -> AZ::RPI::MaterialPropertyIndex - { - const AZ::RPI::MaterialPropertyIndex index = material->FindPropertyIndex(AZ::Name(indexName)); - AZ_Warning(TerrainFPName, index.IsValid(), "Failed to find shader input constant %s.", indexName); - return index; - }; - - auto applyProperty = [&](const char* const indexName, auto& ref) -> void - { - const auto index = getIndex(indexName); - if (index.IsValid()) - { - // GetValue() expects the actaul type, not a reference type, so the reference needs to be removed. - using TypeRefRemoved = AZStd::remove_cvref_t; - ref = material->GetPropertyValue(index).GetValue(); - } - }; - - auto applyImage = [&](const char* const indexName, AZ::Data::Instance& ref, const char* const usingFlagName, DetailTextureFlags flagToSet, uint16_t& imageIndex) -> void - { - // Determine if an image exists and if its using flag allows it to be used. - const auto index = getIndex(indexName); - const auto useTextureIndex = getIndex(usingFlagName); - bool useTextureValue = true; - if (useTextureIndex.IsValid()) - { - useTextureValue = material->GetPropertyValue(useTextureIndex).GetValue(); - } - if (index.IsValid() && useTextureValue) - { - ref = material->GetPropertyValue(index).GetValue>(); - } - useTextureValue = useTextureValue && ref; - flags = DetailTextureFlags(useTextureValue ? (flags | flagToSet) : (flags & ~flagToSet)); - - // Update queues to add/remove textures depending on if the image is used - if (ref) - { - if (imageIndex == InvalidDetailImageIndex) - { - if (m_detailImageViewFreeList.size() > 0) - { - imageIndex = m_detailImageViewFreeList.back(); - m_detailImageViewFreeList.pop_back(); - } - else - { - imageIndex = aznumeric_cast(m_detailImageViews.size()); - m_detailImageViews.push_back(); - } - } - m_detailImageViews.at(imageIndex) = ref->GetImageView(); - m_detailImagesNeedUpdate = true; - } - else if (imageIndex != InvalidDetailImageIndex) - { - m_detailImageViews.at(imageIndex) = AZ::RPI::ImageSystemInterface::Get()->GetSystemImage(AZ::RPI::SystemImage::Magenta)->GetImageView(); - m_detailImageViewFreeList.push_back(imageIndex); - m_detailImagesNeedUpdate = true; - imageIndex = InvalidDetailImageIndex; - } - }; - - auto applyFlag = [&](const char* const indexName, DetailTextureFlags flagToSet) -> void - { - const auto index = getIndex(indexName); - if (index.IsValid()) - { - bool flagValue = material->GetPropertyValue(index).GetValue(); - flags = DetailTextureFlags(flagValue ? flags | flagToSet : flags); - } - }; - - auto getEnumName = [&](const char* const indexName) -> const AZStd::string_view - { - const auto index = getIndex(indexName); - if (index.IsValid()) - { - uint32_t enumIndex = material->GetPropertyValue(index).GetValue(); - const AZ::Name& enumName = material->GetMaterialPropertiesLayout()->GetPropertyDescriptor(index)->GetEnumName(enumIndex); - return enumName.GetStringView(); - } - return ""; - }; - - using namespace DetailMaterialInputs; - applyImage(BaseColorMap, materialData.m_colorImage, BaseColorUseTexture, DetailTextureFlags::UseTextureBaseColor, shaderData.m_colorImageIndex); - applyProperty(BaseColorFactor, shaderData.m_baseColorFactor); - - const auto index = getIndex(BaseColorColor); - if (index.IsValid()) - { - AZ::Color baseColor = material->GetPropertyValue(index).GetValue(); - shaderData.m_baseColorRed = baseColor.GetR(); - shaderData.m_baseColorGreen = baseColor.GetG(); - shaderData.m_baseColorBlue = baseColor.GetB(); - } - - const AZStd::string_view& blendModeString = getEnumName(BaseColorBlendMode); - if (blendModeString == "Multiply") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeMultiply); - } - else if (blendModeString == "LinearLight") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLinearLight); - } - else if (blendModeString == "Lerp") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeLerp); - } - else if (blendModeString == "Overlay") - { - flags = DetailTextureFlags(flags | DetailTextureFlags::BlendModeOverlay); - } - - applyImage(MetallicMap, materialData.m_metalnessImage, MetallicUseTexture, DetailTextureFlags::UseTextureMetallic, shaderData.m_metalnessImageIndex); - applyProperty(MetallicFactor, shaderData.m_metalFactor); - - applyImage(RoughnessMap, materialData.m_roughnessImage, RoughnessUseTexture, DetailTextureFlags::UseTextureRoughness, shaderData.m_roughnessImageIndex); - - if ((flags & DetailTextureFlags::UseTextureRoughness) > 0) - { - float lowerBound = 0.0; - float upperBound = 1.0; - applyProperty(RoughnessLowerBound, lowerBound); - applyProperty(RoughnessUpperBound, upperBound); - shaderData.m_roughnessBias = lowerBound; - shaderData.m_roughnessScale = upperBound - lowerBound; - } - else - { - shaderData.m_roughnessBias = 0.0; - applyProperty(RoughnessFactor, shaderData.m_roughnessScale); - } - - applyImage(SpecularF0Map, materialData.m_specularF0Image, SpecularF0UseTexture, DetailTextureFlags::UseTextureSpecularF0, shaderData.m_specularF0ImageIndex); - applyProperty(SpecularF0Factor, shaderData.m_specularF0Factor); - - applyImage(NormalMap, materialData.m_normalImage, NormalUseTexture, DetailTextureFlags::UseTextureNormal, shaderData.m_normalImageIndex); - applyProperty(NormalFactor, shaderData.m_normalFactor); - applyFlag(NormalFlipX, DetailTextureFlags::FlipNormalX); - applyFlag(NormalFlipY, DetailTextureFlags::FlipNormalY); - - applyImage(DiffuseOcclusionMap, materialData.m_occlusionImage, DiffuseOcclusionUseTexture, DetailTextureFlags::UseTextureOcclusion, shaderData.m_occlusionImageIndex); - applyProperty(DiffuseOcclusionFactor, shaderData.m_occlusionFactor); - - applyImage(HeightMap, materialData.m_heightImage, HeightUseTexture, DetailTextureFlags::UseTextureHeight, shaderData.m_heightImageIndex); - applyProperty(HeightFactor, shaderData.m_heightFactor); - applyProperty(HeightOffset, shaderData.m_heightOffset); - applyProperty(HeightBlendFactor, shaderData.m_heightBlendFactor); - - m_updateDetailMaterialBuffer = true; - } - - void TerrainFeatureProcessor::CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter) - { - if (!m_detailTextureImage) - { - // If the m_detailTextureImage doesn't exist, create it and populate the entire texture - - const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); - AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( - AZ::RHI::ImageBindFlags::ShaderRead, DetailTextureSize, DetailTextureSize, AZ::RHI::Format::R8G8B8A8_UINT - ); - const AZ::Name TerrainDetailName = AZ::Name(TerrainDetailChars); - m_detailTextureImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainDetailName, nullptr, nullptr); - AZ_Error(TerrainFPName, m_detailTextureImage, "Failed to initialize the detail texture image."); - - UpdateDetailTexture(newBounds, newBounds, newCenter); - } - else - { - // If the new bounds of the detail texture are different than the old bounds, then the edges of the texture need to be updated. - - int32_t offsetX = m_detailTextureBounds.m_min.m_x - newBounds.m_min.m_x; - - // Horizontal edge update - if (newBounds.m_min.m_x != m_detailTextureBounds.m_min.m_x) - { - Aabb2i updateBounds; - if (newBounds.m_min.m_x < m_detailTextureBounds.m_min.m_x) - { - updateBounds.m_min.m_x = newBounds.m_min.m_x; - updateBounds.m_max.m_x = m_detailTextureBounds.m_min.m_x; - } - else - { - updateBounds.m_min.m_x = m_detailTextureBounds.m_max.m_x; - updateBounds.m_max.m_x = newBounds.m_max.m_x; - } - updateBounds.m_min.m_y = newBounds.m_min.m_y; - updateBounds.m_max.m_y = newBounds.m_max.m_y; - UpdateDetailTexture(updateBounds, newBounds, newCenter); - } - - // Vertical edge update - if (newBounds.m_min.m_y != m_detailTextureBounds.m_min.m_y) - { - Aabb2i updateBounds; - // Don't update areas that have already been updated in the horizontal update. - updateBounds.m_min.m_x = newBounds.m_min.m_x + AZ::GetMax(0, offsetX); - updateBounds.m_max.m_x = newBounds.m_max.m_x + AZ::GetMin(0, offsetX); - if (newBounds.m_min.m_y < m_detailTextureBounds.m_min.m_y) - { - updateBounds.m_min.m_y = newBounds.m_min.m_y; - updateBounds.m_max.m_y = m_detailTextureBounds.m_min.m_y; - } - else - { - updateBounds.m_min.m_y = m_detailTextureBounds.m_max.m_y; - updateBounds.m_max.m_y = newBounds.m_max.m_y; - } - UpdateDetailTexture(updateBounds, newBounds, newCenter); - } - - if (m_dirtyDetailRegion.IsValid()) - { - // If any regions are marked as dirty, then they should be updated. - - AZ::Vector3 currentMin = AZ::Vector3(newBounds.m_min.m_x * DetailTextureScale, newBounds.m_min.m_y * DetailTextureScale, -0.5f); - AZ::Vector3 currentMax = AZ::Vector3(newBounds.m_max.m_x * DetailTextureScale, newBounds.m_max.m_y * DetailTextureScale, 0.5f); - AZ::Aabb detailTextureCoverage = AZ::Aabb::CreateFromMinMax(currentMin, currentMax); - AZ::Vector3 previousMin = AZ::Vector3(m_detailTextureBounds.m_min.m_x * DetailTextureScale, m_detailTextureBounds.m_min.m_y * DetailTextureScale, -0.5f); - AZ::Vector3 previousMax = AZ::Vector3(m_detailTextureBounds.m_max.m_x * DetailTextureScale, m_detailTextureBounds.m_max.m_y * DetailTextureScale, 0.5f); - AZ::Aabb previousCoverage = AZ::Aabb::CreateFromMinMax(previousMin, previousMax); - - // Area of texture not already updated by camera movement above. - AZ::Aabb clampedCoverage = previousCoverage.GetClamped(detailTextureCoverage); - - // Clamp the dirty region to the area of the detail texture that is visible and not already updated. - clampedCoverage.Clamp(m_dirtyDetailRegion); - - if (clampedCoverage.IsValid()) - { - Aabb2i updateBounds; - updateBounds.m_min.m_x = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMin().GetX() / DetailTextureScale)); - updateBounds.m_min.m_y = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMin().GetY() / DetailTextureScale)); - updateBounds.m_max.m_x = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMax().GetX() / DetailTextureScale)); - updateBounds.m_max.m_y = aznumeric_cast(AZStd::roundf(clampedCoverage.GetMax().GetY() / DetailTextureScale)); - if (updateBounds.m_min.m_x < updateBounds.m_max.m_x && updateBounds.m_min.m_y < updateBounds.m_max.m_y) - { - UpdateDetailTexture(updateBounds, newBounds, newCenter); - } - } - } - } - - } - - uint8_t TerrainFeatureProcessor::CalculateUpdateRegions(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel, - AZStd::array& textureSpaceAreas, AZStd::array& scaledWorldSpaceAreas) - { - Vector2i centerOffset = { centerPixel.m_x - DetailTextureSizeHalf, centerPixel.m_y - DetailTextureSizeHalf }; - - int32_t quadrantXOffset = centerPixel.m_x < DetailTextureSizeHalf ? DetailTextureSize : -DetailTextureSize; - int32_t quadrantYOffset = centerPixel.m_y < DetailTextureSizeHalf ? DetailTextureSize : -DetailTextureSize; - - uint8_t numQuadrants = 0; - - // For each of the 4 quadrants: - auto calculateQuadrant = [&](Vector2i quadrantOffset) - { - Aabb2i offsetUpdateArea = updateArea + centerOffset + quadrantOffset; - Aabb2i updateSectionBounds = textureBounds.GetClamped(offsetUpdateArea); - if (updateSectionBounds.IsValid()) - { - textureSpaceAreas[numQuadrants] = updateSectionBounds - textureBounds.m_min; - scaledWorldSpaceAreas[numQuadrants] = updateSectionBounds - centerOffset - quadrantOffset; - ++numQuadrants; - } - }; - - calculateQuadrant({ 0, 0 }); - calculateQuadrant({ quadrantXOffset, 0 }); - calculateQuadrant({ 0, quadrantYOffset }); - calculateQuadrant({ quadrantXOffset, quadrantYOffset }); - - return numQuadrants; - } - - void TerrainFeatureProcessor::UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel) - { - if (!m_detailTextureImage) - { - return; - } - - struct DetailMaterialPixel - { - uint8_t m_material1{ 255 }; - uint8_t m_material2{ 255 }; - uint8_t m_blend{ 0 }; // 0 = full weight on material1, 255 = full weight on material2 - uint8_t m_padding{ 0 }; - }; - - // Because the center of the detail texture may be offset, each update area may actually need to be split into - // up to 4 separate update areas in each sector of the quadrant. - AZStd::array textureSpaceAreas; - AZStd::array scaledWorldSpaceAreas; - uint8_t updateAreaCount = CalculateUpdateRegions(updateArea, textureBounds, centerPixel, textureSpaceAreas, scaledWorldSpaceAreas); - - // Pull the data for each area updated and use it to construct an update for the detail material id texture. - for (uint8_t i = 0; i < updateAreaCount; ++i) - { - const Aabb2i& quadrantTextureArea = textureSpaceAreas[i]; - const Aabb2i& quadrantWorldArea = scaledWorldSpaceAreas[i]; - - AZStd::vector pixels; - pixels.resize((quadrantWorldArea.m_max.m_x - quadrantWorldArea.m_min.m_x) * (quadrantWorldArea.m_max.m_y - quadrantWorldArea.m_min.m_y)); - uint32_t index = 0; - - for (int yPos = quadrantWorldArea.m_min.m_y; yPos < quadrantWorldArea.m_max.m_y; ++yPos) - { - for (int xPos = quadrantWorldArea.m_min.m_x; xPos < quadrantWorldArea.m_max.m_x; ++xPos) - { - AZ::Vector2 position = AZ::Vector2(xPos * DetailTextureScale, yPos * DetailTextureScale); - AzFramework::SurfaceData::SurfaceTagWeightList surfaceWeights; - AzFramework::Terrain::TerrainDataRequestBus::Broadcast(&AzFramework::Terrain::TerrainDataRequests::GetSurfaceWeightsFromVector2, position, surfaceWeights, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, nullptr); - - // Store the top two surface weights in the texture with m_blend storing the relative weight. - bool isFirstMaterial = true; - float firstWeight = 0.0f; - for (const auto& surfaceTagWeight : surfaceWeights) - { - if (surfaceTagWeight.m_weight > 0.0f) - { - AZ::Crc32 surfaceType = surfaceTagWeight.m_surfaceType; - uint16_t materialId = GetDetailMaterialForSurfaceTypeAndPosition(surfaceType, position); - if (materialId != m_detailMaterials.NoFreeSlot && materialId < 255) - { - if (isFirstMaterial) - { - pixels.at(index).m_material1 = aznumeric_cast(materialId); - firstWeight = surfaceTagWeight.m_weight; - // m_blend only needs to be calculated is material 2 is found, otherwise the initial value of 0 is correct. - isFirstMaterial = false; - } - else - { - pixels.at(index).m_material2 = aznumeric_cast(materialId); - float totalWeight = firstWeight + surfaceTagWeight.m_weight; - float blendWeight = 1.0f - (firstWeight / totalWeight); - pixels.at(index).m_blend = aznumeric_cast(AZStd::round(blendWeight * 255.0f)); - break; - } - } - } - else - { - break; // since the list is ordered, no other materials are in the list with positive weights. - } - } - ++index; - } - } - - const int32_t left = quadrantTextureArea.m_min.m_x; - const int32_t top = quadrantTextureArea.m_min.m_y; - const int32_t width = quadrantTextureArea.m_max.m_x - quadrantTextureArea.m_min.m_x; - const int32_t height = quadrantTextureArea.m_max.m_y - quadrantTextureArea.m_min.m_y; - - AZ::RHI::ImageUpdateRequest imageUpdateRequest; - imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast(left); - imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast(top); - imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(DetailMaterialPixel); - imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(DetailMaterialPixel); - imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1; - imageUpdateRequest.m_sourceData = pixels.data(); - imageUpdateRequest.m_image = m_detailTextureImage->GetRHIImage(); - - m_detailTextureImage->UpdateImageContents(imageUpdateRequest); - } - } - - uint16_t TerrainFeatureProcessor::GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position) - { - for (const auto& materialRegion : m_detailMaterialRegions.GetDataVector()) - { - if (materialRegion.m_region.Contains(AZ::Vector3(position.GetX(), position.GetY(), 0.0f))) - { - for (const auto& materialSurface : materialRegion.m_materialsForSurfaces) - { - if (materialSurface.m_surfaceTag == surfaceType) - { - return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex; - } - } - } - } - return m_detailMaterials.NoFreeSlot; - } - - void TerrainFeatureProcessor::UpdateTerrainData() - { - - const float queryResolution = m_areaData.m_sampleSpacing; - const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds; - - int32_t heightmapImageXStart = aznumeric_cast(AZStd::ceilf(worldBounds.GetMin().GetX() / queryResolution)); - int32_t heightmapImageXEnd = aznumeric_cast(AZStd::floorf(worldBounds.GetMax().GetX() / queryResolution)) + 1; - int32_t heightmapImageYStart = aznumeric_cast(AZStd::ceilf(worldBounds.GetMin().GetY() / queryResolution)); - int32_t heightmapImageYEnd = aznumeric_cast(AZStd::floorf(worldBounds.GetMax().GetY() / queryResolution)) + 1; + int32_t heightmapImageXStart = aznumeric_cast(AZStd::ceilf(m_terrainBounds.GetMin().GetX() / m_sampleSpacing)); + int32_t heightmapImageXEnd = aznumeric_cast(AZStd::floorf(m_terrainBounds.GetMax().GetX() / m_sampleSpacing)) + 1; + int32_t heightmapImageYStart = aznumeric_cast(AZStd::ceilf(m_terrainBounds.GetMin().GetY() / m_sampleSpacing)); + int32_t heightmapImageYEnd = aznumeric_cast(AZStd::floorf(m_terrainBounds.GetMax().GetY() / m_sampleSpacing)) + 1; uint32_t heightmapImageWidth = heightmapImageXEnd - heightmapImageXStart; uint32_t heightmapImageHeight = heightmapImageYEnd - heightmapImageYStart; const AZ::RHI::Size heightmapSize = AZ::RHI::Size(heightmapImageWidth, heightmapImageHeight, 1); - if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != heightmapSize) + if (!m_heightmapImage || m_heightmapImage->GetDescriptor().m_size != heightmapSize) { const AZ::Data::Instance imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool(); AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D( @@ -923,24 +194,28 @@ namespace Terrain ); const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars); - m_areaData.m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr); - AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image."); + m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr); + AZ_Error(TerrainFPName, m_heightmapImage, "Failed to initialize the heightmap image."); // World size changed, so the whole height map needs updating. - m_dirtyRegion = worldBounds; - m_imagesNeedUpdate = true; + m_dirtyRegion = m_terrainBounds; + m_imageBindingsNeedUpdate = true; + } + + if (!m_dirtyRegion.IsValid()) + { + return; } - int32_t xStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetX() / queryResolution)); - int32_t xEnd = aznumeric_cast(AZStd::floorf(m_dirtyRegion.GetMax().GetX() / queryResolution)) + 1; - int32_t yStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetY() / queryResolution)); - int32_t yEnd = aznumeric_cast(AZStd::floorf(m_dirtyRegion.GetMax().GetY() / queryResolution)) + 1; + int32_t xStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetX() / m_sampleSpacing)); + int32_t xEnd = aznumeric_cast(AZStd::floorf(m_dirtyRegion.GetMax().GetX() / m_sampleSpacing)) + 1; + int32_t yStart = aznumeric_cast(AZStd::ceilf(m_dirtyRegion.GetMin().GetY() / m_sampleSpacing)); + int32_t yEnd = aznumeric_cast(AZStd::floorf(m_dirtyRegion.GetMax().GetY() / m_sampleSpacing)) + 1; uint32_t updateWidth = xEnd - xStart; uint32_t updateHeight = yEnd - yStart; AZStd::vector pixels; pixels.reserve(updateWidth * updateHeight); - { // Block other threads from accessing the surface data bus while we are in GetHeightFromFloats (which may call into the SurfaceData bus). // We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions @@ -957,13 +232,13 @@ namespace Terrain { bool terrainExists = true; float terrainHeight = 0.0f; - float xPos = x * queryResolution; - float yPos = y * queryResolution; + float xPos = x * m_sampleSpacing; + float yPos = y * m_sampleSpacing; AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats, xPos, yPos, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists); - const float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f); + const float clampedHeight = AZ::GetClamp((terrainHeight - m_terrainBounds.GetMin().GetZ()) / m_terrainBounds.GetExtents().GetZ(), 0.0f, 1.0f); const float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits::max()); const uint16_t uint16Height = aznumeric_cast(expandedHeight); @@ -972,11 +247,11 @@ namespace Terrain } } - if (m_areaData.m_heightmapImage) + if (m_heightmapImage) { constexpr uint32_t BytesPerPixel = sizeof(uint16_t); - const float left = xStart - (worldBounds.GetMin().GetX() / queryResolution); - const float top = yStart - (worldBounds.GetMin().GetY() / queryResolution); + const float left = xStart - (m_terrainBounds.GetMin().GetX() / m_sampleSpacing); + const float top = yStart - (m_terrainBounds.GetMin().GetY() / m_sampleSpacing); AZ::RHI::ImageUpdateRequest imageUpdateRequest; imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast(left); @@ -984,41 +259,20 @@ namespace Terrain imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = updateWidth * BytesPerPixel; imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = updateWidth * updateHeight * BytesPerPixel; imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = updateHeight; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = updateWidth; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = updateHeight; - imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1; + imageUpdateRequest.m_sourceSubresourceLayout.m_size = AZ::RHI::Size(updateWidth, updateHeight, 1); imageUpdateRequest.m_sourceData = pixels.data(); - imageUpdateRequest.m_image = m_areaData.m_heightmapImage->GetRHIImage(); + imageUpdateRequest.m_image = m_heightmapImage->GetRHIImage(); - m_areaData.m_heightmapImage->UpdateImageContents(imageUpdateRequest); + m_heightmapImage->UpdateImageContents(imageUpdateRequest); } m_dirtyRegion = AZ::Aabb::CreateNull(); } void TerrainFeatureProcessor::PrepareMaterialData() - { - const auto layout = m_materialInstance->GetAsset()->GetObjectSrgLayout(); - - m_modelToWorldIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::ModelToWorld)); - AZ_Error(TerrainFPName, m_modelToWorldIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::ModelToWorld); - - m_terrainDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::TerrainData)); - AZ_Error(TerrainFPName, m_terrainDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::TerrainData); - - m_macroMaterialDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::MacroMaterialData)); - AZ_Error(TerrainFPName, m_macroMaterialDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroMaterialData); - - m_macroMaterialCountIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::MacroMaterialCount)); - AZ_Error(TerrainFPName, m_macroMaterialCountIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroMaterialCount); - - m_macroColorMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroColorMap)); - AZ_Error(TerrainFPName, m_macroColorMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroColorMap); - - m_macroNormalMapIndex = layout->FindShaderInputImageIndex(AZ::Name(ShaderInputs::MacroNormalMap)); - AZ_Error(TerrainFPName, m_macroNormalMapIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::MacroNormalMap); - + { m_terrainSrg = {}; + for (auto& shaderItem : m_materialInstance->GetShaderCollection()) { if (shaderItem.GetShaderAsset()->GetDrawListName() == AZ::Name("forward")) @@ -1034,76 +288,38 @@ namespace Terrain if (m_terrainSrg) { - const AZ::RHI::ShaderResourceGroupLayout* terrainSrgLayout = m_terrainSrg->GetLayout(); + if (m_imageArrayHandler->IsInitialized()) + { + m_imageArrayHandler->UpdateSrgIndices(m_terrainSrg, AZ::Name(TerrainSrgInputs::Textures)); + } + else + { + m_imageArrayHandler->Initialize(m_terrainSrg, AZ::Name(TerrainSrgInputs::Textures)); + } - m_detailMaterialIdPropertyIndex = terrainSrgLayout->FindShaderInputImageIndex(AZ::Name(TerrainSrgInputs::DetailMaterialIdImage)); - AZ_Error(TerrainFPName, m_detailMaterialIdPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailMaterialIdImage); - - m_detailCenterPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailMaterialIdImageCenter)); - AZ_Error(TerrainFPName, m_detailCenterPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailMaterialIdImageCenter); - - m_detailHalfPixelUvPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailHalfPixelUv)); - AZ_Error(TerrainFPName, m_detailHalfPixelUvPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailHalfPixelUv); - - m_detailAabbPropertyIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::DetailAabb)); - AZ_Error(TerrainFPName, m_detailAabbPropertyIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailAabb); - - m_detailTexturesIndex = terrainSrgLayout->FindShaderInputImageUnboundedArrayIndex(AZ::Name(TerrainSrgInputs::DetailTextures)); - AZ_Error(TerrainFPName, m_detailTexturesIndex.IsValid(), "Failed to find view srg input constant %s.", TerrainSrgInputs::DetailTextures); - - // Set up the gpu buffer for detail material data - AZ::Render::GpuBufferHandler::Descriptor desc; - desc.m_bufferName = "Detail Material Data"; - desc.m_bufferSrgName = TerrainSrgInputs::DetailMaterialData; - desc.m_elementSize = sizeof(DetailMaterialShaderData); - desc.m_srgLayout = terrainSrgLayout; - m_detailMaterialDataBuffer = AZ::Render::GpuBufferHandler(desc); + if (m_macroMaterialManager.IsInitialized()) + { + m_macroMaterialManager.UpdateSrgIndices(m_terrainSrg); + } + else + { + m_macroMaterialManager.Initialize(m_imageArrayHandler, m_terrainSrg); + } + + if (m_detailMaterialManager.IsInitialized()) + { + m_detailMaterialManager.UpdateSrgIndices(m_terrainSrg); + } + else + { + m_detailMaterialManager.Initialize(m_imageArrayHandler, m_terrainSrg); + } } - - // Find any macro materials that have already been created. - TerrainMacroMaterialRequestBus::EnumerateHandlers( - [&](TerrainMacroMaterialRequests* handler) - { - MacroMaterialData macroMaterial = handler->GetTerrainMacroMaterialData(); - AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId()); - OnTerrainMacroMaterialCreated(entityId, macroMaterial); - return true; - } - ); - TerrainMacroMaterialNotificationBus::Handler::BusConnect(); - - // Find any detail material areas that have already been created. - TerrainAreaMaterialRequestBus::EnumerateHandlers( - [&](TerrainAreaMaterialRequests* handler) - { - const AZ::Aabb& bounds = handler->GetTerrainSurfaceMaterialRegion(); - const AZStd::vector materialMappings = handler->GetSurfaceMaterialMappings(); - AZ::EntityId entityId = *(Terrain::TerrainAreaMaterialRequestBus::GetCurrentBusId()); - - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - materialRegion.m_region = bounds; - - for (const auto& materialMapping : materialMappings) - { - if (materialMapping.m_materialInstance) - { - OnTerrainSurfaceMaterialMappingCreated(entityId, materialMapping.m_surfaceTag, materialMapping.m_materialInstance); - } - } - return true; - } - ); - TerrainAreaMaterialNotificationBus::Handler::BusConnect(); - - } - - void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData) - { - macroMaterialData = newMaterialData; - - if (macroMaterialData.m_bounds.IsValid()) + else { - m_areaData.m_macroMaterialsUpdated = true; + m_imageArrayHandler->Reset(); + m_macroMaterialManager.Reset(); + m_detailMaterialManager.Reset(); } } @@ -1111,111 +327,13 @@ namespace Terrain { AZ_PROFILE_FUNCTION(AzRender); - const AZ::Aabb& terrainBounds = m_areaData.m_terrainBounds; - - if (!terrainBounds.IsValid()) + if (!m_terrainBounds.IsValid()) { return; } if (m_materialInstance && m_materialInstance->CanCompile()) { - if (m_areaData.m_rebuildSectors) - { - // Something about the whole world changed, so the sectors need to be rebuilt - - m_areaData.m_rebuildSectors = false; - - m_sectorData.clear(); - const float xFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetX() / GridMeters) * GridMeters; - const float xLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetX() / GridMeters) * GridMeters; - const float yFirstPatchStart = AZStd::floorf(terrainBounds.GetMin().GetY() / GridMeters) * GridMeters; - const float yLastPatchStart = AZStd::floorf(terrainBounds.GetMax().GetY() / GridMeters) * GridMeters; - - const auto& materialAsset = m_materialInstance->GetAsset(); - const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); - - for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) - { - for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) - { - auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); - if (!objectSrg) - { - AZ_Warning(TerrainFPName, false, "Failed to create a new shader resource group, skipping."); - continue; - } - - m_sectorData.push_back(); - SectorData& sectorData = m_sectorData.back(); - - for (auto& lod : m_patchModel->GetLods()) - { - AZ::RPI::ModelLod& modelLod = *lod.get(); - sectorData.m_drawPackets.emplace_back(modelLod, 0, m_materialInstance, objectSrg); - AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); - - // set the shader option to select forward pass IBL specular if necessary - if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) - { - AZ_Warning(TerrainFPName, false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); - } - const uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; - drawPacket.SetStencilRef(stencilRef); - drawPacket.Update(*GetParentScene(), true); - } - - sectorData.m_aabb = - AZ::Aabb::CreateFromMinMax( - AZ::Vector3(xPatch, yPatch, terrainBounds.GetMin().GetZ()), - AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, terrainBounds.GetMax().GetZ()) - ); - sectorData.m_srg = objectSrg; - } - } - - if (m_areaData.m_macroMaterialsUpdated) - { - // sectors were rebuilt, so any cached macro material data needs to be regenerated - for (SectorData& sectorData : m_sectorData) - { - for (MacroMaterialData& macroMaterialData : m_macroMaterials.GetDataVector()) - { - if (macroMaterialData.m_bounds.Overlaps(sectorData.m_aabb)) - { - sectorData.m_macroMaterials.push_back(m_macroMaterials.GetIndexForData(¯oMaterialData)); - if (sectorData.m_macroMaterials.size() == MaxMaterialsPerSector) - { - break; - } - } - } - } - } - } - else if (m_forceRebuildDrawPackets) - { - for (auto& sectorData : m_sectorData) - { - for (auto& drawPacket : sectorData.m_drawPackets) - { - drawPacket.Update(*GetParentScene(), true); - } - } - } - m_forceRebuildDrawPackets = false; - - if (m_areaData.m_heightmapUpdated) - { - UpdateTerrainData(); - } - - if (m_updateDetailMaterialBuffer) - { - m_updateDetailMaterialBuffer = false; - m_detailMaterialDataBuffer.UpdateBuffer(m_detailMaterialShaderData.GetRawData(), aznumeric_cast(m_detailMaterialShaderData.GetSize())); - } - AZ::Vector3 cameraPosition = AZ::Vector3::CreateZero(); for (auto& view : process.m_views) { @@ -1226,198 +344,59 @@ namespace Terrain } } - if (m_dirtyDetailRegion.IsValid() || !cameraPosition.IsClose(m_previousCameraPosition) || m_detailImagesNeedUpdate) + if (m_meshManager.IsInitialized()) { - int32_t newDetailTexturePosX = aznumeric_cast(AZStd::roundf(cameraPosition.GetX() / DetailTextureScale)); - int32_t newDetailTexturePosY = aznumeric_cast(AZStd::roundf(cameraPosition.GetY() / DetailTextureScale)); - - Aabb2i newBounds; - newBounds.m_min.m_x = newDetailTexturePosX - DetailTextureSizeHalf; - newBounds.m_min.m_y = newDetailTexturePosY - DetailTextureSizeHalf; - newBounds.m_max.m_x = newDetailTexturePosX + DetailTextureSizeHalf; - newBounds.m_max.m_y = newDetailTexturePosY + DetailTextureSizeHalf; - - // Use modulo to find the center point in texture space. Care must be taken so negative values are - // handled appropriately (ie, we want -1 % 1024 to equal 1023, not -1) - Vector2i newCenter; - newCenter.m_x = (DetailTextureSize + (newDetailTexturePosX % DetailTextureSize)) % DetailTextureSize; - newCenter.m_y = (DetailTextureSize + (newDetailTexturePosY % DetailTextureSize)) % DetailTextureSize; - - CheckUpdateDetailTexture(newBounds, newCenter); - - m_detailTextureBounds = newBounds; - m_dirtyDetailRegion = AZ::Aabb::CreateNull(); - - m_previousCameraPosition = cameraPosition; - - AZ::Vector4 detailAabb = AZ::Vector4( - m_detailTextureBounds.m_min.m_x * DetailTextureScale, - m_detailTextureBounds.m_min.m_y * DetailTextureScale, - m_detailTextureBounds.m_max.m_x * DetailTextureScale, - m_detailTextureBounds.m_max.m_y * DetailTextureScale - ); - AZ::Vector2 detailUvOffset = AZ::Vector2(float(newCenter.m_x) / DetailTextureSize, float(newCenter.m_y) / DetailTextureSize); - - if (m_terrainSrg) - { - m_terrainSrg->SetConstant(m_detailAabbPropertyIndex, detailAabb); - m_terrainSrg->SetConstant(m_detailHalfPixelUvPropertyIndex, 0.5f / DetailTextureSize); - m_terrainSrg->SetConstant(m_detailCenterPropertyIndex, detailUvOffset); - - m_detailMaterialDataBuffer.UpdateSrg(m_terrainSrg.get()); + bool surfacesRebuilt = false; + surfacesRebuilt = m_meshManager.CheckRebuildSurfaces(m_materialInstance, *GetParentScene()); + if (m_forceRebuildDrawPackets && !surfacesRebuilt) + { + m_meshManager.RebuildDrawPackets(*GetParentScene()); } + m_forceRebuildDrawPackets = false; } - if (m_areaData.m_heightmapUpdated || m_areaData.m_macroMaterialsUpdated) - { - // Currently when anything in the heightmap changes we're updating all the srgs, but this could probably - // be optimized to only update the srgs that changed. - - m_areaData.m_heightmapUpdated = false; - m_areaData.m_macroMaterialsUpdated = false; - - AZStd::array uvStep = - { - 1.0f / aznumeric_cast(m_areaData.m_terrainBounds.GetXExtent() / m_areaData.m_sampleSpacing), - 1.0f / aznumeric_cast(m_areaData.m_terrainBounds.GetYExtent() / m_areaData.m_sampleSpacing), - }; - - for (SectorData& sectorData : m_sectorData) - { - ShaderTerrainData terrainDataForSrg; - - const float xPatch = sectorData.m_aabb.GetMin().GetX(); - const float yPatch = sectorData.m_aabb.GetMin().GetY(); - - terrainDataForSrg.m_uvMin = { - (xPatch - terrainBounds.GetMin().GetX()) / terrainBounds.GetXExtent(), - (yPatch - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() - }; - - terrainDataForSrg.m_uvMax = { - ((xPatch + GridMeters) - terrainBounds.GetMin().GetX()) / terrainBounds.GetXExtent(), - ((yPatch + GridMeters) - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent() - }; - - terrainDataForSrg.m_uvStep = uvStep; - - AZ::Transform transform = m_areaData.m_transform; - transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ()); - - terrainDataForSrg.m_sampleSpacing = m_areaData.m_sampleSpacing; - terrainDataForSrg.m_heightScale = terrainBounds.GetZExtent(); - - sectorData.m_srg->SetConstant(m_terrainDataIndex, terrainDataForSrg); - - AZStd::array macroMaterialData; - - uint32_t i = 0; - for (; i < sectorData.m_macroMaterials.size(); ++i) - { - const MacroMaterialData& materialData = m_macroMaterials.GetData(sectorData.m_macroMaterials.at(i)); - ShaderMacroMaterialData& shaderData = macroMaterialData.at(i); - const AZ::Aabb& materialBounds = materialData.m_bounds; - - // Use reverse coordinates (1 - y) for the y direction so that the lower left corner of the macro material images - // map to the lower left corner in world space. This will match up with the height uv coordinate mapping. - shaderData.m_uvMin = { - (xPatch - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), - 1.0f - ((yPatch - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) - }; - shaderData.m_uvMax = { - ((xPatch + GridMeters) - materialBounds.GetMin().GetX()) / materialBounds.GetXExtent(), - 1.0f - (((yPatch + GridMeters) - materialBounds.GetMin().GetY()) / materialBounds.GetYExtent()) - }; - shaderData.m_normalFactor = materialData.m_normalFactor; - shaderData.m_flipNormalX = materialData.m_normalFlipX; - shaderData.m_flipNormalY = materialData.m_normalFlipY; - - const AZ::RHI::ImageView* colorImageView = materialData.m_colorImage ? materialData.m_colorImage->GetImageView() : nullptr; - sectorData.m_srg->SetImageView(m_macroColorMapIndex, colorImageView, i); - - const AZ::RHI::ImageView* normalImageView = materialData.m_normalImage ? materialData.m_normalImage->GetImageView() : nullptr; - sectorData.m_srg->SetImageView(m_macroNormalMapIndex, normalImageView, i); - - // set flags for which images are used. - shaderData.m_mapsInUse = (colorImageView ? ColorImageUsed : 0) | (normalImageView ? NormalImageUsed : 0); - } - for (; i < sectorData.m_macroMaterials.capacity(); ++i) - { - sectorData.m_srg->SetImageView(m_macroColorMapIndex, nullptr, i); - sectorData.m_srg->SetImageView(m_macroNormalMapIndex, nullptr, i); - } - - sectorData.m_srg->SetConstantArray(m_macroMaterialDataIndex, macroMaterialData); - sectorData.m_srg->SetConstant(m_macroMaterialCountIndex, aznumeric_cast(sectorData.m_macroMaterials.size())); - - const AZ::Matrix3x4 matrix3x4 = AZ::Matrix3x4::CreateFromTransform(transform); - sectorData.m_srg->SetConstant(m_modelToWorldIndex, matrix3x4); - - sectorData.m_srg->Compile(); - } - } - - // Currently there seems to be a bug in unbounded image arrays where flickering can occur if this isn't updated every frame. - if (m_terrainSrg/* && m_detailImagesUpdated*/) - { - AZStd::array_view imageViews(m_detailImageViews.data(), m_detailImageViews.size()); - [[maybe_unused]] bool result = m_terrainSrg->SetImageViewUnboundedArray(m_detailTexturesIndex, imageViews); - AZ_Error(TerrainFPName, result, "Failed to set image view unbounded array into shader resource group."); - m_detailImagesNeedUpdate = false; - } - } - - for (auto& sectorData : m_sectorData) - { - uint8_t lodChoice = AZ::RPI::ModelLodAsset::LodCountMax; - - // Go through all cameras and choose an LOD based on the closest camera. - for (auto& view : process.m_views) - { - if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0) - { - const AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); - const AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); - const AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); - - const float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); - - // This will be configurable later - const float minDistanceForLod0 = (GridMeters * 4.0f); - - // For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD - // is exactly half the resolution of the last. - const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); - - // All cameras should render the same LOD so effects like shadows are consistent. - lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); - } - } - - // Add the correct LOD draw packet for visible sectors. - for (auto& view : process.m_views) - { - AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); - if (viewFrustum.IntersectAabb(sectorData.m_aabb) != AZ::IntersectResult::Exterior) - { - const uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); - view->AddDrawPacket(sectorData.m_drawPackets.at(lodToRender).GetRHIDrawPacket()); - } - } - } - - if (m_detailTextureImage && m_areaData.m_heightmapImage && m_imagesNeedUpdate) - { - m_imagesNeedUpdate = false; - for (auto& view : process.m_views) - { - auto viewSrg = view->GetShaderResourceGroup(); - viewSrg->SetImage(m_heightmapPropertyIndex, m_areaData.m_heightmapImage); - } if (m_terrainSrg) { - m_terrainSrg->SetImage(m_detailMaterialIdPropertyIndex, m_detailTextureImage); + if (m_macroMaterialManager.IsInitialized()) + { + m_macroMaterialManager.Update(m_terrainSrg); + } + + if (m_detailMaterialManager.IsInitialized()) + { + m_detailMaterialManager.Update(cameraPosition, m_terrainSrg); + } } + + if (m_heightmapNeedsUpdate) + { + UpdateHeightmapImage(); + m_heightmapNeedsUpdate = false; + } + + if (m_imageArrayHandler->IsInitialized()) + { + bool result [[maybe_unused]] = m_imageArrayHandler->UpdateSrg(m_terrainSrg); + AZ_Error(TerrainFPName, result, "Failed to set image view unbounded array into shader resource group."); + } + } + + if (m_meshManager.IsInitialized()) + { + m_meshManager.DrawMeshes(process); + } + + if (m_heightmapImage && m_imageBindingsNeedUpdate) + { + WorldShaderData worldData; + m_terrainBounds.GetMin().StoreToFloat3(worldData.m_min.data()); + m_terrainBounds.GetMax().StoreToFloat3(worldData.m_max.data()); + + m_imageBindingsNeedUpdate = false; + + auto sceneSrg = GetParentScene()->GetShaderResourceGroup(); + sceneSrg->SetImage(m_heightmapPropertyIndex, m_heightmapImage); + sceneSrg->SetConstant(m_worldDataIndex, worldData); } if (m_materialInstance) @@ -1432,142 +411,11 @@ namespace Terrain } } - void TerrainFeatureProcessor::InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata) - { - patchdata.m_positions.clear(); - patchdata.m_uvs.clear(); - patchdata.m_indices.clear(); - - const uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. - const size_t size = gridVertices * gridVertices; - - patchdata.m_positions.reserve(size); - patchdata.m_uvs.reserve(size); - - for (uint16_t y = 0; y < gridVertices; ++y) - { - for (uint16_t x = 0; x < gridVertices; ++x) - { - patchdata.m_positions.push_back({ aznumeric_cast(x) * gridSpacing, aznumeric_cast(y) * gridSpacing }); - patchdata.m_uvs.push_back({ aznumeric_cast(x) / gridSize, aznumeric_cast(y) / gridSize }); - } - } - - patchdata.m_indices.reserve(gridSize * gridSize * 6); // total number of quads, 2 triangles with 6 indices per quad. - - for (uint16_t y = 0; y < gridSize; ++y) - { - for (uint16_t x = 0; x < gridSize; ++x) - { - const uint16_t topLeft = y * gridVertices + x; - const uint16_t topRight = topLeft + 1; - const uint16_t bottomLeft = (y + 1) * gridVertices + x; - const uint16_t bottomRight = bottomLeft + 1; - - patchdata.m_indices.emplace_back(topLeft); - patchdata.m_indices.emplace_back(topRight); - patchdata.m_indices.emplace_back(bottomLeft); - patchdata.m_indices.emplace_back(bottomLeft); - patchdata.m_indices.emplace_back(topRight); - patchdata.m_indices.emplace_back(bottomRight); - } - } - } - - AZ::Outcome> TerrainFeatureProcessor::CreateBufferAsset( - const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName) - { - AZ::RPI::BufferAssetCreator creator; - creator.Begin(AZ::Uuid::CreateRandom()); - - AZ::RHI::BufferDescriptor bufferDescriptor; - bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead; - bufferDescriptor.m_byteCount = static_cast(bufferViewDescriptor.m_elementSize) * static_cast(bufferViewDescriptor.m_elementCount); - - creator.SetBuffer(data, bufferDescriptor.m_byteCount, bufferDescriptor); - creator.SetBufferViewDescriptor(bufferViewDescriptor); - creator.SetUseCommonPool(AZ::RPI::CommonBufferPoolType::StaticInputAssembly); - - AZ::Data::Asset bufferAsset; - if (creator.End(bufferAsset)) - { - bufferAsset.SetHint(bufferName); - return AZ::Success(bufferAsset); - } - - return AZ::Failure(); - } - - bool TerrainFeatureProcessor::InitializePatchModel() - { - AZ::RPI::ModelAssetCreator modelAssetCreator; - modelAssetCreator.Begin(AZ::Uuid::CreateRandom()); - - uint16_t gridSize = GridSize; - float gridSpacing = GridSpacing; - - for (uint32_t i = 0; i < AZ::RPI::ModelLodAsset::LodCountMax && gridSize > 0; ++i) - { - PatchData patchData; - InitializeTerrainPatch(gridSize, gridSpacing, patchData); - - const auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); - const auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); - - const auto uvBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_uvs.size()), AZ::RHI::Format::R32G32_FLOAT); - const auto uvsOutcome = CreateBufferAsset(patchData.m_uvs.data(), uvBufferViewDesc, "TerrainPatchUvs"); - - const auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); - const auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); - - if (!positionsOutcome.IsSuccess() || !uvsOutcome.IsSuccess() || !indicesOutcome.IsSuccess()) - { - AZ_Error(TerrainFPName, false, "Failed to create GPU buffers for Terrain"); - return false; - } - - AZ::RPI::ModelLodAssetCreator modelLodAssetCreator; - modelLodAssetCreator.Begin(AZ::Uuid::CreateRandom()); - - modelLodAssetCreator.BeginMesh(); - modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), {positionsOutcome.GetValue(), positionBufferViewDesc}); - modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "UV" }, AZ::Name(), {uvsOutcome.GetValue(), uvBufferViewDesc}); - modelLodAssetCreator.SetMeshIndexBuffer({indicesOutcome.GetValue(), indexBufferViewDesc}); - - AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0, 0.0, 0.0), AZ::Vector3(GridMeters, GridMeters, 0.0)); - modelLodAssetCreator.SetMeshAabb(AZStd::move(aabb)); - modelLodAssetCreator.SetMeshName(AZ::Name("Terrain Patch")); - modelLodAssetCreator.EndMesh(); - - AZ::Data::Asset modelLodAsset; - modelLodAssetCreator.End(modelLodAsset); - - modelAssetCreator.AddLodAsset(AZStd::move(modelLodAsset)); - - gridSize = gridSize / 2; - gridSpacing *= 2.0f; - } - - AZ::Data::Asset modelAsset; - bool success = modelAssetCreator.End(modelAsset); - - m_patchModel = AZ::RPI::Model::FindOrCreate(modelAsset); - - return success; - } - void TerrainFeatureProcessor::OnMaterialReinitialized([[maybe_unused]] const MaterialInstance& material) { PrepareMaterialData(); - for (auto& sectorData : m_sectorData) - { - for (auto& drawPacket : sectorData.m_drawPackets) - { - drawPacket.Update(*GetParentScene()); - } - } - m_imagesNeedUpdate = true; - m_detailImagesNeedUpdate = true; + m_forceRebuildDrawPackets = true; + m_imageBindingsNeedUpdate = true; } void TerrainFeatureProcessor::SetWorldSize([[maybe_unused]] AZ::Vector2 sizeInMeters) @@ -1576,62 +424,6 @@ namespace Terrain // larger but this will limit how much is rendered. } - template - T* TerrainFeatureProcessor::FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) - { - for (T& data : container.GetDataVector()) - { - if (data.m_entityId == entityId) - { - return &data; - } - } - return nullptr; - } - - template - T& TerrainFeatureProcessor::FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) - { - T* dataPtr = FindByEntityId(entityId, container); - if (dataPtr != nullptr) - { - return *dataPtr; - } - - const uint16_t slotId = container.GetFreeSlotIndex(); - AZ_Assert(slotId != AZ::Render::IndexedDataVector::NoFreeSlot, "Ran out of indices"); - - T& data = container.GetData(slotId); - data.m_entityId = entityId; - return data; - } - - template - void TerrainFeatureProcessor::RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container) - { - for (T& data : container.GetDataVector()) - { - if (data.m_entityId == entityId) - { - container.RemoveData(&data); - return; - } - } - AZ_Assert(false, "Entity Id not found in container.") - } - - template - void TerrainFeatureProcessor::ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback) - { - for (SectorData& sectorData : m_sectorData) - { - if (sectorData.m_aabb.Overlaps(bounds)) - { - callback(sectorData); - } - } - } - void TerrainFeatureProcessor::CacheForwardPass() { auto rasterPassFilter = AZ::RPI::PassFilter::CreateWithPassClass(); @@ -1653,59 +445,5 @@ namespace Terrain ); } - auto TerrainFeatureProcessor::Vector2i::operator+(const Vector2i& rhs) const -> Vector2i - { - Vector2i offsetPoint = *this; - offsetPoint += rhs; - return offsetPoint; - } - - auto TerrainFeatureProcessor::Vector2i::operator+=(const Vector2i& rhs) -> Vector2i& - { - m_x += rhs.m_x; - m_y += rhs.m_y; - return *this; - } - - auto TerrainFeatureProcessor::Vector2i::operator-(const Vector2i& rhs) const -> Vector2i - { - return *this + -rhs; - } - - auto TerrainFeatureProcessor::Vector2i::operator-=(const Vector2i& rhs) -> Vector2i& - { - return *this += -rhs; - } - - auto TerrainFeatureProcessor::Vector2i::operator-() const -> Vector2i - { - return {-m_x, -m_y}; - } - - auto TerrainFeatureProcessor::Aabb2i::operator+(const Vector2i& rhs) const -> Aabb2i - { - return { m_min + rhs, m_max + rhs }; - } - - auto TerrainFeatureProcessor::Aabb2i::operator-(const Vector2i& rhs) const -> Aabb2i - { - return *this + -rhs; - } - - auto TerrainFeatureProcessor::Aabb2i::GetClamped(Aabb2i rhs) const -> Aabb2i - { - Aabb2i ret; - ret.m_min.m_x = AZ::GetMax(m_min.m_x, rhs.m_min.m_x); - ret.m_min.m_y = AZ::GetMax(m_min.m_y, rhs.m_min.m_y); - ret.m_max.m_x = AZ::GetMin(m_max.m_x, rhs.m_max.m_x); - ret.m_max.m_y = AZ::GetMin(m_max.m_y, rhs.m_max.m_y); - return ret; - } - - bool TerrainFeatureProcessor::Aabb2i::IsValid() const - { - // Intentionally strict, equal min/max not valid. - return m_min.m_x < m_max.m_x && m_min.m_y < m_max.m_y; - } } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h index 7d68e6b185..016c0d478a 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainFeatureProcessor.h @@ -8,20 +8,17 @@ #pragma once -#include - #include -#include -#include + +#include +#include +#include +#include #include #include -#include #include #include -#include -#include -#include namespace AZ::RPI { @@ -30,9 +27,7 @@ namespace AZ::RPI class AsyncAssetLoader; } class Material; - class Model; class RenderPass; - class StreamingImage; } namespace Terrain @@ -41,8 +36,6 @@ namespace Terrain : public AZ::RPI::FeatureProcessor , private AZ::RPI::MaterialReloadNotificationBus::Handler , private AzFramework::Terrain::TerrainDataNotificationBus::Handler - , private TerrainMacroMaterialNotificationBus::Handler - , private TerrainAreaMaterialNotificationBus::Handler { public: AZ_RTTI(TerrainFeatureProcessor, "{D7DAC1F9-4A9F-4D3C-80AE-99579BF8AB1C}", AZ::RPI::FeatureProcessor); @@ -62,189 +55,16 @@ namespace Terrain void SetWorldSize(AZ::Vector2 sizeInMeters); private: - + + static constexpr auto InvalidImageIndex = AZ::Render::BindlessImageArrayHandler::InvalidImageIndex; using MaterialInstance = AZ::Data::Instance; - static constexpr uint32_t MaxMaterialsPerSector = 4; - - enum MacroMaterialFlags - { - ColorImageUsed = 0b01, - NormalImageUsed = 0b10, - }; - - struct ShaderTerrainData // Must align with struct in Object Srg - { - AZStd::array m_uvMin{ 0.0f, 0.0f }; - AZStd::array m_uvMax{ 1.0f, 1.0f }; - AZStd::array m_uvStep{ 1.0f, 1.0f }; - float m_sampleSpacing{ 1.0f }; - float m_heightScale{ 1.0f }; - }; - - struct ShaderMacroMaterialData // Must align with struct in Object Srg - { - AZStd::array m_uvMin{ 0.0f, 0.0f }; - AZStd::array m_uvMax{ 1.0f, 1.0f }; - float m_normalFactor{ 0.0f }; - uint32_t m_flipNormalX{ 0 }; // bool in shader - uint32_t m_flipNormalY{ 0 }; // bool in shader - uint32_t m_mapsInUse{ 0b00 }; // 0b01 = color, 0b10 = normal - }; - - struct VertexPosition - { - float m_posx; - float m_posy; - }; - - struct VertexUv - { - float m_u; - float m_v; - }; - - struct PatchData - { - AZStd::vector m_positions; - AZStd::vector m_uvs; - AZStd::vector m_indices; - }; - struct SectorData + struct WorldShaderData { - AZ::Data::Instance m_srg; // Hold on to ref so it's not dropped - AZ::Aabb m_aabb; - AZStd::fixed_vector m_drawPackets; - AZStd::fixed_vector m_macroMaterials; - }; - - enum DetailTextureFlags : uint32_t - { - UseTextureBaseColor = 0b0000'0000'0000'0000'0000'0000'0000'0001, - UseTextureNormal = 0b0000'0000'0000'0000'0000'0000'0000'0010, - UseTextureMetallic = 0b0000'0000'0000'0000'0000'0000'0000'0100, - UseTextureRoughness = 0b0000'0000'0000'0000'0000'0000'0000'1000, - UseTextureOcclusion = 0b0000'0000'0000'0000'0000'0000'0001'0000, - UseTextureHeight = 0b0000'0000'0000'0000'0000'0000'0010'0000, - UseTextureSpecularF0 = 0b0000'0000'0000'0000'0000'0000'0100'0000, - - FlipNormalX = 0b0000'0000'0000'0001'0000'0000'0000'0000, - FlipNormalY = 0b0000'0000'0000'0010'0000'0000'0000'0000, - - BlendModeMask = 0b0000'0000'0000'1100'0000'0000'0000'0000, - BlendModeLerp = 0b0000'0000'0000'0000'0000'0000'0000'0000, - BlendModeLinearLight = 0b0000'0000'0000'0100'0000'0000'0000'0000, - BlendModeMultiply = 0b0000'0000'0000'1000'0000'0000'0000'0000, - BlendModeOverlay = 0b0000'0000'0000'1100'0000'0000'0000'0000, - }; - - static constexpr uint16_t InvalidDetailImageIndex = 0xFFFF; - - struct DetailMaterialShaderData - { - // Uv - AZStd::array m_uvTransform - { - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - }; - - float m_baseColorRed{ 1.0f }; - float m_baseColorGreen{ 1.0f }; - float m_baseColorBlue{ 1.0f }; - - // Factor / Scale / Bias for input textures - float m_baseColorFactor{ 1.0f }; - - float m_normalFactor{ 1.0f }; - float m_metalFactor{ 1.0f }; - float m_roughnessScale{ 1.0f }; - float m_roughnessBias{ 0.0f }; - - float m_specularF0Factor{ 1.0f }; - float m_occlusionFactor{ 1.0f }; - float m_heightFactor{ 1.0f }; - float m_heightOffset{ 0.0f }; - - float m_heightBlendFactor{ 0.5f }; - - // Flags - DetailTextureFlags m_flags{ 0 }; - - // Image indices - uint16_t m_colorImageIndex{ InvalidDetailImageIndex }; - uint16_t m_normalImageIndex{ InvalidDetailImageIndex }; - uint16_t m_roughnessImageIndex{ InvalidDetailImageIndex }; - uint16_t m_metalnessImageIndex{ InvalidDetailImageIndex }; - - uint16_t m_specularF0ImageIndex{ InvalidDetailImageIndex }; - uint16_t m_occlusionImageIndex{ InvalidDetailImageIndex }; - uint16_t m_heightImageIndex{ InvalidDetailImageIndex }; - - // 16 byte aligned - uint16_t m_padding1; - uint32_t m_padding2; - uint32_t m_padding3; - }; - - struct DetailMaterialData - { - AZ::Data::AssetId m_assetId; - AZ::RPI::Material::ChangeId m_materialChangeId{AZ::RPI::Material::DEFAULT_CHANGE_ID}; - uint32_t refCount = 0; - uint16_t m_detailMaterialBufferIndex{ 0xFFFF }; - - AZ::Data::Instance m_colorImage; - AZ::Data::Instance m_normalImage; - AZ::Data::Instance m_roughnessImage; - AZ::Data::Instance m_metalnessImage; - AZ::Data::Instance m_specularF0Image; - AZ::Data::Instance m_occlusionImage; - AZ::Data::Instance m_heightImage; - }; - - struct DetailMaterialSurface - { - AZ::Crc32 m_surfaceTag; - uint16_t m_detailMaterialId; - }; - - struct DetailMaterialListRegion - { - AZ::EntityId m_entityId; - AZ::Aabb m_region{AZ::Aabb::CreateNull()}; - AZStd::vector m_materialsForSurfaces; - }; - - struct Vector2i - { - int32_t m_x{ 0 }; - int32_t m_y{ 0 }; - - Vector2i operator+(const Vector2i& rhs) const; - Vector2i& operator+=(const Vector2i& rhs); - Vector2i operator-(const Vector2i& rhs) const; - Vector2i& operator-=(const Vector2i& rhs); - Vector2i operator-() const; - }; - - struct Aabb2i - { - Vector2i m_min; - Vector2i m_max; - - Aabb2i operator+(const Vector2i& offset) const; - Aabb2i operator-(const Vector2i& offset) const; - - Aabb2i GetClamped(Aabb2i rhs) const; - bool IsValid() const; - }; - - struct DetailTextureLocation - { - uint16_t m_index; - AZ::Data::Instance m_image; + AZStd::array m_min{ 0.0f, 0.0f, 0.0f }; + float padding1{ 0.0f }; + AZStd::array m_max{ 0.0f, 0.0f, 0.0f }; + float padding2{ 0.0f }; }; // AZ::RPI::MaterialReloadNotificationBus::Handler overrides... @@ -254,122 +74,46 @@ namespace Terrain void OnTerrainDataDestroyBegin() override; void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; - // TerrainMacroMaterialNotificationBus overrides... - void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& material) override; - void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& material) override; - void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; - void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override; - - // TerrainAreaMaterialNotificationBus overrides... - void OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; - void OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) override; - void OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; - void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; - // AZ::RPI::SceneNotificationBus overrides... void OnRenderPipelinePassesChanged(AZ::RPI::RenderPipeline* renderPipeline) override; void Initialize(); - void InitializeTerrainPatch(uint16_t gridSize, float gridSpacing, PatchData& patchdata); - bool InitializePatchModel(); - void UpdateTerrainData(); + void UpdateHeightmapImage(); void PrepareMaterialData(); - void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData); - - void TerrainHeightOrSettingsUpdated(const AZ::Aabb& dirtyRegion); - void TerrainSurfaceDataUpdated(const AZ::Aabb& dirtyRegion); - uint16_t CreateOrUpdateDetailMaterial(MaterialInstance material); - void CheckDetailMaterialForDeletion(uint16_t detailMaterialId); - void UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material); - void CheckUpdateDetailTexture(const Aabb2i& newBounds, const Vector2i& newCenter); - void UpdateDetailTexture(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel); - uint16_t GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position); - uint8_t CalculateUpdateRegions(const Aabb2i& updateArea, const Aabb2i& textureBounds, const Vector2i& centerPixel, - AZStd::array& textureSpaceAreas, AZStd::array& scaledWorldSpaceAreas); + void TerrainHeightOrSettingsUpdated(const AZ::Aabb& dirtyRegion); void ProcessSurfaces(const FeatureProcessor::RenderPacket& process); - template - T* FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); - template - T& FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); - template - void RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector& container); - - template - void ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback); - - AZ::Outcome> CreateBufferAsset( - const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName); - void CacheForwardPass(); - // System-level parameters - static constexpr float GridSpacing{ 1.0f }; - static constexpr int32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1) - static constexpr float GridMeters{ GridSpacing * GridSize }; - static constexpr int32_t DetailTextureSize{ 1024 }; - static constexpr int32_t DetailTextureSizeHalf{ DetailTextureSize / 2 }; - static constexpr float DetailTextureScale{ 0.5f }; + TerrainMeshManager m_meshManager; + TerrainMacroMaterialManager m_macroMaterialManager; + TerrainDetailMaterialManager m_detailMaterialManager; + + AZStd::shared_ptr m_imageArrayHandler; AZStd::unique_ptr m_materialAssetLoader; MaterialInstance m_materialInstance; + AZ::Data::Instance m_terrainSrg; + AZ::Data::Instance m_heightmapImage; - AZ::RHI::ShaderInputConstantIndex m_modelToWorldIndex; - AZ::RHI::ShaderInputConstantIndex m_terrainDataIndex; - AZ::RHI::ShaderInputConstantIndex m_macroMaterialDataIndex; - AZ::RHI::ShaderInputConstantIndex m_macroMaterialCountIndex; - AZ::RHI::ShaderInputImageIndex m_macroColorMapIndex; - AZ::RHI::ShaderInputImageIndex m_macroNormalMapIndex; AZ::RHI::ShaderInputImageIndex m_heightmapPropertyIndex; - AZ::RHI::ShaderInputImageIndex m_detailMaterialIdPropertyIndex; - AZ::RHI::ShaderInputBufferIndex m_detailMaterialDataIndex; - AZ::RHI::ShaderInputConstantIndex m_detailCenterPropertyIndex; - AZ::RHI::ShaderInputConstantIndex m_detailAabbPropertyIndex; - AZ::RHI::ShaderInputConstantIndex m_detailHalfPixelUvPropertyIndex; - AZ::RHI::ShaderInputImageUnboundedArrayIndex m_detailTexturesIndex; + AZ::RHI::ShaderInputConstantIndex m_worldDataIndex; - AZ::Data::Instance m_patchModel; - AZ::Vector3 m_previousCameraPosition = AZ::Vector3(AZStd::numeric_limits::max(), 0.0, 0.0); - - // Per-area data - struct TerrainAreaData - { - AZ::Transform m_transform{ AZ::Transform::CreateIdentity() }; - AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() }; - AZ::Data::Instance m_heightmapImage; - float m_sampleSpacing{ 0.0f }; - bool m_heightmapUpdated{ true }; - bool m_macroMaterialsUpdated{ true }; - bool m_rebuildSectors{ true }; - }; - - TerrainAreaData m_areaData; + AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() }; AZ::Aabb m_dirtyRegion{ AZ::Aabb::CreateNull() }; - AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() }; - bool m_updateDetailMaterialBuffer{ false }; - - Aabb2i m_detailTextureBounds; - Vector2i m_detailTextureCenter; - AZ::Data::Instance m_detailTextureImage; - AZ::RPI::ShaderSystemInterface::GlobalShaderOptionUpdatedEvent::Handler m_handleGlobalShaderOptionUpdate; + + float m_sampleSpacing{ 0.0f }; + + bool m_heightmapNeedsUpdate{ false }; bool m_forceRebuildDrawPackets{ false }; - bool m_imagesNeedUpdate{ false }; + bool m_imageBindingsNeedUpdate{ false }; - AZStd::vector m_sectorData; + AZ::RPI::ShaderSystemInterface::GlobalShaderOptionUpdatedEvent::Handler m_handleGlobalShaderOptionUpdate; - AZ::Render::IndexedDataVector m_macroMaterials; - AZ::Render::IndexedDataVector m_detailMaterials; - AZ::Render::IndexedDataVector m_detailMaterialRegions; - AZ::Render::SparseVector m_detailMaterialShaderData; - AZ::Render::GpuBufferHandler m_detailMaterialDataBuffer; AZ::RPI::RenderPass* m_forwardPass; - - AZStd::vector m_detailImageViews; - AZStd::vector m_detailImageViewFreeList; - bool m_detailImagesNeedUpdate{ false }; }; } diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp index b1c6f2d54b..7f84874e63 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.cpp @@ -66,8 +66,27 @@ namespace Terrain } }; + void MacroMaterialData::Reflect(AZ::ReflectContext* context) + { + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Terrain") + ->Attribute(AZ::Script::Attributes::Module, "terrain") + ->Property("EntityId", BehaviorValueProperty(&MacroMaterialData::m_entityId)) + ->Property("Bounds", BehaviorValueProperty(&MacroMaterialData::m_bounds)) + ->Property("NormalFlipX", BehaviorValueProperty(&MacroMaterialData::m_normalFlipX)) + ->Property("NormalFlipY", BehaviorValueProperty(&MacroMaterialData::m_normalFlipY)) + ->Property("NormalFactor", BehaviorValueProperty(&MacroMaterialData::m_normalFactor)) + ; + } + } + void TerrainMacroMaterialRequests::Reflect(AZ::ReflectContext* context) { + MacroMaterialData::Reflect(context); + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) { behaviorContext->EBus("TerrainMacroMaterialRequestBus") diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h index 9a7151da56..abcf4d4df0 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialBus.h @@ -18,8 +18,10 @@ namespace Terrain { struct MacroMaterialData final { - AZ_RTTI(MacroMaterialData, "{DC68E20A-3251-4E4E-8BC7-F6A2521FEF46}"); - + AZ_TYPE_INFO(MacroMaterialData, "{DC68E20A-3251-4E4E-8BC7-F6A2521FEF46}"); + + static void Reflect(AZ::ReflectContext* context); + AZ::EntityId m_entityId; AZ::Aabb m_bounds = AZ::Aabb::CreateNull(); diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.cpp new file mode 100644 index 0000000000..82349cdcdb --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.cpp @@ -0,0 +1,412 @@ +/* + * 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 Terrain +{ + namespace + { + [[maybe_unused]] static const char* TerrainMacroMaterialManagerName = "TerrainMacroMaterialManager"; + } + + namespace TerrainSrgInputs + { + static const char* const MacroMaterialData("m_macroMaterialData"); + static const char* const MacroMaterialGrid("m_macroMaterialGrid"); + } + + void TerrainMacroMaterialManager::Initialize( + const AZStd::shared_ptr& bindlessImageHandler, + AZ::Data::Instance& terrainSrg) + { + AZ_Error(TerrainMacroMaterialManagerName, bindlessImageHandler, "bindlessImageHandler must not be null."); + AZ_Error(TerrainMacroMaterialManagerName, terrainSrg, "terrainSrg must not be null."); + AZ_Error(TerrainMacroMaterialManagerName, !m_isInitialized, "Already initialized."); + + if (!bindlessImageHandler || !terrainSrg || m_isInitialized) + { + return; + } + + if (UpdateSrgIndices(terrainSrg)) + { + m_bindlessImageHandler = bindlessImageHandler; + + OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::Settings); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + TerrainMacroMaterialNotificationBus::Handler::BusConnect(); + + m_terrainSizeChanged = true; + m_isInitialized = true; + } + } + + void TerrainMacroMaterialManager::Reset() + { + m_isInitialized = false; + + m_macroMaterialDataBuffer = {}; + + m_macroMaterialShaderData.clear(); + m_macroMaterialEntities.clear(); + + RemoveAllImages(); + m_macroMaterials.clear(); + + m_bindlessImageHandler = {}; + + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + TerrainMacroMaterialNotificationBus::Handler::BusDisconnect(); + } + + bool TerrainMacroMaterialManager::IsInitialized() + { + return m_isInitialized; + } + + bool TerrainMacroMaterialManager::UpdateSrgIndices(AZ::Data::Instance& terrainSrg) + { + const AZ::RHI::ShaderResourceGroupLayout* terrainSrgLayout = terrainSrg->GetLayout(); + + m_macroMaterialGridIndex = terrainSrgLayout->FindShaderInputConstantIndex(AZ::Name(TerrainSrgInputs::MacroMaterialGrid)); + AZ_Error(TerrainMacroMaterialManagerName, m_macroMaterialGridIndex.IsValid(), "Failed to find terrain srg input constant %s.", TerrainSrgInputs::MacroMaterialGrid); + + AZ::Render::GpuBufferHandler::Descriptor desc; + + // Set up the gpu buffer for macro material data + desc.m_bufferName = "Macro Material Data"; + desc.m_bufferSrgName = TerrainSrgInputs::MacroMaterialData; + desc.m_elementSize = sizeof(MacroMaterialShaderData); + desc.m_srgLayout = terrainSrgLayout; + m_macroMaterialDataBuffer = AZ::Render::GpuBufferHandler(desc); + + m_bufferNeedsUpdate = true; + + return m_macroMaterialDataBuffer.IsValid() && m_macroMaterialGridIndex.IsValid(); + } + + void TerrainMacroMaterialManager::OnTerrainDataChanged(const AZ::Aabb& dirtyRegion [[maybe_unused]], TerrainDataChangedMask dataChangedMask) + { + if ((dataChangedMask & TerrainDataChangedMask::Settings) != 0) + { + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + m_terrainSizeChanged = m_terrainSizeChanged || m_terrainBounds != worldBounds; + m_terrainBounds = worldBounds; + } + } + + void TerrainMacroMaterialManager::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) + { + AZ_Assert(!m_macroMaterials.contains(entityId), + "OnTerrainMacroMaterialCreated called for a macro material that already exists. This indicates that either the bus is incorrectly sending out " + "OnCreated announcements for existing materials, or the terrain feature processor isn't properly cleaning up macro materials."); + + MacroMaterial& macroMaterial = m_macroMaterials[entityId]; + macroMaterial.m_data = newMaterialData; + if (newMaterialData.m_colorImage) + { + macroMaterial.m_colorIndex = m_bindlessImageHandler->AppendBindlessImage(newMaterialData.m_colorImage->GetImageView()); + } + if (newMaterialData.m_normalImage) + { + macroMaterial.m_normalIndex = m_bindlessImageHandler->AppendBindlessImage(newMaterialData.m_normalImage->GetImageView()); + } + + ForMacroMaterialsInBounds(newMaterialData.m_bounds, + [&](uint16_t idx, [[maybe_unused]] const AZ::Vector2& corner) + { + for (uint16_t offset = 0; offset < MacroMaterialsPerTile; ++offset) + { + MacroMaterialShaderData& macroMaterialShaderData = m_macroMaterialShaderData.at(idx + offset); + if ((macroMaterialShaderData.m_flags & MacroMaterialShaderFlags::IsUsed) == 0) + { + UpdateMacroMaterialShaderEntry(idx + offset, macroMaterial); + break; + } + AZ_Assert(m_macroMaterialEntities.at(idx + offset) != entityId, "Found existing macro material tile for what should be a completely new macro material."); + } + } + ); + + m_bufferNeedsUpdate = true; + } + + void TerrainMacroMaterialManager::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& newMaterialData) + { + AZ_Assert(m_macroMaterials.contains(entityId), + "OnTerrainMacroMaterialChanged called for a macro material that TerrainFeatureProcessor isn't tracking. This indicates that either the bus is sending out " + "Changed announcements for materials that haven't had a OnCreated event sent, or the terrain feature processor isn't properly tracking macro materials."); + + MacroMaterial& macroMaterial = m_macroMaterials[entityId]; + macroMaterial.m_data = newMaterialData; + + auto UpdateImageIndex = [&](uint16_t& indexRef, const AZ::Data::Instance& imageView) + { + if (indexRef) + { + if (imageView) + { + m_bindlessImageHandler->UpdateBindlessImage(indexRef, imageView->GetImageView()); + } + else + { + m_bindlessImageHandler->RemoveBindlessImage(indexRef); + indexRef = 0xFFFF; + } + } + else if (imageView) + { + indexRef = m_bindlessImageHandler->AppendBindlessImage(imageView->GetImageView()); + } + }; + + UpdateImageIndex(macroMaterial.m_colorIndex, newMaterialData.m_colorImage); + UpdateImageIndex(macroMaterial.m_normalIndex, newMaterialData.m_normalImage); + + ForMacroMaterialsInBounds(newMaterialData.m_bounds, + [&](uint16_t idx, [[maybe_unused]] const AZ::Vector2& corner) + { + for (uint16_t offset = 0; offset < MacroMaterialsPerTile; ++offset) + { + if (m_macroMaterialEntities.at(idx + offset) == entityId) + { + UpdateMacroMaterialShaderEntry(idx + offset, macroMaterial); + break; + } + } + } + ); + + m_bufferNeedsUpdate = true; + } + + void TerrainMacroMaterialManager::OnTerrainMacroMaterialRegionChanged( + AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) + { + AZ_Assert(m_macroMaterials.contains(entityId), + "OnTerrainMacroMaterialChanged called for a macro material that TerrainFeatureProcessor isn't tracking. This indicates that either the bus is sending out " + "Changed announcements for materials that haven't had a OnCreated event sent, or the terrain feature processor isn't properly tracking macro materials."); + + MacroMaterial& macroMaterial = m_macroMaterials[entityId]; + macroMaterial.m_data.m_bounds = newRegion; + + AZ::Aabb changedRegion = oldRegion; + changedRegion.AddAabb(newRegion); + + ForMacroMaterialsInBounds(changedRegion, + [&](uint16_t idx, const AZ::Vector2& corner) + { + AZ::Aabb tileAabb = AZ::Aabb::CreateFromMinMaxValues( + corner.GetX(), corner.GetY(), m_terrainBounds.GetMin().GetZ(), + corner.GetX() + MacroMaterialGridSize, corner.GetY() + MacroMaterialGridSize, m_terrainBounds.GetMax().GetZ()); + + bool overlapsNew = tileAabb.Overlaps(newRegion); + uint16_t end = idx + MacroMaterialsPerTile; + + for (; idx < end; ++idx) + { + if (m_macroMaterialEntities.at(idx) == entityId) + { + if (overlapsNew) + { + // Update the macro material entry from this tile. + UpdateMacroMaterialShaderEntry(idx, macroMaterial); + } + else + { + // Remove the macro material entry from this tile. + RemoveMacroMaterialShaderEntry(idx); + } + break; + } + else if (overlapsNew && (m_macroMaterialShaderData.at(idx).m_flags & MacroMaterialShaderFlags::IsUsed) == 0) + { + // Add a macro material entry from this tile. (!overlapsOld && overlapsNew) + UpdateMacroMaterialShaderEntry(idx, macroMaterial); + break; + } + } + } + ); + + m_bufferNeedsUpdate = true; + } + + void TerrainMacroMaterialManager::OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) + { + AZ_Assert(m_macroMaterials.contains(entityId), + "OnTerrainMacroMaterialChanged called for a macro material that TerrainFeatureProcessor isn't tracking. This indicates that either the bus is sending out " + "Changed announcements for materials that haven't had a OnCreated event sent, or the terrain feature processor isn't properly tracking macro materials."); + + const MacroMaterial& macroMaterial = m_macroMaterials[entityId]; + + ForMacroMaterialsInBounds(macroMaterial.m_data.m_bounds, + [&](uint16_t idx, [[maybe_unused]] const AZ::Vector2& corner) + { + uint16_t end = idx + MacroMaterialsPerTile; + + for (; idx < end; ++idx) + { + if (m_macroMaterialEntities.at(idx) == entityId) + { + RemoveMacroMaterialShaderEntry(idx); + } + } + } + ); + + if (macroMaterial.m_colorIndex != 0xFFFF) + { + m_bindlessImageHandler->RemoveBindlessImage(macroMaterial.m_colorIndex); + } + if (macroMaterial.m_normalIndex != 0xFFFF) + { + m_bindlessImageHandler->RemoveBindlessImage(macroMaterial.m_normalIndex); + } + + m_macroMaterials.erase(entityId); + m_bufferNeedsUpdate = true; + } + + void TerrainMacroMaterialManager::UpdateMacroMaterialShaderEntry(uint16_t shaderDataIdx, const MacroMaterial& macroMaterial) + { + m_macroMaterialEntities.at(shaderDataIdx) = macroMaterial.m_data.m_entityId; + MacroMaterialShaderData& macroMaterialShaderData = m_macroMaterialShaderData.at(shaderDataIdx); + + macroMaterialShaderData.m_flags = (MacroMaterialShaderFlags)( + MacroMaterialShaderFlags::IsUsed | + (macroMaterial.m_data.m_normalFlipX ? MacroMaterialShaderFlags::FlipMacroNormalX : 0) | + (macroMaterial.m_data.m_normalFlipY ? MacroMaterialShaderFlags::FlipMacroNormalY : 0) + ); + + macroMaterialShaderData.m_normalFactor = macroMaterial.m_data.m_normalFactor; + macroMaterialShaderData.m_boundsMin = { macroMaterial.m_data.m_bounds.GetMin().GetX(), macroMaterial.m_data.m_bounds.GetMin().GetY() }; + macroMaterialShaderData.m_boundsMax = { macroMaterial.m_data.m_bounds.GetMax().GetX(), macroMaterial.m_data.m_bounds.GetMax().GetY() }; + macroMaterialShaderData.m_colorMapId = macroMaterial.m_colorIndex; + macroMaterialShaderData.m_normalMapId = macroMaterial.m_normalIndex; + } + + void TerrainMacroMaterialManager::RemoveMacroMaterialShaderEntry(uint16_t shaderDataIdx) + { + // Remove the macro material entry from this tile by copying the remaining entries on top. + for (++shaderDataIdx; shaderDataIdx % MacroMaterialsPerTile != 0; ++shaderDataIdx) + { + m_macroMaterialEntities.at(shaderDataIdx - 1) = m_macroMaterialEntities.at(shaderDataIdx); + m_macroMaterialShaderData.at(shaderDataIdx - 1) = m_macroMaterialShaderData.at(shaderDataIdx); + } + // Disable the last entry. + m_macroMaterialEntities.at(shaderDataIdx - 1) = AZ::EntityId(); + m_macroMaterialShaderData.at(shaderDataIdx - 1).m_flags = MacroMaterialShaderFlags(0); + } + + template + void TerrainMacroMaterialManager::ForMacroMaterialsInBounds(const AZ::Aabb& bounds, Callback callback) + { + // Get the macro material bounds relative to the terrain + float yStart = bounds.GetMin().GetY() - m_terrainBounds.GetMin().GetY(); + float yEnd = bounds.GetMax().GetY() - m_terrainBounds.GetMin().GetY(); + float xStart = bounds.GetMin().GetX() - m_terrainBounds.GetMin().GetX(); + float xEnd = bounds.GetMax().GetX() - m_terrainBounds.GetMin().GetX(); + + // Clamp the bounds to the terrain + uint16_t yStartIdx = yStart > 0.0f ? uint16_t(yStart / MacroMaterialGridSize) : 0; + uint16_t yEndIdx = yEnd > 0.0f ? AZStd::GetMin(uint16_t(yEnd / MacroMaterialGridSize) + 1, m_tilesY) : 0; + uint16_t xStartIdx = xStart > 0.0f ? uint16_t(xStart / MacroMaterialGridSize) : 0; + uint16_t xEndIdx = xEnd > 0.0f ? AZStd::GetMin(uint16_t(xEnd / MacroMaterialGridSize) + 1, m_tilesX) : 0; + + AZ::Vector2 gridCorner = AZ::Vector2( + floor(m_terrainBounds.GetMin().GetX() / MacroMaterialGridSize) * MacroMaterialGridSize, + floor(m_terrainBounds.GetMin().GetY() / MacroMaterialGridSize) * MacroMaterialGridSize); + + for (uint16_t y = yStartIdx; y < yEndIdx; ++y) + { + for (uint16_t x = xStartIdx; x < xEndIdx; ++x) + { + uint16_t idx = (y * m_tilesX + x) * MacroMaterialsPerTile; + const AZ::Vector2 corner = gridCorner + AZ::Vector2(x * MacroMaterialGridSize, y * MacroMaterialGridSize); + callback(idx, corner); + } + } + } + + void TerrainMacroMaterialManager::Update(AZ::Data::Instance& terrainSrg) + { + if (m_terrainSizeChanged) + { + m_terrainSizeChanged = false; + + // Rebuild the macro material tiles from scratch when the world size changes. This could be made more efficient + // but is fine for now since world resizes are rare. + + RemoveAllImages(); + m_macroMaterials.clear(); + + m_macroMaterialShaderData.clear(); + m_macroMaterialEntities.clear(); + + m_tilesX = aznumeric_cast(m_terrainBounds.GetXExtent() / MacroMaterialGridSize) + 1; + m_tilesY = aznumeric_cast(m_terrainBounds.GetYExtent() / MacroMaterialGridSize) + 1; + const uint32_t macroMaterialTileCount = m_tilesX * m_tilesY * MacroMaterialsPerTile; + + m_macroMaterialShaderData.resize(macroMaterialTileCount); + m_macroMaterialEntities.resize(macroMaterialTileCount); + + TerrainMacroMaterialRequestBus::EnumerateHandlers( + [&](TerrainMacroMaterialRequests* handler) + { + MacroMaterialData macroMaterial = handler->GetTerrainMacroMaterialData(); + AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId()); + OnTerrainMacroMaterialCreated(entityId, macroMaterial); + return true; + } + ); + } + + if (m_bufferNeedsUpdate) + { + m_bufferNeedsUpdate = false; + m_macroMaterialDataBuffer.UpdateBuffer(m_macroMaterialShaderData.data(), aznumeric_cast(m_macroMaterialShaderData.size())); + + MacroMaterialGridShaderData macroMaterialGridShaderData; + macroMaterialGridShaderData.m_offset = { m_terrainBounds.GetMin().GetX(), m_terrainBounds.GetMin().GetY() }; + macroMaterialGridShaderData.m_resolution = (m_tilesX << 16) | m_tilesY; + macroMaterialGridShaderData.m_tileSize = MacroMaterialGridSize; + + if (terrainSrg) + { + m_macroMaterialDataBuffer.UpdateSrg(terrainSrg.get()); + terrainSrg->SetConstant(m_macroMaterialGridIndex, macroMaterialGridShaderData); + } + } + } + + void TerrainMacroMaterialManager::RemoveAllImages() + { + for (const auto& [entity, macroMaterial] : m_macroMaterials) + { + RemoveImagesForMaterial(macroMaterial); + } + } + + void TerrainMacroMaterialManager::RemoveImagesForMaterial(const MacroMaterial& macroMaterial) + { + if (macroMaterial.m_colorIndex != 0xFFFF) + { + m_bindlessImageHandler->RemoveBindlessImage(macroMaterial.m_colorIndex); + } + if (macroMaterial.m_normalIndex != 0xFFFF) + { + m_bindlessImageHandler->RemoveBindlessImage(macroMaterial.m_normalIndex); + } + } + +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.h new file mode 100644 index 0000000000..6a603eeced --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMacroMaterialManager.h @@ -0,0 +1,116 @@ +/* + * 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 Terrain +{ + class TerrainMacroMaterialManager + : private TerrainMacroMaterialNotificationBus::Handler + , private AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + public: + + TerrainMacroMaterialManager() = default; + ~TerrainMacroMaterialManager() = default; + + void Initialize( + const AZStd::shared_ptr& bindlessImageHandler, + AZ::Data::Instance& terrainSrg); + void Reset(); + bool IsInitialized(); + bool UpdateSrgIndices(AZ::Data::Instance& terrainSrg); + + void Update(AZ::Data::Instance& terrainSrg); + + private: + + static constexpr auto InvalidImageIndex = AZ::Render::BindlessImageArrayHandler::InvalidImageIndex; + static constexpr float MacroMaterialGridSize = 64.0f; + static constexpr uint16_t MacroMaterialsPerTile = 4; + + enum MacroMaterialShaderFlags : uint32_t + { + IsUsed = 0b0000'0000'0000'0000'0000'0000'0000'0001, + FlipMacroNormalX = 0b0000'0000'0000'0000'0000'0000'0000'0010, + FlipMacroNormalY = 0b0000'0000'0000'0000'0000'0000'0000'0100, + }; + + struct MacroMaterialShaderData + { + MacroMaterialShaderFlags m_flags; + uint32_t m_colorMapId{InvalidImageIndex}; + uint32_t m_normalMapId{InvalidImageIndex}; + float m_normalFactor; + + // macro material bounds in world space + AZStd::array m_boundsMin{ 0.0f, 0.0f }; + AZStd::array m_boundsMax{ 0.0f, 0.0f }; + }; + static_assert(sizeof(MacroMaterialShaderData) % 16 == 0, "MacroMaterialShaderData must be 16 byte aligned."); + + struct MacroMaterial + { + MacroMaterialData m_data; + uint16_t m_colorIndex{ 0xFFFF }; + uint16_t m_normalIndex{ 0xFFFF }; + }; + + struct MacroMaterialGridShaderData + { + uint32_t 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. + AZStd::array m_offset; // x/y offset of min x/y corner of grid. + }; + static_assert(sizeof(MacroMaterialGridShaderData) % 16 == 0, "MacroMaterialGridShaderData must be 16 byte aligned."); + + // AzFramework::Terrain::TerrainDataNotificationBus overrides... + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion [[maybe_unused]], TerrainDataChangedMask dataChangedMask) override; + + // TerrainMacroMaterialNotificationBus overrides... + void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& material) override; + void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& material) override; + void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; + void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override; + + void UpdateMacroMaterialShaderEntry(uint16_t shaderDataIdx, const MacroMaterial& macroMaterialData); + void RemoveMacroMaterialShaderEntry(uint16_t shaderDataIdx); + + template + void ForMacroMaterialsInBounds(const AZ::Aabb& bounds, Callback callback); + + void RemoveAllImages(); + void RemoveImagesForMaterial(const MacroMaterial& macroMaterial); + + AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() }; + + // Macro materials stored in a grid of (MacroMaterialGridCount * MacroMaterialGridCount) where each tile in the grid covers + // an area of (MacroMaterialGridSize * MacroMaterialGridSize) and each tile can hold MacroMaterialsPerTile macro materials + AZStd::vector m_macroMaterialShaderData; + AZStd::vector m_macroMaterialEntities; // Same as above, but used to track entity ids which aren't needed by the shader. + AZStd::map m_macroMaterials; // Used for looking up macro materials by entity id when the data isn't provided by a bus. + uint16_t m_tilesX{ 0 }; + uint16_t m_tilesY{ 0 }; + + AZStd::shared_ptr m_bindlessImageHandler; + AZ::Render::GpuBufferHandler m_macroMaterialDataBuffer; + + AZ::RHI::ShaderInputConstantIndex m_macroMaterialGridIndex; + + bool m_terrainSizeChanged{ false }; + bool m_bufferNeedsUpdate{ false }; + bool m_isInitialized{ false }; + + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp new file mode 100644 index 0000000000..d689d2635c --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.cpp @@ -0,0 +1,354 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#include + +namespace Terrain +{ + namespace + { + [[maybe_unused]] static const char* TerrainMeshManagerName = "TerrainMeshManager"; + } + + namespace ShaderInputs + { + static const char* const PatchData("m_patchData"); + } + + void TerrainMeshManager::Initialize() + { + if (!InitializePatchModel()) + { + AZ_Error(TerrainMeshManagerName, false, "Failed to create Terrain render buffers!"); + return; + } + + OnTerrainDataChanged(AZ::Aabb::CreateNull(), TerrainDataChangedMask::HeightData); + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusConnect(); + + m_isInitialized = true; + } + + bool TerrainMeshManager::IsInitialized() const + { + return m_isInitialized; + } + + void TerrainMeshManager::Reset() + { + AzFramework::Terrain::TerrainDataNotificationBus::Handler::BusDisconnect(); + m_patchModel = {}; + m_sectorData.clear(); + m_rebuildSectors = true; + m_isInitialized = false; + } + + bool TerrainMeshManager::CheckRebuildSurfaces(MaterialInstance materialInstance, AZ::RPI::Scene& parentScene) + { + if (!m_rebuildSectors) + { + return false; + } + + m_rebuildSectors = false; + m_sectorData.clear(); + + const auto layout = materialInstance->GetAsset()->GetObjectSrgLayout(); + + AZ::RHI::ShaderInputConstantIndex patchDataIndex = layout->FindShaderInputConstantIndex(AZ::Name(ShaderInputs::PatchData)); + AZ_Error(TerrainMeshManagerName, patchDataIndex.IsValid(), "Failed to find shader input constant %s.", ShaderInputs::PatchData); + + const float xFirstPatchStart = AZStd::floorf(m_worldBounds.GetMin().GetX() / GridMeters) * GridMeters; + const float xLastPatchStart = AZStd::floorf(m_worldBounds.GetMax().GetX() / GridMeters) * GridMeters; + const float yFirstPatchStart = AZStd::floorf(m_worldBounds.GetMin().GetY() / GridMeters) * GridMeters; + const float yLastPatchStart = AZStd::floorf(m_worldBounds.GetMax().GetY() / GridMeters) * GridMeters; + + const auto& materialAsset = materialInstance->GetAsset(); + const auto& shaderAsset = materialAsset->GetMaterialTypeAsset()->GetShaderAssetForObjectSrg(); + + for (float yPatch = yFirstPatchStart; yPatch <= yLastPatchStart; yPatch += GridMeters) + { + for (float xPatch = xFirstPatchStart; xPatch <= xLastPatchStart; xPatch += GridMeters) + { + ShaderTerrainData objectSrgData; + objectSrgData.m_xyTranslation = { xPatch, yPatch }; + + m_sectorData.push_back(); + SectorData& sectorData = m_sectorData.back(); + + for (auto& lod : m_patchModel->GetLods()) + { + objectSrgData.m_xyScale = m_sampleSpacing * GridSize; + + auto objectSrg = AZ::RPI::ShaderResourceGroup::Create(shaderAsset, materialAsset->GetObjectSrgLayout()->GetName()); + if (!objectSrg) + { + AZ_WarningOnce(TerrainMeshManagerName, false, "Failed to create a new shader resource group, skipping."); + continue; + } + objectSrg->SetConstant(patchDataIndex, objectSrgData); + objectSrg->Compile(); + + AZ::RPI::ModelLod& modelLod = *lod.get(); + sectorData.m_drawPackets.emplace_back(modelLod, 0, materialInstance, objectSrg); + AZ::RPI::MeshDrawPacket& drawPacket = sectorData.m_drawPackets.back(); + + sectorData.m_srgs.emplace_back(objectSrg); + + // set the shader option to select forward pass IBL specular if necessary + if (!drawPacket.SetShaderOption(AZ::Name("o_meshUseForwardPassIBLSpecular"), AZ::RPI::ShaderOptionValue{ false })) + { + AZ_Warning(TerrainMeshManagerName, false, "Failed to set o_meshUseForwardPassIBLSpecular on mesh draw packet"); + } + const uint8_t stencilRef = AZ::Render::StencilRefs::UseDiffuseGIPass | AZ::Render::StencilRefs::UseIBLSpecularPass; + drawPacket.SetStencilRef(stencilRef); + drawPacket.Update(parentScene, true); + } + + sectorData.m_aabb = + AZ::Aabb::CreateFromMinMax( + AZ::Vector3(xPatch, yPatch, m_worldBounds.GetMin().GetZ()), + AZ::Vector3(xPatch + GridMeters, yPatch + GridMeters, m_worldBounds.GetMax().GetZ()) + ); + } + } + return true; + } + + void TerrainMeshManager::DrawMeshes(const AZ::RPI::FeatureProcessor::RenderPacket& process) + { + for (auto& sectorData : m_sectorData) + { + uint8_t lodChoice = AZ::RPI::ModelLodAsset::LodCountMax; + + // Go through all cameras and choose an LOD based on the closest camera. + for (auto& view : process.m_views) + { + if ((view->GetUsageFlags() & AZ::RPI::View::UsageFlags::UsageCamera) > 0) + { + const AZ::Vector3 cameraPosition = view->GetCameraTransform().GetTranslation(); + const AZ::Vector2 cameraPositionXY = AZ::Vector2(cameraPosition.GetX(), cameraPosition.GetY()); + const AZ::Vector2 sectorCenterXY = AZ::Vector2(sectorData.m_aabb.GetCenter().GetX(), sectorData.m_aabb.GetCenter().GetY()); + + const float sectorDistance = sectorCenterXY.GetDistance(cameraPositionXY); + + // This will be configurable later + const float minDistanceForLod0 = (GridMeters * 4.0f); + + // For every distance doubling beyond a minDistanceForLod0, we only need half the mesh density. Each LOD + // is exactly half the resolution of the last. + const float lodForCamera = AZStd::floorf(AZ::GetMax(0.0f, log2f(sectorDistance / minDistanceForLod0))); + + // All cameras should render the same LOD so effects like shadows are consistent. + lodChoice = AZ::GetMin(lodChoice, aznumeric_cast(lodForCamera)); + } + } + + // Add the correct LOD draw packet for visible sectors. + for (auto& view : process.m_views) + { + AZ::Frustum viewFrustum = AZ::Frustum::CreateFromMatrixColumnMajor(view->GetWorldToClipMatrix()); + if (viewFrustum.IntersectAabb(sectorData.m_aabb) != AZ::IntersectResult::Exterior) + { + const uint8_t lodToRender = AZ::GetMin(lodChoice, aznumeric_cast(sectorData.m_drawPackets.size() - 1)); + view->AddDrawPacket(sectorData.m_drawPackets.at(lodToRender).GetRHIDrawPacket()); + } + } + } + } + + void TerrainMeshManager::RebuildDrawPackets(AZ::RPI::Scene& scene) + { + for (auto& sectorData : m_sectorData) + { + for (auto& drawPacket : sectorData.m_drawPackets) + { + drawPacket.Update(scene, true); + } + } + } + + void TerrainMeshManager::OnTerrainDataDestroyBegin() + { + Reset(); + } + + void TerrainMeshManager::OnTerrainDataChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) + { + if ((dataChangedMask & (TerrainDataChangedMask::HeightData | TerrainDataChangedMask::Settings)) != 0) + { + AZ::Aabb worldBounds = AZ::Aabb::CreateNull(); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + worldBounds, &AzFramework::Terrain::TerrainDataRequests::GetTerrainAabb); + + AZ::Vector2 queryResolution2D = AZ::Vector2(1.0f); + AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult( + queryResolution2D, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution); + // Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension. + float queryResolution = queryResolution2D.GetX(); + + // Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes. + m_rebuildSectors = m_rebuildSectors || + m_worldBounds.GetMin().GetX() != worldBounds.GetMin().GetX() || + m_worldBounds.GetMin().GetY() != worldBounds.GetMin().GetY() || + m_worldBounds.GetMax().GetX() != worldBounds.GetMax().GetX() || + m_worldBounds.GetMax().GetY() != worldBounds.GetMax().GetY() || + m_sampleSpacing != queryResolution; + + m_worldBounds = worldBounds; + m_sampleSpacing = queryResolution; + } + } + + void TerrainMeshManager::InitializeTerrainPatch(uint16_t gridSize, PatchData& patchdata) + { + patchdata.m_positions.clear(); + patchdata.m_indices.clear(); + + const uint16_t gridVertices = gridSize + 1; // For m_gridSize quads, (m_gridSize + 1) vertices are needed. + const size_t size = gridVertices * gridVertices; + + patchdata.m_positions.reserve(size); + + for (uint16_t y = 0; y < gridVertices; ++y) + { + for (uint16_t x = 0; x < gridVertices; ++x) + { + patchdata.m_positions.push_back({ aznumeric_cast(x) / gridSize, aznumeric_cast(y) / gridSize }); + } + } + + patchdata.m_indices.reserve(gridSize * gridSize * 6); // total number of quads, 2 triangles with 6 indices per quad. + + for (uint16_t y = 0; y < gridSize; ++y) + { + for (uint16_t x = 0; x < gridSize; ++x) + { + const uint16_t topLeft = y * gridVertices + x; + const uint16_t topRight = topLeft + 1; + const uint16_t bottomLeft = (y + 1) * gridVertices + x; + const uint16_t bottomRight = bottomLeft + 1; + + patchdata.m_indices.emplace_back(topLeft); + patchdata.m_indices.emplace_back(topRight); + patchdata.m_indices.emplace_back(bottomLeft); + patchdata.m_indices.emplace_back(bottomLeft); + patchdata.m_indices.emplace_back(topRight); + patchdata.m_indices.emplace_back(bottomRight); + } + } + } + + AZ::Outcome> TerrainMeshManager::CreateBufferAsset( + const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName) + { + AZ::RPI::BufferAssetCreator creator; + creator.Begin(AZ::Uuid::CreateRandom()); + + AZ::RHI::BufferDescriptor bufferDescriptor; + bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead; + bufferDescriptor.m_byteCount = static_cast(bufferViewDescriptor.m_elementSize) * static_cast(bufferViewDescriptor.m_elementCount); + + creator.SetBuffer(data, bufferDescriptor.m_byteCount, bufferDescriptor); + creator.SetBufferViewDescriptor(bufferViewDescriptor); + creator.SetUseCommonPool(AZ::RPI::CommonBufferPoolType::StaticInputAssembly); + + AZ::Data::Asset bufferAsset; + if (creator.End(bufferAsset)) + { + bufferAsset.SetHint(bufferName); + return AZ::Success(bufferAsset); + } + + return AZ::Failure(); + } + + bool TerrainMeshManager::InitializePatchModel() + { + AZ::RPI::ModelAssetCreator modelAssetCreator; + modelAssetCreator.Begin(AZ::Uuid::CreateRandom()); + + uint16_t gridSize = GridSize; + float gridSpacing = GridSpacing; + + for (uint32_t i = 0; i < AZ::RPI::ModelLodAsset::LodCountMax && gridSize > 0; ++i) + { + PatchData patchData; + InitializeTerrainPatch(gridSize, patchData); + + const auto positionBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_positions.size()), AZ::RHI::Format::R32G32_FLOAT); + const auto positionsOutcome = CreateBufferAsset(patchData.m_positions.data(), positionBufferViewDesc, "TerrainPatchPositions"); + + const auto indexBufferViewDesc = AZ::RHI::BufferViewDescriptor::CreateTyped(0, aznumeric_cast(patchData.m_indices.size()), AZ::RHI::Format::R16_UINT); + const auto indicesOutcome = CreateBufferAsset(patchData.m_indices.data(), indexBufferViewDesc, "TerrainPatchIndices"); + + if (!positionsOutcome.IsSuccess() || !indicesOutcome.IsSuccess()) + { + AZ_Error(TerrainMeshManagerName, false, "Failed to create GPU buffers for Terrain"); + return false; + } + + AZ::RPI::ModelLodAssetCreator modelLodAssetCreator; + modelLodAssetCreator.Begin(AZ::Uuid::CreateRandom()); + + modelLodAssetCreator.BeginMesh(); + modelLodAssetCreator.AddMeshStreamBuffer(AZ::RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), {positionsOutcome.GetValue(), positionBufferViewDesc}); + modelLodAssetCreator.SetMeshIndexBuffer({indicesOutcome.GetValue(), indexBufferViewDesc}); + + AZ::Aabb aabb = AZ::Aabb::CreateFromMinMax(AZ::Vector3(0.0, 0.0, 0.0), AZ::Vector3(GridMeters, GridMeters, 0.0)); + modelLodAssetCreator.SetMeshAabb(AZStd::move(aabb)); + modelLodAssetCreator.SetMeshName(AZ::Name("Terrain Patch")); + modelLodAssetCreator.EndMesh(); + + AZ::Data::Asset modelLodAsset; + modelLodAssetCreator.End(modelLodAsset); + + modelAssetCreator.AddLodAsset(AZStd::move(modelLodAsset)); + + gridSize = gridSize / 2; + gridSpacing *= 2.0f; + } + + AZ::Data::Asset modelAsset; + bool success = modelAssetCreator.End(modelAsset); + + m_patchModel = AZ::RPI::Model::FindOrCreate(modelAsset); + + return success; + } + + template + void TerrainMeshManager::ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback) + { + for (SectorData& sectorData : m_sectorData) + { + if (sectorData.m_aabb.Overlaps(bounds)) + { + callback(sectorData); + } + } + } + +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.h new file mode 100644 index 0000000000..7252a85686 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainMeshManager.h @@ -0,0 +1,117 @@ +/* + * 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 + +#include + +#include +#include +#include + +#include + + +namespace AZ::RPI +{ + class BufferAsset; +} + +namespace AZ::RHI +{ + struct BufferViewDescriptor; +} + +namespace Terrain +{ + class TerrainMeshManager + : private AzFramework::Terrain::TerrainDataNotificationBus::Handler + { + private: + + using MaterialInstance = AZ::Data::Instance; + + public: + + AZ_RTTI(TerrainMeshManager, "{62C84AD8-05FE-4C78-8501-A2DB6731B9B7}"); + AZ_DISABLE_COPY_MOVE(TerrainMeshManager); + + TerrainMeshManager() = default; + ~TerrainMeshManager() = default; + + void Initialize(); + bool IsInitialized() const; + void Reset(); + + bool CheckRebuildSurfaces(MaterialInstance materialInstance, AZ::RPI::Scene& parentScene); + void DrawMeshes(const AZ::RPI::FeatureProcessor::RenderPacket& process); + void RebuildDrawPackets(AZ::RPI::Scene& scene); + + static constexpr float GridSpacing{ 1.0f }; + static constexpr int32_t GridSize{ 64 }; // number of terrain quads (vertices are m_gridSize + 1) + static constexpr float GridMeters{ GridSpacing * GridSize }; + static constexpr uint32_t MaxMaterialsPerSector = 4; + + private: + + struct VertexPosition + { + float m_posx; + float m_posy; + }; + + struct PatchData + { + AZStd::vector m_positions; + AZStd::vector m_indices; + }; + + struct SectorData + { + AZStd::fixed_vector m_drawPackets; + AZStd::fixed_vector, AZ::RPI::ModelLodAsset::LodCountMax> m_srgs; // Hold on to refs so it's not dropped + AZ::Aabb m_aabb; + }; + + struct ShaderTerrainData // Must align with struct in Object Srg + { + AZStd::array m_xyTranslation{ 0.0f, 0.0f }; + float m_xyScale{ 1.0f }; + }; + + // AzFramework::Terrain::TerrainDataNotificationBus overrides... + void OnTerrainDataDestroyBegin() override; + void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; + + AZ::Outcome> CreateBufferAsset( + const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor, const AZStd::string& bufferName); + + void InitializeTerrainPatch(uint16_t gridSize, PatchData& patchdata); + bool InitializePatchModel(); + + template + void ForOverlappingSectors(const AZ::Aabb& bounds, Callback callback); + + AZStd::vector m_sectorData; + AZ::Data::Instance m_patchModel; + + AZ::Aabb m_worldBounds{ AZ::Aabb::CreateNull() }; + float m_sampleSpacing = 1.0f; + + bool m_isInitialized{ false }; + bool m_rebuildSectors{ true }; + + }; +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.cpp new file mode 100644 index 0000000000..1df945b88c --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.cpp @@ -0,0 +1,41 @@ +/* + * 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 Terrain +{ + auto Vector2i::operator+(const Vector2i& rhs) const -> Vector2i + { + Vector2i offsetPoint = *this; + offsetPoint += rhs; + return offsetPoint; + } + + auto Vector2i::operator+=(const Vector2i& rhs) -> Vector2i& + { + m_x += rhs.m_x; + m_y += rhs.m_y; + return *this; + } + + auto Vector2i::operator-(const Vector2i& rhs) const -> Vector2i + { + return *this + -rhs; + } + + auto Vector2i::operator-=(const Vector2i& rhs) -> Vector2i& + { + return *this += -rhs; + } + + auto Vector2i::operator-() const -> Vector2i + { + return {-m_x, -m_y}; + } +} diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.h b/Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.h new file mode 100644 index 0000000000..1244972fe9 --- /dev/null +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Vector2i.h @@ -0,0 +1,29 @@ +/* + * 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 Terrain +{ + class Vector2i + { + public: + + Vector2i operator+(const Vector2i& rhs) const; + Vector2i& operator+=(const Vector2i& rhs); + Vector2i operator-(const Vector2i& rhs) const; + Vector2i& operator-=(const Vector2i& rhs); + Vector2i operator-() const; + + int32_t m_x{ 0 }; + int32_t m_y{ 0 }; + + }; +} diff --git a/Gems/Terrain/Code/terrain_files.cmake b/Gems/Terrain/Code/terrain_files.cmake index a4f39056e4..ab19d33618 100644 --- a/Gems/Terrain/Code/terrain_files.cmake +++ b/Gems/Terrain/Code/terrain_files.cmake @@ -31,11 +31,23 @@ set(FILES Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.cpp Source/TerrainRenderer/Components/TerrainMacroMaterialComponent.h + Source/TerrainRenderer/Aabb2i.cpp + Source/TerrainRenderer/Aabb2i.h Source/TerrainRenderer/TerrainFeatureProcessor.cpp Source/TerrainRenderer/TerrainFeatureProcessor.h + Source/TerrainRenderer/TerrainDetailMaterialManager.cpp + Source/TerrainRenderer/TerrainDetailMaterialManager.h + Source/TerrainRenderer/TerrainMacroMaterialManager.cpp + Source/TerrainRenderer/TerrainMacroMaterialManager.h + Source/TerrainRenderer/TerrainMeshManager.cpp + Source/TerrainRenderer/TerrainMeshManager.h + Source/TerrainRenderer/BindlessImageArrayHandler.cpp + Source/TerrainRenderer/BindlessImageArrayHandler.h Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h Source/TerrainRenderer/TerrainMacroMaterialBus.cpp Source/TerrainRenderer/TerrainMacroMaterialBus.h + Source/TerrainRenderer/Vector2i.cpp + Source/TerrainRenderer/Vector2i.h Source/TerrainSystem/TerrainSystem.cpp Source/TerrainSystem/TerrainSystem.h Source/TerrainSystem/TerrainSystemBus.h From 49652352f483a92a142fe04a8d526dbf4c00ec97 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Wed, 29 Dec 2021 16:38:21 -0800 Subject: [PATCH 26/39] Allowing default ImGui clipboard behavior so copy+paste works in the imgui console menu Signed-off-by: Gene Walters --- Gems/ImGui/Code/Source/Platform/Windows/imgui_windows.cmake | 1 - 1 file changed, 1 deletion(-) 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 ) From 48260486fb606020f4715f4299e46b26faa0c0cd Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Thu, 30 Dec 2021 12:43:00 -0600 Subject: [PATCH 27/39] Change gradients to use cached GradientTransform instance (#6591) * Change flow so that TerrainSystem stops responding during deactivation. Some systems might accidentally try to call back to the TerrainSystem inside a DestroyBegin notification, so make sure it stops listening before sending out the notification. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Change gradients to cache and use a GradientTransform instance. In my local test case, calling EBus on every call took 337 ms, using a lambda to wrap the calls took 197 ms, and using the fully cached version took 170 ms. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Remove the wrappingTransform function and go back to the switch statement. There was a bit of overhead to each function call due to using AZStd::function that just isn't necessary for this use case. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Add profile markers to the heightfield updates so that they're more visible. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Cleared state while component is deactivated. The state was getting refreshed even while the component was in a deactivated state, which meant that it wasn't properly notifying of state changes when it became active since it wasn't detecting an actual change. By clearing the state when deactivated, and ensuring the state isn't getting refreshed *while* deactivated, the notifications work properly. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed compile warning on unit test. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Addressed PR feedback - changed comments, reduced mutex scope Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- .../Source/FastNoiseGradientComponent.cpp | 18 +++- .../Code/Source/FastNoiseGradientComponent.h | 16 ++-- Gems/FastNoise/Code/Tests/FastNoiseTest.cpp | 13 +-- .../Ebuses/GradientTransformRequestBus.h | 51 +++++++++-- .../GradientSignal/GradientTransform.h | 2 - .../Components/GradientTransformComponent.cpp | 85 +++++++++---------- .../Components/GradientTransformComponent.h | 9 +- .../Components/ImageGradientComponent.cpp | 23 +++-- .../Components/ImageGradientComponent.h | 17 ++-- .../Components/PerlinGradientComponent.cpp | 24 ++++-- .../Components/PerlinGradientComponent.h | 16 ++-- .../Components/RandomGradientComponent.cpp | 19 ++++- .../Components/RandomGradientComponent.h | 16 ++-- .../EditorGradientTransformComponent.cpp | 12 ++- .../Code/Source/GradientTransform.cpp | 58 +++++++------ .../TerrainPhysicsColliderComponent.cpp | 4 + .../Source/TerrainSystem/TerrainSystem.cpp | 6 +- 17 files changed, 245 insertions(+), 144 deletions(-) diff --git a/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp b/Gems/FastNoise/Code/Source/FastNoiseGradientComponent.cpp index bb4c337299..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,13 +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; - GradientSignal::GradientTransformRequestBus::Event( - GetEntityId(), &GradientSignal::GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, 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 620f538c71..f0d5a9d1bd 100644 --- a/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp +++ b/Gems/FastNoise/Code/Tests/FastNoiseTest.cpp @@ -46,17 +46,10 @@ public: //////////////////////////////////////////////////////////////////////////// //// GradientTransformRequestBus - void TransformPositionToUVW([[maybe_unused]] const AZ::Vector3& inPosition, [[maybe_unused]] AZ::Vector3& outUVW, [[maybe_unused]] bool& wasPointRejected) const override {} - void TransformPositionToUVWNormalized( - [[maybe_unused]] const AZ::Vector3& inPosition, - [[maybe_unused]] AZ::Vector3& outUVW, - [[maybe_unused]] bool& wasPointRejected) const override + const GradientSignal::GradientTransform& GetGradientTransform() const override { + return m_gradientTransform; } - void GetGradientLocalBounds([[maybe_unused]] AZ::Aabb& bounds) const override - { - } - void GetGradientEncompassingBounds([[maybe_unused]] AZ::Aabb& bounds) const override {} ////////////////////////////////////////////////////////////////////////// // GradientTransformModifierRequestBus @@ -104,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/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h b/Gems/GradientSignal/Code/Include/GradientSignal/Ebuses/GradientTransformRequestBus.h index ac2f1a9cb1..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,16 +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, bool& wasPointRejected) const = 0; - virtual void TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, 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 index c8073c2f4f..b191ea245a 100644 --- a/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h +++ b/Gems/GradientSignal/Code/Include/GradientSignal/GradientTransform.h @@ -111,7 +111,6 @@ namespace GradientSignal private: //! These are the various transformations that will be performed, based on wrapping type. - using WrappingTransformFunction = AZStd::function; 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); @@ -144,7 +143,6 @@ namespace GradientSignal //! How the gradient should repeat itself outside of the shape bounds. WrappingType m_wrappingType = WrappingType::None; - WrappingTransformFunction m_wrappingTransform = NoTransform; /** * Cached reciprocal for performing an inverse lerp back to shape bounds. diff --git a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.cpp index 8cd7dc56a1..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,28 +327,10 @@ namespace GradientSignal return false; } - void GradientTransformComponent::TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const + const GradientTransform& GradientTransformComponent::GetGradientTransform() const { AZStd::lock_guard lock(m_cacheMutex); - m_gradientTransform.TransformPositionToUVW(inPosition, outUVW, wasPointRejected); - } - - void GradientTransformComponent::TransformPositionToUVWNormalized( - const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const - { - AZStd::lock_guard lock(m_cacheMutex); - m_gradientTransform.TransformPositionToUVWNormalized(inPosition, outUVW, wasPointRejected); - } - - 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() @@ -355,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); @@ -385,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) { @@ -428,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(); } } @@ -453,19 +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( - m_shapeBounds, shapeTransformFinal, use3dGradients, m_configuration.m_frequencyZoom, m_configuration.m_wrappingType); + 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 50ae6fe475..805fba9e74 100644 --- a/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h +++ b/Gems/GradientSignal/Code/Source/Components/GradientTransformComponent.h @@ -101,10 +101,7 @@ namespace GradientSignal ////////////////////////////////////////////////////////////////////////// // GradientTransformRequestBus - void TransformPositionToUVW(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const override; - void TransformPositionToUVWNormalized(const AZ::Vector3& inPosition, AZ::Vector3& outUVW, bool& wasPointRejected) const override; - void GetGradientLocalBounds(AZ::Aabb& bounds) const override; - void GetGradientEncompassingBounds(AZ::Aabb& bounds) const override; + const GradientTransform& GetGradientTransform() const override; ////////////////////////////////////////////////////////////////////////// // DependencyNotificationBus @@ -114,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; @@ -169,8 +166,6 @@ 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; diff --git a/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp b/Gems/GradientSignal/Code/Source/Components/ImageGradientComponent.cpp index 2d594a201c..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,18 +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; - GradientTransformRequestBus::Event( - GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVWNormalized, sampleParams.m_position, uvw, 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 d5e846b9ef..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,21 +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; - GradientTransformRequestBus::Event( - GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, 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 d320dbeb3d..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,15 +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; - GradientTransformRequestBus::Event( - GetEntityId(), &GradientTransformRequestBus::Events::TransformPositionToUVW, sampleParams.m_position, uvw, 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 index acf9e2f150..ffaa8f7e6a 100644 --- a/Gems/GradientSignal/Code/Source/GradientTransform.cpp +++ b/Gems/GradientSignal/Code/Source/GradientTransform.cpp @@ -20,7 +20,6 @@ namespace GradientSignal , m_inverseTransform(transform.GetInverseFull()) , m_frequencyZoom(frequencyZoom) , m_wrappingType(wrappingType) - , m_wrappingTransform(NoTransform) , 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. @@ -30,31 +29,17 @@ namespace GradientSignal m_inverseTransform.SetRow(2, AZ::Vector4::CreateZero()); } - // Set up the appropriate wrapping transform function for the the given wrapping type. - // Also note that ClampToZero is the only wrapping type that allows us to return a "pointIsRejected" result - // for points that fall outside the shape bounds. - if (m_shapeBounds.IsValid()) + // If we have invalid shape bounds, reset the wrapping type back to None. Wrapping won't work without valid bounds. + if (!m_shapeBounds.IsValid()) { - switch (wrappingType) - { - default: - case WrappingType::None: - m_wrappingTransform = GetUnboundedPointInAabb; - break; - case WrappingType::ClampToEdge: - m_wrappingTransform = GetClampedPointInAabb; - break; - case WrappingType::ClampToZero: - m_alwaysAcceptPoint = false; - m_wrappingTransform = GetClampedPointInAabb; - break; - case WrappingType::Mirror: - m_wrappingTransform = GetMirroredPointInAabb; - break; - case WrappingType::Repeat: - m_wrappingTransform = GetWrappedPointInAabb; - break; - } + 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( @@ -76,7 +61,26 @@ namespace GradientSignal (outUVW.IsGreaterEqualThan(m_shapeBounds.GetMin()) && outUVW.IsLessThan(m_shapeBounds.GetMax())); wasPointRejected = !wasPointAccepted; - outUVW = m_wrappingTransform(outUVW, m_shapeBounds); + 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; } @@ -121,7 +125,7 @@ namespace GradientSignal * [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 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. */ 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/TerrainSystem/TerrainSystem.cpp b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp index 1b2dedd469..7c8b6021af 100644 --- a/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp +++ b/Gems/Terrain/Code/Source/TerrainSystem/TerrainSystem.cpp @@ -105,11 +105,13 @@ void TerrainSystem::Activate() void TerrainSystem::Deactivate() { + // Stop listening to the bus even before we signal DestroyBegin so that way any calls to the terrain system as a *result* of + // calling DestroyBegin will fail to reach the terrain system. + AzFramework::Terrain::TerrainDataRequestBus::Handler::BusDisconnect(); + AzFramework::Terrain::TerrainDataNotificationBus::Broadcast( &AzFramework::Terrain::TerrainDataNotificationBus::Events::OnTerrainDataDestroyBegin); - AzFramework::Terrain::TerrainDataRequestBus::Handler::BusDisconnect(); - { AZStd::unique_lock lock(m_areaMutex); m_registeredAreas.clear(); From 8cd4a3dceda4654bdb9741d9b7a0c75e84309de4 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Thu, 30 Dec 2021 12:50:06 -0800 Subject: [PATCH 28/39] Fix netbind component to use the new svg icons instead of png (which no longer exist) Signed-off-by: Gene Walters --- Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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")); } } From 79dd65e1ccfdef85747cbc26c8f868b42df060d6 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Mon, 3 Jan 2022 10:20:27 +0100 Subject: [PATCH 29/39] Quaternion shortest equivalent (#6472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added new helper functions to 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. * Added unit test. * Renamed the angle parameter of the CreateRotationX/Y/Z() functions into angleInRadians. Signed-off-by: Benjamin Jillich --- .../Framework/AzCore/AzCore/Math/Quaternion.h | 16 +++++++--- .../AzCore/AzCore/Math/Quaternion.inl | 29 +++++++++++++++---- .../AzCore/Tests/Math/QuaternionTests.cpp | 19 ++++++++++-- 3 files changed, 51 insertions(+), 13 deletions(-) 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)); + } } From 346a414aff6a79b4651337b473c82f8c924aa749 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon, 3 Jan 2022 09:44:34 -0600 Subject: [PATCH 30/39] Change AP unit test project to a DLL like all others, add benchmarks project (#6588) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- Code/Tools/AssetProcessor/CMakeLists.txt | 15 ++-- .../native/tests/AssetProcessorTest.cpp | 8 +-- .../AssetProcessor/native/tests/test_main.cpp | 70 +------------------ 3 files changed, 13 insertions(+), 80 deletions(-) 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/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) From a95b303f197d242c01db1100e235870047ae0c4d Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon, 3 Jan 2022 09:44:51 -0600 Subject: [PATCH 31/39] Remove Shader compiler tab from Asset Processor (#6486) * Remove Shader compiler tab from Asset Processor Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Remove more references to shader compiler Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../assetprocessor_static_files.cmake | 7 - .../assetprocessor_test_files.cmake | 2 - .../shadercompiler/shadercompilerManager.cpp | 162 --------------- .../shadercompiler/shadercompilerManager.h | 67 ------ .../shadercompiler/shadercompilerMessages.h | 24 --- .../shadercompiler/shadercompilerModel.cpp | 150 -------------- .../shadercompiler/shadercompilerModel.h | 93 --------- .../shadercompiler/shadercompilerjob.cpp | 194 ------------------ .../native/shadercompiler/shadercompilerjob.h | 44 ---- .../AssetProcessor/native/ui/MainWindow.cpp | 10 - .../AssetProcessor/native/ui/MainWindow.h | 1 - .../AssetProcessor/native/ui/MainWindow.ui | 53 ----- .../unittests/ShaderCompilerUnitTests.cpp | 188 ----------------- .../unittests/ShaderCompilerUnitTests.h | 81 -------- .../utilities/GUIApplicationManager.cpp | 63 +----- .../native/utilities/GUIApplicationManager.h | 11 +- 16 files changed, 8 insertions(+), 1142 deletions(-) delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.cpp delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerManager.h delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerMessages.h delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.cpp delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerModel.h delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.cpp delete mode 100644 Code/Tools/AssetProcessor/native/shadercompiler/shadercompilerjob.h delete mode 100644 Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.cpp delete mode 100644 Code/Tools/AssetProcessor/native/unittests/ShaderCompilerUnitTests.h 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/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; From 81453defa02e691f17860ab7327a169c7b7cc0b4 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon, 3 Jan 2022 09:56:19 -0600 Subject: [PATCH 32/39] Load QIcons once for asset tree and share with each Asset Tree Item to avoid file loads for every asset and improve start up time (#6485) Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> --- .../Tools/AssetProcessor/native/ui/AssetTreeItem.cpp | 12 +++++++----- Code/Tools/AssetProcessor/native/ui/AssetTreeItem.h | 2 ++ .../AssetProcessor/native/ui/AssetTreeModel.cpp | 9 ++++++--- Code/Tools/AssetProcessor/native/ui/AssetTreeModel.h | 2 ++ 4 files changed, 17 insertions(+), 8 deletions(-) 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; }; } From ee554f6464715c342acb6d26f64bd27b71200560 Mon Sep 17 00:00:00 2001 From: Roman <69218254+amzn-rhhong@users.noreply.github.com> Date: Mon, 3 Jan 2022 09:25:41 -0800 Subject: [PATCH 33/39] ActorInstanceId default to -1 when no %lastresult matches (#6442) * small bugfix Signed-off-by: rhhong * ActorInstanceId default to -1 when no %lastresult matches Signed-off-by: rhhong * CR feedback - wrap function to get the first available editor actor instance. Signed-off-by: rhhong * Remove mcore inline Signed-off-by: rhhong * Fixed the bug that delete an instance from actor manager crashes the editor. Signed-off-by: rhhong --- .../Source/ActorInstanceCommands.cpp | 20 ++++++++++++++++++- .../Source/AnimGraphCommands.cpp | 13 ++++++++++-- .../Code/EMotionFX/Source/ActorManager.cpp | 14 +++++++++++++ .../Code/EMotionFX/Source/ActorManager.h | 6 ++++++ .../EMStudioSDK/Source/MainWindow.cpp | 10 +++++++++- .../Source/RenderPlugin/RenderPlugin.cpp | 10 +++++++++- .../EMStudioSDK/Source/Workspace.cpp | 13 ++++++++++++ .../Code/MCore/Source/MCoreCommandManager.cpp | 7 ++++++- 8 files changed, 87 insertions(+), 6 deletions(-) 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/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/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 From 1d819e25917a41a5f619195c6c1070b07bd83f90 Mon Sep 17 00:00:00 2001 From: amzn-sj Date: Mon, 3 Jan 2022 11:50:49 -0800 Subject: [PATCH 34/39] Fix crash caused by trying to dereference a nullptr Early return if input attachment is null (#6590) Signed-off-by: amzn-sj --- .../Common/Code/Source/CoreLights/EsmShadowmapsPass.cpp | 7 +++++++ 1 file changed, 7 insertions(+) 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; From 75476032ab97809e37507e7a1801562bc450efeb Mon Sep 17 00:00:00 2001 From: Junbo Liang <68558268+junbo75@users.noreply.github.com> Date: Mon, 3 Jan 2022 11:59:03 -0800 Subject: [PATCH 35/39] Define AWSI automation tests pipe for Jenkins (#6518) * Define AWSI automation tests pipe for Jenkins Signed-off-by: Junbo Liang <68558268+junbo75@users.noreply.github.com> --- .../build/Platform/Linux/build_config.json | 49 +++++++++++++++ .../Platform/Linux/deploy_cdk_applications.sh | 8 +++ .../Linux/destroy_cdk_applications.sh | 8 +++ scripts/build/Platform/Linux/pipeline.json | 62 +++++++++++++++++++ 4 files changed, 127 insertions(+) diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index 5290a32f6d..988cf23703 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -132,6 +132,35 @@ "ASSET_PROCESSOR_PLATFORMS": "linux,server" } }, + "awsi_test_profile_pipe": { + "TAGS": [ + "nightly-incremental", + "nightly-clean" + ], + "steps": [ + "awsi_deployment", + "awsi_test_profile", + "awsi_destruction" + ] + }, + "awsi_test_profile": { + "TAGS": [ + "weekly-build-metrics" + ], + "PIPELINE_ENV": { + "NONBLOCKING_STEP": "True" + }, + "COMMAND": "build_test_linux.sh", + "PARAMETERS": { + "CONFIGURATION": "profile", + "OUTPUT_DIRECTORY": "build\\linux", + "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DLY_PARALLEL_LINK_JOBS=4", + "CMAKE_LY_PROJECTS": "AutomatedTesting", + "CMAKE_TARGET": "TEST_SUITE_awsi", + "CTEST_OPTIONS": "-L \"(SUITE_awsi)\" --no-tests=error", + "TEST_RESULTS": "True" + } + }, "periodic_test_profile": { "TAGS": [ "nightly-incremental", @@ -275,5 +304,25 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DLY_PARALLEL_LINK_JOBS=4 -DCMAKE_MODULE_PATH=${WORKSPACE}/o3de/install/cmake", "CMAKE_TARGET": "all" } + }, + "awsi_deployment": { + "TAGS": [], + "PIPELINE_ENV": { + "NONBLOCKING_STEP": "True" + }, + "COMMAND": "deploy_cdk_applications.sh", + "PARAMETERS": { + "NVM_VERSION": "v0.39.1" + } + }, + "awsi_destruction": { + "TAGS": [], + "PIPELINE_ENV": { + "NONBLOCKING_STEP": "True" + }, + "COMMAND": "destroy_cdk_applications.sh", + "PARAMETERS": { + "NVM_VERSION": "v0.39.1" + } } } diff --git a/scripts/build/Platform/Linux/deploy_cdk_applications.sh b/scripts/build/Platform/Linux/deploy_cdk_applications.sh index 93a3dccd9f..97668ee70c 100755 --- a/scripts/build/Platform/Linux/deploy_cdk_applications.sh +++ b/scripts/build/Platform/Linux/deploy_cdk_applications.sh @@ -61,6 +61,14 @@ then exit 1 fi +echo [cdk_installation] Install nvm $NVM_VERSION +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +echo [cdk_installation] Install the current version of nodejs +nvm install node + echo [cdk_installation] Install the latest version of CDK if ! sudo npm uninstall -g aws-cdk; then diff --git a/scripts/build/Platform/Linux/destroy_cdk_applications.sh b/scripts/build/Platform/Linux/destroy_cdk_applications.sh index 795ab78484..54f84911a6 100755 --- a/scripts/build/Platform/Linux/destroy_cdk_applications.sh +++ b/scripts/build/Platform/Linux/destroy_cdk_applications.sh @@ -61,6 +61,14 @@ then exit 1 fi +echo [cdk_installation] Install nvm $NVM_VERSION +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +echo [cdk_installation] Install the current version of nodejs +nvm install node + echo [cdk_installation] Install the latest version of CDK if ! sudo npm uninstall -g aws-cdk; then diff --git a/scripts/build/Platform/Linux/pipeline.json b/scripts/build/Platform/Linux/pipeline.json index f44ed5f9de..d10e2886f2 100644 --- a/scripts/build/Platform/Linux/pipeline.json +++ b/scripts/build/Platform/Linux/pipeline.json @@ -16,5 +16,67 @@ "nightly-clean": { "CLEAN_WORKSPACE": true } + }, + "PIPELINE_JENKINS_PARAMETERS": { + "nightly-incremental": [ + { + "parameter_name": "O3DE_AWS_PROJECT_NAME", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The name of the O3DE project that stacks should be deployed for." + }, + { + "parameter_name": "O3DE_AWS_DEPLOY_REGION", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The region to deploy the stacks into." + }, + { + "parameter_name": "ASSUME_ROLE_ARN", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The ARN of the IAM role to assume to retrieve temporary AWS credentials." + }, + { + "parameter_name": "COMMIT_ID", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The commit ID for locking the version of CDK applications to deploy." + } + ], + "nightly-clean": [ + { + "parameter_name": "O3DE_AWS_PROJECT_NAME", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The name of the O3DE project that stacks should be deployed for." + }, + { + "parameter_name": "O3DE_AWS_DEPLOY_REGION", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The region to deploy the stacks into." + }, + { + "parameter_name": "ASSUME_ROLE_ARN", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The ARN of the IAM role to assume to retrieve temporary AWS credentials." + }, + { + "parameter_name": "COMMIT_ID", + "parameter_type": "string", + "default_value": "", + "use_last_run_value": true, + "description": "The commit ID for locking the version of CDK applications to deploy." + } + ] } } From fd9574c6489bcbcb644db6e49ff876b79c72a17b Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Mon, 3 Jan 2022 14:21:44 -0600 Subject: [PATCH 36/39] Removed legacy QtUi/ClickableLabel and re-worked about dialog to use QLabel for terms of use label Signed-off-by: Chris Galvan --- Code/Editor/AboutDialog.cpp | 7 -- Code/Editor/AboutDialog.h | 2 - Code/Editor/AboutDialog.ui | 14 ++- Code/Editor/Lib/Tests/test_ClickableLabel.cpp | 58 ---------- Code/Editor/QtUI/ClickableLabel.cpp | 101 ------------------ Code/Editor/QtUI/ClickableLabel.h | 39 ------- Code/Editor/editor_lib_files.cmake | 2 - Code/Editor/editor_lib_test_files.cmake | 1 - 8 files changed, 6 insertions(+), 218 deletions(-) delete mode 100644 Code/Editor/Lib/Tests/test_ClickableLabel.cpp delete mode 100644 Code/Editor/QtUI/ClickableLabel.cpp delete mode 100644 Code/Editor/QtUI/ClickableLabel.h 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
qsvgwidget.h
- - ClickableLabel - QLabel -
QtUI/ClickableLabel.h
-
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 From 6c986657b416b151a4f5a0ea2ff73416b6ab9eb6 Mon Sep 17 00:00:00 2001 From: carlitosan <82187351+carlitosan@users.noreply.github.com> Date: Mon, 3 Jan 2022 13:18:18 -0800 Subject: [PATCH 37/39] Fix for file-save-as-new and open asset (#6490) Signed-off-by: carlitosan <82187351+carlitosan@users.noreply.github.com> --- .../EditorScriptCanvasComponent.cpp | 2 +- .../Code/Editor/View/Widgets/GraphTabBar.cpp | 2 +- .../Code/Editor/View/Windows/MainWindow.cpp | 37 ++++++++++++------- 3 files changed, 26 insertions(+), 15 deletions(-) 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() From a000198b1bd5e38feb18355b6cdafbea3e85c814 Mon Sep 17 00:00:00 2001 From: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> Date: Mon, 3 Jan 2022 16:07:31 -0600 Subject: [PATCH 38/39] Add benchmarks to GradientSignal. (#6616) * Add benchmarks to GradientSignal. Cleaned up and rearranged a bit of the unit testing code to make it reusable from a benchmark suite as well. Added set of benchmarks for measuring GetValue(), so that we can compare against GetValues() when it gets added. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed Editor unit tests. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> --- Gems/GradientSignal/Code/CMakeLists.txt | 6 + .../Code/Include/GradientSignal/ImageAsset.h | 7 + .../EditorGradientSignalPreviewTests.cpp | 4 +- .../Code/Tests/GradientSignalBenchmarks.cpp | 106 +++++++++++++++ .../Code/Tests/GradientSignalImageTests.cpp | 125 +----------------- .../Tests/GradientSignalReferencesTests.cpp | 2 +- .../Tests/GradientSignalServicesTests.cpp | 2 +- .../Code/Tests/GradientSignalSurfaceTests.cpp | 2 +- .../Code/Tests/GradientSignalTest.cpp | 2 +- .../Code/Tests/GradientSignalTestFixtures.cpp | 121 +++++++++++++++++ .../Code/Tests/GradientSignalTestFixtures.h | 124 +++++++++++++++++ .../Code/Tests/GradientSignalTestMocks.cpp | 72 ++++++++++ .../Code/Tests/GradientSignalTestMocks.h | 93 +++++-------- .../Tests/GradientSignalTransformTests.cpp | 2 +- .../gradientsignal_editor_tests_files.cmake | 1 + .../Code/gradientsignal_tests_files.cmake | 4 + 16 files changed, 487 insertions(+), 186 deletions(-) create mode 100644 Gems/GradientSignal/Code/Tests/GradientSignalBenchmarks.cpp create mode 100644 Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.cpp create mode 100644 Gems/GradientSignal/Code/Tests/GradientSignalTestFixtures.h create mode 100644 Gems/GradientSignal/Code/Tests/GradientSignalTestMocks.cpp diff --git a/Gems/GradientSignal/Code/CMakeLists.txt b/Gems/GradientSignal/Code/CMakeLists.txt index f5c0457c23..d1ac8cdacf 100644 --- a/Gems/GradientSignal/Code/CMakeLists.txt +++ b/Gems/GradientSignal/Code/CMakeLists.txt @@ -145,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} 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/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 index 73dabf1d6a..ddebc0ce1f 100644 --- a/Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp +++ b/Gems/GradientSignal/Code/Tests/GradientSignalTransformTests.cpp @@ -7,7 +7,7 @@ */ -#include "Tests/GradientSignalTestMocks.h" +#include #include #include 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_tests_files.cmake b/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake index 5f8ffe2ebb..8e081e5711 100644 --- a/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake +++ b/Gems/GradientSignal/Code/gradientsignal_tests_files.cmake @@ -7,11 +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 From 95245e2424681d2b86c35cb1c7f9847540ff2451 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Tue, 4 Jan 2022 09:13:14 +0100 Subject: [PATCH 39/39] EMotion FX: Linear and angular velocity calculation and debug visualization helpers (#6613) * Added helper functions for calculating linear and angular velocities. * Added debug visualization helper for linear and angular velocities. Signed-off-by: Benjamin Jillich --- .../Code/EMotionFX/Source/Velocity.cpp | 83 +++++++++++++++++++ .../Code/EMotionFX/Source/Velocity.h | 23 +++++ .../Code/EMotionFX/emotionfx_files.cmake | 2 + 3 files changed, 108 insertions(+) create mode 100644 Gems/EMotionFX/Code/EMotionFX/Source/Velocity.cpp create mode 100644 Gems/EMotionFX/Code/EMotionFX/Source/Velocity.h 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/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