From 304696fa5cc59b3b517d5545844d2d25d35ee610 Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 27 Apr 2021 19:26:35 -0700 Subject: [PATCH 01/38] Shader compile fixes --- .../DiffuseComposite_nomsaa.azsl | 8 ++++---- Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl index 74571b5c6a..c243af0855 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseComposite_nomsaa.azsl @@ -91,7 +91,7 @@ float3 SampleProbeIrradiance(uint2 probeIrradianceCoords, float depth, float3 no { for (int x = -extent; x <= extent; ++x) { - float3 downsampledNormal = PassSrg::m_downsampledNormal.Load(int3(probeIrradianceCoords, 0), int2(x, y)).rgb; + float3 downsampledNormal = PassSrg::m_downsampledNormal.Load(int3(probeIrradianceCoords + int2(x, y), 0)).rgb; downsampledNormal = downsampledNormal * 2.0f - 1.0f; float normalDot = dot(downsampledNormal, normal); @@ -100,10 +100,10 @@ float3 SampleProbeIrradiance(uint2 probeIrradianceCoords, float depth, float3 no if (normalDot > NormalMatchTolerance) { // the normals are almost identical, if the depth is within the tolerance we can optimize by just taking this sample - float downsampledDepth = PassSrg::m_downsampledDepth.Load(int3(probeIrradianceCoords, 0), int2(x, y)).r; + float downsampledDepth = PassSrg::m_downsampledDepth.Load(int3(probeIrradianceCoords + int2(x, y), 0)).r; if (abs(depth - downsampledDepth) <= DepthTolerance) { - float3 probeIrradiance = PassSrg::m_downsampledProbeIrradiance.Load(int3(probeIrradianceCoords,0), int2(x, y)).rgb; + float3 probeIrradiance = PassSrg::m_downsampledProbeIrradiance.Load(int3(probeIrradianceCoords + int2(x, y),0)).rgb; probeIrradiance = saturate(probeIrradiance); return probeIrradiance; } @@ -115,7 +115,7 @@ float3 SampleProbeIrradiance(uint2 probeIrradianceCoords, float depth, float3 no } } - float3 probeIrradiance = PassSrg::m_downsampledProbeIrradiance.Load(int3(probeIrradianceCoords, 0), closestOffset).rgb; + float3 probeIrradiance = PassSrg::m_downsampledProbeIrradiance.Load(int3(probeIrradianceCoords + closestOffset, 0)).rgb; probeIrradiance = saturate(probeIrradiance); return probeIrradiance; } diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp index 00b5dada69..957c076592 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp @@ -308,7 +308,7 @@ namespace AZ uint32_t exitCode = 0; bool timedOut = false; - const AZStd::sys_time_t maxWaitTimeSeconds = 120; + const AZStd::sys_time_t maxWaitTimeSeconds = 240; const AZStd::sys_time_t startTimeSeconds = AZStd::GetTimeNowSecond(); const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks(); From 12d5288e32ea98420d2585498e2d5fb02c731963 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Wed, 28 Apr 2021 15:15:14 -0700 Subject: [PATCH 02/38] Add codegen for BehaviorContext binding of RPC Send functions --- .../Source/AutoGen/AutoComponent_Header.jinja | 1 + .../Source/AutoGen/AutoComponent_Source.jinja | 34 +++++++++++++++++++ ...tionPlayerInputComponent.AutoComponent.xml | 6 ++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index f5774b07c0..b2d2792e9b 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -416,6 +416,7 @@ namespace {{ Component.attrib['Namespace'] }} static void Reflect(AZ::ReflectContext* context); static void ReflectToEditContext(AZ::ReflectContext* context); + static void ReflectToBehaviorContext(AZ::ReflectContext* context); static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index d6907876e1..f2d6ca71c6 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -315,6 +315,22 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(par {% endmacro %} {# +#} +{% macro ReflectRpcInvocations(Component, ClassName, InvokeFrom, HandleOn) %} +{% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% if Property.attrib['CanScript']|booleanTrue == true %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} + ->Method("{{ UpperFirst(Property.attrib['Name']) }}", [](const {{ ClassName }}* self, {{ ', '.join(paramDefines) }}) { + self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); + }) +{% endif %} +{% endcall %} +{% endmacro %} +{# + #} {% macro DeclareRpcHandleCases(Component, ComponentDerived, InvokeFrom, HandleOn, ValidationFunction) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} @@ -1114,6 +1130,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ DefineArchetypePropertyReflection(Component, ComponentBaseName)|indent(16) }}; } ReflectToEditContext(context); + ReflectToBehaviorContext(context); } void {{ ComponentBaseName }}::{{ ComponentBaseName }}::ReflectToEditContext(AZ::ReflectContext* context) @@ -1138,6 +1155,23 @@ namespace {{ Component.attrib['Namespace'] }} } } + void {{ ComponentBaseName }}::{{ ComponentBaseName }}::ReflectToBehaviorContext(AZ::ReflectContext* context) + { + AZ::BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Category, "Multiplayer") + ->Attribute(AZ::Script::Attributes::Module, "Multiplayer") + {{ ReflectRpcInvocations(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + ; + } + } + void {{ ComponentBaseName }}::{{ ComponentBaseName }}::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC_CE("{{ ComponentName }}Service")); diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index d38ebbb1b8..336909a102 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -18,18 +18,18 @@ - + - + - + From e2ca84ceb253d0930a006b45c66f745d4f597c8a Mon Sep 17 00:00:00 2001 From: moudgils Date: Fri, 7 May 2021 17:41:24 -0700 Subject: [PATCH 03/38] More fixes to shaders with the latest dxc --- .../Assets/Materials/Types/EnhancedPBR_ForwardPass.shader | 3 +-- .../DiffuseProbeGridDownsample_nomsaa.azsl | 4 ++-- Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp | 2 +- .../Code/Source/Platform/Windows/Vulkan_Traits_Windows.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader index 7964e3c84a..764b67c82f 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader @@ -31,8 +31,7 @@ }, "CompilerHints" : { - "DisableOptimizations" : true, - "DxcGenerateDebugInfo" : true + "DisableOptimizations" : true }, "ProgramSettings": diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl index f10fc67da5..d2e73927e0 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/DiffuseGlobalIllumination/DiffuseProbeGridDownsample_nomsaa.azsl @@ -70,8 +70,8 @@ PSOutput MainPS(VSOutput IN) { for (uint x = 0; x < ImageScale; ++x) { - float depth = PassSrg::m_depth.Load(int3(screenCoords, 0), int2(x, y)).r; - float4 encodedNormal = PassSrg::m_normal.Load(int3(screenCoords, 0), int2(x, y)); + float depth = PassSrg::m_depth.Load(int3(screenCoords + int2(x, y), 0)).r; + float4 encodedNormal = PassSrg::m_normal.Load(int3(screenCoords + int2(x, y), 0)); // take the closest depth sample to ensure we're getting the normal closest to the viewer // (larger depth value due to reverse depth) diff --git a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp index 957c076592..00b5dada69 100644 --- a/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp +++ b/Gems/Atom/RHI/Code/Source/RHI.Edit/Utils.cpp @@ -308,7 +308,7 @@ namespace AZ uint32_t exitCode = 0; bool timedOut = false; - const AZStd::sys_time_t maxWaitTimeSeconds = 240; + const AZStd::sys_time_t maxWaitTimeSeconds = 120; const AZStd::sys_time_t startTimeSeconds = AZStd::GetTimeNowSecond(); const AZStd::sys_time_t startTime = AZStd::GetTimeNowTicks(); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/Vulkan_Traits_Windows.h b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/Vulkan_Traits_Windows.h index c7c9902115..c1314aaf66 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/Vulkan_Traits_Windows.h +++ b/Gems/Atom/RHI/Vulkan/Code/Source/Platform/Windows/Vulkan_Traits_Windows.h @@ -11,7 +11,7 @@ */ #pragma once -#define AZ_TRAIT_ATOM_SHADERBUILDER_DXC "Builders/DirectXShaderCompilerAz/dxc.exe" +#define AZ_TRAIT_ATOM_SHADERBUILDER_DXC "Builders/DirectXShaderCompiler/dxc.exe" #define AZ_TRAIT_ATOM_VULKAN_DISABLE_DUAL_SOURCE_BLENDING 0 #define AZ_TRAIT_ATOM_VULKAN_DLL "vulkan.dll" #define AZ_TRAIT_ATOM_VULKAN_DLL_1 "vulkan-1.dll" From b225dcfa79a4ee9bfe48456899dba0632e632e24 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 12 May 2021 19:37:44 -0700 Subject: [PATCH 04/38] Update cmake to use the new Dxc binaries for windows --- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index bdc93d9a0e..9b202f2eb2 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -1,4 +1,4 @@ -# +# # All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or # its licensors. # @@ -28,8 +28,8 @@ ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME SPIRVCross-2020.04.20-rev1-multiplatform TARGETS SPIRVCross PACKAGE_HASH 7c8c0eaa0166c26745c62d2238525af7e27ac058a5db3defdbaec1878e8798dd) -ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-2020.08.07-rev1-multiplatform TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 04a6850ce03d4c16e19ed206f7093d885276dfb74047e6aa99f0a834c8b7cc73) ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxcAz-5.0.0_az-rev1-multiplatform TARGETS DirectXShaderCompilerDxcAz PACKAGE_HASH 94f24989a7a371d840b513aa5ffaff02747b3d19b119bc1f899427e29978f753) +ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-2021.05.05-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH b2e34c4a19b8a996c1e488aeb83233abe1985b6502ef644516ef692029b98f6d) ly_associate_package(PACKAGE_NAME azslc-1.7.20-rev1-multiplatform TARGETS azslc PACKAGE_HASH 45d55f28bea2ef823ed3204f60df52e5e329f42923923d4555fdbdf3bea0af60) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) From 83f29f4a3443de16c793e28d12848bc77889bae7 Mon Sep 17 00:00:00 2001 From: moudgils Date: Mon, 17 May 2021 20:39:39 -0700 Subject: [PATCH 05/38] Updated to one Dxc package --- Gems/Atom/Asset/Shader/Code/CMakeLists.txt | 1 - .../ShaderResourceGroups/BindlessPrototypeSrg.azsli | 10 +++++----- .../Platform/Windows/BuiltInPackages_windows.cmake | 3 +-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Gems/Atom/Asset/Shader/Code/CMakeLists.txt b/Gems/Atom/Asset/Shader/Code/CMakeLists.txt index 7d82ca5869..a06aa79d24 100644 --- a/Gems/Atom/Asset/Shader/Code/CMakeLists.txt +++ b/Gems/Atom/Asset/Shader/Code/CMakeLists.txt @@ -98,7 +98,6 @@ ly_add_target( Gem::Atom_RPI.Edit RUNTIME_DEPENDENCIES 3rdParty::DirectXShaderCompilerDxc - 3rdParty::DirectXShaderCompilerDxcAz 3rdParty::SPIRVCross 3rdParty::azslc ) diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli index 7304af9e1e..0b7224019d 100644 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli +++ b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli @@ -29,6 +29,11 @@ ShaderResourceGroupSemantic FloatBufferSemanticId FrequencyId = 7; }; +ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId +{ + StructuredBuffer m_floatBuffer; +}; + ShaderResourceGroup ImageSrg : FrequencyPerScene { Sampler m_sampler @@ -43,11 +48,6 @@ ShaderResourceGroup ImageSrg : FrequencyPerScene Texture2D m_textureArray[]; } -ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId -{ - StructuredBuffer m_floatBuffer; -}; - // Helper functions to read data from the FloatBuffer. The FloatBuffer is accessed with a descriptor and a index. // The descriptor holds the initial offset within the FloatBuffer, and the index is a sub-index, which increments with each property that is being read. // The data needs to be read in the same order as it is allocated on the host. diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 9b202f2eb2..78c9dc7336 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -28,8 +28,7 @@ ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME SPIRVCross-2020.04.20-rev1-multiplatform TARGETS SPIRVCross PACKAGE_HASH 7c8c0eaa0166c26745c62d2238525af7e27ac058a5db3defdbaec1878e8798dd) -ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxcAz-5.0.0_az-rev1-multiplatform TARGETS DirectXShaderCompilerDxcAz PACKAGE_HASH 94f24989a7a371d840b513aa5ffaff02747b3d19b119bc1f899427e29978f753) -ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-2021.05.05-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH b2e34c4a19b8a996c1e488aeb83233abe1985b6502ef644516ef692029b98f6d) +ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 48367b1237c41e17deef3bf39b964665d46daa587de890190fe5dc7224f9beb4) ly_associate_package(PACKAGE_NAME azslc-1.7.20-rev1-multiplatform TARGETS azslc PACKAGE_HASH 45d55f28bea2ef823ed3204f60df52e5e329f42923923d4555fdbdf3bea0af60) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) From f087b3be89870dd2740f972c95bbb0f0d11ec809 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Tue, 18 May 2021 12:13:03 -0700 Subject: [PATCH 06/38] AutoComponent jinja formatting --- .../Source/AutoGen/AutoComponent_Source.jinja | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 41666264e0..751b55be86 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -1121,9 +1121,9 @@ namespace {{ Component.attrib['Namespace'] }} void {{ RecordName }}::SetPredictableBits() { {{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Authority', 'Client')|indent(8) -}} -{{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Authority', 'Server')|indent(8) -}} -{{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Authority', 'Autonomous')|indent(8) -}} -{{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Autonomous', 'Authority')|indent(8) }} + {{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Authority', 'Server')|indent(8) -}} + {{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Authority', 'Autonomous')|indent(8) -}} + {{ GenerateModelReplicationRecordPredictableBits(Component, ClassType, 'Autonomous', 'Authority')|indent(8) }} } {% if NetworkInputCount > 0 %} @@ -1186,22 +1186,22 @@ namespace {{ Component.attrib['Namespace'] }} } {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', false, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', true, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', false, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', true, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', false, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', true, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', false, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', true, ControllerBaseName)|indent(4) }} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', true, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', false, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', true, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', false, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', true, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', false, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Autonomous', 'Authority', true, ControllerBaseName)|indent(4) }} {{ DefineArchetypePropertyGets(Component, ClassType, ControllerBaseName, "GetParent().")|indent(4) -}} -{{ DefineRpcInvocations(Component, ControllerBaseName, 'Autonomous', 'Authority', false)|indent(4) -}} -{{ DefineRpcInvocations(Component, ControllerBaseName, 'Autonomous', 'Authority', true)|indent(4) -}} -{{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Autonomous', false)|indent(4) -}} -{{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Autonomous', true)|indent(4) -}} -{{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Client', false)|indent(4) -}} -{{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Client', true)|indent(4) }} + {{ DefineRpcInvocations(Component, ControllerBaseName, 'Autonomous', 'Authority', false)|indent(4) -}} + {{ DefineRpcInvocations(Component, ControllerBaseName, 'Autonomous', 'Authority', true)|indent(4) -}} + {{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Autonomous', false)|indent(4) -}} + {{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Autonomous', true)|indent(4) -}} + {{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Client', false)|indent(4) -}} + {{ DefineRpcInvocations(Component, ControllerBaseName, 'Authority', 'Client', true)|indent(4) }} {% for Service in Component.iter('ComponentRelation') %} {% if (Service.attrib['HasController']|booleanTrue) and (Service.attrib['Constraint'] != 'Incompatible') %} {{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}Controller* {{ ControllerBaseName }}::Get{{ Service.attrib['Name'] }}Controller() @@ -1221,10 +1221,10 @@ namespace {{ Component.attrib['Namespace'] }} serializeContext->Class<{{ ComponentBaseName }}, Multiplayer::MultiplayerComponent>() ->Version(1) {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(16) -}} -{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(16) -}} -{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(16) -}} -{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(16) -}} -{{ DefineNetworkPropertyReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(16) }} + {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(16) -}} + {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(16) -}} + {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(16) -}} + {{ DefineNetworkPropertyReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(16) }} {{ DefineArchetypePropertyReflection(Component, ComponentBaseName)|indent(16) }}; } ReflectToEditContext(context); @@ -1244,10 +1244,10 @@ namespace {{ Component.attrib['Namespace'] }} ->Attribute(AZ::Edit::Attributes::Category, "{{ Component.attrib['Namespace'] }}") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(20) }} + {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}} + {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(20) -}} + {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(20) -}} + {{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(20) }} {{ DefineArchetypePropertyEditReflection(Component, ComponentBaseName)|indent(20) }}; {% if ComponentDerived %} @@ -1275,12 +1275,14 @@ namespace {{ Component.attrib['Namespace'] }} {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Client', ComponentName) | indent(16) -}} {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Autonomous', ComponentName) | indent(16) -}} {{ DefineNetworkPropertyBehaviorReflection(Component, 'Autonomous', 'Authority', ComponentName) | indent(16) -}} + + // Reflect RPCs + {{ ReflectRpcInvocations(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + {{- DefineArchetypePropertyBehaviorReflection(Component, ComponentName) | indent(16) }} - - {{ ReflectRpcInvocations(Component, ComponentName, 'Server', 'Authority')|indent(16) -}} - {{ ReflectRpcInvocations(Component, ComponentName, 'Autonomous', 'Authority')|indent(16) -}} - {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(16) -}} - {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(16) -}} ; } } From c4641f2594d0c9e63058a930b571c64d6b6545fc Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Tue, 18 May 2021 14:13:01 -0700 Subject: [PATCH 07/38] ScriptCanvas can now check if an entity net-component is authority, autonomous, server, or client --- .../Components/MultiplayerComponent.cpp | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp index 8542288b23..ff3b64aa38 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp @@ -24,6 +24,82 @@ namespace Multiplayer serializeContext->Class() ->Version(1); } + + AZ::BehaviorContext* behaviorContext = azrtti_cast(context); + if (behaviorContext) + { + behaviorContext->Class("MultiplayerComponent") + ->Attribute(AZ::Script::Attributes::Module, "multiplayer") + ->Attribute(AZ::Script::Attributes::Category, "Multiplayer") + + ->Method("Is Authority", [](AZ::EntityId id) -> bool { + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning( "MultiplayerComponent", false, "MultiplayerComponent IsAuthority failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return false; + } + + MultiplayerComponent* multiplayerComponent = entity->FindComponent(); + if (!multiplayerComponent) + { + AZ_Warning( "MultiplayerComponent", false, "MultiplayerComponent IsAuthority failed. Entity '%s' (id: %s) is missing a MultiplayerComponent, make sure this entity contains a component which derives from MultiplayerComponent.", entity->GetName().c_str(), id.ToString().c_str()) + return false; + } + return multiplayerComponent->IsAuthority(); + }) + ->Method("Is Autonomous", [](AZ::EntityId id) -> bool { + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning( "MultiplayerComponent", false, "MultiplayerComponent IsAutonomous failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return false; + } + + MultiplayerComponent* multiplayerComponent = entity->FindComponent(); + if (!multiplayerComponent) + { + AZ_Warning( + "MultiplayerComponent", false, + "MultiplayerComponent IsAutonomous failed. Entity '%s' (id: %s) is missing a MultiplayerComponent, make sure this entity contains a component which derives from MultiplayerComponent.", + entity->GetName().c_str(), id.ToString().c_str()) return false; + } + return multiplayerComponent->IsAutonomous(); + }) + ->Method("Is Client", [](AZ::EntityId id) -> bool { + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning( "MultiplayerComponent", false, "MultiplayerComponent IsClient failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return false; + } + + MultiplayerComponent* multiplayerComponent = entity->FindComponent(); + if (!multiplayerComponent) + { + AZ_Warning("MultiplayerComponent", false, "MultiplayerComponent IsClient failed. Entity '%s' (id: %s) is missing a MultiplayerComponent, make sure this entity contains a component which derives from MultiplayerComponent.", entity->GetName().c_str(), id.ToString().c_str()) + return false; + } + return multiplayerComponent->IsClient(); + }) + ->Method("Is Server", [](AZ::EntityId id) -> bool { + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning( "MultiplayerComponent", false, "MultiplayerComponent IsServer failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return false; + } + + MultiplayerComponent* multiplayerComponent = entity->FindComponent(); + if (!multiplayerComponent) + { + AZ_Warning("MultiplayerComponent", false, "MultiplayerComponent IsServer failed. Entity '%s' (id: %s) is missing a MultiplayerComponent, make sure this entity contains a component which derives from MultiplayerComponent.", entity->GetName().c_str(), id.ToString().c_str()) + return false; + } + return multiplayerComponent->IsServer(); + }) + ; + } } void MultiplayerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) From 0d207eab191e1f86a84167506eb6e1d3b21a1b56 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Tue, 18 May 2021 14:27:17 -0700 Subject: [PATCH 08/38] fixes minor auto-formatting issue --- .../Code/Source/Components/MultiplayerComponent.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp index ff3b64aa38..b0fd671686 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp @@ -59,10 +59,8 @@ namespace Multiplayer MultiplayerComponent* multiplayerComponent = entity->FindComponent(); if (!multiplayerComponent) { - AZ_Warning( - "MultiplayerComponent", false, - "MultiplayerComponent IsAutonomous failed. Entity '%s' (id: %s) is missing a MultiplayerComponent, make sure this entity contains a component which derives from MultiplayerComponent.", - entity->GetName().c_str(), id.ToString().c_str()) return false; + AZ_Warning("MultiplayerComponent", false, "MultiplayerComponent IsAutonomous failed. Entity '%s' (id: %s) is missing a MultiplayerComponent, make sure this entity contains a component which derives from MultiplayerComponent.", entity->GetName().c_str(), id.ToString().c_str()) + return false; } return multiplayerComponent->IsAutonomous(); }) From 0a2c95cc201f4094f316732e96f83e6ba07d812e Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 18 May 2021 17:42:41 -0700 Subject: [PATCH 09/38] Add Mac's dxc package --- .../Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl | 4 ++-- cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl index e7e743adda..23eb79650a 100644 --- a/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl +++ b/Gems/Atom/Utils/Code/Include/Atom/Utils/ImGuiTransientAttachmentProfiler.inl @@ -269,13 +269,13 @@ namespace AZ { ImGui::BeginChild(heapMemoryId.c_str()); ImGui::SetScrollY(scrollingY); - ImGui::End(); + ImGui::EndChild(); } { ImGui::BeginChild(scopesId.c_str()); ImGui::SetScrollX(scrollingX); - ImGui::End(); + ImGui::EndChild(); } ImGui::PopStyleVar(3); diff --git a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake index 8636715c39..643dcbb632 100644 --- a/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake +++ b/cmake/3rdParty/Platform/Mac/BuiltInPackages_mac.cmake @@ -28,8 +28,7 @@ ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME SPIRVCross-2020.04.20-rev1-multiplatform TARGETS SPIRVCross PACKAGE_HASH 7c8c0eaa0166c26745c62d2238525af7e27ac058a5db3defdbaec1878e8798dd) -ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxc-2020.08.07-rev1-multiplatform TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 04a6850ce03d4c16e19ed206f7093d885276dfb74047e6aa99f0a834c8b7cc73) -ly_associate_package(PACKAGE_NAME DirectXShaderCompilerDxcAz-5.0.0_az-rev1-multiplatform TARGETS DirectXShaderCompilerDxcAz PACKAGE_HASH 94f24989a7a371d840b513aa5ffaff02747b3d19b119bc1f899427e29978f753) +ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-mac TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 4e97484f8fcf73fc39f22fc85ae86933a8f2e3ba0748fcec128bce05795035a6) ly_associate_package(PACKAGE_NAME azslc-1.7.20-rev1-multiplatform TARGETS azslc PACKAGE_HASH 45d55f28bea2ef823ed3204f60df52e5e329f42923923d4555fdbdf3bea0af60) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) From cc2e3aed58e8815c544595bb3bf7021b0fee3dbc Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 18 May 2021 20:54:06 -0700 Subject: [PATCH 10/38] Revert a change to resolve conflict --- .../BindlessPrototypeSrg.azsli | 136 ------------------ 1 file changed, 136 deletions(-) delete mode 100644 Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli diff --git a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli b/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli deleted file mode 100644 index 0b7224019d..0000000000 --- a/Gems/Atom/RPI/Assets/ShaderLib/Atom/RPI/ShaderResourceGroups/BindlessPrototypeSrg.azsli +++ /dev/null @@ -1,136 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -// NOTE: Nest this array, so Azslc will output a size of the bindingslot to 1 -struct FloatBuffer -{ - float buffer; -}; - -// Listed on update frequency -ShaderResourceGroupSemantic FrequencyPerScene -{ - FrequencyId = 6; -}; - -ShaderResourceGroupSemantic FloatBufferSemanticId -{ - FrequencyId = 7; -}; - -ShaderResourceGroup FloatBufferSrg : FloatBufferSemanticId -{ - StructuredBuffer m_floatBuffer; -}; - -ShaderResourceGroup ImageSrg : FrequencyPerScene -{ - Sampler m_sampler - { - MaxAnisotropy = 16; - AddressU = Wrap; - AddressV = Wrap; - AddressW = Wrap; - }; - - // Array of textures - Texture2D m_textureArray[]; -} - -// Helper functions to read data from the FloatBuffer. The FloatBuffer is accessed with a descriptor and a index. -// The descriptor holds the initial offset within the FloatBuffer, and the index is a sub-index, which increments with each property that is being read. -// The data needs to be read in the same order as it is allocated on the host. - -// All float setters -void SetFloat(out float outFloat, in uint desc, inout uint index) -{ - outFloat = FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer; - index += 1; -} - -void SetFloat2(out float2 outFloat, in uint desc, inout uint index) -{ - outFloat.x = FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer; - outFloat.y = FloatBufferSrg::m_floatBuffer[desc + index + 1].buffer; - index += 2; -} - -void SetFloat3(out float3 outFloat, in uint desc, inout uint index) -{ - outFloat.x = FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer; - outFloat.y = FloatBufferSrg::m_floatBuffer[desc + index + 1].buffer; - outFloat.z = FloatBufferSrg::m_floatBuffer[desc + index + 2].buffer; - index += 3; -} - -void SetFloat4(out float4 outFloat, in uint desc, inout uint index) -{ - outFloat.x = FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer; - outFloat.y = FloatBufferSrg::m_floatBuffer[desc + index + 1].buffer; - outFloat.z = FloatBufferSrg::m_floatBuffer[desc + index + 2].buffer; - outFloat.w = FloatBufferSrg::m_floatBuffer[desc + index + 3].buffer; - index += 4; -} - -// All matrix setters -void SetFloat4x4(out float4x4 outFloat, in uint desc, inout uint index) -{ - [unroll(4)] - for(uint i = 0; i < 4; i++) - { - SetFloat4(outFloat[i], desc, index); - } -} - -// All uint setters -void SetUint(out uint outUInt, in uint desc, inout uint index) -{ - outUInt = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer); - index += 1; -} - -void SetUint2(out uint2 outUInt, in uint desc, inout uint index) -{ - outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer); - outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 1].buffer); - index += 2; -} - -void SetUint3(out uint3 outUInt, in uint desc, inout uint index) -{ - outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer); - outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 1].buffer); - outUInt.z = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 2].buffer); - index += 3; -} - -void SetUint4(out uint4 outUInt, in uint desc, inout uint index) -{ - outUInt.x = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 0].buffer); - outUInt.y = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 1].buffer); - outUInt.z = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 2].buffer); - outUInt.w = asuint(FloatBufferSrg::m_floatBuffer[desc + index + 3].buffer); - index += 4; -} - -// All double setters -void SetDouble(out double outDouble, in uint desc, inout uint index) -{ - uint lowBits; - uint highBits; - SetUint(highBits, desc, index); - SetUint(lowBits, desc, index); - - outDouble = asdouble(lowBits, highBits); -} From aa79dfbf9565fedf8b554815134a75eb2f2747d4 Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 18 May 2021 21:29:23 -0700 Subject: [PATCH 11/38] Update hash for 3rdParty cmake file --- cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake index 3cdf18808e..cd5ff8e68f 100644 --- a/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake +++ b/cmake/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake @@ -27,7 +27,7 @@ ly_associate_package(PACKAGE_NAME expat-2.1.0-multiplatform ly_associate_package(PACKAGE_NAME zstd-1.35-multiplatform TARGETS zstd PACKAGE_HASH 45d466c435f1095898578eedde85acf1fd27190e7ea99aeaa9acfd2f09e12665) ly_associate_package(PACKAGE_NAME SQLite-3.32.2-rev3-multiplatform TARGETS SQLite PACKAGE_HASH dd4d3de6cbb4ce3d15fc504ba0ae0587e515dc89a25228037035fc0aef4831f4) ly_associate_package(PACKAGE_NAME SPIRVCross-2020.04.20-rev1-multiplatform TARGETS SPIRVCross PACKAGE_HASH 7c8c0eaa0166c26745c62d2238525af7e27ac058a5db3defdbaec1878e8798dd) -ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 48367b1237c41e17deef3bf39b964665d46daa587de890190fe5dc7224f9beb4) +ly_associate_package(PACKAGE_NAME DirectXShaderCompiler-1.6.2104-o3de-rev1-windows TARGETS DirectXShaderCompilerDxc PACKAGE_HASH 2c60297758d73f7833911e5ae3006fe0b10ced6e0b1b54764b33ae2b86e0d41d) ly_associate_package(PACKAGE_NAME azslc-1.7.20-rev1-multiplatform TARGETS azslc PACKAGE_HASH 45d55f28bea2ef823ed3204f60df52e5e329f42923923d4555fdbdf3bea0af60) ly_associate_package(PACKAGE_NAME glad-2.0.0-beta-rev2-multiplatform TARGETS glad PACKAGE_HASH ff97ee9664e97d0854b52a3734c2289329d9f2b4cd69478df6d0ca1f1c9392ee) ly_associate_package(PACKAGE_NAME lux_core-2.2-rev5-multiplatform TARGETS lux_core PACKAGE_HASH c8c13cf7bc351643e1abd294d0841b24dee60e51647dff13db7aec396ad1e0b5) From 487e989e683fcbed55ab8bb7498b624e44d8b77e Mon Sep 17 00:00:00 2001 From: karlberg Date: Wed, 19 May 2021 13:27:24 -0700 Subject: [PATCH 12/38] Several updates to complete rewind support and remove unneeded functionality --- .../AzNetworking/Framework/INetworking.h | 22 +++ .../Framework/NetworkingSystemComponent.cpp | 33 ++++- .../Framework/NetworkingSystemComponent.h | 6 +- .../Multiplayer/Components/NetBindComponent.h | 7 + .../Code/Include/Multiplayer/IMultiplayer.h | 5 - .../Include/Multiplayer/MultiplayerTypes.h | 11 +- .../EntityReplication/ReplicationRecord.h | 7 - .../Source/AutoGen/AutoComponent_Header.jinja | 20 +-- .../Source/AutoGen/AutoComponent_Source.jinja | 44 +----- ...tionPlayerInputComponent.AutoComponent.xml | 2 +- .../AutoGen/Multiplayer.AutoPackets.xml | 16 --- .../Source/Components/NetBindComponent.cpp | 16 +++ .../Debug/MultiplayerDebugSystemComponent.cpp | 39 ++++++ .../Source/MultiplayerSystemComponent.cpp | 129 +++++++----------- .../Code/Source/MultiplayerSystemComponent.h | 8 +- .../EntityReplicationManager.cpp | 57 ++++---- .../EntityReplicationManager.h | 5 +- .../EntityReplication/ReplicationRecord.cpp | 46 +------ .../Code/Source/NetworkTime/NetworkTime.cpp | 57 ++++++-- .../Code/Source/NetworkTime/NetworkTime.h | 3 + 20 files changed, 265 insertions(+), 268 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h b/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h index 72ffce4202..fb6d217b80 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/INetworking.h @@ -19,6 +19,8 @@ namespace AzNetworking { + using NetworkInterfaces = AZStd::unordered_map>; + //! @class INetworking //! @brief The interface for creating and working with network interfaces. class INetworking @@ -60,5 +62,25 @@ namespace AzNetworking //! @param name The name of the Compressor factory to unregister, must match result of factory->GetFactoryName() //! @return Whether the factory was found and unregistered virtual bool UnregisterCompressorFactory(AZ::Name name) = 0; + + //! Returns the raw network interfaces owned by the networking instance. + //! @return the raw network interfaces owned by the networking instance + virtual const NetworkInterfaces& GetNetworkInterfaces() const = 0; + + //! Returns the number of sockets monitored by our TcpListenThread. + //! @return the number of sockets monitored by our TcpListenThread + virtual uint32_t GetTcpListenThreadSocketCount() const = 0; + + //! Returns the total time spent updating our TcpListenThread. + //! @return the total time spent updating our TcpListenThread + virtual AZ::TimeMs GetTcpListenThreadUpdateTime() const = 0; + + //! Returns the number of sockets monitored by our UdpReaderThread. + //! @return the number of sockets monitored by our UdpReaderThread + virtual uint32_t GetUdpReaderThreadSocketCount() const = 0; + + //! Returns the total time spent updating our UdpReaderThread. + //! @return the total time spent updating our UdpReaderThread + virtual AZ::TimeMs GetUdpReaderThreadUpdateTime() const = 0; }; } diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp index c275ad9057..1a1476dcc8 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.cpp @@ -149,12 +149,37 @@ namespace AzNetworking return m_compressorFactories.erase(name) > 0; } + const NetworkInterfaces& NetworkingSystemComponent::GetNetworkInterfaces() const + { + return m_networkInterfaces; + } + + uint32_t NetworkingSystemComponent::GetTcpListenThreadSocketCount() const + { + return m_listenThread->GetSocketCount(); + } + + AZ::TimeMs NetworkingSystemComponent::GetTcpListenThreadUpdateTime() const + { + return m_listenThread->GetUpdateTimeMs(); + } + + uint32_t NetworkingSystemComponent::GetUdpReaderThreadSocketCount() const + { + return m_readerThread->GetSocketCount(); + } + + AZ::TimeMs NetworkingSystemComponent::GetUdpReaderThreadUpdateTime() const + { + return m_readerThread->GetUpdateTimeMs(); + } + void NetworkingSystemComponent::DumpStats([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { - AZLOG_INFO("Total sockets monitored by TcpListenThread: %u", m_listenThread->GetSocketCount()); - AZLOG_INFO("Total time spent updating TcpListenThread: %lld", aznumeric_cast(m_listenThread->GetUpdateTimeMs())); - AZLOG_INFO("Total sockets monitored by UdpReaderThread: %u", m_readerThread->GetSocketCount()); - AZLOG_INFO("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(m_readerThread->GetUpdateTimeMs())); + AZLOG_INFO("Total sockets monitored by TcpListenThread: %u", GetTcpListenThreadSocketCount()); + AZLOG_INFO("Total time spent updating TcpListenThread: %lld", aznumeric_cast(GetTcpListenThreadUpdateTime())); + AZLOG_INFO("Total sockets monitored by UdpReaderThread: %u", GetUdpReaderThreadSocketCount()); + AZLOG_INFO("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(GetUdpReaderThreadUpdateTime())); for (auto& networkInterface : m_networkInterfaces) { diff --git a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h index b0b4d83d54..2fdc773fb0 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h +++ b/Code/Framework/AzNetworking/AzNetworking/Framework/NetworkingSystemComponent.h @@ -63,6 +63,11 @@ namespace AzNetworking void RegisterCompressorFactory(ICompressorFactory* factory) override; AZStd::unique_ptr CreateCompressor(AZ::Name name) override; bool UnregisterCompressorFactory(AZ::Name name) override; + const NetworkInterfaces& GetNetworkInterfaces() const override; + uint32_t GetTcpListenThreadSocketCount() const override; + AZ::TimeMs GetTcpListenThreadUpdateTime() const override; + uint32_t GetUdpReaderThreadSocketCount() const override; + AZ::TimeMs GetUdpReaderThreadUpdateTime() const override; //! @} //! Console commands. @@ -74,7 +79,6 @@ namespace AzNetworking AZ_CONSOLEFUNC(NetworkingSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for all instantiated network interfaces"); - using NetworkInterfaces = AZStd::unordered_map>; NetworkInterfaces m_networkInterfaces; AZStd::unique_ptr m_listenThread; AZStd::unique_ptr m_readerThread; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index 41503396fb..4fe60f14a3 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -35,6 +35,7 @@ namespace Multiplayer using EntityStopEvent = AZ::Event; using EntityDirtiedEvent = AZ::Event<>; + using EntitySyncRewindEvent = AZ::Event<>; using EntityMigrationStartEvent = AZ::Event; using EntityMigrationEndEvent = AZ::Event<>; using EntityServerMigrationEvent = AZ::Event; @@ -73,6 +74,7 @@ namespace Multiplayer NetworkEntityHandle GetEntityHandle(); void SetOwningConnectionId(AzNetworking::ConnectionId connectionId); + AzNetworking::ConnectionId GetOwningConnectionId() const; void SetAllowAutonomy(bool value); MultiplayerComponentInputVector AllocateComponentInputs(); bool IsProcessingInput() const; @@ -91,12 +93,14 @@ namespace Multiplayer void MarkDirty(); void NotifyLocalChanges(); + void NotifySyncRewindState(); void NotifyMigrationStart(ClientInputId migratedInputId); void NotifyMigrationEnd(); void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId); void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler); void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler); + void AddEntitySyncRewindEventHandler(EntitySyncRewindEvent::Handler& eventHandler); void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler); void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler); void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler); @@ -144,6 +148,7 @@ namespace Multiplayer EntityStopEvent m_entityStopEvent; EntityDirtiedEvent m_dirtiedEvent; + EntitySyncRewindEvent m_syncRewindEvent; EntityMigrationStartEvent m_entityMigrationStartEvent; EntityMigrationEndEvent m_entityMigrationEndEvent; EntityServerMigrationEvent m_entityServerMigrationEvent; @@ -157,6 +162,8 @@ namespace Multiplayer NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole; NetEntityId m_netEntityId = InvalidNetEntityId; + AzNetworking::ConnectionId m_owningConnectionId = AzNetworking::InvalidConnectionId; + bool m_isProcessingInput = false; bool m_isMigrationDataValid = false; bool m_needsToBeStopped = false; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h index 6a615465c2..579ca195e5 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/IMultiplayer.h @@ -48,7 +48,6 @@ namespace Multiplayer using ConnectionAcquiredEvent = AZ::Event; using SessionInitEvent = AZ::Event; using SessionShutdownEvent = AZ::Event; - using OnConnectFunctor = AZStd::function; //! IMultiplayer provides insight into the Multiplayer session and its Agents class IMultiplayer @@ -78,10 +77,6 @@ namespace Multiplayer //! @param handler The SessionShutdownEvent handler to add virtual void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) = 0; - //! Overrides the default connect behaviour with the provided functor. - //! @param functor the function to invoke during a new connection event - virtual void SetOnConnectFunctor(const OnConnectFunctor& functor) = 0; - //! Sends a packet telling if entity update messages can be sent //! @param readyForEntityUpdates Ready for entity updates or not virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h index e9f3865563..16cc4146dd 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/MultiplayerTypes.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace Multiplayer { @@ -85,8 +86,7 @@ namespace Multiplayer Activate }; - // This is just a placeholder - // The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab + // Structure for identifying a specific entity within a spawnable struct PrefabEntityId { AZ_TYPE_INFO(PrefabEntityId, "{EFD37465-CCAC-4E87-A825-41B4010A2C75}"); @@ -121,6 +121,13 @@ namespace Multiplayer return serializer.IsValid(); } }; + + struct EntityMigrationMessage + { + NetEntityId m_entityId; + PrefabEntityId m_prefabEntityId; + AzNetworking::PacketEncodingBuffer m_propertyUpdateData; + }; } AZ_TYPE_SAFE_INTEGRAL_SERIALIZEBINDING(Multiplayer::HostId); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h index f6eb93c4ba..3dfc4b8016 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h @@ -24,14 +24,12 @@ namespace Multiplayer ReplicationRecordStats() = default; ReplicationRecordStats ( - uint32_t authorityToAuthorityCount, uint32_t authorityToClientCount, uint32_t authorityToServerCount, uint32_t authorityToAutonomousCount, uint32_t autonomousToAuthorityCount ); - uint32_t m_authorityToAuthorityCount = 0; uint32_t m_authorityToClientCount = 0; uint32_t m_authorityToServerCount = 0; uint32_t m_authorityToAutonomousCount = 0; @@ -63,19 +61,16 @@ namespace Multiplayer bool Serialize(AzNetworking::ISerializer& serializer); - void ConsumeAuthorityToAuthorityBits(uint32_t consumedBits); void ConsumeAuthorityToClientBits(uint32_t consumedBits); void ConsumeAuthorityToServerBits(uint32_t consumedBits); void ConsumeAuthorityToAutonomousBits(uint32_t consumedBits); void ConsumeAutonomousToAuthorityBits(uint32_t consumedBits); - bool ContainsAuthorityToAuthorityBits() const; bool ContainsAuthorityToClientBits() const; bool ContainsAuthorityToServerBits() const; bool ContainsAuthorityToAutonomousBits() const; bool ContainsAutonomousToAuthorityBits() const; - uint32_t GetRemainingAuthorityToAuthorityBits() const; uint32_t GetRemainingAuthorityToClientBits() const; uint32_t GetRemainingAuthorityToServerBits() const; uint32_t GetRemainingAuthorityToAutonomousBits() const; @@ -84,13 +79,11 @@ namespace Multiplayer ReplicationRecordStats GetStats() const; using RecordBitset = AzNetworking::FixedSizeVectorBitset; - RecordBitset m_authorityToAuthority; RecordBitset m_authorityToClient; RecordBitset m_authorityToServer; RecordBitset m_authorityToAutonomous; RecordBitset m_autonomousToAuthority; - uint32_t m_authorityToAuthorityConsumedBits = 0; uint32_t m_authorityToClientConsumedBits = 0; uint32_t m_authorityToServerConsumedBits = 0; uint32_t m_authorityToAutonomousConsumedBits = 0; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 8ae8fee618..ea55d43dcb 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -274,13 +274,6 @@ namespace {{ Component.attrib['Namespace'] }} //! Sets the bits in the attached record that correspond to predictable network properties. void SetPredictableBits(); -{% set networkPropertyCount = {'value' : 0} %} -{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Authority') %} -{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} -{% endcall %} -{% if networkPropertyCount.value > 0 %} - AzNetworking::FixedSizeBitsetView m_authorityToAuthority; -{% endif %} {% set networkPropertyCount = {'value' : 0} %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Client') %} {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} @@ -293,7 +286,7 @@ namespace {{ Component.attrib['Namespace'] }} {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} {% if networkPropertyCount.value > 0 %} - AzNetworking::FixedSizeBitsetView m_authorityToServer}; + AzNetworking::FixedSizeBitsetView m_authorityToServer; {% endif %} {% set networkPropertyCount = {'value' : 0} %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Autonomous') %} @@ -314,7 +307,6 @@ namespace {{ Component.attrib['Namespace'] }} {{ RecordName }} ( Multiplayer::ReplicationRecord& replicationRecord, - uint32_t authorityToAuthoritySimluationStartOffset, uint32_t authorityToClientSimluationStartOffset, uint32_t authorityToServerSimluationStartOffset, uint32_t authorityToAutonomousStartOffset, @@ -367,8 +359,6 @@ namespace {{ Component.attrib['Namespace'] }} void ProcessInput([[maybe_unused]] Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime) override {} //! @} - {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Authority', false)|indent(8) -}} - {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Authority', true)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', false)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', true)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', false)|indent(8) -}} @@ -457,7 +447,6 @@ namespace {{ Component.attrib['Namespace'] }} void NetworkAttach(Multiplayer::NetBindComponent* netBindComponent, Multiplayer::ReplicationRecord& currentEntityRecord, Multiplayer::ReplicationRecord& predictableEntityRecord) override; //! @} - {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Authority', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}} @@ -471,10 +460,6 @@ namespace {{ Component.attrib['Namespace'] }} {% endif %} {% endfor %} private: - //! Authority To Authority serializers (hot backup in case of server failure) - bool SerializeAuthorityToAuthorityProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer); - void NotifyChangesAuthorityToAuthorityProperties(const {{ RecordName }}& replicationRecord) const; - //! Authority to Client serializers bool SerializeAuthorityToClientProperties({{ RecordName }}& replicationRecord, AzNetworking::ISerializer& serializer); void NotifyChangesAuthorityToClientProperties(const {{ RecordName }}& replicationRecord) const; @@ -499,21 +484,18 @@ namespace {{ Component.attrib['Namespace'] }} AZStd::unique_ptr<{{ ControllerName }}> m_controller; //! Network Properties - {{ DeclareNetworkPropertyVars(Component, 'Authority', 'Authority')|indent(8) -}} {{ DeclareNetworkPropertyVars(Component, 'Authority', 'Server')|indent(8) -}} {{ DeclareNetworkPropertyVars(Component, 'Authority', 'Client')|indent(8) -}} {{ DeclareNetworkPropertyVars(Component, 'Authority', 'Autonomous')|indent(8) -}} {{ DeclareNetworkPropertyVars(Component, 'Autonomous', 'Authority')|indent(8) }} //! Network Properties for reflection and editor support - {{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Authority')|indent(8) -}} {{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Server')|indent(8) -}} {{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Client')|indent(8) -}} {{ DeclareNetworkPropertyReflectVars(Component, 'Authority', 'Autonomous')|indent(8) -}} {{ DeclareNetworkPropertyReflectVars(Component, 'Autonomous', 'Authority')|indent(8) }} //! NetworkProperty Events - {{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Authority')|indent(8) -}} {{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Server')|indent(8) -}} {{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Client')|indent(8) -}} {{ DeclareNetworkPropertyEvents(Component, 'Authority', 'Autonomous')|indent(8) -}} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 141c35e1fe..6a3fea82c0 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -980,7 +980,6 @@ namespace {{ Component.attrib['Namespace'] }} { {{ DeclareRemoteProcedureEnumerations(Component)|indent(8) }} {{ DeclareNetworkPropertyEnumerations(Component)|indent(8) }} - {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Authority')|indent(8) }} {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Client')|indent(8) }} {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Server')|indent(8) }} {{ DefineNetworkPropertyDirtyEnumeration(Component, ClassType, 'Authority', 'Autonomous')|indent(8) }} @@ -995,7 +994,6 @@ namespace {{ Component.attrib['Namespace'] }} {{ RecordName }}::{{ RecordName }} ( [[maybe_unused]] Multiplayer::ReplicationRecord& replicationRecord, - [[maybe_unused]] uint32_t authorityToAuthorityStartOffset, [[maybe_unused]] uint32_t authorityToClientStartOffset, [[maybe_unused]] uint32_t authorityToServerStartOffset, [[maybe_unused]] uint32_t authorityToAutonomousStartOffset, @@ -1003,13 +1001,6 @@ namespace {{ Component.attrib['Namespace'] }} ) {% set comma = joiner(" ,") %} {% set networkPropertyCount = {'value' : 0} %} -{% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Authority') %} -{%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} -{% endcall %} -{% if networkPropertyCount.value > 0 %} -{{ comma()|default(" :", true) }} m_authorityToAuthority(replicationRecord.m_authorityToAuthority, authorityToAuthorityStartOffset, replicationRecord.ContainsAuthorityToAuthorityBits() ? static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count) : 0) -{% endif %} -{% set networkPropertyCount = {'value' : 0} %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Client') %} {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} @@ -1021,7 +1012,7 @@ namespace {{ Component.attrib['Namespace'] }} {%- if networkPropertyCount.update({'value': networkPropertyCount.value + 1}) %}{% endif -%} {% endcall %} {% if networkPropertyCount.value > 0 %} - {{ comma()|default(" :", true) }} authorityToServer }}(replicationRecord.m_authorityToServer, authorityToServerStartOffset, replicationRecord.ContainsAuthorityToServerBits() ? static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count) : 0) + {{ comma()|default(" :", true) }} m_authorityToServer(replicationRecord.m_authorityToServer, authorityToServerStartOffset, replicationRecord.ContainsAuthorityToServerBits() ? static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count) : 0) {% endif %} {% set networkPropertyCount = {'value' : 0} %} {% call(Property) AutoComponentMacros.ParseNetworkProperties(Component, 'Authority', 'Autonomous') %} @@ -1043,9 +1034,6 @@ namespace {{ Component.attrib['Namespace'] }} AZStd::unique_ptr<{{ RecordName }}> {{ RecordName }}::AllocateRecord(Multiplayer::ReplicationRecord& replicationRecord) { - uint32_t authorityToAuthorityStart = replicationRecord.m_authorityToAuthority.GetSize(); - replicationRecord.m_authorityToAuthority.Resize(authorityToAuthorityStart + static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)); - uint32_t authorityToClientStart = replicationRecord.m_authorityToClient.GetSize(); replicationRecord.m_authorityToClient.Resize(authorityToClientStart + static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)); @@ -1059,7 +1047,6 @@ namespace {{ Component.attrib['Namespace'] }} replicationRecord.m_autonomousToAuthority.Resize(autonomousToAuthorityStart + static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Autonomous', 'Authority') }}::Count)); return AZStd::unique_ptr<{{ RecordName }}>(new {{ RecordName }}(replicationRecord, - authorityToAuthorityStart, authorityToClientStart, authorityToServerStart, authorityToAutonomousStart, @@ -1069,7 +1056,6 @@ namespace {{ Component.attrib['Namespace'] }} bool {{ RecordName }}::CanAttachRecord(Multiplayer::ReplicationRecord& replicationRecord) { bool canAttach{ true }; - canAttach &= replicationRecord.ContainsAuthorityToAuthorityBits() ? (replicationRecord.GetRemainingAuthorityToAuthorityBits() >= static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)) : true; canAttach &= replicationRecord.ContainsAuthorityToClientBits() ? (replicationRecord.GetRemainingAuthorityToClientBits() >= static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)) : true; canAttach &= replicationRecord.ContainsAuthorityToServerBits() ? (replicationRecord.GetRemainingAuthorityToServerBits() >= static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Server') }}::Count)) : true; canAttach &= replicationRecord.ContainsAuthorityToAutonomousBits() ? (replicationRecord.GetRemainingAuthorityToAutonomousBits() >= static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Autonomous') }}::Count)) : true; @@ -1079,9 +1065,6 @@ namespace {{ Component.attrib['Namespace'] }} {{ RecordName }} {{ RecordName }}::AttachRecord(Multiplayer::ReplicationRecord& replicationRecord) { - uint32_t authorityToAuthorityStart = replicationRecord.m_authorityToAuthorityConsumedBits; - replicationRecord.ConsumeAuthorityToAuthorityBits(static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Authority') }}::Count)); - uint32_t authorityToClientStart = replicationRecord.m_authorityToClientConsumedBits; replicationRecord.ConsumeAuthorityToClientBits(static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Authority', 'Client') }}::Count)); @@ -1095,7 +1078,6 @@ namespace {{ Component.attrib['Namespace'] }} replicationRecord.ConsumeAutonomousToAuthorityBits(static_cast({{ AutoComponentMacros.GetNetPropertiesDirtyEnumName(ComponentName, 'Autonomous', 'Authority') }}::Count)); return {{ RecordName }}(replicationRecord, - authorityToAuthorityStart, authorityToClientStart, authorityToServerStart, authorityToAutonomousStart, @@ -1169,9 +1151,7 @@ namespace {{ Component.attrib['Namespace'] }} return static_cast<{{ ComponentName }}&>(GetOwner()); } - {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', false, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Authority', true, ControllerBaseName)|indent(4) -}} -{{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', false, ControllerBaseName)|indent(4) -}} + {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', false, ControllerBaseName)|indent(4) -}} {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Server', true, ControllerBaseName)|indent(4) -}} {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', false, ControllerBaseName)|indent(4) -}} {{ DefineNetworkPropertyAccessors(Component, 'Authority', 'Client', true, ControllerBaseName)|indent(4) -}} @@ -1204,8 +1184,7 @@ namespace {{ Component.attrib['Namespace'] }} { serializeContext->Class<{{ ComponentBaseName }}, Multiplayer::MultiplayerComponent>() ->Version(1) - {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(16) -}} -{{ DefineNetworkPropertyReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(16) -}} + {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(16) -}} {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(16) -}} {{ DefineNetworkPropertyReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(16) -}} {{ DefineNetworkPropertyReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(16) }} @@ -1227,8 +1206,7 @@ namespace {{ Component.attrib['Namespace'] }} ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Category, "{{ Component.attrib['Namespace'] }}") ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game")) - {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Authority', ComponentBaseName)|indent(20) -}} -{{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}} + {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Server', ComponentBaseName)|indent(20) -}} {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Client', ComponentBaseName)|indent(20) -}} {{ DefineNetworkPropertyEditReflection(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(20) -}} {{ DefineNetworkPropertyEditReflection(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(20) }} @@ -1254,7 +1232,6 @@ namespace {{ Component.attrib['Namespace'] }} ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") // Reflect Network Properties Get, Set, and OnChanged methods - {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Authority', ComponentName) | indent(16) -}} {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Server', ComponentName) | indent(16) -}} {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Client', ComponentName) | indent(16) -}} {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Autonomous', ComponentName) | indent(16) -}} @@ -1348,12 +1325,10 @@ namespace {{ Component.attrib['Namespace'] }} {% endcall %} } - {{ DefineNetworkPropertyGets(Component, 'Authority', 'Authority', false, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}} + {{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', false, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', false, ComponentBaseName)|indent(4) -}} -{{ DefineNetworkPropertyGets(Component, 'Authority', 'Authority', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Server', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) -}} @@ -1411,10 +1386,6 @@ namespace {{ Component.attrib['Namespace'] }} {{ RecordName }} record = {{ RecordName }}::AttachRecord(replicationRecord); - if (replicationRecord.ContainsAuthorityToAuthorityBits()) - { - SerializeAuthorityToAuthorityProperties(record, serializer); - } if (replicationRecord.ContainsAuthorityToClientBits()) { SerializeAuthorityToClientProperties(record, serializer); @@ -1490,8 +1461,7 @@ namespace {{ Component.attrib['Namespace'] }} void {{ ComponentBaseName }}::NetworkAttach(Multiplayer::NetBindComponent* netBindComponent, Multiplayer::ReplicationRecord& currentEntityRecord, Multiplayer::ReplicationRecord& predictableEntityRecord) { m_netBindComponent = netBindComponent; - {{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Authority', ComponentBaseName)|indent(8) -}} -{{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Server', ComponentBaseName)|indent(8) -}} + {{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Server', ComponentBaseName)|indent(8) -}} {{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Client', ComponentBaseName)|indent(8) -}} {{ DefineNetworkPropertyEditConstruction(Component, 'Authority', 'Autonomous', ComponentBaseName)|indent(8) -}} {{ DefineNetworkPropertyEditConstruction(Component, 'Autonomous', 'Authority', ComponentBaseName)|indent(8) }} @@ -1504,8 +1474,6 @@ namespace {{ Component.attrib['Namespace'] }} m_controller.get()->NetworkAttach(netBindComponent, predictableEntityRecord); } - {{ DeclareNetworkPropertySetSerializer(Component, 'Authority', 'Authority', ComponentBaseName, RecordName)|indent(4) }} - {{ DeclareNetworkPropertySetNotifyChanges(Component, 'Authority', 'Authority', ComponentBaseName, RecordName)|indent(4) }} {{ DeclareNetworkPropertySetSerializer(Component, 'Authority', 'Server', ComponentBaseName, RecordName)|indent(4) }} {{ DeclareNetworkPropertySetNotifyChanges(Component, 'Authority', 'Server', ComponentBaseName, RecordName)|indent(4) }} {{ DeclareNetworkPropertySetSerializer(Component, 'Authority', 'Client', ComponentBaseName, RecordName)|indent(4) }} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index 94bdac2b5d..57e8a67fb3 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -17,7 +17,7 @@ - + diff --git a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml index 2f934979b1..642832805d 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/Multiplayer.AutoPackets.xml @@ -28,12 +28,6 @@ - - - - - - @@ -49,14 +43,4 @@ - - - - - - - - - - diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index e48d0b0d09..adc369e9ed 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -154,12 +154,18 @@ namespace Multiplayer void NetBindComponent::SetOwningConnectionId(AzNetworking::ConnectionId connectionId) { + m_owningConnectionId = connectionId; for (MultiplayerComponent* multiplayerComponent : m_multiplayerInputComponentVector) { multiplayerComponent->SetOwningConnectionId(connectionId); } } + AzNetworking::ConnectionId NetBindComponent::GetOwningConnectionId() const + { + return m_owningConnectionId; + } + void NetBindComponent::SetAllowAutonomy(bool value) { // This flag allows a player host to autonomously control their player entity, even though the entity is in an authority role @@ -290,6 +296,11 @@ namespace Multiplayer m_localNotificationRecord.Clear(); } + void NetBindComponent::NotifySyncRewindState() + { + m_syncRewindEvent.Signal(); + } + void NetBindComponent::NotifyMigrationStart(ClientInputId migratedInputId) { m_entityMigrationStartEvent.Signal(migratedInputId); @@ -315,6 +326,11 @@ namespace Multiplayer eventHandler.Connect(m_dirtiedEvent); } + void NetBindComponent::AddEntitySyncRewindEventHandler(EntitySyncRewindEvent::Handler& eventHandler) + { + eventHandler.Connect(m_syncRewindEvent); + } + void NetBindComponent::AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler) { eventHandler.Connect(m_entityMigrationStartEvent); diff --git a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp index 4ae7c3fdfe..88029430c3 100644 --- a/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Debug/MultiplayerDebugSystemComponent.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include namespace Multiplayer @@ -233,6 +235,43 @@ namespace Multiplayer const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x; const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing(); + if (m_displayNetworkingStats) + { + if (ImGui::Begin("Networking Stats", &m_displayNetworkingStats, ImGuiWindowFlags_None)) + { + AzNetworking::INetworking* networking = AZ::Interface::Get(); + + ImGui::Text("Total sockets monitored by TcpListenThread: %u", networking->GetTcpListenThreadSocketCount()); + ImGui::Text("Total time spent updating TcpListenThread: %lld", aznumeric_cast(networking->GetTcpListenThreadUpdateTime())); + ImGui::Text("Total sockets monitored by UdpReaderThread: %u", networking->GetUdpReaderThreadSocketCount()); + ImGui::Text("Total time spent updating UdpReaderThread: %lld", aznumeric_cast(networking->GetUdpReaderThreadUpdateTime())); + + for (auto& networkInterface : networking->GetNetworkInterfaces()) + { + const char* protocol = networkInterface.second->GetType() == AzNetworking::ProtocolType::Tcp ? "Tcp" : "Udp"; + const char* trustZone = networkInterface.second->GetTrustZone() == AzNetworking::TrustZone::ExternalClientToServer ? "ExternalClientToServer" : "InternalServerToServer"; + const uint32_t port = aznumeric_cast(networkInterface.second->GetPort()); + ImGui::Text("%sNetworkInterface: %s - open to %s on port %u", protocol, networkInterface.second->GetName().GetCStr(), trustZone, port); + + const AzNetworking::NetworkInterfaceMetrics& metrics = networkInterface.second->GetMetrics(); + ImGui::Text(" - Total time spent updating in milliseconds: %lld", aznumeric_cast(metrics.m_updateTimeMs)); + ImGui::Text(" - Total number of connections: %llu", aznumeric_cast(metrics.m_connectionCount)); + ImGui::Text(" - Total send time in milliseconds: %lld", aznumeric_cast(metrics.m_sendTimeMs)); + ImGui::Text(" - Total sent packets: %llu", aznumeric_cast(metrics.m_sendPackets)); + ImGui::Text(" - Total sent bytes after compression: %llu", aznumeric_cast(metrics.m_sendBytes)); + ImGui::Text(" - Total sent bytes before compression: %llu", aznumeric_cast(metrics.m_sendBytesUncompressed)); + ImGui::Text(" - Total sent compressed packets without benefit: %llu", aznumeric_cast(metrics.m_sendCompressedPacketsNoGain)); + ImGui::Text(" - Total gain from packet compression: %lld", aznumeric_cast(metrics.m_sendBytesCompressedDelta)); + ImGui::Text(" - Total packets resent: %llu", aznumeric_cast(metrics.m_resentPackets)); + ImGui::Text(" - Total receive time in milliseconds: %lld", aznumeric_cast(metrics.m_recvTimeMs)); + ImGui::Text(" - Total received packets: %llu", aznumeric_cast(metrics.m_recvPackets)); + ImGui::Text(" - Total received bytes after compression: %llu", aznumeric_cast(metrics.m_recvBytes)); + ImGui::Text(" - Total received bytes before compression: %llu", aznumeric_cast(metrics.m_recvBytesUncompressed)); + ImGui::Text(" - Total packets discarded due to load: %llu", aznumeric_cast(metrics.m_discardedPackets)); + } + } + } + if (m_displayMultiplayerStats) { if (ImGui::Begin("Multiplayer Stats", &m_displayMultiplayerStats, ImGuiWindowFlags_None)) diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 8eb5bf7b0c..ce776904d5 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -161,9 +161,13 @@ namespace Multiplayer // Handle deferred local rpc messages that were generated during the updates m_networkEntityManager.DispatchLocalDeferredRpcMessages(); - m_networkEntityManager.NotifyEntitiesChanged(); + + // INetworking ticks immediately before IMultiplayer, so all our pending RPC's and network property updates have now been processed + // Restore any entities that were rewound during input processing so that normal gameplay updates have the correct state + Multiplayer::GetNetworkTime()->ClearRewoundEntities(); // Let the network system know the frame is done and we can collect dirty bits + m_networkEntityManager.NotifyEntitiesChanged(); m_networkEntityManager.NotifyEntitiesDirtied(); MultiplayerStats& stats = GetStats(); @@ -302,25 +306,32 @@ namespace Multiplayer bool MultiplayerSystemComponent::HandleRequest ( - [[maybe_unused]] AzNetworking::IConnection* connection, - [[maybe_unused]] const IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::SyncConsole& packet + AzNetworking::IConnection* connection, + [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, + MultiplayerPackets::ReadyForEntityUpdates& packet ) { - ExecuteConsoleCommandList(connection, packet.GetCommandSet()); - return true; + IConnectionData* connectionData = reinterpret_cast(connection->GetUserData()); + if (connectionData) + { + connectionData->SetCanSendUpdates(packet.GetReadyForEntityUpdates()); + return true; + } + return false; } bool MultiplayerSystemComponent::HandleRequest ( [[maybe_unused]] AzNetworking::IConnection* connection, [[maybe_unused]] const IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::ConsoleCommand& packet + [[maybe_unused]] MultiplayerPackets::SyncConsole& packet ) { - const bool isAcceptor = (connection->GetConnectionRole() == ConnectionRole::Acceptor); // We're hosting if we accepted the connection - const AZ::ConsoleFunctorFlags requiredSet = isAcceptor ? AZ::ConsoleFunctorFlags::AllowClientSet : AZ::ConsoleFunctorFlags::Null; - AZ::Interface::Get()->PerformCommand(packet.GetCommand().c_str(), AZ::ConsoleSilentMode::NotSilent, AZ::ConsoleInvokedFrom::AzNetworking, requiredSet); + if (GetAgentType() != MultiplayerAgentType::Client) + { + return false; + } + ExecuteConsoleCommandList(connection, packet.GetCommandSet()); return true; } @@ -328,10 +339,12 @@ namespace Multiplayer ( [[maybe_unused]] AzNetworking::IConnection* connection, [[maybe_unused]] const IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::SyncConnectionCvars& packet + [[maybe_unused]] MultiplayerPackets::ConsoleCommand& packet ) { - connection->SetConnectionQuality(ConnectionQuality(packet.GetLossPercent(), packet.GetLatencyMs(), packet.GetVarianceMs())); + const bool isClient = (GetAgentType() == MultiplayerAgentType::Client); + const AZ::ConsoleFunctorFlags requiredSet = isClient ? AZ::ConsoleFunctorFlags::Null : AZ::ConsoleFunctorFlags::AllowClientSet; + AZ::Interface::Get()->PerformCommand(packet.GetCommand().c_str(), AZ::ConsoleSilentMode::NotSilent, AZ::ConsoleInvokedFrom::AzNetworking, requiredSet); return true; } @@ -395,39 +408,6 @@ namespace Multiplayer return false; } - bool MultiplayerSystemComponent::HandleRequest - ( - [[maybe_unused]] AzNetworking::IConnection* connection, - [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::NotifyClientMigration& packet - ) - { - return false; - } - - bool MultiplayerSystemComponent::HandleRequest - ( - [[maybe_unused]] AzNetworking::IConnection* connection, - [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, - [[maybe_unused]] MultiplayerPackets::EntityMigration& packet - ) - { - return false; - } - - bool MultiplayerSystemComponent::HandleRequest( AzNetworking::IConnection* connection, - [[maybe_unused]] const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet) - { - IConnectionData* connectionData = reinterpret_cast(connection->GetUserData()); - if (connectionData) - { - connectionData->SetCanSendUpdates(packet.GetReadyForEntityUpdates()); - return true; - } - - return false; - } - ConnectResult MultiplayerSystemComponent::ValidateConnect ( [[maybe_unused]] const IpAddress& remoteAddress, @@ -456,44 +436,36 @@ namespace Multiplayer m_connAcquiredEvent.Signal(datum); } - if (m_onConnectFunctor) + if (GetAgentType() == MultiplayerAgentType::ClientServer + || GetAgentType() == MultiplayerAgentType::DedicatedServer) { - // Default OnConnect behaviour has been overridden - m_onConnectFunctor(connection, datum); + PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str()), 1); + INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity()); + + NetworkEntityHandle controlledEntity; + if (entityList.size() > 0) + { + controlledEntity = entityList[0]; + controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); + } + + if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so + { + connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); + } + + AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); + reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); } else { - if (GetAgentType() == MultiplayerAgentType::ClientServer - || GetAgentType() == MultiplayerAgentType::DedicatedServer) + if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so { - PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast(sv_defaultPlayerSpawnAsset).c_str()), 1); - INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity()); - - NetworkEntityHandle controlledEntity; - if (entityList.size() > 0) - { - controlledEntity = entityList[0]; - controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId()); - } - - if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so - { - connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity)); - } - - AZStd::unique_ptr window = AZStd::make_unique(controlledEntity, connection); - reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetReplicationWindow(AZStd::move(window)); + connection->SetUserData(new ClientToServerConnectionData(connection, *this)); } - else - { - if (connection->GetUserData() == nullptr) // Only add user data if the connect event handler has not already done so - { - connection->SetUserData(new ClientToServerConnectionData(connection, *this)); - } - AZStd::unique_ptr window = AZStd::make_unique(); - reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs); - } + AZStd::unique_ptr window = AZStd::make_unique(); + reinterpret_cast(connection->GetUserData())->GetReplicationManager().SetEntityActivationTimeSliceMs(cl_defaultNetworkEntityActivationTimeSliceMs); } } @@ -566,11 +538,6 @@ namespace Multiplayer handler.Connect(m_shutdownEvent); } - void MultiplayerSystemComponent::SetOnConnectFunctor(const OnConnectFunctor& functor) - { - m_onConnectFunctor = functor; - } - void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates) { IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet(); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index 6bedd0599b..da6ff14a2b 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -63,15 +63,12 @@ namespace Multiplayer bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Connect& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::Accept& packet); + bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConsole& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ConsoleCommand& packet); - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::SyncConnectionCvars& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityUpdates& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityRpcs& packet); bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ClientMigration& packet); - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::NotifyClientMigration& packet); - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::EntityMigration& packet); - bool HandleRequest(AzNetworking::IConnection* connection, const AzNetworking::IPacketHeader& packetHeader, MultiplayerPackets::ReadyForEntityUpdates& packet); //! IConnectionListener interface //! @{ @@ -89,7 +86,6 @@ namespace Multiplayer void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override; void AddSessionInitHandler(SessionInitEvent::Handler& handler) override; void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override; - void SetOnConnectFunctor(const OnConnectFunctor& functor) override; void SendReadyForEntityUpdates(bool readyForEntityUpdates) override; AZ::TimeMs GetCurrentHostTimeMs() const override; INetworkTime* GetNetworkTime() override; @@ -120,8 +116,6 @@ namespace Multiplayer SessionShutdownEvent m_shutdownEvent; ConnectionAcquiredEvent m_connAcquiredEvent; - OnConnectFunctor m_onConnectFunctor = nullptr; - AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId; }; diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e47d142be8..13cfd3d6fd 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -656,7 +656,7 @@ namespace Multiplayer { case Mode::LocalServerToRemoteClient: { - // don't trust the client by default + // Don't trust the client by default result = UpdateValidationResult::DropMessageAndDisconnect; // Clients sending data must have a replicator and be sending in the correct mode, further, they must have a replicator and can never delete a replicator if (updateMessage.GetNetworkRole() == NetEntityRole::Authority && entityReplicator && !updateMessage.GetIsDelete()) @@ -671,7 +671,7 @@ namespace Multiplayer } else { - // we can process this + // We can process this result = UpdateValidationResult::HandleMessage; } } // If we've migrated the entity away from the server, but we get this late, just drop it @@ -699,7 +699,7 @@ namespace Multiplayer case Mode::LocalServerToRemoteServer: { AZ_Assert(updateMessage.GetNetworkRole() == NetEntityRole::Server || updateMessage.GetIsDelete(), "Unexpected update type coming from peer server"); - // trust messages from a peer server by default + // Trust messages from a peer server by default result = UpdateValidationResult::HandleMessage; // If we have a replicator, make sure we're in the correct state if (entityReplicator) @@ -782,7 +782,7 @@ namespace Multiplayer PrefabEntityId prefabEntityId; if (updateMessage.GetHasValidPrefabId()) { - // If the update packet contained a sliceEntryId, use that directly + // If the update packet contained a PrefabEntityId, use that directly prefabEntityId = updateMessage.GetPrefabEntityId(); } else @@ -940,7 +940,7 @@ namespace Multiplayer { const ReplicationSet& newWindow = m_replicationWindow->GetReplicationSet(); - // walk both for adds and removals + // Walk both for adds and removals auto newWindowIter = newWindow.begin(); auto currWindowIter = m_entityReplicatorMap.begin(); while (newWindowIter != newWindow.end() && currWindowIter != m_entityReplicatorMap.end()) @@ -959,9 +959,9 @@ namespace Multiplayer } ++currWindowIter; } - else // same entity + else // Same entity { - // check if we changed modes + // Check if we changed modes EntityReplicator* currReplicator = currWindowIter->second.get(); if (currReplicator->GetRemoteNetworkRole() != newWindowIter->second.m_netEntityRole) { @@ -973,14 +973,14 @@ namespace Multiplayer } } - // do remaining adds + // Do remaining adds while (newWindowIter != newWindow.end()) { AddEntityReplicator(newWindowIter->first, newWindowIter->second.m_netEntityRole); ++newWindowIter; } - // do remaining removes + // Do remaining removes while (currWindowIter != m_entityReplicatorMap.end()) { EntityReplicator* currReplicator = currWindowIter->second.get(); @@ -1028,13 +1028,13 @@ namespace Multiplayer const EntityReplicator* entityReplicator = GetEntityReplicator(entityHandle.GetNetEntityId()); hasAuthority = (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority); // Make sure someone hasn't migrated this already - isInDomain = (m_remoteEntityDomain && m_remoteEntityDomain->IsInDomain(entityHandle)); // Make sure the remote side would want it + isInDomain = (m_remoteEntityDomain && m_remoteEntityDomain->IsInDomain(entityHandle)); // Make sure the remote side would want it if (entityReplicator && entityReplicator->GetBoundLocalNetworkRole() == NetEntityRole::Authority) { - isMarkedForRemoval = entityReplicator->IsMarkedForRemoval(); // Make sure we aren't telling the other side to remove the replicator + isMarkedForRemoval = entityReplicator->IsMarkedForRemoval(); // Make sure we aren't telling the other side to remove the replicator const PropertyPublisher* propertyPublisher = entityReplicator->GetPropertyPublisher(); AZ_Assert(propertyPublisher, "Expected to have a property publisher"); - isRemoteReplicatorEstablished = propertyPublisher->IsRemoteReplicatorEstablished(); // Make sure they are setup to receive the replicator + isRemoteReplicatorEstablished = propertyPublisher->IsRemoteReplicatorEstablished(); // Make sure they are setup to receive the replicator } return hasAuthority && isInDomain && !isMarkedForRemoval && isRemoteReplicatorEstablished; @@ -1094,9 +1094,9 @@ namespace Multiplayer } bool didSucceed = true; - MultiplayerPackets::EntityMigration message; - message.SetEntityId(replicator->GetEntityHandle().GetNetEntityId()); - message.SetPrefabEntityId(netBindComponent->GetPrefabEntityId()); + EntityMigrationMessage message; + message.m_entityId = replicator->GetEntityHandle().GetNetEntityId(); + message.m_prefabEntityId = netBindComponent->GetPrefabEntityId(); if (localEnt->GetState() == AZ::Entity::State::Active) { @@ -1110,17 +1110,18 @@ namespace Multiplayer // Send an update packet if it needs one propPublisher->GenerateRecord(); bool needsNetworkPropertyUpdate = propPublisher->PrepareSerialization(); - AzNetworking::NetworkInputSerializer inputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetCapacity()); + AzNetworking::NetworkInputSerializer inputSerializer(message.m_propertyUpdateData.GetBuffer(), message.m_propertyUpdateData.GetCapacity()); if (needsNetworkPropertyUpdate) { - // write out entity state into the buffer + // Write out entity state into the buffer propPublisher->UpdateSerialization(inputSerializer); } didSucceed &= inputSerializer.IsValid(); - message.ModifyPropertyUpdateData().Resize(inputSerializer.GetSize()); + message.m_propertyUpdateData.Resize(inputSerializer.GetSize()); } AZ_Assert(didSucceed, "Failed to migrate entity from server"); - m_connection.SendReliablePacket(message); + // TODO: Move this to an event + //m_connection.SendReliablePacket(message); AZLOG(NET_RepDeletes, "Migration packet sent %u to remote manager id %d", netEntityId, aznumeric_cast(GetRemoteHostId())); // Immediately add a new replicator so that we catch RPC invocations, the remote side will make us a new one, and then remove us if needs be @@ -1128,21 +1129,21 @@ namespace Multiplayer } } - bool EntityReplicationManager::HandleMessage([[maybe_unused]] AzNetworking::IConnection* invokingConnection, MultiplayerPackets::EntityMigration& message) + bool EntityReplicationManager::HandleEntityMigration([[maybe_unused]] AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message) { - EntityReplicator* replicator = GetEntityReplicator(message.GetEntityId()); + EntityReplicator* replicator = GetEntityReplicator(message.m_entityId); { - if (message.GetPropertyUpdateData().GetSize() > 0) + if (message.m_propertyUpdateData.GetSize() > 0) { - AzNetworking::TrackChangedSerializer outputSerializer(message.ModifyPropertyUpdateData().GetBuffer(), message.ModifyPropertyUpdateData().GetSize()); + AzNetworking::TrackChangedSerializer outputSerializer(message.m_propertyUpdateData.GetBuffer(), message.m_propertyUpdateData.GetSize()); if (!HandlePropertyChangeMessage ( replicator, AzNetworking::InvalidPacketId, - message.GetEntityId(), + message.m_entityId, NetEntityRole::Server, outputSerializer, - message.GetPrefabEntityId() + message.m_prefabEntityId )) { AZ_Assert(false, "Unable to process network properties during server entity migration"); @@ -1150,10 +1151,10 @@ namespace Multiplayer } } } - // the HandlePropertyChangeMessage will have made a replicator if we didn't have one already + // The HandlePropertyChangeMessage will have made a replicator if we didn't have one already if (!replicator) { - replicator = GetEntityReplicator(message.GetEntityId()); + replicator = GetEntityReplicator(message.m_entityId); } AZ_Assert(replicator, "Do not have replicator after handling migration message"); @@ -1170,7 +1171,7 @@ namespace Multiplayer netBindComponent->ActivateControllers(EntityIsMigrating::True); } - // change the role on the replicator + // Change the role on the replicator AddEntityReplicator(entityHandle, NetEntityRole::Server); AZLOG(NET_RepDeletes, "Handle Migration %u new authority from remote manager id %d", entityHandle.GetNetEntityId(), aznumeric_cast(GetRemoteHostId())); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h index 083413e19e..6172f30e8a 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -26,7 +28,6 @@ #include #include #include -#include namespace AzNetworking { @@ -82,7 +83,7 @@ namespace Multiplayer void AddAutonomousEntityReplicatorCreatedHandle(AZ::Event::Handler& handler); - bool HandleMessage(AzNetworking::IConnection* invokingConnection, MultiplayerPackets::EntityMigration& message); + bool HandleEntityMigration(AzNetworking::IConnection* invokingConnection, EntityMigrationMessage& message); bool HandleEntityDeleteMessage(EntityReplicator* entityReplicator, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage); bool HandleEntityUpdateMessage(AzNetworking::IConnection* invokingConnection, const AzNetworking::IPacketHeader& packetHeader, const NetworkEntityUpdateMessage& updateMessage); bool HandleEntityRpcMessage(AzNetworking::IConnection* invokingConnection, NetworkEntityRpcMessage& message); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp index 41cc86aaee..7fe0efd323 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp @@ -16,14 +16,12 @@ namespace Multiplayer { ReplicationRecordStats::ReplicationRecordStats ( - uint32_t authorityToAuthorityCount, uint32_t authorityToClientCount, uint32_t authorityToServerCount, uint32_t authorityToAutonomousCount, uint32_t autonomousToAuthorityCount ) - : m_authorityToAuthorityCount(authorityToAuthorityCount) - , m_authorityToClientCount(authorityToClientCount) + : m_authorityToClientCount(authorityToClientCount) , m_authorityToServerCount(authorityToServerCount) , m_authorityToAutonomousCount(authorityToAutonomousCount) , m_autonomousToAuthorityCount(autonomousToAuthorityCount) @@ -33,8 +31,7 @@ namespace Multiplayer bool ReplicationRecordStats::operator ==(const ReplicationRecordStats& rhs) const { - return (m_authorityToAuthorityCount == rhs.m_authorityToAuthorityCount) - && (m_authorityToClientCount == rhs.m_authorityToClientCount) + return (m_authorityToClientCount == rhs.m_authorityToClientCount) && (m_authorityToServerCount == rhs.m_authorityToServerCount) && (m_authorityToAutonomousCount == rhs.m_authorityToAutonomousCount) && (m_autonomousToAuthorityCount == rhs.m_autonomousToAuthorityCount); @@ -44,7 +41,6 @@ namespace Multiplayer { return ReplicationRecordStats { - (m_authorityToAuthorityCount - rhs.m_authorityToAuthorityCount), (m_authorityToClientCount - rhs.m_authorityToClientCount), (m_authorityToServerCount - rhs.m_authorityToServerCount), (m_authorityToAutonomousCount - rhs.m_authorityToAutonomousCount), @@ -71,7 +67,6 @@ namespace Multiplayer bool ReplicationRecord::AreAllBitsConsumed() const { bool ret = true; - ret &= m_authorityToAuthorityConsumedBits == m_authorityToAuthority.GetSize(); ret &= m_authorityToClientConsumedBits == m_authorityToClient.GetSize(); ret &= m_authorityToServerConsumedBits == m_authorityToServer.GetSize(); ret &= m_authorityToAutonomousConsumedBits == m_authorityToAutonomous.GetSize(); @@ -81,7 +76,6 @@ namespace Multiplayer void ReplicationRecord::ResetConsumedBits() { - m_authorityToAuthorityConsumedBits = 0; m_authorityToClientConsumedBits = 0; m_authorityToServerConsumedBits = 0; m_authorityToAutonomousConsumedBits = 0; @@ -92,11 +86,7 @@ namespace Multiplayer { ResetConsumedBits(); - uint32_t recordSize = m_authorityToAuthority.GetSize(); - m_authorityToAuthority.Clear(); - m_authorityToAuthority.Resize(recordSize); - - recordSize = m_authorityToClient.GetSize(); + uint32_t recordSize = m_authorityToClient.GetSize(); m_authorityToClient.Clear(); m_authorityToClient.Resize(recordSize); @@ -115,7 +105,6 @@ namespace Multiplayer void ReplicationRecord::Append(const ReplicationRecord &rhs) { - m_authorityToAuthority |= rhs.m_authorityToAuthority; m_authorityToClient |= rhs.m_authorityToClient; m_authorityToServer |= rhs.m_authorityToServer; m_authorityToAutonomous |= rhs.m_authorityToAutonomous; @@ -124,7 +113,6 @@ namespace Multiplayer void ReplicationRecord::Subtract(const ReplicationRecord &rhs) { - m_authorityToAuthority.Subtract(rhs.m_authorityToAuthority); m_authorityToClient.Subtract(rhs.m_authorityToClient); m_authorityToServer.Subtract(rhs.m_authorityToServer); m_authorityToAutonomous.Subtract(rhs.m_authorityToAutonomous); @@ -134,10 +122,6 @@ namespace Multiplayer bool ReplicationRecord::HasChanges() const { bool hasChanges(false); - if (ContainsAuthorityToAuthorityBits()) - { - hasChanges = hasChanges ? hasChanges : m_authorityToAuthority.AnySet(); - } if (ContainsAuthorityToClientBits()) { hasChanges = hasChanges ? hasChanges : m_authorityToClient.AnySet(); @@ -159,10 +143,6 @@ namespace Multiplayer bool ReplicationRecord::Serialize(AzNetworking::ISerializer& serializer) { - if (ContainsAuthorityToAuthorityBits()) - { - serializer.Serialize(m_authorityToAuthority, "AuthorityToAuthorityRecord"); - } if (ContainsAuthorityToClientBits()) { serializer.Serialize(m_authorityToClient, "AuthorityToClientRecord"); @@ -182,14 +162,6 @@ namespace Multiplayer return serializer.IsValid(); } - void ReplicationRecord::ConsumeAuthorityToAuthorityBits(uint32_t consumedBits) - { - if (ContainsAuthorityToAuthorityBits()) - { - m_authorityToAuthorityConsumedBits += consumedBits; - } - } - void ReplicationRecord::ConsumeAuthorityToClientBits(uint32_t consumedBits) { if (ContainsAuthorityToClientBits()) @@ -222,12 +194,6 @@ namespace Multiplayer } } - bool ReplicationRecord::ContainsAuthorityToAuthorityBits() const - { - return (m_netEntityRole == NetEntityRole::Authority) - || (m_netEntityRole == NetEntityRole::InvalidRole); - } - bool ReplicationRecord::ContainsAuthorityToClientBits() const { return (m_netEntityRole != NetEntityRole::Authority) @@ -252,11 +218,6 @@ namespace Multiplayer || (m_netEntityRole == NetEntityRole::InvalidRole); } - uint32_t ReplicationRecord::GetRemainingAuthorityToAuthorityBits() const - { - return m_authorityToAuthorityConsumedBits < m_authorityToAuthority.GetValidBitCount() ? m_authorityToAuthority.GetValidBitCount() - m_authorityToAuthorityConsumedBits : 0; - } - uint32_t ReplicationRecord::GetRemainingAuthorityToClientBits() const { return m_authorityToClientConsumedBits < m_authorityToClient.GetValidBitCount() ? m_authorityToClient.GetValidBitCount() - m_authorityToClientConsumedBits : 0; @@ -281,7 +242,6 @@ namespace Multiplayer { return ReplicationRecordStats { - m_authorityToAuthorityConsumedBits, m_authorityToClientConsumedBits, m_authorityToServerConsumedBits, m_authorityToAutonomousConsumedBits, diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp index 98ece0a8cc..f94a8c59d0 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp @@ -13,10 +13,15 @@ #include #include #include +#include +#include #include +#include namespace Multiplayer { + AZ_CVAR(float, sv_RewindVolumeExtrudeDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The amount to increase rewind volume checks to account for fast moving entities"); + NetworkTime::NetworkTime() { AZ::Interface::Register(this); @@ -73,35 +78,59 @@ namespace Multiplayer void NetworkTime::SyncEntitiesToRewindState(const AZ::Aabb& rewindVolume) { - // TODO: extrude rewind volume for initial gather - AZStd::vector gatheredEntries; - AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(rewindVolume, [&gatheredEntries](const AzFramework::IVisibilityScene::NodeData& nodeData) + // Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities + const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance)); + + AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface::Get(); + AZStd::vector gatheredEntities; + AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume, + [entityBoundsUnion, rewindVolume, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData) { - gatheredEntries.reserve(gatheredEntries.size() + nodeData.m_entries.size()); + gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size()); for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries) { if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity) { - // TODO: offset aabb for exact rewound position and check against the non-extruded rewind volume - gatheredEntries.push_back(visEntry); + AZ::Entity* entity = static_cast(visEntry->m_userData); + const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityLocalBoundsUnion(entity->GetId()); + const AZ::Vector3 currentCenter = currentBounds.GetCenter(); + + NetworkTransformComponent* networkTransform = entity->template FindComponent(); + + if (networkTransform != nullptr) + { + const AZ::Vector3 rewindCenter = networkTransform->GetTranslation(); // Get the rewound position + const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions + const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb + + if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume + { + // Due to component constraints, netBindComponent must exist if networkTransform exists + NetBindComponent* netBindComponent = entity->template FindComponent(); + gatheredEntities.push_back(netBindComponent); + } + } } } }); - for (AzFramework::VisibilityEntry* visEntry : gatheredEntries) + NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker(); + for (NetBindComponent* netBindComponent : gatheredEntities) { - AZ::Entity* entity = static_cast(visEntry->m_userData); - [[maybe_unused]] NetBindComponent* entryNetBindComponent = entity->template FindComponent(); - if (entryNetBindComponent != nullptr) - { - // TODO: invoke the sync to rewind event on the netBindComponent and add the entity to the rewound entity set - } + netBindComponent->NotifySyncRewindState(); + m_rewoundEntities.push_back(NetworkEntityHandle(netBindComponent, networkEntityTracker)); } } void NetworkTime::ClearRewoundEntities() { AZ_Assert(!IsTimeRewound(), "Cannot clear rewound entity state while still within scoped rewind"); - // TODO: iterate all rewound entities, signal them to sync rewind state, and clear the rewound entity set + + for (NetworkEntityHandle entityHandle : m_rewoundEntities) + { + NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); + netBindComponent->NotifySyncRewindState(); + } + m_rewoundEntities.clear(); } } diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h index f714e046b3..c36b04be27 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include #include @@ -42,6 +43,8 @@ namespace Multiplayer private: + AZStd::vector m_rewoundEntities; + HostFrameId m_hostFrameId = HostFrameId{ 0 }; HostFrameId m_unalteredFrameId = HostFrameId{ 0 }; AZ::TimeMs m_hostTimeMs = AZ::TimeMs{ 0 }; From 9788caa6cb2b81f92c97f34d1683f5cfa2a52f54 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 19 May 2021 13:48:02 -0700 Subject: [PATCH 13/38] [cpack_installer] move non-cmake packaging resources into their own folder --- .../{PackagingBootstrapper.wxs => Packaging/Bootstrapper.wxs} | 0 .../{PackagingTemplate.wxs.in => Packaging/Template.wxs.in} | 0 cmake/Platform/Windows/PackagingPostBuild.cmake | 2 +- cmake/Platform/Windows/Packaging_windows.cmake | 2 +- cmake/Platform/Windows/platform_windows_files.cmake | 4 ++-- 5 files changed, 4 insertions(+), 4 deletions(-) rename cmake/Platform/Windows/{PackagingBootstrapper.wxs => Packaging/Bootstrapper.wxs} (100%) rename cmake/Platform/Windows/{PackagingTemplate.wxs.in => Packaging/Template.wxs.in} (100%) diff --git a/cmake/Platform/Windows/PackagingBootstrapper.wxs b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs similarity index 100% rename from cmake/Platform/Windows/PackagingBootstrapper.wxs rename to cmake/Platform/Windows/Packaging/Bootstrapper.wxs diff --git a/cmake/Platform/Windows/PackagingTemplate.wxs.in b/cmake/Platform/Windows/Packaging/Template.wxs.in similarity index 100% rename from cmake/Platform/Windows/PackagingTemplate.wxs.in rename to cmake/Platform/Windows/Packaging/Template.wxs.in diff --git a/cmake/Platform/Windows/PackagingPostBuild.cmake b/cmake/Platform/Windows/PackagingPostBuild.cmake index 3ba6ef2096..dbc54528b6 100644 --- a/cmake/Platform/Windows/PackagingPostBuild.cmake +++ b/cmake/Platform/Windows/PackagingPostBuild.cmake @@ -43,7 +43,7 @@ set(_candle_command "-I${_cpack_wix_out_dir}" # to include cpack_variables.wxi ${_addtional_defines} ${_ext_flags} - "${CPACK_SOURCE_DIR}/Platform/Windows/PackagingBootstrapper.wxs" + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Bootstrapper.wxs" -o "${_bootstrap_out_dir}/" ) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 2ea35c1d9b..3b99992ad3 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -78,7 +78,7 @@ endif() set(CPACK_WIX_PRODUCT_GUID ${LY_WIX_PRODUCT_GUID}) set(CPACK_WIX_UPGRADE_GUID ${LY_WIX_UPGRADE_GUID}) -set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/PackagingTemplate.wxs.in") +set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") set(_embed_artifacts "yes") diff --git a/cmake/Platform/Windows/platform_windows_files.cmake b/cmake/Platform/Windows/platform_windows_files.cmake index 84d1a3098c..b760a8760d 100644 --- a/cmake/Platform/Windows/platform_windows_files.cmake +++ b/cmake/Platform/Windows/platform_windows_files.cmake @@ -24,7 +24,7 @@ set(FILES PALDetection_windows.cmake Install_windows.cmake Packaging_windows.cmake - PackagingBootstrapper.wxs PackagingPostBuild.cmake - PackagingTemplate.wxs.in + Packaging/Bootstrapper.wxs + Packaging/Template.wxs.in ) From 3d4d63ab1d8b974a4e17ed0f4601111984358c85 Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 19 May 2021 14:08:38 -0700 Subject: [PATCH 14/38] [cpack_installer] installer product icons --- cmake/Platform/Windows/Packaging/Bootstrapper.wxs | 3 +++ cmake/Platform/Windows/Packaging/product_icon.ico | 3 +++ cmake/Platform/Windows/Packaging/product_logo.png | 3 +++ cmake/Platform/Windows/PackagingPostBuild.cmake | 1 + cmake/Platform/Windows/Packaging_windows.cmake | 3 +++ 5 files changed, 13 insertions(+) create mode 100644 cmake/Platform/Windows/Packaging/product_icon.ico create mode 100644 cmake/Platform/Windows/Packaging/product_logo.png diff --git a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs index c3d1dd7a7b..55e8a8cd95 100644 --- a/cmake/Platform/Windows/Packaging/Bootstrapper.wxs +++ b/cmake/Platform/Windows/Packaging/Bootstrapper.wxs @@ -9,6 +9,7 @@ Version="$(var.CPACK_PACKAGE_VERSION)" Manufacturer="$(var.CPACK_PACKAGE_VENDOR)" UpgradeCode="$(var.CPACK_BOOTSTRAP_UPGRADE_GUID)" + IconSourceFile="$(var.CPACK_WIX_PRODUCT_ICON)" DisableModify="yes"> diff --git a/cmake/Platform/Windows/Packaging/product_icon.ico b/cmake/Platform/Windows/Packaging/product_icon.ico new file mode 100644 index 0000000000..0680ceea19 --- /dev/null +++ b/cmake/Platform/Windows/Packaging/product_icon.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c042fce57915fc749abc7b37de765fd697c3c4d7de045a3d44805aa0ce29901a +size 107016 diff --git a/cmake/Platform/Windows/Packaging/product_logo.png b/cmake/Platform/Windows/Packaging/product_logo.png new file mode 100644 index 0000000000..d5fd60ffb8 --- /dev/null +++ b/cmake/Platform/Windows/Packaging/product_logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac0348c906c91de864cba91c0231b4794d8a00fafa630d13f2232351b90aa59b +size 11074 diff --git a/cmake/Platform/Windows/PackagingPostBuild.cmake b/cmake/Platform/Windows/PackagingPostBuild.cmake index dbc54528b6..d379358bf4 100644 --- a/cmake/Platform/Windows/PackagingPostBuild.cmake +++ b/cmake/Platform/Windows/PackagingPostBuild.cmake @@ -30,6 +30,7 @@ set(_addtional_defines -dCPACK_LOCAL_INSTALLER_DIR=${_cpack_wix_out_dir} -dCPACK_PACKAGE_FILE_NAME=${CPACK_PACKAGE_FILE_NAME} -dCPACK_PACKAGE_INSTALL_DIRECTORY=${_fixed_package_install_dir} + -dCPACK_WIX_PRODUCT_LOGO=${CPACK_WIX_PRODUCT_LOGO} ) if(CPACK_LICENSE_URL) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 3b99992ad3..8504447d4f 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -78,6 +78,9 @@ endif() set(CPACK_WIX_PRODUCT_GUID ${LY_WIX_PRODUCT_GUID}) set(CPACK_WIX_UPGRADE_GUID ${LY_WIX_UPGRADE_GUID}) +set(CPACK_WIX_PRODUCT_LOGO ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/product_logo.png) +set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/product_icon.ico) + set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") set(_embed_artifacts "yes") From f61b9c4081ff1b2c40f500c796cd2da3207f1d64 Mon Sep 17 00:00:00 2001 From: Gene Walters Date: Wed, 19 May 2021 16:03:13 -0700 Subject: [PATCH 15/38] Removing spaces in behavior context method names. While whitespace works in Lua and ScriptCanvas, the Scripting team wants to keep the script API and code API consistent (ie: no spaces) --- .../Code/Source/Components/MultiplayerComponent.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp index b0fd671686..21ae9d9f01 100644 --- a/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/MultiplayerComponent.cpp @@ -32,7 +32,7 @@ namespace Multiplayer ->Attribute(AZ::Script::Attributes::Module, "multiplayer") ->Attribute(AZ::Script::Attributes::Category, "Multiplayer") - ->Method("Is Authority", [](AZ::EntityId id) -> bool { + ->Method("IsAuthority", [](AZ::EntityId id) -> bool { AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); if (!entity) { @@ -48,7 +48,7 @@ namespace Multiplayer } return multiplayerComponent->IsAuthority(); }) - ->Method("Is Autonomous", [](AZ::EntityId id) -> bool { + ->Method("IsAutonomous", [](AZ::EntityId id) -> bool { AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); if (!entity) { @@ -64,7 +64,7 @@ namespace Multiplayer } return multiplayerComponent->IsAutonomous(); }) - ->Method("Is Client", [](AZ::EntityId id) -> bool { + ->Method("IsClient", [](AZ::EntityId id) -> bool { AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); if (!entity) { @@ -80,7 +80,7 @@ namespace Multiplayer } return multiplayerComponent->IsClient(); }) - ->Method("Is Server", [](AZ::EntityId id) -> bool { + ->Method("IsServer", [](AZ::EntityId id) -> bool { AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); if (!entity) { From e265990a0c40a119b7320cb8e1f4bc2161dd9182 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 19 May 2021 16:18:11 -0700 Subject: [PATCH 16/38] Set DisableOptimizations to false. --- .../Assets/Materials/Types/EnhancedPBR_ForwardPass.shader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader index 764b67c82f..3513ce8dd1 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader @@ -31,7 +31,7 @@ }, "CompilerHints" : { - "DisableOptimizations" : true + "DisableOptimizations" : false, }, "ProgramSettings": From 94a8c8258b7a679ce7725e18e609a6566b6fe5cc Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 19 May 2021 19:22:35 -0700 Subject: [PATCH 17/38] Bumped shader builders --- .../Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp | 4 ++-- .../Assets/Materials/Types/EnhancedPBR_ForwardPass.shader | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp index a0f9f7db22..d7e6e6013e 100644 --- a/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp +++ b/Gems/Atom/Asset/Shader/Code/Source/Editor/AzslShaderBuilderSystemComponent.cpp @@ -118,7 +118,7 @@ namespace AZ // Register Shader Asset Builder AssetBuilderSDK::AssetBuilderDesc shaderAssetBuilderDescriptor; shaderAssetBuilderDescriptor.m_name = "Shader Asset Builder"; - shaderAssetBuilderDescriptor.m_version = 98; // Enable Null Rhi for AutomatedTesting + shaderAssetBuilderDescriptor.m_version = 99; // ATOM-14298 // .shader file changes trigger rebuilds shaderAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern( AZStd::string::format("*.%s", RPI::ShaderSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); shaderAssetBuilderDescriptor.m_busId = azrtti_typeid(); @@ -133,7 +133,7 @@ namespace AZ shaderVariantAssetBuilderDescriptor.m_name = "Shader Variant Asset Builder"; // Both "Shader Variant Asset Builder" and "Shader Asset Builder" produce ShaderVariantAsset products. If you update // ShaderVariantAsset you will need to update BOTH version numbers, not just "Shader Variant Asset Builder". - shaderVariantAssetBuilderDescriptor.m_version = 19; // Enable Null Rhi for AutomatedTesting + shaderVariantAssetBuilderDescriptor.m_version = 20; // ATOM-14298 shaderVariantAssetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", RPI::ShaderVariantListSourceData::Extension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); shaderVariantAssetBuilderDescriptor.m_busId = azrtti_typeid(); shaderVariantAssetBuilderDescriptor.m_createJobFunction = AZStd::bind(&ShaderVariantAssetBuilder::CreateJobs, &m_shaderVariantAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader index 3513ce8dd1..a0e9708468 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.shader @@ -31,7 +31,7 @@ }, "CompilerHints" : { - "DisableOptimizations" : false, + "DisableOptimizations" : false }, "ProgramSettings": From 2452149e7d6c790f69a463cc8008efa13dfe4fb0 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Wed, 19 May 2021 19:34:48 -0700 Subject: [PATCH 18/38] Add RPC Events plus reflection plus fix Ctrl+G retry --- .../AzNetworking/DataStructures/ByteBuffer.h | 1 + .../Source/AutoGen/AutoComponent_Common.jinja | 75 +++++++++++++++++++ .../Source/AutoGen/AutoComponent_Header.jinja | 17 +++++ .../Source/AutoGen/AutoComponent_Source.jinja | 64 +++++++++++++++- .../MultiplayerEditorSystemComponent.cpp | 4 + .../Source/NetworkInput/NetworkInputArray.h | 2 + .../NetworkInputMigrationVector.h | 1 + 7 files changed, 163 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h index 3d9259a256..892c63079d 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h @@ -23,6 +23,7 @@ namespace AzNetworking class ByteBuffer { public: + AZ_RTTI(ByteBuffer, "{CD6BFA48-290D-44B4-B376-2463F526BF1F}"); ByteBuffer() = default; ~ByteBuffer() = default; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja index 4279c26cf2..15223fba26 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja @@ -188,6 +188,63 @@ virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnect {% endmacro %} {# +#} +{% macro DeclareRpcEventGetter(Property, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{% set PropertyName = UpperFirst(Property.attrib['Name']) %} +{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +AZ::Event<{{ ', '.join(paramTypes) }}>& Get{{ PropertyName }}Event() { return m_{{ PropertyName }}Event; } +{% endmacro %} +{# + +#} +{% macro DeclareRpcEventGetters(Component, InvokeFrom, HandleOn) %} +{% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{{- DeclareRpcEventGetter(Property, HandleOn) -}} +{% endcall %} +{% endmacro %} +{# + +#} +{% macro DeclareRpcEvent(Property, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{% set PropertyName = UpperFirst(Property.attrib['Name']) %} +{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +AZ::Event<{{ ', '.join(paramTypes) }}> m_{{ PropertyName }}Event; +{% endmacro %} +{# + +#} +{% macro DeclareRpcEvents(Component, InvokeFrom, HandleOn) %} +{% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{{- DeclareRpcEvent(Property, HandleOn) -}} +{% endcall %} +{% endmacro %} +{# + +#} +{% macro DeclareRpcSignal(Property, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{% set PropertyName = UpperFirst(Property.attrib['Name']) %} +{{ ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +void Signal{{ PropertyName }}({{ ', '.join(paramDefines) }}); +{% endmacro %} +{# + +#} +{% macro DeclareRpcSignals(Component, InvokeFrom, HandleOn) %} +{% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{{- DeclareRpcSignal(Property, HandleOn) -}} +{% endcall %} +{% endmacro %} +{# + #} {%- macro EmitDerivedClassesComment(dataFileNames, Component, ComponentName, ComponentNameBase, ComponentDerived, ControllerName, ControllerNameBase, ControllerDerived, NetworkInputCount) -%} {% if ComponentDerived or ControllerDerived %} @@ -214,6 +271,10 @@ namespace {{ Component.attrib['Namespace'] }} void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; {{ DeclareRpcHandlers(Component, 'Authority', 'Client', true)|indent(8) }} + {{ DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) }} + protected: + {{ DeclareRpcEvents(Component, 'Authority', 'Client')|indent(8) }} }; {% endif %} @@ -236,6 +297,20 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareRpcHandlers(Component, 'Client', 'Authority', true)|indent(8) }} {{ DeclareRpcHandlers(Component, 'Autonomous', 'Authority', true)|indent(8) }} {{ DeclareRpcHandlers(Component, 'Authority', 'Autonomous', true)|indent(8) }} + {{ DeclareRpcSignals(Component, 'Server', 'Authority')|indent(8) }} + {{ DeclareRpcSignals(Component, 'Client', 'Authority')|indent(8) }} + {{ DeclareRpcSignals(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcSignals(Component, 'Authority', 'Autonomous')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Server', 'Authority')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Client', 'Authority')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcEventGetters(Component, 'Authority', 'Autonomous')|indent(8) }} + + protected: + {{ DeclareRpcEvents(Component, 'Server', 'Authority')|indent(8) }} + {{ DeclareRpcEvents(Component, 'Client', 'Authority')|indent(8) }} + {{ DeclareRpcEvents(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcEvents(Component, 'Authority', 'Autonomous')|indent(8) }} }; {% endif %} } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index 8ae8fee618..edc67a5da4 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -390,11 +390,25 @@ namespace {{ Component.attrib['Namespace'] }} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Client', 'Authority', false)|indent(8) }} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Autonomous', 'Authority', false)|indent(8) }} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Autonomous', false)|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Server', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Client', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Autonomous')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Server', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Client', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Autonomous')|indent(8) }} {% for Service in Component.iter('ComponentRelation') %} {% if (Service.attrib['HasController']|booleanTrue) and (Service.attrib['Constraint'] != 'Incompatible') %} {{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}Controller* Get{{ Service.attrib['Name'] }}Controller(); {% endif %} {% endfor %} + + protected: + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Server', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Client', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Autonomous')|indent(8) }} }; static const AZ::Uuid s_{{ LowerFirst(ComponentName) }}ConcreteUuid = "{{ (ComponentName) | createHashGuid }}"; @@ -438,6 +452,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) }} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) }} //! MultiplayerComponent interface //! @{ @@ -464,6 +479,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) }} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Client')|indent(8) }} {% for Service in Component.iter('ComponentRelation') %} {% if Service.attrib['Constraint'] != 'Incompatible' %} const {{ Service.attrib['Namespace'] }}::{{ Service.attrib['Name'] }}* Get{{ Service.attrib['Name'] }}() const; diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 5e60510a5d..a9d2ecf3de 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -315,11 +315,25 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(par {% endmacro %} {# +#} +{% macro DefineRpcSignal(Component, ClassName, Property, InvokeFrom) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} +void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }}) +{ + m_{{ UpperFirst(Property.attrib['Name']) }}Event.Signal({{ ', '.join(paramNames) }}); +} +{% endmacro %} +{# + #} {% macro DefineRpcInvocations(Component, ClassName, InvokeFrom, HandleOn, ProctectedSection) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} {% if Property.attrib['IsPublic']|booleanTrue == ProctectedSection %} {{ DefineRpcInvocation(Component, ClassName, Property, InvokeFrom, HandleOn) }} +{{ DefineRpcSignal(Component, ClassName, Property, InvokeFrom) }} {% endif %} {% endcall %} {% endmacro %} @@ -341,6 +355,42 @@ void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(par {% endmacro %} {# +#} +{% macro ReflectRpcEventDescs(Component, ClassName, InvokeFrom, HandleOn) %} +{% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} + + // Create the BehaviorAZEventDescription needed to reflect the + // Get{{ UpperFirst(Property.attrib['Name']) }}Event method to the BehaviorContext without errors + AZ::BehaviorAzEventDescription {{ LowerFirst(Property.attrib['Name']) }}EventDesc; + {{ LowerFirst(Property.attrib['Name']) }}EventDesc.m_eventName = "{{ UpperFirst(Property.attrib['Name']) }} Notify Event"; + {% for Param in Property.iter('Param') %} + {{ LowerFirst(Property.attrib['Name']) }}EventDesc.m_parameterNames.push_back("{{ LowerFirst(Param.attrib['Name']) }}"); + {% endfor %} + +{% endcall %} +{% endmacro %} +{# + +#} +{% macro ReflectRpcEvents(Component, ClassName, InvokeFrom, HandleOn) %} +{% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% set paramNames = [] %} +{% set paramTypes = [] %} +{% set paramDefines = [] %} +{{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} + ->Method("Get{{ UpperFirst(Property.attrib['Name']) }}Event", [](const {{ ClassName }}* self) -> AZ::Event<{{ ', '.join(paramTypes) }}>& + { + return self->m_controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); + }) + ->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move({{ LowerFirst(Property.attrib['Name']) }}EventDesc)) +{% endcall %} +{% endmacro %} +{# + #} {% macro DeclareRpcHandleCases(Component, ComponentDerived, InvokeFrom, HandleOn, ValidationFunction) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} @@ -363,6 +413,7 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Authority, "Entity proxy does not have authority"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); + m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); } {% if Property.attrib['IsReliable']|booleanTrue %} {# if the rpc is not reliable we can simply drop it, also note message reliability type is default reliable in EntityRpcMessage #} @@ -377,6 +428,7 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Autonomous, "Entity proxy does not have autonomy"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); + m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); } {% else %} Handle{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); @@ -1302,6 +1354,11 @@ namespace {{ Component.attrib['Namespace'] }} AZ::BehaviorContext* behaviorContext = azrtti_cast(context); if (behaviorContext) { + {{ ReflectRpcEventDescs(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") ->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}") ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") @@ -1319,6 +1376,11 @@ namespace {{ Component.attrib['Namespace'] }} {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + {{- DefineArchetypePropertyBehaviorReflection(Component, ComponentName) | indent(16) }} ; } @@ -1418,7 +1480,7 @@ namespace {{ Component.attrib['Namespace'] }} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Autonomous', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Autonomous', 'Authority', true, ComponentBaseName)|indent(4) -}} {{ DefineNetworkPropertyGets(Component, 'Authority', 'Client', true, ComponentBaseName)|indent(4) }} - {{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}} +{{ DefineArchetypePropertyGets(Component, ClassType, ComponentBaseName)|indent(4) -}} {{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', false)|indent(4) -}} {{ DefineRpcInvocations(Component, ComponentBaseName, 'Server', 'Authority', true)|indent(4) }} diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 829fe7e495..523ffd90de 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -113,6 +113,10 @@ namespace Multiplayer { editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient); } + if (auto console = AZ::Interface::Get(); console) + { + console->PerformCommand("disconnect"); + } break; } } diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h index 293fb18928..6053040e08 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h @@ -24,6 +24,8 @@ namespace Multiplayer class NetworkInputArray final { public: + AZ_RTTI(NetworkInputArray, "{4908CE9F-8BCD-47C8-837F-09DC695ED2D7}"); + static constexpr uint32_t MaxElements = 8; // Never try to replicate a list larger than this amount NetworkInputArray(); diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h index e5f8fdf648..f08bd023a0 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h @@ -24,6 +24,7 @@ namespace Multiplayer class NetworkInputMigrationVector final { public: + AZ_RTTI(NetworkInputMigrationVector, "{BDF19B57-A11F-4185-9FA9-86AC12E67414}"); static constexpr uint32_t MaxElements = 90; // Never try to migrate a list larger than this amount, bumped up to handle DTLS connection time NetworkInputMigrationVector(); From e0cb0fec9b8fba61ffbe0ceb4e5f00a0263a3d2b Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 19 May 2021 19:39:52 -0700 Subject: [PATCH 19/38] [cpack_installer] add desktop and start menu shortcuts --- .../Platform/Windows/Packaging/Shortcuts.wxs | 68 +++++++++++++++++++ .../Windows/Packaging/Template.wxs.in | 5 +- .../Platform/Windows/Packaging_windows.cmake | 4 ++ .../Windows/platform_windows_files.cmake | 1 + 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 cmake/Platform/Windows/Packaging/Shortcuts.wxs diff --git a/cmake/Platform/Windows/Packaging/Shortcuts.wxs b/cmake/Platform/Windows/Packaging/Shortcuts.wxs new file mode 100644 index 0000000000..fb9d359b5a --- /dev/null +++ b/cmake/Platform/Windows/Packaging/Shortcuts.wxs @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/Platform/Windows/Packaging/Template.wxs.in b/cmake/Platform/Windows/Packaging/Template.wxs.in index 0b3c597ab6..2900b96f41 100644 --- a/cmake/Platform/Windows/Packaging/Template.wxs.in +++ b/cmake/Platform/Windows/Packaging/Template.wxs.in @@ -38,7 +38,10 @@ - + + + + diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 8504447d4f..204d59852a 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -83,6 +83,10 @@ set(CPACK_WIX_PRODUCT_ICON ${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/produc set(CPACK_WIX_TEMPLATE "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Template.wxs.in") +set(CPACK_WIX_EXTRA_SOURCES + "${CPACK_SOURCE_DIR}/Platform/Windows/Packaging/Shortcuts.wxs" +) + set(_embed_artifacts "yes") if(LY_INSTALLER_DOWNLOAD_URL) diff --git a/cmake/Platform/Windows/platform_windows_files.cmake b/cmake/Platform/Windows/platform_windows_files.cmake index b760a8760d..3ce53fbcea 100644 --- a/cmake/Platform/Windows/platform_windows_files.cmake +++ b/cmake/Platform/Windows/platform_windows_files.cmake @@ -26,5 +26,6 @@ set(FILES Packaging_windows.cmake PackagingPostBuild.cmake Packaging/Bootstrapper.wxs + Packaging/Shortcuts.wxs Packaging/Template.wxs.in ) From d83d9c9bff49e3ce064e2c171ab5017ca1d2272c Mon Sep 17 00:00:00 2001 From: scottr Date: Wed, 19 May 2021 20:26:27 -0700 Subject: [PATCH 20/38] [cpack_installer] fixed issue with applying default installer GUIDs when seed property changes --- .../Platform/Windows/Packaging_windows.cmake | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/cmake/Platform/Windows/Packaging_windows.cmake b/cmake/Platform/Windows/Packaging_windows.cmake index 204d59852a..ce73e9a07b 100644 --- a/cmake/Platform/Windows/Packaging_windows.cmake +++ b/cmake/Platform/Windows/Packaging_windows.cmake @@ -48,29 +48,28 @@ set(_guid_seed_base "${PROJECT_NAME}_${LY_VERSION_STRING}") generate_wix_guid(_wix_default_product_guid "${_guid_seed_base}_ProductID" ) generate_wix_guid(_wix_default_upgrade_guid "${_guid_seed_base}_UpgradeCode") -set(LY_WIX_PRODUCT_GUID "${_wix_default_product_guid}" CACHE STRING "GUID for the Product ID field. Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") -set(LY_WIX_UPGRADE_GUID "${_wix_default_upgrade_guid}" CACHE STRING "GUID for the Upgrade Code field. Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") +set(LY_WIX_PRODUCT_GUID "" CACHE STRING "GUID for the Product ID field. Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") +set(LY_WIX_UPGRADE_GUID "" CACHE STRING "GUID for the Upgrade Code field. Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") -set(_uses_default_product_guid FALSE) -if(NOT LY_WIX_PRODUCT_GUID OR LY_WIX_PRODUCT_GUID STREQUAL ${_wix_default_product_guid}) - set(_uses_default_product_guid TRUE) - set(LY_WIX_PRODUCT_GUID ${_wix_default_product_guid}) +# clear previously cached default values to correct future runs. this will +# unfortunately only work if the seed properties still haven't changed +if(LY_WIX_PRODUCT_GUID STREQUAL ${_wix_default_product_guid}) + unset(LY_WIX_PRODUCT_GUID CACHE) endif() - -set(_uses_default_upgrade_guid FALSE) -if(NOT LY_WIX_UPGRADE_GUID OR LY_WIX_UPGRADE_GUID STREQUAL ${_wix_default_upgrade_guid}) - set(_uses_default_upgrade_guid TRUE) - set(LY_WIX_UPGRADE_GUID ${_wix_default_upgrade_guid}) +if(LY_WIX_UPGRADE_GUID STREQUAL ${_wix_default_upgrade_guid}) + unset(LY_WIX_UPGRADE_GUID CACHE) endif() -if(_uses_default_product_guid OR _uses_default_upgrade_guid) +if(NOT (LY_WIX_PRODUCT_GUID AND LY_WIX_UPGRADE_GUID)) message(STATUS "One or both WiX GUIDs were auto generated. It is recommended you supply your own GUIDs through LY_WIX_PRODUCT_GUID and LY_WIX_UPGRADE_GUID.") - if(_uses_default_product_guid) + if(NOT LY_WIX_PRODUCT_GUID) + set(LY_WIX_PRODUCT_GUID ${_wix_default_product_guid}) message(STATUS "-> Default LY_WIX_PRODUCT_GUID = ${LY_WIX_PRODUCT_GUID}") endif() - if(_uses_default_upgrade_guid) + if(NOT LY_WIX_UPGRADE_GUID) + set(LY_WIX_UPGRADE_GUID ${_wix_default_upgrade_guid}) message(STATUS "-> Default LY_WIX_UPGRADE_GUID = ${LY_WIX_UPGRADE_GUID}") endif() endif() From 451b850f2748c3b69d478418c359e17864785518 Mon Sep 17 00:00:00 2001 From: abrmich Date: Wed, 12 May 2021 21:17:32 -0700 Subject: [PATCH 21/38] Bring back AtlasBuilder files that were removed with the ImageProcessing gem --- .../AtlasBuilder/AtlasBuilderComponent.cpp | 99 + .../AtlasBuilder/AtlasBuilderComponent.h | 44 + .../AtlasBuilder/AtlasBuilderWorker.cpp | 1607 +++++++++++++++++ .../Source/AtlasBuilder/AtlasBuilderWorker.h | 230 +++ 4 files changed, 1980 insertions(+) create mode 100644 Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.cpp create mode 100644 Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.h create mode 100644 Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.cpp create mode 100644 Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.cpp b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.cpp new file mode 100644 index 0000000000..e8d4948cc9 --- /dev/null +++ b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.cpp @@ -0,0 +1,99 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "ImageProcessing_precompiled.h" +#include "AtlasBuilderComponent.h" + +#include + +namespace TextureAtlasBuilder +{ + // AZ Components should only initialize their members to null and empty in constructor + // Allocation of data should occur in Init(), once we can guarantee reflection and registration of types + AtlasBuilderComponent::AtlasBuilderComponent() + { + } + + // Handle deallocation of your memory allocated in Init() + AtlasBuilderComponent::~AtlasBuilderComponent() + { + } + + // Init is where you'll actually allocate memory or create objects + // This ensures that any dependency components will have been been created and serialized + void AtlasBuilderComponent::Init() + { + } + + // Activate is where you'd perform registration with other objects and systems. + // All builder classes owned by this component should be registered here + // Any EBuses for the builder classes should also be connected at this point + void AtlasBuilderComponent::Activate() + { + AssetBuilderSDK::AssetBuilderDesc builderDescriptor; + builderDescriptor.m_name = "Atlas Worker Builder"; + builderDescriptor.m_version = 1; + builderDescriptor.m_patterns.emplace_back(AssetBuilderSDK::AssetBuilderPattern("*.texatlas", AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); + builderDescriptor.m_busId = azrtti_typeid(); + builderDescriptor.m_createJobFunction = AZStd::bind(&AtlasBuilderWorker::CreateJobs, &m_atlasBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + builderDescriptor.m_processJobFunction = AZStd::bind(&AtlasBuilderWorker::ProcessJob, &m_atlasBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + + m_atlasBuilder.BusConnect(builderDescriptor.m_busId); + + AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor); + } + + // Disconnects from any EBuses we connected to in Activate() + // Unregisters from objects and systems we register with in Activate() + void AtlasBuilderComponent::Deactivate() + { + m_atlasBuilder.BusDisconnect(); + + // We don't need to unregister the builder - the AP will handle this for us, because it is managing the lifecycle of this component + } + + // Reflect the input and output formats for the serializer + void AtlasBuilderComponent::Reflect(AZ::ReflectContext* context) + { + // components also get Reflect called automatically + // this is your opportunity to perform static reflection or type registration of any types you want the serializer to know about + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })) + ; + } + + AtlasBuilderInput::Reflect(context); + } + + void AtlasBuilderComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("Atlas Builder Plugin Service", 0x35974d0d)); + } + + void AtlasBuilderComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("Atlas Builder Plugin Service", 0x35974d0d)); + } + + void AtlasBuilderComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + AZ_UNUSED(required); + } + + void AtlasBuilderComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + AZ_UNUSED(dependent); + } +} diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.h b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.h new file mode 100644 index 0000000000..eb8b85dfcf --- /dev/null +++ b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.h @@ -0,0 +1,44 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include "AtlasBuilderWorker.h" + +namespace TextureAtlasBuilder +{ + class AtlasBuilderComponent : public AZ::Component + { + public: + AZ_COMPONENT(AtlasBuilderComponent, "{F49987FB-3375-4417-AB83-97B44C78B335}"); + + AtlasBuilderComponent(); + ~AtlasBuilderComponent() override; + + void Init() override; + void Activate() override; + void Deactivate() override; + + //! Reflect formats for input and output + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + private: + AtlasBuilderWorker m_atlasBuilder; + }; +} // namespace TextureAtlasBuilder diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.cpp b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.cpp new file mode 100644 index 0000000000..81e98251cc --- /dev/null +++ b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.cpp @@ -0,0 +1,1607 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include "ImageProcessing_precompiled.h" +#include "AtlasBuilderWorker.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace TextureAtlasBuilder +{ + //! Counts leading zeros + uint32 CountLeadingZeros32(uint32 x) + { + return x == 0 ? 32 : az_clz_u32(x); + } + + //! Integer log2 + uint32 IntegerLog2(uint32 x) + { + return 31 - CountLeadingZeros32(x); + } + + bool IsFolderPath(const AZStd::string& path) + { + bool hasExtension = AzFramework::StringFunc::Path::HasExtension(path.c_str()); + return !hasExtension; + } + + bool HasTrailingSlash(const AZStd::string& path) + { + size_t pathLength = path.size(); + return (pathLength > 0 && (path.at(pathLength - 1) == '/' || path.at(pathLength - 1) == '\\')); + } + + bool GetCanonicalPathFromFullPath(const AZStd::string& fullPath, AZStd::string& canonicalPathOut) + { + AZStd::string curPath = fullPath; + + // We avoid using LocalFileIO::ConvertToAbsolutePath for this because it does not behave consistently across platforms. + // On non-Windows platforms, LocalFileIO::ConvertToAbsolutePath requires that the path exist, otherwise the path + // remains unchanged. This won't work for paths that include wildcards. + // Also, on non-Windows platforms, if the path is already a full path, it will remain unchanged even if it contains + // "./" or "../" somewhere other than the beginning of the path + + // Normalize path + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, curPath); + + const AZStd::string slash("/"); + + // Replace "/./" occurrances with "/" + const AZStd::string slashDotSlash("/./"); + bool replaced = false; + do + { + // Replace first occurrance + replaced = AzFramework::StringFunc::Replace(curPath, slashDotSlash.c_str(), slash.c_str(), false, true, false); + } while (replaced); + + // Replace "/xxx/../" with "/" + const AZStd::regex slashDotDotSlash("\\/[^/.]*\\/\\.\\.\\/"); + AZStd::string prevPath; + while (prevPath != curPath) + { + prevPath = curPath; + curPath = AZStd::regex_replace(prevPath, slashDotDotSlash, slash, AZStd::regex_constants::match_flag_type::format_first_only); + } + + if ((curPath.find("..") != AZStd::string::npos) || (curPath.find("./") != AZStd::string::npos) || (curPath.find("/.") != AZStd::string::npos)) + { + return false; + } + + canonicalPathOut = curPath; + return true; + } + + bool ResolveRelativePath(const AZStd::string& relativePath, const AZStd::string& watchDirectory, AZStd::string& resolvedFullPathOut) + { + bool resolved = false; + + // Get full path by appending the relative path to the watch directory + AZStd::string fullPath = watchDirectory; + fullPath.append("/"); + fullPath.append(relativePath); + + // Resolve to canonical path (remove "./" and "../") + resolved = GetCanonicalPathFromFullPath(fullPath, resolvedFullPathOut); + + return resolved; + } + + bool GetAbsoluteSourcePathFromRelativePath(const AZStd::string& relativeSourcePath, AZStd::string& absoluteSourcePathOut) + { + bool result = false; + AZ::Data::AssetInfo info; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, relativeSourcePath.c_str(), info, watchFolder); + if (result) + { + absoluteSourcePathOut = AZStd::string::format("%s/%s", watchFolder.c_str(), info.m_relativePath.c_str()); + + // Normalize path + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, absoluteSourcePathOut); + } + return result; + } + + const ImageProcessing::PresetSettings* GetImageProcessPresetSettings(const AZStd::string& presetName, const AZStd::string& platformIdentifier) + { + // Get the specified presetId + AZ::Uuid presetId = ImageProcessing::BuilderSettingManager::Instance()->GetPresetIdFromName(presetName); + if (presetId.IsNull()) + { + AZ_Error("Texture Editor", false, "Texture Preset %s has no associated UUID.", presetName.c_str()); + return nullptr; + } + + // Get the preset settings for the platform this job is building for + const ImageProcessing::PresetSettings* presetSettings = ImageProcessing::BuilderSettingManager::Instance()->GetPreset( + presetId, platformIdentifier); + + return presetSettings; + } + + // Reflect the input parameters + void AtlasBuilderInput::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(1) + ->Field("Force Square", &AtlasBuilderInput::m_forceSquare) + ->Field("Force Power of Two", &AtlasBuilderInput::m_forcePowerOf2) + ->Field("Include White Texture", &AtlasBuilderInput::m_includeWhiteTexture) + ->Field("Maximum Dimension", &AtlasBuilderInput::m_maxDimension) + ->Field("Padding", &AtlasBuilderInput::m_padding) + ->Field("UnusedColor", &AtlasBuilderInput::m_unusedColor) + ->Field("PresetName", &AtlasBuilderInput::m_presetName) + ->Field("Textures to Add", &AtlasBuilderInput::m_filePaths); + } + } + + // Supports a custom parser format + AtlasBuilderInput AtlasBuilderInput::ReadFromFile(const AZStd::string& path, const AZStd::string& directory, bool& valid) + { + // Open the file + AZ::IO::FileIOBase* input = AZ::IO::FileIOBase::GetInstance(); + AZ::IO::HandleType handle; + input->Open(path.c_str(), AZ::IO::OpenMode::ModeRead, handle); + + // Read the file + AZ::u64 size; + input->Size(handle, size); + char* buffer = new char[size + 1]; + input->Read(handle, buffer, size); + buffer[size] = 0; + + // Close the file + input->Close(handle); + + // Prepare the output + AtlasBuilderInput data; + + // Parse the input into lines + AZStd::vector lines; + AzFramework::StringFunc::Tokenize(buffer, lines, "\n\t"); + delete[] buffer; + + // Parse the individual lines + for (auto line : lines) + { + line = AzFramework::StringFunc::TrimWhiteSpace(line, true, true); + // Check for comments and empty lines + if ((line.length() >= 2 && line[0] == '/' && line[1] == '/') || line.length() < 1) + { + continue; + } + else if (line.find('=') != -1) + { + AZStd::vector args; + AzFramework::StringFunc::Tokenize(line.c_str(), args, '=', true, true); + + if (args.size() > 2) + { + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to parse line: Excessive '=' symbols were found: \"%s\"", line.c_str()).c_str()); + valid = false; + } + + // Trim whitespace + args[0] = AzFramework::StringFunc::TrimWhiteSpace(args[0], true, true); + args[1] = AzFramework::StringFunc::TrimWhiteSpace(args[1], true, true); + + // No case sensitivity for property names + AZStd::to_lower(args[0].begin(), args[0].end()); + + // Keep track of if the value is rejected + bool accepted = false; + + if (args[0] == "square") + { + accepted = AzFramework::StringFunc::LooksLikeBool(args[1].c_str()); + if (accepted) + { + data.m_forceSquare = AzFramework::StringFunc::ToBool(args[1].c_str()); + } + } + else if (args[0] == "poweroftwo") + { + accepted = AzFramework::StringFunc::LooksLikeBool(args[1].c_str()); + if (accepted) + { + data.m_forcePowerOf2 = AzFramework::StringFunc::ToBool(args[1].c_str()); + } + } + else if (args[0] == "whitetexture") + { + accepted = AzFramework::StringFunc::LooksLikeBool(args[1].c_str()); + if (accepted) + { + data.m_includeWhiteTexture = AzFramework::StringFunc::ToBool(args[1].c_str()); + } + } + else if (args[0] == "maxdimension") + { + accepted = AzFramework::StringFunc::LooksLikeInt(args[1].c_str()); + if (accepted) + { + data.m_maxDimension = AzFramework::StringFunc::ToInt(args[1].c_str()); + } + } + else if (args[0] == "padding") + { + accepted = AzFramework::StringFunc::LooksLikeInt(args[1].c_str()); + if (accepted) + { + data.m_padding = AzFramework::StringFunc::ToInt(args[1].c_str()); + } + } + else if (args[0] == "unusedcolor") + { + accepted = args[1].at(0) == '#' && args[1].length() == 9; + if (accepted) + { + AZStd::string color = AZStd::string::format("%s%s%s%s", args[1].substr(7).c_str(), args[1].substr(5, 2).c_str(), + args[1].substr(3, 2).c_str(), args[1].substr(1, 2).c_str()); + data.m_unusedColor.FromU32(AZStd::stoul(color, nullptr, 16)); + } + } + else if (args[0] == "presetname") + { + accepted = true; + data.m_presetName = args[1]; + } + else + { + // Supress accepted error because this error superceeds it + accepted = true; + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to parse line: Unrecognized property: \"%s\"", args[0].c_str()).c_str()); + } + + // If the property is recognized but the value is rejected, fail the job + if (!accepted) + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to parse line: Invalid value assigned to property: Property: \"%s\" Value: \"%s\"", args[0].c_str(), args[1].c_str()).c_str()); + } + } + else if ((line[0] == '-')) + { + // Remove image files + AZStd::string remove = line.substr(1); + remove = AzFramework::StringFunc::TrimWhiteSpace(remove, true, true); + if (remove.find('*') != -1) + { + AZStd::string resolvedAbsolutePath; + bool resolved = ResolveRelativePath(remove, directory, resolvedAbsolutePath); + if (resolved) + { + RemoveFilesUsingWildCard(data.m_filePaths, resolvedAbsolutePath); + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to resolve relative path: %s", remove.c_str()).c_str()); + } + } + else if (IsFolderPath(remove)) + { + AZStd::string resolvedAbsolutePath; + bool resolved = ResolveRelativePath(remove, directory, resolvedAbsolutePath); + if (resolved) + { + RemoveFolderContents(data.m_filePaths, resolvedAbsolutePath); + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to resolve relative path: %s", remove.c_str()).c_str()); + } + } + else + { + // Get the full path to the source image from the relative source path + AZStd::string fullSourceAssetPathName; + bool fullPathFound = GetAbsoluteSourcePathFromRelativePath(remove, fullSourceAssetPathName); + + if (!fullPathFound) + { + // Try to resolve relative path as it might be using "./" or "../" + fullPathFound = ResolveRelativePath(remove, directory, fullSourceAssetPathName); + } + + if (fullPathFound) + { + for (size_t i = 0; i < data.m_filePaths.size(); ++i) + { + if (data.m_filePaths[i] == fullSourceAssetPathName) + { + data.m_filePaths.erase(data.m_filePaths.begin() + i); + } + } + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to get source asset path for image: %s", remove.c_str()).c_str()); + } + } + } + else + { + // Add image files + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, line); + bool duplicate = false; + if (line.find('*') != -1) + { + AZStd::string resolvedAbsolutePath; + bool resolved = ResolveRelativePath(line, directory, resolvedAbsolutePath); + if (resolved) + { + AddFilesUsingWildCard(data.m_filePaths, resolvedAbsolutePath); + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to resolve relative path: %s", line.c_str()).c_str()); + } + } + else if (IsFolderPath(line)) + { + AZStd::string resolvedAbsolutePath; + bool resolved = ResolveRelativePath(line, directory, resolvedAbsolutePath); + if (resolved) + { + AddFolderContents(data.m_filePaths, resolvedAbsolutePath, valid); + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to resolve relative path: %s", line.c_str()).c_str()); + } + } + else + { + // Get the full path to the source image from the relative source path + AZStd::string fullSourceAssetPathName; + bool fullPathFound = GetAbsoluteSourcePathFromRelativePath(line, fullSourceAssetPathName); + + if (!fullPathFound) + { + // Try to resolve relative path as it might be using "./" or "../" + fullPathFound = ResolveRelativePath(line, directory, fullSourceAssetPathName); + } + + if (fullPathFound) + { + // Prevent duplicates + for (size_t i = 0; i < data.m_filePaths.size() && !duplicate; ++i) + { + duplicate = data.m_filePaths[i] == fullSourceAssetPathName; + } + if (!duplicate) + { + data.m_filePaths.push_back(fullSourceAssetPathName); + } + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to get source asset path for image: %s", line.c_str()).c_str()); + } + } + } + } + + return data; + } + + void AtlasBuilderInput::AddFilesUsingWildCard(AZStd::vector& paths, const AZStd::string& insert) + { + const AZStd::string& fullPath = insert; + + AZStd::vector candidates; + AZStd::string fixedPath = fullPath.substr(0, fullPath.find('*')); + fixedPath = fixedPath.substr(0, fixedPath.find_last_of('/')); + candidates.push_back(fixedPath); + + AZStd::vector wildPath; + AzFramework::StringFunc::Tokenize(fullPath.substr(fixedPath.length()).c_str(), wildPath, "/"); + + for (size_t i = 0; i < wildPath.size() && candidates.size() > 0; ++i) + { + AZStd::vector nextCandidates; + for (size_t j = 0; j < candidates.size(); ++j) + { + AZStd::string compare = AZStd::string::format("%s/%s", candidates[j].c_str(), wildPath[i].c_str()); + QDir inputFolder(candidates[j].c_str()); + if (inputFolder.exists()) + { + QFileInfoList entries = inputFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files); + for (const QFileInfo& entry : entries) + { + AZStd::string child = (entry.filePath().toStdString()).c_str(); + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, child); + if (DoesPathnameMatchWildCard(compare, child)) + { + nextCandidates.push_back(child); + } + } + } + } + candidates = nextCandidates; + } + + for (size_t i = 0; i < candidates.size(); ++i) + { + if (!IsFolderPath(candidates[i]) && !HasTrailingSlash(fullPath)) + { + AZStd::string ext; + AzFramework::StringFunc::Path::GetExtension(candidates[i].c_str(), ext, false); + if (ImageProcessing::IsExtensionSupported(ext.c_str()) && ext != "dds") + { + bool duplicate = false; + for (size_t j = 0; j < paths.size() && !duplicate; ++j) + { + duplicate = paths[j] == candidates[i]; + } + if (!duplicate) + { + paths.push_back(candidates[i]); + } + } + } + else if (IsFolderPath(candidates[i]) && HasTrailingSlash(fullPath)) + { + bool waste = true; + AddFolderContents(paths, candidates[i], waste); + } + } + } + + void AtlasBuilderInput::RemoveFilesUsingWildCard(AZStd::vector& paths, const AZStd::string& remove) + { + bool isDir = (remove.at(remove.length() - 1) == '/'); + for (size_t i = 0; i < paths.size(); ++i) + { + if (isDir ? DoesWildCardDirectoryIncludePathname(remove, paths[i]) : DoesPathnameMatchWildCard(remove, paths[i])) + { + paths.erase(paths.begin() + i); + --i; + } + } + } + + // Tells us if the child follows the rule + bool AtlasBuilderInput::DoesPathnameMatchWildCard(const AZStd::string& rule, const AZStd::string& child) + { + AZStd::vector rulePathTokens; + AzFramework::StringFunc::Tokenize(rule.c_str(), rulePathTokens, "/"); + AZStd::vector pathTokens; + AzFramework::StringFunc::Tokenize(child.c_str(), pathTokens, "/"); + if (rulePathTokens.size() != pathTokens.size()) + { + return false; + } + for (size_t i = 0; i < rulePathTokens.size(); ++i) + { + if (!TokenMatchesWildcard(rulePathTokens[i], pathTokens[i])) + { + return false; + } + } + return true; + } + + bool AtlasBuilderInput::DoesWildCardDirectoryIncludePathname(const AZStd::string& rule, const AZStd::string& child) + { + AZStd::vector rulePathTokens; + AzFramework::StringFunc::Tokenize(rule.c_str(), rulePathTokens, "/"); + AZStd::vector pathTokens; + AzFramework::StringFunc::Tokenize(child.c_str(), pathTokens, "/"); + if (rulePathTokens.size() >= pathTokens.size()) + { + return false; + } + for (size_t i = 0; i < rulePathTokens.size(); ++i) + { + if (!TokenMatchesWildcard(rulePathTokens[i], pathTokens[i])) + { + return false; + } + } + return true; + } + + bool AtlasBuilderInput::TokenMatchesWildcard(const AZStd::string& rule, const AZStd::string& child) + { + AZStd::vector ruleTokens; + AzFramework::StringFunc::Tokenize(rule.c_str(), ruleTokens, "*"); + size_t pos = 0; + int token = 0; + if (rule.at(0) != '*' && child.find(ruleTokens[0]) != 0) + { + return false; + } + + while (pos != AZStd::string::npos && token < ruleTokens.size()) + { + pos = child.find(ruleTokens[token], pos); + if (pos != AZStd::string::npos) + { + pos += ruleTokens[token].size(); + } + ++token; + } + return pos == child.size() || (pos != AZStd::string::npos && rule.at(rule.length() - 1) == '*'); + } + + // Replaces all folder paths with the files they contain + void AtlasBuilderInput::AddFolderContents(AZStd::vector& paths, const AZStd::string& insert, bool& valid) + { + QDir inputFolder(insert.c_str()); + + if (inputFolder.exists()) + { + QFileInfoList entries = inputFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files); + for (const QFileInfo& entry : entries) + { + AZStd::string child = (entry.filePath().toStdString()).c_str(); + AZStd::string ext; + bool isDir = !AzFramework::StringFunc::Path::GetExtension(child.c_str(), ext, false); + if (isDir) + { + AddFolderContents(paths, child, valid); + } + else if (ImageProcessing::IsExtensionSupported(ext.c_str()) && ext != "dds") + { + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, child); + bool duplicate = false; + for (size_t i = 0; i < paths.size() && !duplicate; ++i) + { + duplicate = paths[i] == child; + } + if (!duplicate) + { + paths.push_back(child); + } + } + } + } + else + { + valid = false; + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to find requested directory: %s", insert.c_str()).c_str()); + } + } + + // Removes all of the contents of a folder + void AtlasBuilderInput::RemoveFolderContents(AZStd::vector& paths, const AZStd::string& remove) + { + AZStd::string folder = remove; + AzFramework::StringFunc::Strip(folder, "/", false, false, true); + folder.append("/"); + for (size_t i = 0; i < paths.size(); ++i) + { + if (paths[i].find(folder) == 0) + { + paths.erase(paths.begin() + i); + --i; + } + } + } + + // Note - Shutdown will be called on a different thread than your process job thread + void AtlasBuilderWorker::ShutDown() { m_isShuttingDown = true; } + + void AtlasBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, + AssetBuilderSDK::CreateJobsResponse& response) + { + // Read in settings/filepaths to set dependencies + AZStd::string fullPath; + AzFramework::StringFunc::Path::Join( + request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), fullPath, true, true); + // Check if input is valid + bool valid = true; + AtlasBuilderInput input = AtlasBuilderInput::ReadFromFile(fullPath, request.m_watchFolder, valid); + + // Set dependencies + for (int i = 0; i < input.m_filePaths.size(); ++i) + { + AssetBuilderSDK::SourceFileDependency dependency; + dependency.m_sourceFileDependencyPath = input.m_filePaths[i].c_str(); + response.m_sourceFileDependencyList.push_back(dependency); + } + + // We process the same file for all platforms + for (const AssetBuilderSDK::PlatformInfo& info : request.m_enabledPlatforms) + { + if (ImageProcessing::BuilderSettingManager::Instance()->DoesSupportPlatform(info.m_identifier)) + { + AssetBuilderSDK::JobDescriptor descriptor = GetJobDescriptor(request.m_sourceFile, input); + descriptor.SetPlatformIdentifier(info.m_identifier.c_str()); + response.m_createJobOutputs.push_back(descriptor); + } + } + + if (valid) + { + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; + } + + return; + } + + AssetBuilderSDK::JobDescriptor AtlasBuilderWorker::GetJobDescriptor(const AZStd::string& sourceFile, const AtlasBuilderInput& input) + { + // Get the extension of the file + AZStd::string ext; + AzFramework::StringFunc::Path::GetExtension(sourceFile.c_str(), ext, false); + AZStd::to_upper(ext.begin(), ext.end()); + + AssetBuilderSDK::JobDescriptor descriptor; + descriptor.m_jobKey = ext + " Atlas"; + descriptor.m_critical = false; + descriptor.m_jobParameters[AZ_CRC("forceSquare")] = input.m_forceSquare ? "true" : "false"; + descriptor.m_jobParameters[AZ_CRC("forcePowerOf2")] = input.m_forcePowerOf2 ? "true" : "false"; + descriptor.m_jobParameters[AZ_CRC("includeWhiteTexture")] = input.m_includeWhiteTexture ? "true" : "false"; + descriptor.m_jobParameters[AZ_CRC("padding")] = AZStd::to_string(input.m_padding); + descriptor.m_jobParameters[AZ_CRC("maxDimension")] = AZStd::to_string(input.m_maxDimension); + descriptor.m_jobParameters[AZ_CRC("filePaths")] = AZStd::to_string(input.m_filePaths.size()); + + AZ::u32 col = input.m_unusedColor.ToU32(); + descriptor.m_jobParameters[AZ_CRC("unusedColor")] = AZStd::to_string(*reinterpret_cast(&col)); + descriptor.m_jobParameters[AZ_CRC("presetName")] = input.m_presetName; + + // The starting point for the list + const int start = static_cast(descriptor.m_jobParameters.size()) + 1; + descriptor.m_jobParameters[AZ_CRC("startPoint")] = AZStd::to_string(start); + + for (int i = 0; i < input.m_filePaths.size(); ++i) + { + descriptor.m_jobParameters[start + i] = input.m_filePaths[i]; + } + + return descriptor; + } + + void AtlasBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, + AssetBuilderSDK::ProcessJobResponse& response) + { + // Before we begin, let's make sure we are not meant to abort. + AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId); + + AZStd::vector productFilepaths; + + const AZStd::string path = request.m_fullPath; + + bool imageProcessingSuccessful = false; + + // read in settings/filepaths + AtlasBuilderInput input; + input.m_forceSquare = AzFramework::StringFunc::ToBool(request.m_jobDescription.m_jobParameters.find(AZ_CRC("forceSquare"))->second.c_str()); + input.m_forcePowerOf2 = AzFramework::StringFunc::ToBool(request.m_jobDescription.m_jobParameters.find(AZ_CRC("forcePowerOf2"))->second.c_str()); + input.m_includeWhiteTexture = AzFramework::StringFunc::ToBool(request.m_jobDescription.m_jobParameters.find(AZ_CRC("includeWhiteTexture"))->second.c_str()); + input.m_padding = AzFramework::StringFunc::ToInt(request.m_jobDescription.m_jobParameters.find(AZ_CRC("padding"))->second.c_str()); + input.m_maxDimension = AzFramework::StringFunc::ToInt(request.m_jobDescription.m_jobParameters.find(AZ_CRC("maxDimension"))->second.c_str()); + int startAsInt = AzFramework::StringFunc::ToInt(request.m_jobDescription.m_jobParameters.find(AZ_CRC("startPoint"))->second.c_str()); + int sizeAsInt = AzFramework::StringFunc::ToInt(request.m_jobDescription.m_jobParameters.find(AZ_CRC("filePaths"))->second.c_str()); + AZ::u32 start = static_cast(AZStd::max(0, startAsInt)); + AZ::u32 size = static_cast(AZStd::max(0, sizeAsInt)); + + int col = AzFramework::StringFunc::ToInt(request.m_jobDescription.m_jobParameters.find(AZ_CRC("unusedColor"))->second.c_str()); + input.m_unusedColor.FromU32(*reinterpret_cast(&col)); + + input.m_presetName = request.m_jobDescription.m_jobParameters.find(AZ_CRC("presetName"))->second; + + for (AZ::u32 i = 0; i < size; ++i) + { + input.m_filePaths.push_back(request.m_jobDescription.m_jobParameters.find(start + i)->second); + } + + if (input.m_filePaths.empty()) + { + AZ_Error("AtlasBuilder", false, "No image files specified. Cannot create an empty atlas."); + return; + } + + // Don't allow padding to be less than zero + if (input.m_padding < 0) + { + input.m_padding = 0; + } + + if (input.m_presetName.empty()) + { + // Default to the TextureAtlas preset which is currently set to use compression for all platforms except for iOS. + // Currently the only fully supported compression for iOS is PVRTC which requires the texture to be square and a power of 2. + // Due to this limitation, we default to using no compression for iOS until ASTC is fully supported + const AZStd::string defaultPresetName = "TextureAtlas"; + input.m_presetName = defaultPresetName; + } + + // Get a preset to use for the output image + const ImageProcessing::PresetSettings* preset = GetImageProcessPresetSettings(input.m_presetName, request.m_platformInfo.m_identifier); + if (preset) + { + // Check the preset's pixel format requirements + const ImageProcessing::PixelFormatInfo* pixelFormatInfo = ImageProcessing::CPixelFormats::GetInstance().GetPixelFormatInfo(preset->m_pixelFormat); + if (pixelFormatInfo && pixelFormatInfo->bSquarePow2) + { + // Override the user config settings to force square and power of 2. + // Otherwise the image conversion process will stretch the image to satisfy these requirements + input.m_forceSquare = true; + input.m_forcePowerOf2 = true; + } + } + else + { + AZ_Error("AtlasBuilder", false, "Could not find a preset setting for the output image."); + return; + } + + // Read in images + AZStd::vector images; + AZ::u64 totalArea = 0; + int maxArea = input.m_maxDimension * input.m_maxDimension; + bool sizeFailure = false; + for (int i = 0; i < input.m_filePaths.size() && !jobCancelListener.IsCancelled(); ++i) + { + ImageProcessing::IImageObject* inputImage = ImageProcessing::LoadImageFromFile(input.m_filePaths[i]); + // Check if we were able to load the image + if (inputImage) + { + ImageProcessing::IImageObjectPtr image = ImageProcessing::IImageObjectPtr(inputImage); + images.push_back(image); + totalArea += inputImage->GetWidth(0) * inputImage->GetHeight(0); + } + else + { + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to load file: %s", input.m_filePaths[i].c_str()).c_str()); + return; + } + if (maxArea < totalArea) + { + sizeFailure = true; + } + } + // If we get cancelled, return + if (jobCancelListener.IsCancelled()) + { + return; + } + + if (sizeFailure) + { + AZ_Error("AtlasBuilder", false, AZStd::string::format("Total image area exceeds maximum alotted area. %llu > %d", totalArea, maxArea).c_str()); + return; + } + + // Convert all image paths to their output format referenced at runtime + for (auto& filePath : input.m_filePaths) + { + // Get path relative to the watch folder + bool result = false; + AZ::Data::AssetInfo info; + AZStd::string watchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, filePath.c_str(), info, watchFolder); + if (!result) + { + AZ_Error("AtlasBuilder", false, AZStd::string::format("Atlas Builder unable to get relative source path for image: %s", filePath.c_str()).c_str()); + return; + } + + // Remove extension + filePath = info.m_relativePath.substr(0, info.m_relativePath.find_last_of('.')); + + // Normalize path + AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::NormalizePathKeepCase, filePath); + } + + // Add white texture if we need to + if (input.m_includeWhiteTexture) + { + ImageProcessing::IImageObjectPtr texture(ImageProcessing::IImageObject::CreateImage( + cellSize, cellSize, 1, ImageProcessing::EPixelFormat::ePixelFormat_R8G8B8A8)); + + // Make the texture white + texture->ClearColor(1, 1, 1, 1); + images.push_back(texture); + input.m_filePaths.push_back("WhiteTexture"); + } + + // Generate algorithm inputs + ImageDimensionData data; + for (int i = 0; i < images.size(); ++i) + { + data.push_back(IndexImageDimension(i, + ImageDimension(images[i]->GetWidth(0), + images[i]->GetHeight(0)))); + } + AZStd::sort(data.begin(), data.end()); + + // Run algorithm + + // Variables that keep track of the optimal solution + int resultWidth = -1; + int resultHeight = -1; + + // Check that the max dimension is not large enough for the area to loop past the maximum integer + // This is important because we do not want the area to be calculated negative + if (input.m_maxDimension > 65535) + { + input.m_maxDimension = 65535; + } + + // Get the optimal mappings based on the input settings + AZStd::vector paddedMap; + size_t amountFit = 0; + if (!TryTightening( + input, data, GetWidest(data), GetTallest(data), aznumeric_cast(totalArea), input.m_padding, resultWidth, resultHeight, amountFit, paddedMap)) + { + AZ_Error("AtlasBuilder", false, AZStd::string::format("Cannot fit images into given maximum atlas size (%dx%d). Only %zu out of %zu images fit.", input.m_maxDimension, input.m_maxDimension, amountFit, input.m_filePaths.size()).c_str()); + // For some reason, failing the assert isn't enough to stop the Asset builder. It will still fail further + // down when it tries to assemble the atlas, but returning here is cleaner. + return; + } + + // Move coordinates from algorithm space to padded result space + TextureAtlasNamespace::AtlasCoordinateSets output; + resultWidth = 0; + resultHeight = 0; + AZStd::vector map; + for (int i = 0; i < paddedMap.size(); ++i) + { + map.push_back(AtlasCoordinates(paddedMap[i].GetLeft(), paddedMap[i].GetLeft() + images[data[i].first]->GetWidth(0), paddedMap[i].GetTop(), paddedMap[i].GetTop() + images[data[i].first]->GetHeight(0))); + resultHeight = resultHeight > map[i].GetBottom() ? resultHeight : map[i].GetBottom(); + resultWidth = resultWidth > map[i].GetRight() ? resultWidth : map[i].GetRight(); + + const AZStd::string& outputFilePath = input.m_filePaths[data[i].first]; + output.push_back(AZStd::pair(outputFilePath, map[i])); + } + if (input.m_forcePowerOf2) + { + resultWidth = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(resultWidth - 1)))); + resultHeight = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(resultHeight - 1)))); + } + else + { + resultWidth = (resultWidth + (cellSize - 1)) / cellSize * cellSize; + resultHeight = (resultHeight + (cellSize - 1)) / cellSize * cellSize; + } + if (input.m_forceSquare) + { + if (resultWidth > resultHeight) + { + resultHeight = resultWidth; + } + else + { + resultWidth = resultHeight; + } + } + + // Process texture sheet + ImageProcessing::IImageObjectPtr outImage(ImageProcessing::IImageObject::CreateImage( + resultWidth, resultHeight, 1, ImageProcessing::EPixelFormat::ePixelFormat_R8G8B8A8)); + + // Clear the sheet + outImage->ClearColor(input.m_unusedColor.GetR(), input.m_unusedColor.GetG(), input.m_unusedColor.GetB(), input.m_unusedColor.GetA()); + + AZ::u8* outBuffer = nullptr; + AZ::u32 outPitch; + outImage->GetImagePointer(0, outBuffer, outPitch); + + // Copy images over + for (int i = 0; i < map.size() && !jobCancelListener.IsCancelled(); ++i) + { + AZ::u8* inBuffer = nullptr; + AZ::u32 inPitch; + images[data[i].first]->GetImagePointer(0, inBuffer, inPitch); + int j = 0; + + // The padding calculated here is the amount of excess horizontal space measured in bytes that are in each + // row of the destination space AFTER the placement of the source row. + int rightPadding = (paddedMap[i].GetRight() - map[i].GetRight() - input.m_padding); + if (map[i].GetRight() + rightPadding > resultWidth) + { + rightPadding = resultWidth - map[i].GetRight(); + } + rightPadding *= bytesPerPixel; + int bottomPadding = (paddedMap[i].GetBottom() - map[i].GetBottom() - input.m_padding); + if (map[i].GetBottom() + bottomPadding > resultHeight) + { + bottomPadding = resultHeight - map[i].GetBottom(); + } + + int leftPadding = 0; + if (map[i].GetLeft() - input.m_padding >= 0) + { + leftPadding = input.m_padding * bytesPerPixel; + } + + int topPadding = 0; + if (map[i].GetTop() - input.m_padding >= 0) + { + topPadding = input.m_padding; + } + + for (j = 0; j < map[i].GetHeight(); ++j) + { + // When we multiply `map[i].GetLeft()` by 4, we are changing the measure from atlas space, to byte array + // space. The number is 4 because in this format, each pixel is 4 bytes long. + memcpy(outBuffer + (map[i].GetTop() + j) * outPitch + (map[i].GetLeft() * bytesPerPixel), + inBuffer + inPitch * j, + inPitch); + // Fill in the last bit of the row in the destination space with the same colors + SetPixels(outBuffer + (map[i].GetTop() + j) * outPitch + (map[i].GetLeft() * bytesPerPixel) + inPitch, + outBuffer + (map[i].GetTop() + j) * outPitch + (map[i].GetLeft() * bytesPerPixel) + inPitch - bytesPerPixel, + rightPadding); + // Fill in the first bit of the row in the destination space with the same colors + SetPixels(outBuffer + (map[i].GetTop() + j) * outPitch + (map[i].GetLeft() * bytesPerPixel) - leftPadding, + outBuffer + (map[i].GetTop() + j) * outPitch + (map[i].GetLeft() * bytesPerPixel), + leftPadding); + } + // Fill in the last few rows of the buffer with the same colors + for (; j < map[i].GetHeight() + bottomPadding; ++j) + { + memcpy(outBuffer + (map[i].GetTop() + j) * outPitch + (map[i].GetLeft() * bytesPerPixel) - leftPadding, + outBuffer + (map[i].GetBottom() - 1) * outPitch + (map[i].GetLeft() * bytesPerPixel) - leftPadding, + inPitch + leftPadding + rightPadding); + } + for (j = 1; j <= topPadding; ++j) + { + memcpy(outBuffer + (map[i].GetTop() - j) * outPitch + (map[i].GetLeft() * bytesPerPixel) - leftPadding, + outBuffer + map[i].GetTop() * outPitch + (map[i].GetLeft() * bytesPerPixel) - leftPadding, + inPitch + rightPadding + leftPadding); + } + } + + // If we get cancelled, return + if (jobCancelListener.IsCancelled()) + { + return; + } + + // Output Atlas Coordinates + AZStd::string fileName; + AZStd::string outputPath; + AzFramework::StringFunc::Path::GetFullFileName(request.m_sourceFile.c_str(), fileName); + fileName = fileName.append("idx"); + AzFramework::StringFunc::Path::Join( + request.m_tempDirPath.c_str(), fileName.c_str(), outputPath, true, true); + + // Output texture sheet + AZStd::string imageFileName, imageOutputPath; + AzFramework::StringFunc::Path::GetFileName(request.m_sourceFile.c_str(), imageFileName); + imageFileName += ".dds"; + AzFramework::StringFunc::Path::Join( + request.m_tempDirPath.c_str(), imageFileName.c_str(), imageOutputPath, true, true); + + // Let the ImageProcessor do the rest of the work. + ImageProcessing::TextureSettings textureSettings; + textureSettings.m_preset = preset->m_uuid; + + // Mipmaps for the texture atlas would require more work than the Image Processor does. This is because if we + // let the Image Processor make mipmaps, it might bleed the textures in the atlas together. + textureSettings.m_enableMipmap = false; + + // Check if the ImageBuilder wants to enable streaming + bool isStreaming = ImageProcessing::BuilderSettingManager::Instance() + ->GetBuilderSetting(request.m_platformInfo.m_identifier) + ->m_enableStreaming; + + bool canOverridePreset = false; + ImageProcessing::ImageConvertProcess* process = + new ImageProcessing::ImageConvertProcess(outImage, + textureSettings, + *preset, + false, + isStreaming, + canOverridePreset, + imageOutputPath, + request.m_platformInfo.m_identifier); + + if (process != nullptr) + { + // the process can be stopped if the job is cancelled or the worker is shutting down + while (!process->IsFinished() && !m_isShuttingDown && !jobCancelListener.IsCancelled()) + { + process->UpdateProcess(); + } + + // get process result + imageProcessingSuccessful = process->IsSucceed(); + process->GetAppendOutputFilePaths(productFilepaths); + + delete process; + } + else + { + imageProcessingSuccessful = false; + } + + if (imageProcessingSuccessful) + { + TextureAtlasNamespace::TextureAtlasRequestBus::Broadcast( + &TextureAtlasNamespace::TextureAtlasRequests::SaveAtlasToFile, outputPath, output, resultWidth, resultHeight); + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(outputPath)); + response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_productAssetType = azrtti_typeid(); + response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_productSubID = 0; + + // The Image Processing Gem can produce multiple output files under certain + // circumstances, but the texture atlas is not expected to produce such output + if (productFilepaths.size() > 1) + { + AZ_Error("AtlasBuilder", false, "Image processing resulted in multiple output files. Texture atlas is expected to produce one output."); + response.m_outputProducts.clear(); + return; + } + + if (productFilepaths.size() > 0) + { + response.m_outputProducts.push_back(AssetBuilderSDK::JobProduct(productFilepaths[0])); + response.m_outputProducts.back().m_productAssetType = azrtti_typeid(); + response.m_outputProducts.back().m_productSubID = 1; + + // The texatlasidx file is a data file that indicates where the original parts are inside the atlas, + // and this would usually imply that it refers to its dds file in some way or needs it to function. + // The texatlasidx file should be the one that depends on the DDS because its possible to use the DDS + // without the texatlasid, but not the other way around + AZ::Data::AssetId productAssetId(request.m_sourceFileUUID, response.m_outputProducts.back().m_productSubID); + response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_dependencies.push_back(AssetBuilderSDK::ProductDependency(productAssetId, 0)); + response.m_outputProducts[static_cast(Product::TexatlasidxProduct)].m_dependenciesHandled = true; // We've populated the dependencies immediately above so it's OK to tell the AP we've handled dependencies + } + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; + } + } + + bool AtlasBuilderWorker::TryPack(const ImageDimensionData& images, + int targetWidth, + int targetHeight, + int padding, + size_t& amountFit, + AZStd::vector& out) + { + // Start with one open slot and initialize a vector to store the closed products + AZStd::vector open; + AZStd::vector closed; + open.push_back(AtlasCoordinates(0, targetWidth, 0, targetHeight)); + bool slotNotFound = false; + for (size_t i = 0; i < images.size() && !slotNotFound; ++i) + { + slotNotFound = true; + // Try to place the image in every open slot + for (size_t j = 0; j < open.size(); ++j) + { + if (CanInsert(open[j], images[i].second, padding, targetWidth, targetHeight)) + { + // if it fits, subdivide the excess space in the slot, add it back to the open list and place the + // filled space into the closed vector + slotNotFound = false; + AtlasCoordinates spent(open[j].GetLeft(), + open[j].GetLeft() + images[i].second.m_width, + open[j].GetTop(), + open[j].GetTop() + images[i].second.m_height); + + // We are going to try pushing the object up / left to try to avoid creating tight open spaces. + bool needTrim = false; + AtlasCoordinates coords = spent; + // Modifying left will preserve width + coords.SetLeft(coords.GetLeft() - 1); + AddPadding(coords, padding, targetWidth, targetHeight); + while (spent.GetLeft() > 0 && !Collides(coords, closed)) + { + spent.SetLeft(coords.GetLeft()); + coords = spent; + coords.SetLeft(coords.GetLeft() - 1); + AddPadding(coords, padding, targetWidth, targetHeight); + needTrim = true; + } + // Refocus the search to see if we can push up + coords = spent; + coords.SetTop(coords.GetTop() - 1); + AddPadding(coords, padding, targetWidth, targetHeight); + while (spent.GetTop() > 0 && !Collides(coords, closed)) + { + spent.SetTop(coords.GetTop()); + coords = spent; + coords.SetTop(coords.GetTop() - 1); + AddPadding(coords, padding, targetWidth, targetHeight); + needTrim = true; + } + AddPadding(spent, padding, targetWidth, targetHeight); + if (needTrim) + { + TrimOverlap(open, spent); + closed.push_back(spent); + break; + } + AtlasCoordinates bigCoords; + AtlasCoordinates smallCoords; + + // Create the largest possible subdivision and another subdivision that uses the left over space + if (open[j].GetBottom() - spent.GetBottom() < open[j].GetRight() - spent.GetRight()) + { + smallCoords = AtlasCoordinates( + open[j].GetLeft(), spent.GetRight(), spent.GetBottom(), open[j].GetBottom()); + bigCoords = AtlasCoordinates(spent.GetRight(), open[j].GetRight(), open[j].GetTop(), smallCoords.GetBottom()); + } + else + { + bigCoords = AtlasCoordinates( + open[j].GetLeft(), open[j].GetRight(), spent.GetBottom(), open[j].GetBottom()); + smallCoords = AtlasCoordinates(spent.GetRight(), open[j].GetRight(), open[j].GetTop(), bigCoords.GetTop()); + } + + open.erase(open.begin() + j, open.begin() + j + 1); + if (bigCoords.GetHeight() > 0 && bigCoords.GetHeight() > 0) + { + InsertInOrder(open, bigCoords); + } + if (smallCoords.GetHeight() > 0 && smallCoords.GetHeight() > 0) + { + InsertInOrder(open, smallCoords); + } + + closed.push_back(spent); + break; + } + } + if (slotNotFound) + { + // If no single open slot can fit the object, do one last check to see if we can fit it in at any open + // corner. The reason we perform this check is in case the object can be fit across multiple different + // open spaces. If there is a space that an object can be fit in, it will probably involve the top left + // corner of that object in the top left corner of an open slot. This may miss some odd fits, but due to + // the nature of the packing algorithm, such solutions are highly unlikely to exist. If we wanted to + // expand the algorithm, we could theoretically base it on edges instead of corners to find all results, + // but it would not be time efficient. + for (size_t j = 0; j < open.size(); ++j) + { + AtlasCoordinates insert = AtlasCoordinates(open[j].GetLeft(), + open[j].GetLeft() + images[i].second.m_width, + open[j].GetTop(), + open[j].GetTop() + images[i].second.m_height); + AddPadding(insert, padding, targetWidth, targetHeight); + if (insert.GetRight() <= targetWidth && insert.GetBottom() <= targetHeight) + { + bool collision = Collides(insert, closed); + if (!collision) + { + closed.push_back(insert); + // Trim overlapping open slots + TrimOverlap(open, insert); + slotNotFound = false; + break; + } + } + } + } + } + // If we succeeded, update the output + if (!slotNotFound) + { + out = closed; + } + amountFit = amountFit > closed.size() ? amountFit : closed.size(); + return !slotNotFound; + } + + // Modifies slotList so that no items in slotList overlap with item + void AtlasBuilderWorker::TrimOverlap(AZStd::vector& slotList, AtlasCoordinates item) + { + for (size_t i = 0; i < slotList.size(); ++i) + { + if (Collides(slotList[i], item)) + { + // Subdivide the overlapping slot to seperate overlapping and non overlapping portions + AtlasCoordinates overlap = GetOverlap(item, slotList[i]); + AZStd::vector excess; + excess.push_back(AtlasCoordinates( + slotList[i].GetLeft(), overlap.GetRight(), slotList[i].GetTop(), overlap.GetTop())); + excess.push_back(AtlasCoordinates( + slotList[i].GetLeft(), overlap.GetLeft(), overlap.GetTop(), slotList[i].GetBottom())); + excess.push_back(AtlasCoordinates( + overlap.GetRight(), slotList[i].GetRight(), slotList[i].GetTop(), overlap.GetBottom())); + excess.push_back(AtlasCoordinates( + overlap.GetLeft(), slotList[i].GetRight(), overlap.GetBottom(), slotList[i].GetBottom())); + slotList.erase(slotList.begin() + i); + for (size_t j = 0; j < excess.size(); ++j) + { + if (excess[j].GetWidth() > 0 && excess[j].GetHeight() > 0) + { + InsertInOrder(slotList, excess[j]); + } + } + --i; + } + } + } + + // This function interprets input and performs the proper tightening option + bool AtlasBuilderWorker::TryTightening(AtlasBuilderInput input, + const ImageDimensionData& images, + int smallestWidth, + int smallestHeight, + int targetArea, + int padding, + int& resultWidth, + int& resultHeight, + size_t& amountFit, + AZStd::vector& out) + { + if (input.m_forceSquare) + { + return TryTighteningSquare(images, + smallestWidth > smallestHeight ? smallestWidth : smallestHeight, + input.m_maxDimension, + targetArea, + input.m_forcePowerOf2, + padding, + resultWidth, + resultHeight, + amountFit, + out); + } + else + { + return TryTighteningOptimal(images, + smallestWidth, + smallestHeight, + input.m_maxDimension, + targetArea, + input.m_forcePowerOf2, + padding, + resultWidth, + resultHeight, + amountFit, + out); + } + } + + // Finds the optimal square solution by starting with the ideal solution and expanding the size of the space until everything fits + bool AtlasBuilderWorker::TryTighteningSquare(const ImageDimensionData& images, + int lowerBound, + int maxDimension, + int targetArea, + bool powerOfTwo, + int padding, + int& resultWidth, + int& resultHeight, + size_t& amountFit, + AZStd::vector& out) + { + // Square solution cannot be smaller than the target area + int dimension = aznumeric_cast(sqrt(static_cast(targetArea))); + // Solution cannot be smaller than the smallest side + dimension = dimension > lowerBound ? dimension : lowerBound; + if (powerOfTwo) + { + // Starting dimension needs to be rounded up to the nearest power of two + dimension = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(dimension - 1)))); + } + + AZStd::vector track; + // Expand the square until the contents fit + while (!TryPack(images, dimension, dimension, padding, amountFit, track) && dimension <= maxDimension) + { + // Step to the next valid value + dimension = powerOfTwo ? dimension * 2 : dimension + cellSize; + } + // Make sure we found a solution + if (dimension > maxDimension) + { + return false; + } + + resultHeight = dimension; + resultWidth = dimension; + out = track; + return true; + } + + // Finds the optimal solution by starting with a somewhat optimal solution and searching for better solutions + bool AtlasBuilderWorker::TryTighteningOptimal(const ImageDimensionData& images, + int smallestWidth, + int smallestHeight, + int maxDimension, + int targetArea, + bool powerOfTwo, + int padding, + int& resultWidth, + int& resultHeight, + size_t& amountFit, + AZStd::vector& out) + { + AZStd::vector track; + + // round max dimension down to a multiple of cellSize + AZ::u32 maxDimensionRounded = maxDimension - (maxDimension % cellSize); + + // The starting width is the larger of the widest individual texture and the width required + // to fit the total texture area given the max dimension + AZ::u32 smallestWidthDueToArea = targetArea / maxDimensionRounded; + AZ::u32 minWidth = AZStd::max(static_cast(smallestWidth), smallestWidthDueToArea); + + if (powerOfTwo) + { + // Starting dimension needs to be rounded up to the nearest power of two + minWidth = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(minWidth - 1)))); + } + + // Round min width up to the nearest compression unit + minWidth = (minWidth + (cellSize - 1)) / cellSize * cellSize; + + AZ::u32 height = 0; + // Finds the optimal thin solution + // This uses a standard binary search to find the smallest width that can pack everything + AZ::u32 lower = minWidth; + AZ::u32 upper = maxDimensionRounded; + AZ::u32 width = 0; + while (lower <= upper) + { + AZ::u32 testWidth = (lower + upper) / 2; // must be divisible by cellSize because lower and upper are + bool canPack = TryPack(images, testWidth, maxDimension, padding, amountFit, track); + if (canPack) + { + // it packed, continue looking for smaller widths that pack + width = testWidth; // best fit so far + upper = testWidth - cellSize; + } + else + { + // it failed to pack, don't try any widths smaller than this + lower = testWidth + cellSize; + } + } + // Make sure we found a solution + if (width == 0) + { + return false; + } + + // Find the height of the solution + for (int i = 0; i < track.size(); ++i) + { + uint32 bottom = static_cast(AZStd::max(0, track[i].GetBottom())); + if (height < bottom) + { + height = bottom; + } + } + + // Fix height for power of two when applicable + if (powerOfTwo) + { + // Starting dimensions need to be rounded up to the nearest power of two + height = aznumeric_cast(pow(2, 1 + IntegerLog2(static_cast(height - 1)))); + } + + AZ::u32 resultArea = height * width; + // This for loop starts with the optimal thin width and makes it wider at each step. For each width, it + // calculates what height would be neccesary to have a more optimal solution than the stored solution. If the + // more optimal solution is valid, it tries shrinking the height until the solution fails. The loop ends when it + // is determined that a valid solution cannot exist at further steps + for (AZ::u32 testWidth = width; testWidth <= maxDimensionRounded && resultArea / testWidth >= static_cast(smallestHeight); + testWidth = powerOfTwo ? testWidth * 2 : testWidth + cellSize) + { + // The area of test height and width should be equal or less than resultArea + // Note: We don't need to force powers of two here because the Area and the width are already powers of two + int testHeight = resultArea / testWidth * cellSize / cellSize; + // Try the tighter pack + while (TryPack(images, static_cast(testWidth), testHeight, padding, amountFit, track)) + { + // Loop and continue to shrink the height until you cannot do so any further + width = testWidth; + height = testHeight; + resultArea = height * width; + // Try to step down a level + testHeight = powerOfTwo ? testHeight / 2 : testHeight - cellSize; + } + } + // Output the results of the function + out = track; + resultHeight = height; + resultWidth = width; + return true; + } + + // Allows us to keep the list of open spaces in order from lowest to highest area + void AtlasBuilderWorker::InsertInOrder(AZStd::vector& slotList, AtlasCoordinates item) + { + int area = item.GetWidth() * item.GetHeight(); + for (size_t i = 0; i < slotList.size(); ++i) + { + if (area < slotList[i].GetWidth() * slotList[i].GetHeight()) + { + slotList.insert(slotList.begin() + i, item); + return; + } + } + slotList.push_back(item); + } + + // Defines priority so that sorting can be meaningful. It may seem odd that larger items are "less than" smaller + // ones, but as this is a deduction of priority, not value, it is correct. + static bool operator<(ImageDimension a, ImageDimension b) + { + // Prioritize first by longest size + if ((a.m_width > a.m_height ? a.m_width : a.m_height) != (b.m_width > b.m_height ? b.m_width : b.m_height)) + { + return (a.m_width > a.m_height ? a.m_width : a.m_height) > (b.m_width > b.m_height ? b.m_width : b.m_height); + } + // Prioritize second by the length of the smaller side + if (a.m_width * a.m_height != b.m_width * b.m_height) + { + return a.m_width * a.m_height > b.m_width * b.m_height; + } + // Prioritize wider objects over taller objects for objects of the same size + else + { + return a.m_width > b.m_width; + } + } + + // Exposes priority logic to the sorting algorithm + static bool operator<(IndexImageDimension a, IndexImageDimension b) { return a.second < b.second; } + + // Tests if two coordinate sets intersect + bool Collides(AtlasCoordinates a, AtlasCoordinates b) + { + return !((a.GetRight() <= b.GetLeft()) || (a.GetBottom() <= b.GetTop()) || (b.GetRight() <= a.GetLeft()) + || (b.GetBottom() <= a.GetTop())); + } + + // Tests if an item collides with any items in a list + bool Collides(AtlasCoordinates item, AZStd::vector list) + { + for (size_t i = 0; i < list.size(); ++i) + { + if (Collides(list[i], item)) + { + return true; + } + } + return false; + } + + // Returns the overlap of two intersecting coordinate sets + AtlasCoordinates GetOverlap(AtlasCoordinates a, AtlasCoordinates b) + { + return AtlasCoordinates(b.GetLeft() > a.GetLeft() ? b.GetLeft() : a.GetLeft(), + b.GetRight() < a.GetRight() ? b.GetRight() : a.GetRight(), + b.GetTop() > a.GetTop() ? b.GetTop() : a.GetTop(), + b.GetBottom() < a.GetBottom() ? b.GetBottom() : a.GetBottom()); + } + + // Returns the width of the widest element in imageList + int AtlasBuilderWorker::GetWidest(const ImageDimensionData& imageList) + { + int max = 0; + for (size_t i = 0; i < imageList.size(); ++i) + { + if (max < imageList[i].second.m_width) + { + max = imageList[i].second.m_width; + } + } + return max; + } + + // Returns the height of the tallest element in imageList + int AtlasBuilderWorker::GetTallest(const ImageDimensionData& imageList) + { + int max = 0; + for (size_t i = 0; i < imageList.size(); ++i) + { + if (max < imageList[i].second.m_height) + { + max = imageList[i].second.m_height; + } + } + return max; + } + + // Performs an operation that copies a pixel to the output + void SetPixels(AZ::u8* dest, const AZ::u8* source, int destBytes) + { + if (destBytes >= bytesPerPixel) + { + memcpy(dest, source, bytesPerPixel); + int bytesCopied = bytesPerPixel; + while (bytesCopied * 2 < destBytes) + { + memcpy(dest + bytesCopied, dest, bytesCopied); + bytesCopied *= 2; + } + memcpy(dest + bytesCopied, dest, destBytes - bytesCopied); + } + } + + // Checks if we can insert an image into a slot + bool CanInsert(AtlasCoordinates slot, ImageDimension image, int padding, int farRight, int farBot) + { + int right = slot.GetLeft() + image.m_width; + if (slot.GetRight() < farRight) + { + // Add padding for my right border + right += padding; + // Round up to the nearest compression unit + right = (right + (cellSize - 1)) / cellSize * cellSize; + // Add padding for an adjacent unit's left border + right += padding; + } + + int bot = slot.GetTop() + image.m_height; + if (slot.GetBottom() < farBot) + { + // Add padding for my right border + bot += padding; + // Round up to the nearest compression unit + bot = (bot + (cellSize - 1)) / cellSize * cellSize; + // Add padding for an adjacent unit's left border + bot += padding; + } + + return slot.GetRight() >= right && slot.GetBottom() >= bot; + } + + // Adds the necessary padding to an Atlas Coordinate + void AddPadding(AtlasCoordinates& slot, int padding, [[maybe_unused]] int farRight, [[maybe_unused]] int farBot) + { + // Add padding for my right border + int right = slot.GetRight() + padding; + // Round up to the nearest compression unit + right = (right + (cellSize - 1)) / cellSize * cellSize; + // Add padding for an adjacent unit's left border + right += padding; + + // Add padding for my right border + int bot = slot.GetBottom() + padding; + // Round up to the nearest compression unit + bot = (bot + (cellSize - 1)) / cellSize * cellSize; + // Add padding for an adjacent unit's left border + bot += padding; + + slot.SetRight(right); + slot.SetBottom(bot); + } + +} diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h new file mode 100644 index 0000000000..94e2b5b226 --- /dev/null +++ b/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h @@ -0,0 +1,230 @@ +/* +* All or portions of this file Copyright(c) Amazon.com, Inc.or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +*or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace TextureAtlasBuilder +{ + //! Struct that is used to communicate input commands + struct AtlasBuilderInput + { + AZ_CLASS_ALLOCATOR(AtlasBuilderInput, AZ::SystemAllocator, 0); + AZ_TYPE_INFO(AtlasBuilderInput, "{F54477F9-1BDE-4274-8CC0-8320A3EF4A42}"); + + bool m_forceSquare; + bool m_forcePowerOf2; + // Includes a white default texture for the UI to use under certain circumstances + bool m_includeWhiteTexture; + int m_maxDimension; + // At least this much padding will surround each texture except on the edges of the atlas + int m_padding; + // Color used in wasted space + AZ::Color m_unusedColor; + // A preset to use for the texture atlas image processing + AZStd::string m_presetName; + + AZStd::vector m_filePaths; + AtlasBuilderInput(): + m_forceSquare(false), + m_forcePowerOf2(false), + m_includeWhiteTexture(true), + m_maxDimension(4096), + m_padding(1), + // Default color should be a non-transparent color that isn't used often in uis + m_unusedColor(.235f, .702f, .443f, 1) + { + } + + static void Reflect(AZ::ReflectContext* context); + + //! Attempts to read the input from a .texatlas file. "valid" is for reporting exceptions and telling the asset + //! proccesor to fail the job. Supports parsing through a human readable custom parser. + static AtlasBuilderInput ReadFromFile(const AZStd::string& path, const AZStd::string& directory, bool& valid); + + //! Resolves any wild cards in paths + static void AddFilesUsingWildCard(AZStd::vector& paths, const AZStd::string& insert); + + //! Removes anything that matches the wildcard + static void RemoveFilesUsingWildCard(AZStd::vector& paths, const AZStd::string& remove); + + //! Compare considering wildcards + static bool DoesPathnameMatchWildCard(const AZStd::string& rule, const AZStd::string& path); + + //! As FollowsRule but allows extra items after the last '/' + static bool DoesWildCardDirectoryIncludePathname(const AZStd::string& rule, const AZStd::string& path); + + //! Helper function for DoesPathnameMatchWildCard + static bool TokenMatchesWildcard(const AZStd::string& rule, const AZStd::string& token); + + //! Resolves any folder paths into image file paths + static void AddFolderContents(AZStd::vector& paths, const AZStd::string& insert, bool& valid); + + //! Resolves remove commands for folders + static void RemoveFolderContents(AZStd::vector& paths, const AZStd::string& remove); + }; + + //! Struct that is used to represent an object with a width and height in pixels + struct ImageDimension + { + int m_width; + int m_height; + + ImageDimension(int width, int height) + { + m_width = width; + m_height = height; + } + }; + + //! Typedef for an ImageDimension paired with an integer + using IndexImageDimension = AZStd::pair; + + //! Typedef for a list of ImageDimensions paired with integers + using ImageDimensionData = AZStd::vector; + + //! Typedef to simplify references to TextureAtlas::AtlasCoordinates + using AtlasCoordinates = TextureAtlasNamespace::AtlasCoordinates; + + //! Number of bytes in a pixel + const int bytesPerPixel = 4; + + //! The size of the padded sorting units (important for compression) + const int cellSize = 4; + + //! Indexes of the products + enum class Product + { + TexatlasidxProduct = 0, + DdsProduct = 1 + }; + + //! An asset builder for texture atlases + class AtlasBuilderWorker : public AssetBuilderSDK::AssetBuilderCommandBus::Handler + { + public: + AZ_RTTI(AtlasBuilderWorker, "{79036188-E017-4575-9EC0-8D39CB560EA6}"); + + AtlasBuilderWorker() = default; + ~AtlasBuilderWorker() = default; + + //! Asset Builder Callback Functions + + //! Called by asset processor to gather information on a job for a ".texatlas" file + void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, + AssetBuilderSDK::CreateJobsResponse& response); + //! Called by asset proccessor when it wants us to execute a job + void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, + AssetBuilderSDK::ProcessJobResponse& response); + + //! Returns the job related information used by the builder + static AssetBuilderSDK::JobDescriptor GetJobDescriptor(const AZStd::string& sourceFile, const AtlasBuilderInput& input); + + ////////////////////////////////////////////////////////////////////////// + //! AssetBuilderSDK::AssetBuilderCommandBus interface + void ShutDown() override; // if you get this you must fail all existing jobs and return. + ////////////////////////////////////////////////////////////////////////// + + private: + bool m_isShuttingDown = false; + + //! This is the main function that takes a set of inputs and attempts to pack them into an atlas of a given + //! size. Returns true if succesful, does not update out on failure. + static bool TryPack(const ImageDimensionData& images, + int targetWidth, + int targetHeight, + int padding, + size_t& amountFit, + AZStd::vector& out); + + //! Removes any overlap between slotList and the given item + static void TrimOverlap(AZStd::vector& slotList, AtlasCoordinates item); + + //! Uses the proper tightening method based on the input and returns the maximum number of items that were able to be fit + bool TryTightening(AtlasBuilderInput input, + const ImageDimensionData& images, + int smallestWidth, + int smallestHeight, + int targetArea, + int padding, + int& resultWidth, + int& resultHeight, + size_t& amountFit, + AZStd::vector& out); + + //! Finds the tightest square fit achievable by expanding a square area until a valid fit is found + bool TryTighteningSquare(const ImageDimensionData& images, + int lowerBound, + int maxDimension, + int targetArea, + bool powerOfTwo, + int padding, + int& resultWidth, + int& resultHeight, + size_t& amountFit, + AZStd::vector& out); + + //! Finds the tightest fit achievable by starting with the optimal thin solution and attempting to resize to be + //! a better shape + bool TryTighteningOptimal(const ImageDimensionData& images, + int smallestWidth, + int smallestHeight, + int maxDimension, + int targetArea, + bool powerOfTwo, + int padding, + int& resultWidth, + int& resultHeight, + size_t& amountFit, + AZStd::vector& out); + + //! Sorting logic for adding a slot to a sorted list in order to maintain increasing order + static void InsertInOrder(AZStd::vector& slotList, AtlasCoordinates item); + + //! Misc Logic For Estimating Target Shape + + //! Returns the width of the widest element + static int GetWidest(const ImageDimensionData& imageList); + + //! Returns the height of the tallest area + static int GetTallest(const ImageDimensionData& imageList); + }; + + //! Used for sorting ImageDimensions + static bool operator<(ImageDimension a, ImageDimension b); + + //! Used to expose the ImageDimension in a pair to AZStd::Sort + static bool operator<(IndexImageDimension a, IndexImageDimension b); + + //! Returns true if two coordinate sets overlap + static bool Collides(AtlasCoordinates a, AtlasCoordinates b); + + //! Returns true if item collides with any object in list + static bool Collides(AtlasCoordinates item, AZStd::vector list); + + //! Returns the portion of the second item that overlaps with the first + static AtlasCoordinates GetOverlap(AtlasCoordinates a, AtlasCoordinates b); + + //! Performs an operation that copies a pixel to the output + static void SetPixels(AZ::u8* dest, const AZ::u8* source, int destBytes); + + //! Checks if we can insert an image into a slot + static bool CanInsert(AtlasCoordinates slot, ImageDimension image, int padding, int farRight, int farBot); + + //! Adds the necessary padding to an Atlas Coordinate + static void AddPadding(AtlasCoordinates& slot, int padding, int farRight, int farBot); +} From e975a622b080fadb5977d7576954d0aaea1a666c Mon Sep 17 00:00:00 2001 From: abrmich Date: Wed, 12 May 2021 21:55:19 -0700 Subject: [PATCH 22/38] Move TextureAtlas builder files to the gem --- .../Code/Source/Editor}/AtlasBuilderComponent.cpp | 0 .../Code/Source/Editor}/AtlasBuilderComponent.h | 0 .../Code/Source/Editor}/AtlasBuilderWorker.cpp | 0 .../Code/Source/Editor}/AtlasBuilderWorker.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename Gems/{ImageProcessing/Code/Source/AtlasBuilder => TextureAtlas/Code/Source/Editor}/AtlasBuilderComponent.cpp (100%) rename Gems/{ImageProcessing/Code/Source/AtlasBuilder => TextureAtlas/Code/Source/Editor}/AtlasBuilderComponent.h (100%) rename Gems/{ImageProcessing/Code/Source/AtlasBuilder => TextureAtlas/Code/Source/Editor}/AtlasBuilderWorker.cpp (100%) rename Gems/{ImageProcessing/Code/Source/AtlasBuilder => TextureAtlas/Code/Source/Editor}/AtlasBuilderWorker.h (100%) diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.cpp b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.cpp similarity index 100% rename from Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.cpp rename to Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.cpp diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.h b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.h similarity index 100% rename from Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderComponent.h rename to Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderComponent.h diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.cpp b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp similarity index 100% rename from Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.cpp rename to Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.cpp diff --git a/Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h b/Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.h similarity index 100% rename from Gems/ImageProcessing/Code/Source/AtlasBuilder/AtlasBuilderWorker.h rename to Gems/TextureAtlas/Code/Source/Editor/AtlasBuilderWorker.h From 7ecb00cca1667c7660331b4a3a3b76518388a181 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 20 May 2021 10:46:48 -0700 Subject: [PATCH 23/38] Cleanup jinja formatting and fix log spam --- .../TcpTransport/TcpSocketManager_Select.cpp | 6 ++ .../Source/AutoGen/AutoComponent_Common.jinja | 6 ++ .../Source/AutoGen/AutoComponent_Header.jinja | 48 ++++++------- .../Source/AutoGen/AutoComponent_Source.jinja | 70 ++++++++++--------- ...tionPlayerInputComponent.AutoComponent.xml | 6 +- 5 files changed, 77 insertions(+), 59 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp index 4070f74d67..fc3ada6fc3 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp @@ -49,6 +49,12 @@ namespace AzNetworking m_readerFdSet = m_sourceFdSet; m_writerFdSet = m_sourceFdSet; + if(static_cast(m_maxFd) <= 0 && m_socketFds.empty()) + { + // There are no available sockets to process + return; + } + struct timeval tv = { 0, static_cast(maxBlockMs) * 1000 }; const int32_t selectResult = ::select(static_cast(m_maxFd) + 1, &m_readerFdSet, &m_writerFdSet, nullptr, &tv); if (selectResult < 0) diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja index 15223fba26..61dcacaa94 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja @@ -202,7 +202,9 @@ AZ::Event<{{ ', '.join(paramTypes) }}>& Get{{ PropertyName }}Event() { return m_ #} {% macro DeclareRpcEventGetters(Component, InvokeFrom, HandleOn) %} {% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} {{- DeclareRpcEventGetter(Property, HandleOn) -}} +{% endif %} {% endcall %} {% endmacro %} {# @@ -221,7 +223,9 @@ AZ::Event<{{ ', '.join(paramTypes) }}> m_{{ PropertyName }}Event; #} {% macro DeclareRpcEvents(Component, InvokeFrom, HandleOn) %} {% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} {{- DeclareRpcEvent(Property, HandleOn) -}} +{% endif %} {% endcall %} {% endmacro %} {# @@ -240,7 +244,9 @@ void Signal{{ PropertyName }}({{ ', '.join(paramDefines) }}); #} {% macro DeclareRpcSignals(Component, InvokeFrom, HandleOn) %} {% call(Property) ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} {{- DeclareRpcSignal(Property, HandleOn) -}} +{% endif %} {% endcall %} {% endmacro %} {# diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja index edc67a5da4..0c264c1d36 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Header.jinja @@ -125,7 +125,7 @@ void {{ PropertyName }}({{ ', '.join(paramDefines) }}); {% macro DeclareRpcInvocations(Component, Section, HandleOn, ProctectedSection) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, Section, HandleOn) %} {% if Property.attrib['IsPublic']|booleanTrue == ProctectedSection %} -{{- DeclareRpcInvocation(Property, HandleOn) -}} +{{ DeclareRpcInvocation(Property, HandleOn) -}} {% endif %} {% endcall %} {% endmacro %} @@ -373,8 +373,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Server', true)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', false)|indent(8) -}} {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Client', true)|indent(8) -}} - {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false)|indent(8) }} - {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true)|indent(8) }} + {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', false)|indent(8) -}} + {{ DeclareNetworkPropertyAccessors(Component, 'Authority', 'Autonomous', true)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} @@ -384,19 +384,19 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareRpcInvocations(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Autonomous', false)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Authority', 'Autonomous', true)|indent(8) -}} - {{ DeclareRpcInvocations(Component, 'Authority', 'Client', false)|indent(8) }} - {{ DeclareRpcInvocations(Component, 'Authority', 'Client', true)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Server', 'Authority', false)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Client', 'Authority', false)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Autonomous', 'Authority', false)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Autonomous', false)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Server', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Client', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Autonomous', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Autonomous')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Server', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Client', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ DeclareRpcInvocations(Component, 'Authority', 'Client', false)|indent(8) -}} + {{ DeclareRpcInvocations(Component, 'Authority', 'Client', true)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Server', 'Authority', false)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Client', 'Authority', false)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Autonomous', 'Authority', false)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Autonomous', false)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Server', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Client', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Autonomous', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Autonomous')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Server', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Client', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Autonomous', 'Authority')|indent(8) -}} {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Autonomous')|indent(8) }} {% for Service in Component.iter('ComponentRelation') %} {% if (Service.attrib['HasController']|booleanTrue) and (Service.attrib['Constraint'] != 'Incompatible') %} @@ -405,9 +405,9 @@ namespace {{ Component.attrib['Namespace'] }} {% endfor %} protected: - {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Server', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Client', 'Authority')|indent(8) }} - {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Autonomous', 'Authority')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Server', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Client', 'Authority')|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Autonomous', 'Authority')|indent(8) -}} {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Autonomous')|indent(8) }} }; @@ -449,10 +449,10 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Server', false)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Autonomous', false)|indent(8) -}} - {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) }} + {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', false)|indent(8) -}} {{ DeclareArchetypePropertyGetters(Component)|indent(8) -}} - {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) }} + {{ DeclareRpcInvocations(Component, 'Server', 'Authority', false)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcEventGetters(Component, 'Authority', 'Client')|indent(8) -}} //! MultiplayerComponent interface //! @{ @@ -478,8 +478,8 @@ namespace {{ Component.attrib['Namespace'] }} {{ DeclareNetworkPropertyGetters(Component, 'Autonomous', 'Authority', true)|indent(8) -}} {{ DeclareNetworkPropertyGetters(Component, 'Authority', 'Client', true)|indent(8) -}} {{ DeclareRpcInvocations(Component, 'Server', 'Authority', true)|indent(8) -}} - {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) }} - {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) }} + {{ AutoComponentMacros.DeclareRpcHandlers(Component, 'Authority', 'Client', false)|indent(8) -}} + {{ AutoComponentMacros.DeclareRpcSignals(Component, 'Authority', 'Client')|indent(8) -}} {{ AutoComponentMacros.DeclareRpcEvents(Component, 'Authority', 'Client')|indent(8) }} {% for Service in Component.iter('ComponentRelation') %} {% if Service.attrib['Constraint'] != 'Incompatible' %} diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index a9d2ecf3de..b026971654 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -332,8 +332,10 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo {% macro DefineRpcInvocations(Component, ClassName, InvokeFrom, HandleOn, ProctectedSection) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} {% if Property.attrib['IsPublic']|booleanTrue == ProctectedSection %} -{{ DefineRpcInvocation(Component, ClassName, Property, InvokeFrom, HandleOn) }} -{{ DefineRpcSignal(Component, ClassName, Property, InvokeFrom) }} +{{ DefineRpcInvocation(Component, ClassName, Property, InvokeFrom, HandleOn) -}} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} +{{ DefineRpcSignal(Component, ClassName, Property, InvokeFrom) -}} +{% endif %} {% endif %} {% endcall %} {% endmacro %} @@ -342,7 +344,7 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo #} {% macro ReflectRpcInvocations(Component, ClassName, InvokeFrom, HandleOn) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} -{% if Property.attrib['CanScript']|booleanTrue == true %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} {% set paramNames = [] %} {% set paramTypes = [] %} {% set paramDefines = [] %} @@ -358,19 +360,19 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo #} {% macro ReflectRpcEventDescs(Component, ClassName, InvokeFrom, HandleOn) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} {% set paramNames = [] %} {% set paramTypes = [] %} {% set paramDefines = [] %} {{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} - // Create the BehaviorAZEventDescription needed to reflect the // Get{{ UpperFirst(Property.attrib['Name']) }}Event method to the BehaviorContext without errors AZ::BehaviorAzEventDescription {{ LowerFirst(Property.attrib['Name']) }}EventDesc; {{ LowerFirst(Property.attrib['Name']) }}EventDesc.m_eventName = "{{ UpperFirst(Property.attrib['Name']) }} Notify Event"; - {% for Param in Property.iter('Param') %} +{% for Param in Property.iter('Param') %} {{ LowerFirst(Property.attrib['Name']) }}EventDesc.m_parameterNames.push_back("{{ LowerFirst(Param.attrib['Name']) }}"); - {% endfor %} - +{% endfor %} +{% endif %} {% endcall %} {% endmacro %} {# @@ -378,6 +380,7 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo #} {% macro ReflectRpcEvents(Component, ClassName, InvokeFrom, HandleOn) %} {% call(Property) AutoComponentMacros.ParseRemoteProcedures(Component, InvokeFrom, HandleOn) %} +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} {% set paramNames = [] %} {% set paramTypes = [] %} {% set paramDefines = [] %} @@ -387,6 +390,7 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo return self->m_controller->Get{{ UpperFirst(Property.attrib['Name']) }}Event(); }) ->Attribute(AZ::Script::Attributes::AzEventDescription, AZStd::move({{ LowerFirst(Property.attrib['Name']) }}EventDesc)) +{% endif %} {% endcall %} {% endmacro %} {# @@ -413,7 +417,9 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Authority, "Entity proxy does not have authority"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); +{% endif %} } {% if Property.attrib['IsReliable']|booleanTrue %} {# if the rpc is not reliable we can simply drop it, also note message reliability type is default reliable in EntityRpcMessage #} @@ -428,7 +434,9 @@ case {{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ Upp { AZ_Assert(GetNetBindComponent()->GetNetEntityRole() == Multiplayer::NetEntityRole::Autonomous, "Entity proxy does not have autonomy"); m_controller->Handle{{ UpperFirst(Property.attrib['Name']) }}(invokingConnection, {{ ', '.join(rpcParamList) }}); +{% if Property.attrib['GenerateEventBindings']|booleanTrue == true %} m_controller->Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); +{% endif %} } {% else %} Handle{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(rpcParamList) }}); @@ -1357,32 +1365,30 @@ namespace {{ Component.attrib['Namespace'] }} {{ ReflectRpcEventDescs(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} {{ ReflectRpcEventDescs(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} - {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} - - behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") - ->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}") - ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") - - // Reflect Network Properties Get, Set, and OnChanged methods - {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Authority', ComponentName) | indent(16) -}} - {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Server', ComponentName) | indent(16) -}} - {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Client', ComponentName) | indent(16) -}} - {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Autonomous', ComponentName) | indent(16) -}} - {{ DefineNetworkPropertyBehaviorReflection(Component, 'Autonomous', 'Authority', ComponentName) | indent(16) -}} + {{ ReflectRpcEventDescs(Component, ComponentName, 'Authority', 'Client')|indent(4) }} + behaviorContext->Class<{{ ComponentName }}>("{{ ComponentName }}") + ->Attribute(AZ::Script::Attributes::Module, "{{ LowerFirst(Component.attrib['Namespace']) }}") + ->Attribute(AZ::Script::Attributes::Category, "{{ UpperFirst(Component.attrib['Namespace']) }}") + + // Reflect Network Properties Get, Set, and OnChanged methods + {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Authority', ComponentName) | indent(16) -}} + {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Server', ComponentName) | indent(16) -}} + {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Client', ComponentName) | indent(16) -}} + {{ DefineNetworkPropertyBehaviorReflection(Component, 'Authority', 'Autonomous', ComponentName) | indent(16) -}} + {{ DefineNetworkPropertyBehaviorReflection(Component, 'Autonomous', 'Authority', ComponentName) | indent(16) -}} - // Reflect RPCs - {{ ReflectRpcInvocations(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} - {{ ReflectRpcInvocations(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} - {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} - {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} - - {{ ReflectRpcEvents(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} - {{ ReflectRpcEvents(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} - {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} - {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} - - {{- DefineArchetypePropertyBehaviorReflection(Component, ComponentName) | indent(16) }} - ; + // Reflect RPCs + {{ ReflectRpcInvocations(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcInvocations(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Server', 'Authority')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Autonomous', 'Authority')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Autonomous')|indent(4) -}} + {{ ReflectRpcEvents(Component, ComponentName, 'Authority', 'Client')|indent(4) -}} + + {{- DefineArchetypePropertyBehaviorReflection(Component, ComponentName) | indent(16) }} + ; } } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml index 7322dbe923..9e1c4a6d58 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml +++ b/Gems/Multiplayer/Code/Source/AutoGen/LocalPredictionPlayerInputComponent.AutoComponent.xml @@ -19,18 +19,18 @@ - + - + - + From d26d24d9bd39efc4e07196967eb527acba13a791 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 20 May 2021 11:38:06 -0700 Subject: [PATCH 24/38] Remove test RTTIs --- .../AzNetworking/AzNetworking/DataStructures/ByteBuffer.h | 2 -- Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h | 2 -- .../Code/Source/NetworkInput/NetworkInputMigrationVector.h | 1 - 3 files changed, 5 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h index 892c63079d..89c8f41d55 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h @@ -23,8 +23,6 @@ namespace AzNetworking class ByteBuffer { public: - AZ_RTTI(ByteBuffer, "{CD6BFA48-290D-44B4-B376-2463F526BF1F}"); - ByteBuffer() = default; ~ByteBuffer() = default; diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h index 6053040e08..293fb18928 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputArray.h @@ -24,8 +24,6 @@ namespace Multiplayer class NetworkInputArray final { public: - AZ_RTTI(NetworkInputArray, "{4908CE9F-8BCD-47C8-837F-09DC695ED2D7}"); - static constexpr uint32_t MaxElements = 8; // Never try to replicate a list larger than this amount NetworkInputArray(); diff --git a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h index f08bd023a0..e5f8fdf648 100644 --- a/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h +++ b/Gems/Multiplayer/Code/Source/NetworkInput/NetworkInputMigrationVector.h @@ -24,7 +24,6 @@ namespace Multiplayer class NetworkInputMigrationVector final { public: - AZ_RTTI(NetworkInputMigrationVector, "{BDF19B57-A11F-4185-9FA9-86AC12E67414}"); static constexpr uint32_t MaxElements = 90; // Never try to migrate a list larger than this amount, bumped up to handle DTLS connection time NetworkInputMigrationVector(); From 80e12a2df3a3bd214223f8566597343fdc61da78 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 20 May 2021 11:39:25 -0700 Subject: [PATCH 25/38] Fix whitespace delta --- .../AzNetworking/AzNetworking/DataStructures/ByteBuffer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h index 89c8f41d55..3d9259a256 100644 --- a/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h +++ b/Code/Framework/AzNetworking/AzNetworking/DataStructures/ByteBuffer.h @@ -23,6 +23,7 @@ namespace AzNetworking class ByteBuffer { public: + ByteBuffer() = default; ~ByteBuffer() = default; From ba6f9867f6d83a267350011477a69b782ed7b6f4 Mon Sep 17 00:00:00 2001 From: puvvadar Date: Thu, 20 May 2021 12:48:49 -0700 Subject: [PATCH 26/38] Reorder TCP Select early exit --- .../AzNetworking/TcpTransport/TcpSocketManager_Select.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp index fc3ada6fc3..e8b2527638 100644 --- a/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/TcpTransport/TcpSocketManager_Select.cpp @@ -46,15 +46,15 @@ namespace AzNetworking void TcpSocketManager::ProcessEvents(AZ::TimeMs maxBlockMs, const SocketEventCallback& readCallback, const SocketEventCallback& writeCallback) { - m_readerFdSet = m_sourceFdSet; - m_writerFdSet = m_sourceFdSet; - if(static_cast(m_maxFd) <= 0 && m_socketFds.empty()) { // There are no available sockets to process return; } + m_readerFdSet = m_sourceFdSet; + m_writerFdSet = m_sourceFdSet; + struct timeval tv = { 0, static_cast(maxBlockMs) * 1000 }; const int32_t selectResult = ::select(static_cast(m_maxFd) + 1, &m_readerFdSet, &m_writerFdSet, nullptr, &tv); if (selectResult < 0) From 87b1a19df4a1fc7b148002d743275ab863751823 Mon Sep 17 00:00:00 2001 From: greerdv Date: Thu, 20 May 2021 22:04:37 +0100 Subject: [PATCH 27/38] remove vector scale from Blast in preparation for removal from Transform --- .../Configuration/SimulatedBodyConfiguration.cpp | 13 +++++++++++-- .../Configuration/SimulatedBodyConfiguration.h | 1 - Gems/Blast/Code/Source/Actor/BlastActorDesc.h | 11 ++++++----- Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp | 5 +++-- Gems/Blast/Code/Source/Actor/BlastActorImpl.h | 1 + .../Code/Source/Components/BlastFamilyComponent.cpp | 1 + .../Source/Editor/EditorBlastFamilyComponent.cpp | 1 + .../Source/Editor/EditorBlastMeshDataComponent.cpp | 1 + Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp | 4 ++-- Gems/Blast/Code/Tests/BlastFamilyTest.cpp | 2 +- 10 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.cpp index 01bff3ccbb..4aea643c6c 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.cpp @@ -30,6 +30,16 @@ namespace AzPhysics classElement.AddElementWithData(context, "name", name); return true; } + + bool SimulatedBodyVersionConverter([[maybe_unused]] AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + if (classElement.GetVersion() <= 1) + { + classElement.RemoveElementByName(AZ_CRC_CE("scale")); + } + + return true; + } } AZ_CLASS_ALLOCATOR_IMPL(SimulatedBodyConfiguration, AZ::SystemAllocator, 0); @@ -40,11 +50,10 @@ namespace AzPhysics { serializeContext->ClassDeprecate("WorldBodyConfiguration", "{6EEB377C-DC60-4E10-AF12-9626C0763B2D}", &Internal::DeprecateWorldBodyConfiguration); serializeContext->Class() - ->Version(1) + ->Version(2, &Internal::SimulatedBodyVersionConverter) ->Field("name", &SimulatedBodyConfiguration::m_debugName) ->Field("position", &SimulatedBodyConfiguration::m_position) ->Field("orientation", &SimulatedBodyConfiguration::m_orientation) - ->Field("scale", &SimulatedBodyConfiguration::m_scale) ->Field("entityId", &SimulatedBodyConfiguration::m_entityId) ->Field("startSimulationEnabled", &SimulatedBodyConfiguration::m_startSimulationEnabled) ; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h index 6862bfccb8..5ac920ab9d 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SimulatedBodyConfiguration.h @@ -38,7 +38,6 @@ namespace AzPhysics // Basic initial settings. AZ::Vector3 m_position = AZ::Vector3::CreateZero(); AZ::Quaternion m_orientation = AZ::Quaternion::CreateIdentity(); - AZ::Vector3 m_scale = AZ::Vector3::CreateOne(); bool m_startSimulationEnabled = true; // Entity/object association. diff --git a/Gems/Blast/Code/Source/Actor/BlastActorDesc.h b/Gems/Blast/Code/Source/Actor/BlastActorDesc.h index a58fbd04df..67a3c8d338 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorDesc.h +++ b/Gems/Blast/Code/Source/Actor/BlastActorDesc.h @@ -32,10 +32,11 @@ namespace Blast Physics::MaterialId m_physicsMaterialId; AZ::Vector3 m_parentLinearVelocity = AZ::Vector3::CreateZero(); AZ::Vector3 m_parentCenterOfMass = AZ::Vector3::CreateZero(); - AzPhysics::RigidBodyConfiguration m_bodyConfiguration; //! Either rigid dynamic or rigid static - AZStd::vector m_chunkIndices; //! Chunks that are going to simulate this actor. - AZStd::shared_ptr m_entity; //! Entity that the actor should use to simulate rigid body - bool m_isStatic = false; //! Denotes whether actor should be simulated by a static or dynamic rigid body. - bool m_isLeafChunk = false; //! Denotes whether this actor represented by a single leaf chunk. + AzPhysics::RigidBodyConfiguration m_bodyConfiguration; //!< Either rigid dynamic or rigid static + AZStd::vector m_chunkIndices; //!< Chunks that are going to simulate this actor. + AZStd::shared_ptr m_entity; //!< Entity that the actor should use to simulate rigid body + bool m_isStatic = false; //!< Denotes whether actor should be simulated by a static or dynamic rigid body. + bool m_isLeafChunk = false; //!< Denotes whether this actor represented by a single leaf chunk. + float m_scale = 1.0f; //!< Uniform scale applied to the actor. }; } // namespace Blast diff --git a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp index 1f05793b4b..0336ae8c09 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp +++ b/Gems/Blast/Code/Source/Actor/BlastActorImpl.cpp @@ -45,6 +45,7 @@ namespace Blast , m_parentLinearVelocity(desc.m_parentLinearVelocity) , m_parentCenterOfMass(desc.m_parentCenterOfMass) , m_bodyConfiguration(desc.m_bodyConfiguration) + , m_scale(desc.m_scale) { // Store pointer to ourselves in the blast toolkit actor's userData m_tkActor.userData = this; @@ -67,7 +68,7 @@ namespace Blast auto transform = AZ::Transform::CreateFromQuaternionAndTranslation( m_bodyConfiguration.m_orientation, m_bodyConfiguration.m_position); - transform.MultiplyByScale(m_bodyConfiguration.m_scale); + transform.MultiplyByScale(AZ::Vector3(m_scale)); AZ::TransformBus::Event(m_entity->GetId(), &AZ::TransformInterface::SetWorldTM, transform); @@ -130,7 +131,7 @@ namespace Blast Physics::NativeShapeConfiguration shapeConfiguration; shapeConfiguration.m_nativeShapePtr = reinterpret_cast(const_cast(&subchunk.geometry)->convexMesh); - shapeConfiguration.m_nativeShapeScale = m_bodyConfiguration.m_scale; + shapeConfiguration.m_nativeShapeScale = AZ::Vector3(m_scale); AZStd::shared_ptr shape = AZ::Interface::Get()->CreateShape( colliderConfiguration, shapeConfiguration); diff --git a/Gems/Blast/Code/Source/Actor/BlastActorImpl.h b/Gems/Blast/Code/Source/Actor/BlastActorImpl.h index 3b686b3641..e3ba8880be 100644 --- a/Gems/Blast/Code/Source/Actor/BlastActorImpl.h +++ b/Gems/Blast/Code/Source/Actor/BlastActorImpl.h @@ -77,5 +77,6 @@ namespace Blast AZ::Vector3 m_parentLinearVelocity = AZ::Vector3::CreateZero(); AZ::Vector3 m_parentCenterOfMass = AZ::Vector3::CreateZero(); AzPhysics::RigidBodyConfiguration m_bodyConfiguration; + float m_scale = 1.0f; }; } // namespace Blast diff --git a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp index 841163d2ab..0f2668442c 100644 --- a/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Components/BlastFamilyComponent.cpp @@ -147,6 +147,7 @@ namespace Blast void BlastFamilyComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC("BlastFamilyService")); + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); } void BlastFamilyComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp index fc6967b96e..9241449483 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastFamilyComponent.cpp @@ -85,6 +85,7 @@ namespace Blast void EditorBlastFamilyComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC_CE("BlastFamilyService")); + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); } void EditorBlastFamilyComponent::OnAssetReady(AZ::Data::Asset asset) diff --git a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp index f8fd97dc52..77788b6aee 100644 --- a/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp +++ b/Gems/Blast/Code/Source/Editor/EditorBlastMeshDataComponent.cpp @@ -43,6 +43,7 @@ namespace Blast AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC("BlastMeshDataService")); + incompatible.push_back(AZ_CRC_CE("NonUniformScaleService")); } void EditorBlastMeshDataComponent::Reflect(AZ::ReflectContext* context) diff --git a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp index 87865d0e08..5fe1e0ab30 100644 --- a/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp +++ b/Gems/Blast/Code/Source/Family/BlastFamilyImpl.cpp @@ -202,7 +202,7 @@ namespace Blast if (parentBody) { parentTransform = parentBody->GetTransform(); - parentTransform.MultiplyByScale(m_initialTransform.GetScale()); + parentTransform.MultiplyByScale(AZ::Vector3(m_initialTransform.GetScale().GetMaxElement())); } else { @@ -239,7 +239,6 @@ namespace Blast AzPhysics::RigidBodyConfiguration configuration; configuration.m_position = transform.GetTranslation(); configuration.m_orientation = transform.GetRotation(); - configuration.m_scale = transform.GetScale(); configuration.m_ccdEnabled = m_actorConfiguration.m_isCcdEnabled; configuration.m_startSimulationEnabled = m_actorConfiguration.m_isSimulated; configuration.m_initialAngularVelocity = AZ::Vector3::CreateZero(); @@ -255,6 +254,7 @@ namespace Blast actorDesc.m_parentCenterOfMass = transform.GetTranslation(); actorDesc.m_parentLinearVelocity = AZ::Vector3::CreateZero(); actorDesc.m_bodyConfiguration = configuration; + actorDesc.m_scale = transform.GetScale().GetMaxElement(); return actorDesc; } diff --git a/Gems/Blast/Code/Tests/BlastFamilyTest.cpp b/Gems/Blast/Code/Tests/BlastFamilyTest.cpp index 2e6fd7f2bb..06af890a44 100644 --- a/Gems/Blast/Code/Tests/BlastFamilyTest.cpp +++ b/Gems/Blast/Code/Tests/BlastFamilyTest.cpp @@ -137,7 +137,7 @@ namespace Blast .Times(1) .WillOnce(Return(false)); - AZ::Transform transform = AZ::Transform::CreateScale(AZ::Vector3::CreateOne()); + AZ::Transform transform = AZ::Transform::CreateIdentity(); blastFamily->Spawn(transform); } From 01f31cdc564a92e315e55bd313c3bba3476d58f7 Mon Sep 17 00:00:00 2001 From: chiyenteng <82238204+chiyenteng@users.noreply.github.com> Date: Thu, 20 May 2021 14:44:27 -0700 Subject: [PATCH 28/38] Move the basic Prefab workflows out from behind the WIP flag (#769) * Move the basic Prefab workflows out from behind the WIP flag --- .../UI/Prefab/PrefabIntegrationManager.cpp | 95 ++++++++----------- 1 file changed, 38 insertions(+), 57 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index bc7afbf085..3edc190fb7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -93,28 +93,12 @@ namespace AzToolsFramework EditorContextMenuBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); - - bool prefabWipFeaturesEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - - if (prefabWipFeaturesEnabled) - { - AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); - } + AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension); } PrefabIntegrationManager::~PrefabIntegrationManager() { - bool prefabWipFeaturesEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - - if (prefabWipFeaturesEnabled) - { - AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); - } - + AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect(); AZ::Interface::Unregister(this); PrefabInstanceContainerNotificationBus::Handler::BusDisconnect(); EditorContextMenuBus::Handler::BusDisconnect(); @@ -137,66 +121,63 @@ namespace AzToolsFramework void PrefabIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu) const { - bool prefabWipFeaturesEnabled = false; - AzFramework::ApplicationRequests::Bus::BroadcastResult( - prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); - AzToolsFramework::EntityIdList selectedEntities; AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult( selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities); - if (prefabWipFeaturesEnabled) + bool prefabWipFeaturesEnabled = false; + AzFramework::ApplicationRequests::Bus::BroadcastResult( + prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled); + + // Create Prefab { - // Create Prefab + if (!selectedEntities.empty()) { - if (!selectedEntities.empty()) + // Hide if the only selected entity is the Level Container + if (selectedEntities.size() > 1 || !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])) { - // Hide if the only selected entity is the Level Container - if (selectedEntities.size() > 1 || !s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntities[0])) - { - bool layerInSelection = false; + bool layerInSelection = false; - for (AZ::EntityId entityId : selectedEntities) + for (AZ::EntityId entityId : selectedEntities) + { + if (!layerInSelection) { - if (!layerInSelection) - { - AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult( - layerInSelection, entityId, - &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer); + AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult( + layerInSelection, entityId, + &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer); - if (layerInSelection) - { - break; - } + if (layerInSelection) + { + break; } } + } - // Layers can't be in prefabs. - if (!layerInSelection) - { - QAction* createAction = menu->addAction(QObject::tr("Create Prefab...")); - createAction->setToolTip(QObject::tr("Creates a prefab out of the currently selected entities.")); + // Layers can't be in prefabs. + if (!layerInSelection) + { + QAction* createAction = menu->addAction(QObject::tr("Create Prefab...")); + createAction->setToolTip(QObject::tr("Creates a prefab out of the currently selected entities.")); - QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] { - ContextMenu_CreatePrefab(selectedEntities); - }); - } + QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] { + ContextMenu_CreatePrefab(selectedEntities); + }); } } } + } - // Instantiate Prefab - { - QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab...")); - instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene.")); - - QObject::connect( - instantiateAction, &QAction::triggered, instantiateAction, [this] { ContextMenu_InstantiatePrefab(); }); - } + // Instantiate Prefab + { + QAction* instantiateAction = menu->addAction(QObject::tr("Instantiate Prefab...")); + instantiateAction->setToolTip(QObject::tr("Instantiates a prefab file in the scene.")); - menu->addSeparator(); + QObject::connect( + instantiateAction, &QAction::triggered, instantiateAction, [this] { ContextMenu_InstantiatePrefab(); }); } + menu->addSeparator(); + bool itemWasShown = false; // Edit/Save Prefab From 34be1caa99b67130addd5d2a7238dad0a9363433 Mon Sep 17 00:00:00 2001 From: Vincent Liu <5900509+onecent1101@users.noreply.github.com> Date: Thu, 20 May 2021 14:53:26 -0700 Subject: [PATCH 29/38] [HOTFIX] Fix broken nightly build because of invalid module name (#851) --- Gems/AWSClientAuth/Code/Source/AWSClientAuthModule.cpp | 2 +- Gems/AWSMetrics/Code/Source/AWSMetricsModule.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gems/AWSClientAuth/Code/Source/AWSClientAuthModule.cpp b/Gems/AWSClientAuth/Code/Source/AWSClientAuthModule.cpp index 9f027613b6..ccf71cb89e 100644 --- a/Gems/AWSClientAuth/Code/Source/AWSClientAuthModule.cpp +++ b/Gems/AWSClientAuth/Code/Source/AWSClientAuthModule.cpp @@ -39,4 +39,4 @@ namespace AWSClientAuth // DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM // The first parameter should be GemName_GemIdLower // The second should be the fully qualified name of the class above -AZ_DECLARE_MODULE_CLASS(AWSClientAuth_c74f2756f5874c0d8d29646dfc9cb0ad, AWSClientAuth::AWSClientAuthModule) +AZ_DECLARE_MODULE_CLASS(Gem_AWSClientAuth, AWSClientAuth::AWSClientAuthModule) diff --git a/Gems/AWSMetrics/Code/Source/AWSMetricsModule.cpp b/Gems/AWSMetrics/Code/Source/AWSMetricsModule.cpp index 4967202483..f180e3e248 100644 --- a/Gems/AWSMetrics/Code/Source/AWSMetricsModule.cpp +++ b/Gems/AWSMetrics/Code/Source/AWSMetricsModule.cpp @@ -32,4 +32,4 @@ namespace AWSMetrics // DO NOT MODIFY THIS LINE UNLESS YOU RENAME THE GEM // The first parameter should be GemName_GemIdLower // The second should be the fully qualified name of the class above -AZ_DECLARE_MODULE_CLASS(AWSMetrics_cc6fc7a18fc047039a369a26100fcbbe, AWSMetrics::AWSMetricsModule) +AZ_DECLARE_MODULE_CLASS(Gem_AWSMetrics, AWSMetrics::AWSMetricsModule) From cfbae9a18b730201bedd2f44fe0648f9cc8d7875 Mon Sep 17 00:00:00 2001 From: Alex Peterson <26804013+AMZN-alexpete@users.noreply.github.com> Date: Thu, 20 May 2021 15:13:39 -0700 Subject: [PATCH 30/38] add/remove gem python bindings --- .../ProjectManager/Source/PythonBindings.cpp | 36 +++++++++++++++++++ .../ProjectManager/Source/PythonBindings.h | 2 ++ .../Source/PythonBindingsInterface.h | 16 +++++++++ 3 files changed, 54 insertions(+) diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 2c2c143845..e4642c95e0 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -508,6 +508,42 @@ namespace O3DE::ProjectManager } } + bool PythonBindings::AddGemToProject(const QString& gemPath, const QString& projectPath) + { + bool result = ExecuteWithLock([&] { + pybind11::str pyGemPath = gemPath.toStdString(); + pybind11::str pyProjectPath = projectPath.toStdString(); + + m_registration.attr("add_gem_to_project")( + pybind11::none(), // gem_name + pyGemPath, + pybind11::none(), // gem_target + pybind11::none(), // project_name + pyProjectPath + ); + }); + + return result; + } + + bool PythonBindings::RemoveGemFromProject(const QString& gemPath, const QString& projectPath) + { + bool result = ExecuteWithLock([&] { + pybind11::str pyGemPath = gemPath.toStdString(); + pybind11::str pyProjectPath = projectPath.toStdString(); + + m_registration.attr("remove_gem_to_project")( + pybind11::none(), // gem_name + pyGemPath, + pybind11::none(), // gem_target + pybind11::none(), // project_name + pyProjectPath + ); + }); + + return result; + } + bool PythonBindings::UpdateProject([[maybe_unused]] const ProjectInfo& projectInfo) { return false; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.h b/Code/Tools/ProjectManager/Source/PythonBindings.h index ffabf99b49..892e13a65b 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.h +++ b/Code/Tools/ProjectManager/Source/PythonBindings.h @@ -47,6 +47,8 @@ namespace O3DE::ProjectManager AZ::Outcome GetProject(const QString& path) override; AZ::Outcome> GetProjects() override; bool UpdateProject(const ProjectInfo& projectInfo) override; + bool AddGemToProject(const QString& gemPath, const QString& projectPath) override; + bool RemoveGemFromProject(const QString& gemPath, const QString& projectPath) override; // ProjectTemplate AZ::Outcome> GetProjectTemplates() override; diff --git a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h index 2377da1461..b5c8f1a76a 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h +++ b/Code/Tools/ProjectManager/Source/PythonBindingsInterface.h @@ -96,6 +96,22 @@ namespace O3DE::ProjectManager */ virtual bool UpdateProject(const ProjectInfo& projectInfo) = 0; + /** + * Add a gem to a project + * @param gemPath the absolute path to the gem + * @param projectPath the absolute path to the project + * @return true on success, false on failure + */ + virtual bool AddGemToProject(const QString& gemPath, const QString& projectPath) = 0; + + /** + * Remove gem to a project + * @param gemPath the absolute path to the gem + * @param projectPath the absolute path to the project + * @return true on success, false on failure + */ + virtual bool RemoveGemFromProject(const QString& gemPath, const QString& projectPath) = 0; + // Project Templates From 42ccdf05725109c31ddde2ebe51ca5c45743a65b Mon Sep 17 00:00:00 2001 From: Steve Pham <82231385+spham-amzn@users.noreply.github.com> Date: Thu, 20 May 2021 17:08:14 -0700 Subject: [PATCH 31/38] Python3 installation script for Linux machines (#832) --- .../Platform/Linux/install-ubuntu-python3.sh | 68 +++++++++++++++++++ .../Platform/Linux/requirements.txt | 43 ++++++++++++ 2 files changed, 111 insertions(+) create mode 100755 scripts/build/build_node/Platform/Linux/install-ubuntu-python3.sh create mode 100644 scripts/build/build_node/Platform/Linux/requirements.txt diff --git a/scripts/build/build_node/Platform/Linux/install-ubuntu-python3.sh b/scripts/build/build_node/Platform/Linux/install-ubuntu-python3.sh new file mode 100755 index 0000000000..d0855aff43 --- /dev/null +++ b/scripts/build/build_node/Platform/Linux/install-ubuntu-python3.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +# This script must be run as root +if [[ $EUID -ne 0 ]] +then + echo "This script must be run as root (sudo)" + exit 1 +fi + +# Install python3 if necessary +python3 --version >/dev/null 2>&1 +if [ $? -ne 0 ] +then + echo Installing Python3 + apt-get install python3 + + if [ $? -ne 0 ] + then + echo Error installing python3 + exit 1 + fi + +else + PYTHON_VERSION=$(python3 --version) + echo Python3 already installed \($PYTHON_VERSION\) +fi + +# Install python3 pip if necessary +pip3 --version >/dev/null 2>&1 +if [ $? -ne 0 ] +then + echo Installing Python3 PIP + apt-get install -y python3-pip + + if [ $? -ne 0 ] + then + echo Error installing python3 + exit 1 + fi + +else + PYTHON_VERSION=$(pip3 --version | awk '{print $2}') + echo Python3 Pip already installed \($PYTHON_VERSION\) +fi + + +# Read from the package list and process each package +PIP_REQUIREMENTS_FILE=requirements.txt + +pip3 install -r $PIP_REQUIREMENTS_FILE +if [ $? -ne 0 ] +then + echo Error installing python3 + exit 1 +fi + + +echo Python3 setup complete +exit 0 diff --git a/scripts/build/build_node/Platform/Linux/requirements.txt b/scripts/build/build_node/Platform/Linux/requirements.txt new file mode 100644 index 0000000000..1a466c03f8 --- /dev/null +++ b/scripts/build/build_node/Platform/Linux/requirements.txt @@ -0,0 +1,43 @@ +boto3==1.16.18 \ + --hash=sha256:51c419d890ae216b9b031be31f3182739dc3deb5b64351f286bffca2818ddb35 \ + --hash=sha256:d70d21ea137d786e84124639a62be42f92f4b09472ebfb761156057c92dc5366 +psutil==5.8.0 \ + --hash=sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64 \ + --hash=sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131 \ + --hash=sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c \ + --hash=sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6 \ + --hash=sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023 \ + --hash=sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df \ + --hash=sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394 \ + --hash=sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4 \ + --hash=sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b \ + --hash=sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2 \ + --hash=sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d \ + --hash=sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65 \ + --hash=sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d \ + --hash=sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef \ + --hash=sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7 \ + --hash=sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60 \ + --hash=sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6 \ + --hash=sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8 \ + --hash=sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b \ + --hash=sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d \ + --hash=sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac \ + --hash=sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935 \ + --hash=sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d \ + --hash=sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28 \ + --hash=sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876 \ + --hash=sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0 \ + --hash=sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3 \ + --hash=sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563 +requests==2.25.1 \ + --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ + --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e +traceback2==1.4.0 \ + --hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 \ + --hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23 +urllib3==1.26.4 \ + --hash=sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df \ + --hash=sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937 +tempfile2==0.1.1 \ + --hash=sha256:77fdd256c16804053d3d588168b79595099ea5e874c3fb171893b0ababd10340 From e19f1c0147ac3883f471019636972a18455f4309 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Thu, 20 May 2021 17:09:05 -0700 Subject: [PATCH 32/38] Ensure AtomFont loads with the Editor -Use PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE which will correctly be set to GEM_MODULE to get loaded at runtime with non-monolithic builds instead of PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE -Removed the unneeded entries from AutomatedTesting's runtime config now that this is fixed (AtomBridge will bring it in again) --- AutomatedTesting/Gem/Code/runtime_dependencies.cmake | 1 - AutomatedTesting/Gem/Code/tool_dependencies.cmake | 1 - Gems/AtomLyIntegration/AtomFont/Code/CMakeLists.txt | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake index 15715f2136..33c2bf8d5f 100644 --- a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake @@ -43,7 +43,6 @@ set(GEM_DEPENDENCIES Gem::GradientSignal Gem::Vegetation Gem::Atom_AtomBridge - Gem::AtomFont Gem::NvCloth Gem::Blast Gem::AWSCore diff --git a/AutomatedTesting/Gem/Code/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/tool_dependencies.cmake index 1c0db5753b..c8eccab947 100644 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/tool_dependencies.cmake @@ -55,7 +55,6 @@ set(GEM_DEPENDENCIES Gem::Atom_RHI.Private Gem::Atom_Feature_Common.Editor Gem::Atom_AtomBridge.Editor - Gem::AtomFont Gem::NvCloth.Editor Gem::Blast.Editor Gem::AWSCore.Editor diff --git a/Gems/AtomLyIntegration/AtomFont/Code/CMakeLists.txt b/Gems/AtomLyIntegration/AtomFont/Code/CMakeLists.txt index 1066cc33e8..d3b8c8cde7 100644 --- a/Gems/AtomLyIntegration/AtomFont/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/AtomFont/Code/CMakeLists.txt @@ -12,7 +12,7 @@ ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME}) ly_add_target( - NAME AtomFont ${PAL_TRAIT_MONOLITHIC_DRIVEN_LIBRARY_TYPE} + NAME AtomFont ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} NAMESPACE Gem FILES_CMAKE atomfont_files.cmake From 8028cbbe39953c238a2a32d14bb0b17e4c9033df Mon Sep 17 00:00:00 2001 From: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> Date: Thu, 20 May 2021 20:41:30 -0500 Subject: [PATCH 33/38] Fixed issue where the SettingsRegistryImpl::LessThan function would set the collisionFound boolean to true when comparing two elements that happened to be at the same address via std::sort. (#857) In reality there is no such collision and the comparisons needs to early return with false, but not change the collisionFound flag. --- .../AzCore/AzCore/Settings/SettingsRegistryImpl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp index 2421c75be3..dbd8df4df1 100644 --- a/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp +++ b/Code/Framework/AzCore/AzCore/Settings/SettingsRegistryImpl.cpp @@ -880,6 +880,13 @@ namespace AZ const Specializations& specializations, const rapidjson::Pointer& historyPointer, AZStd::string_view folderPath) { using namespace rapidjson; + + if (&lhs == &rhs) + { + // Early return to avoid setting the collisionFound reference to true + // std::sort is allowed to pass in the same memory address for the left and right elements + return false; + } AZ_Assert(!lhs.m_tags.empty(), "Comparing a settings file without at least a name tag."); AZ_Assert(!rhs.m_tags.empty(), "Comparing a settings file without at least a name tag."); From bffc72794b3222f4ccae60b5321e41f865886a6e Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Thu, 20 May 2021 22:24:19 -0700 Subject: [PATCH 34/38] ATOM-15597 Accessing Material Instance Panel Crashes Editor There were two issues fixed here. First, I broke the material inspector with my changes at 53188a12da7d3ce90de64a0d184b6a5f9df613d8 which added support for hiding entire property groups. I'm not sure how this happened because I definitely tested the MaterialComponent's material inspector. Perhaps there was a bad merge or something otherwise got clobbered after testing and before committing. Anyway, this issue was I accidentally delete the code that prepared the list of material properties for functor processing. The second issue was the MaterialFunctor class needs to return null when metadata can't be found; it was proceeding to dereference an end iterator. Testing: Successfully opened the material inspector through the MaterialComponent. Was able to change property flags in the inspector and see other properties change visibility as expected. --- .../RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp | 2 ++ .../Source/Material/EditorMaterialComponentInspector.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp index a41ab9eea6..2979819aa6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp @@ -334,6 +334,7 @@ namespace AZ if (it == m_propertyMetadata.end()) { AZ_Error("MaterialFunctor", false, "Couldn't find metadata for material property: %s.", propertyName.GetCStr()); + return nullptr; } return &it->second; @@ -345,6 +346,7 @@ namespace AZ if (it == m_propertyGroupMetadata.end()) { AZ_Error("MaterialFunctor", false, "Couldn't find metadata for material property group: %s.", propertyGroupName.GetCStr()); + return nullptr; } return &it->second; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 31d69569db..3192900ca4 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -304,6 +304,11 @@ namespace AZ for (auto& groupPair : m_groups) { AZ::RPI::MaterialPropertyGroupDynamicMetadata& metadata = propertyGroupDynamicMetadata[AZ::Name{groupPair.first}]; + + for (auto& property : groupPair.second.m_properties) + { + AtomToolsFramework::ConvertToPropertyMetaData(propertyDynamicMetadata[property.GetId()], property.GetConfig()); + } // It's significant that we check IsGroupHidden rather than IsGroupVisisble, because it follows the same rules as QWidget::isHidden(). // We don't care whether the widget and all its parents are visible, we only care about whether the group was hidden within the context From 74a273576641da4dd619ecfecd156978b23432e1 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Fri, 21 May 2021 10:35:33 +0100 Subject: [PATCH 35/38] Add better support for mouse deltas with camera system (#846) * add better support for mouse deltas with camera system * small fixes spotted during review * rename after review feedback * small refactor to reduce duplication --- .../AzFramework/Viewport/CameraInput.cpp | 30 ++++++++++--------- .../AzFramework/Viewport/CameraInput.h | 21 +++++++------ Code/Framework/Tests/CameraInputTests.cpp | 15 ++++------ .../Editor/ModernViewportCameraController.cpp | 6 +--- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index bd826544a1..e4833ccb3c 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -18,7 +18,6 @@ #include #include #include -#include namespace AzFramework { @@ -160,24 +159,27 @@ namespace AzFramework bool CameraSystem::HandleEvents(const InputEvent& event) { - if (const auto& cursor = AZStd::get_if(&event)) + if (const auto& horizonalMotion = AZStd::get_if(&event)) { - m_cursorState.SetCurrentPosition(cursor->m_position); + m_motionDelta.m_x = horizonalMotion->m_delta; + } + else if (const auto& verticalMotion = AZStd::get_if(&event)) + { + m_motionDelta.m_y = verticalMotion->m_delta; } else if (const auto& scroll = AZStd::get_if(&event)) { m_scrollDelta = scroll->m_delta; } - return m_cameras.HandleEvents(event, m_cursorState.CursorDelta(), m_scrollDelta); + return m_cameras.HandleEvents(event, m_motionDelta, m_scrollDelta); } Camera CameraSystem::StepCamera(const Camera& targetCamera, const float deltaTime) { - const auto nextCamera = m_cameras.StepCamera(targetCamera, m_cursorState.CursorDelta(), m_scrollDelta, deltaTime); - - m_cursorState.Update(); + const auto nextCamera = m_cameras.StepCamera(targetCamera, m_motionDelta, m_scrollDelta, deltaTime); + m_motionDelta = ScreenVector{0, 0}; m_scrollDelta = 0.0f; return nextCamera; @@ -720,7 +722,7 @@ namespace AzFramework return camera; } - InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize) + InputEvent BuildInputEvent(const InputChannel& inputChannel) { const auto& inputChannelId = inputChannel.GetInputChannelId(); const auto& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId(); @@ -730,13 +732,13 @@ namespace AzFramework return button == inputChannelId; }); - if (inputChannelId == InputDeviceMouse::Movement::X || inputChannelId == InputDeviceMouse::Movement::Y) + if (inputChannelId == InputDeviceMouse::Movement::X) { - const auto* position = inputChannel.GetCustomData(); - AZ_Assert(position, "Expected PositionData2D but found nullptr"); - - return CursorEvent{ScreenPoint( - position->m_normalizedPosition.GetX() * windowSize.m_width, position->m_normalizedPosition.GetY() * windowSize.m_height)}; + return HorizontalMotionEvent{(int)inputChannel.GetValue()}; + } + else if (inputChannelId == InputDeviceMouse::Movement::Y) + { + return VerticalMotionEvent{(int)inputChannel.GetValue()}; } else if (inputChannelId == InputDeviceMouse::Movement::Z) { diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index 582fb5a6de..ec70fc00de 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -72,11 +71,16 @@ namespace AzFramework void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform); - struct CursorEvent + //! Generic motion type + template + struct MotionEvent { - ScreenPoint m_position; + int m_delta; }; + using HorizontalMotionEvent = MotionEvent; + using VerticalMotionEvent = MotionEvent; + struct ScrollEvent { float m_delta; @@ -88,7 +92,7 @@ namespace AzFramework InputChannel::State m_state; //!< Channel state. (e.g. Begin/update/end event). }; - using InputEvent = AZStd::variant; + using InputEvent = AZStd::variant; class CameraInput { @@ -194,6 +198,7 @@ namespace AzFramework m_activeCameraInputs.begin(), m_activeCameraInputs.end(), [](const auto& cameraInput) { return cameraInput->Exclusive(); }); } + //! Responsible for updating a series of cameras given various inputs. class CameraSystem { public: @@ -203,8 +208,8 @@ namespace AzFramework Cameras m_cameras; private: - CursorState m_cursorState; - float m_scrollDelta = 0.0f; + ScreenVector m_motionDelta; //!< The delta used for look/orbit/pan (rotation + translation) - two dimensional. + float m_scrollDelta = 0.0f; //!< The delta used for dolly/movement (translation) - one dimensional. }; class RotateCameraInput : public CameraInput @@ -419,8 +424,6 @@ namespace AzFramework return true; } - struct WindowSize; - //! Map from a generic InputChannel event to a camera specific InputEvent. - InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize); + InputEvent BuildInputEvent(const InputChannel& inputChannel); } // namespace AzFramework diff --git a/Code/Framework/Tests/CameraInputTests.cpp b/Code/Framework/Tests/CameraInputTests.cpp index 6fe9837c22..3fc826975e 100644 --- a/Code/Framework/Tests/CameraInputTests.cpp +++ b/Code/Framework/Tests/CameraInputTests.cpp @@ -15,7 +15,6 @@ #include #include #include -#include namespace UnitTest { @@ -68,23 +67,21 @@ namespace UnitTest TEST_F(CameraInputFixture, BeginEndOrbitCameraConsumesCorrectEvents) { - // set initial mouse position - const bool consumed1 = HandleEventAndUpdate(AzFramework::CursorEvent{AzFramework::ScreenPoint(5, 5)}); // begin orbit camera - const bool consumed2 = HandleEventAndUpdate( + const bool consumed1 = HandleEventAndUpdate( AzFramework::DiscreteInputEvent{AzFramework::InputDeviceKeyboard::Key::ModifierAltL, AzFramework::InputChannel::State::Began}); // begin listening for orbit rotate (click detector) - event is not consumed - const bool consumed3 = HandleEventAndUpdate( + const bool consumed2 = HandleEventAndUpdate( AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began}); // begin orbit rotate (mouse has moved sufficient distance to initiate) - const bool consumed4 = HandleEventAndUpdate(AzFramework::CursorEvent{AzFramework::ScreenPoint(10, 10)}); + const bool consumed3 = HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{5}); // end orbit (mouse up) - event is not consumed - const bool consumed5 = HandleEventAndUpdate( + const bool consumed4 = HandleEventAndUpdate( AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended}); - const auto allConsumed = AZStd::vector{consumed1, consumed2, consumed3, consumed4, consumed5}; + const auto allConsumed = AZStd::vector{consumed1, consumed2, consumed3, consumed4}; using ::testing::ElementsAre; - EXPECT_THAT(allConsumed, ElementsAre(false, true, false, true, false)); + EXPECT_THAT(allConsumed, ElementsAre(true, false, true, false)); } } // namespace UnitTest diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.cpp b/Code/Sandbox/Editor/ModernViewportCameraController.cpp index 83ab2ef0b5..0779542878 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.cpp +++ b/Code/Sandbox/Editor/ModernViewportCameraController.cpp @@ -109,13 +109,9 @@ namespace SandboxEditor bool ModernViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) { - AzFramework::WindowSize windowSize; - AzFramework::WindowRequestBus::EventResult( - windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize); - if (ShouldHandle(event.m_priority, m_cameraSystem.m_cameras.Exclusive())) { - return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize)); + return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel)); } return false; From 1e2017d04f276249902f8a4de3661f26b42b7d5d Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Fri, 21 May 2021 13:29:04 +0100 Subject: [PATCH 36/38] Update .clang-format file for clang-format version 11.0 (#849) * update .clang-format file now clang-format version 11.0 is widely available * update to braced style lists formatting rules --- .clang-format | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/.clang-format b/.clang-format index 2a9205219a..565f28130e 100644 --- a/.clang-format +++ b/.clang-format @@ -7,28 +7,33 @@ AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right AlignOperands: false AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: None AlwaysBreakAfterReturnType: None AlwaysBreakTemplateDeclarations: true BreakBeforeBraces: Custom BraceWrapping: AfterClass: true + AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true + BeforeLambdaBody: true AfterStruct: true - SplitEmptyFunction: true - AfterControlStatement: true BeforeElse: true + SplitEmptyFunction: true BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma ColumnLimit: 140 ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true +Cpp11BracedListStyle: false FixNamespaceComments: true IncludeBlocks: Preserve +IndentCaseBlocks: true IndentCaseLabels: false IndentPPDirectives: None IndentWidth: 4 @@ -38,27 +43,17 @@ NamespaceIndentation: All PenaltyReturnTypeOnItsOwnLine: 1000 PointerAlignment: Left SortIncludes: true +SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false +Standard: c++17 UseTab: Never - -# Not available in clang-format version 6.0.0 -# BasedOnStyle: Microsoft -# Standard: c++17 -# AllowAllArgumentsOnNextLine: true -# AllowShortLambdasOnASingleLine: None -# BreakInheritanceList: BeforeComma -# SpaceAfterLogicalNot: false -# SpaceBeforeCpp11BracedList: false -# SpaceBeforeCtorInitializerColon: true -# SpaceBeforeInheritanceColon: true -# SpaceBeforeRangeBasedForLoopColon: true - -# Not available in clang-format version 10.0.0 -# BeforeLambdaBody: true (BraceWrapping) -# IndentCaseBlocks: true From ead54c85d7374fd22cb874d19cb4e5d75abaa58b Mon Sep 17 00:00:00 2001 From: AMZN-stankowi Date: Fri, 21 May 2021 06:21:26 -0700 Subject: [PATCH 37/38] Helios - SPEC-6963 - Fixed unused variables in release builds (#856) --- .../Importers/AssImpBitangentStreamImporter.cpp | 12 ++++++------ .../Importers/AssImpTangentStreamImporter.cpp | 12 ++++++------ .../Importers/AssImpUvMapImporter.cpp | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp index 2ce9bc14f4..0b366d96ea 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp @@ -75,13 +75,13 @@ namespace AZ const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); if (!allMeshesHaveTangentsAndBitangents) { - const char* mixedBitangentsError = - "Node with name %s has meshes with and without bitangents. " - "Placeholder incorrect bitangents will be generated to allow the data to process, " - "but the source art needs to be fixed to correct this. Either apply bitangents to all meshes on this node, " - "or remove all bitangents from all meshes on this node."; AZ_Error( - Utilities::ErrorWindow, false, mixedBitangentsError, currentNode->mName.C_Str()); + Utilities::ErrorWindow, false, + "Node with name %s has meshes with and without bitangents. " + "Placeholder incorrect bitangents will be generated to allow the data to process, " + "but the source art needs to be fixed to correct this. Either apply bitangents to all meshes on this node, " + "or remove all bitangents from all meshes on this node.", + currentNode->mName.C_Str()); } const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp index 47b7e410b4..b61baa9ff6 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp @@ -77,13 +77,13 @@ namespace AZ const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); if (!allMeshesHaveTangentsAndBitangents) { - const char* mixedTangentsError = - "Node with name %s has meshes with and without tangents. " - "Placeholder incorrect tangents will be generated to allow the data to process, " - "but the source art needs to be fixed to correct this. Either apply tangents to all meshes on this node, " - "or remove all tangents from all meshes on this node."; AZ_Error( - Utilities::ErrorWindow, false, mixedTangentsError, currentNode->mName.C_Str()); + Utilities::ErrorWindow, false, + "Node with name %s has meshes with and without tangents. " + "Placeholder incorrect tangents will be generated to allow the data to process, " + "but the source art needs to be fixed to correct this. Either apply tangents to all meshes on this node, " + "or remove all tangents from all meshes on this node.", + currentNode->mName.C_Str()); } const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp index e37a4f4285..8c1e0e7caa 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp @@ -89,13 +89,13 @@ namespace AZ for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex) { - int meshesWithIndex = meshesPerTextureCoordinateIndex[texCoordIndex]; AZ_Error( Utilities::ErrorWindow, - meshesWithIndex == 0 || meshesWithIndex == currentNode->mNumMeshes, + meshesPerTextureCoordinateIndex[texCoordIndex] == 0 || + meshesPerTextureCoordinateIndex[texCoordIndex] == currentNode->mNumMeshes, "Texture coordinate index %d for node %s is not on all meshes on this node. " - "Placeholder arbitrary texture values will be generated to allow the data to process, but the source art " - "needs to be fixed to correct this. All meshes on this node should have the same number of texture coordinate channels.", + "Placeholder arbitrary texture values will be generated to allow the data to process, but the source art " + "needs to be fixed to correct this. All meshes on this node should have the same number of texture coordinate channels.", texCoordIndex, currentNode->mName.C_Str()); } From d8bd6ef407e88d5c4029cb9607d698c4409a3bab Mon Sep 17 00:00:00 2001 From: Chris Burel Date: Fri, 21 May 2021 08:39:10 -0700 Subject: [PATCH 38/38] Preserve asset ids for assets that fail to load, when deserializing from json (#847) Sometimes deserializing a Json document happens when asset handlers are not registered. In that case, `FindOrCreateAsset` will fail to create the asset, since there's no handler registered to create it. When this happens, `FindOrCreateAsset` returns an Asset instance with a null asset id. This effectively causes the json deserializer to lose that data, even in situations where the the actual asset data doesn't need to be loaded, but the asset id needs to be preserved. --- .../AzCore/AzCore/Asset/AssetJsonSerializer.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp index 555eedf034..a72fe4e013 100644 --- a/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Asset/AssetJsonSerializer.cpp @@ -133,7 +133,15 @@ namespace AZ if (!id.m_guid.IsNull()) { *instance = AssetManager::Instance().FindOrCreateAsset(id, instance->GetType(), instance->GetAutoLoadBehavior()); - + if (!instance->GetId().IsValid()) + { + // If the asset failed to be created, FindOrCreateAsset returns an asset instance with a null + // id. To preserve the asset id in the source json, reset the asset to an empty one, but with + // the right id. + const auto loadBehavior = instance->GetAutoLoadBehavior(); + *instance = Asset(id, instance->GetType()); + instance->SetAutoLoadBehavior(loadBehavior); + } result.Combine(context.Report(result, "Successfully created Asset with id.")); }