From 304696fa5cc59b3b517d5545844d2d25d35ee610 Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 27 Apr 2021 19:26:35 -0700 Subject: [PATCH 01/48] 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 e2ca84ceb253d0930a006b45c66f745d4f597c8a Mon Sep 17 00:00:00 2001 From: moudgils Date: Fri, 7 May 2021 17:41:24 -0700 Subject: [PATCH 02/48] 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 89bb0edb3082b40de3c9b3737ad31e845b4cdf75 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Mon, 10 May 2021 23:04:56 -0700 Subject: [PATCH 03/48] ATOM-15518 Change Multilayer PBR To Use Lerp Base Blending Changed to lerp-based blending, with an implicit base layer. Renamed some variables to be more clear (blendWeight instead of blendMask, since the weights could come from vertex colors instead of a texture). Updated DefaultBlendMask_layers.png to better suit the new layering model. It has a black background and overlapping R, G, and B areas. Updated some of the test materials UV transforms to better fit the new DefaultBlendMask_layers image. Added a new test object, which is a plane that has painted vertices. Note that I updated test criteria in AtomSampleViewer to account for these changes as well. --- .../Types/StandardMultilayerPBR.materialtype | 2 +- .../Types/StandardMultilayerPBR_Common.azsli | 79 +++++++++++-------- ...tandardMultilayerPBR_DepthPass_WithPS.azsl | 2 +- .../StandardMultilayerPBR_ForwardPass.azsl | 39 +++++---- ...tandardMultilayerPBR_Shadowmap_WithPS.azsl | 2 +- .../Textures/DefaultBlendMask_layers.png | 4 +- .../001_ManyFeatures.material | 12 ++- .../002_ParallaxPdo.material | 3 +- ...aterial => 003_Debug_BlendSource.material} | 2 +- .../TestData/Objects/PaintedPlane.fbx | 3 + 10 files changed, 87 insertions(+), 61 deletions(-) rename Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/{003_Debug_BlendMaskValues.material => 003_Debug_BlendSource.material} (86%) create mode 100644 Gems/Atom/TestData/TestData/Objects/PaintedPlane.fbx diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index e2119dcf12..3e95846b6e 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -204,7 +204,7 @@ "displayName": "Debug Draw Mode", "description": "Enables various debug view features.", "type": "Enum", - "enumValues": [ "None", "BlendMaskValues", "DepthMaps" ], + "enumValues": [ "None", "BlendSource", "DepthMaps" ], "defaultValue": "None", "connection": { "type": "ShaderOption", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli index ba0eaf2ac1..f1f6d90e14 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -42,7 +42,7 @@ COMMON_SRG_INPUTS_PARALLAX(prefix) ShaderResourceGroup MaterialSrg : SRG_PerMaterial { - Texture2D m_blendMaskTexture; + Texture2D m_blendMaskTexture; uint m_blendMaskUvIndex; // Auto-generate material SRG fields for common inputs for each layer @@ -113,7 +113,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial // ------ Shader Options ---------------------------------------- -enum class DebugDrawMode { None, BlendMaskValues, DepthMaps }; +enum class DebugDrawMode { None, BlendSource, DepthMaps }; option DebugDrawMode o_debugDrawMode; enum class BlendMaskSource { TextureMap, VertexColors, Fallback }; @@ -127,6 +127,10 @@ option bool o_blendMask_isBound; // ------ Blend Utilities ---------------------------------------- +// This is mainly used to pass extra data to the GetDepth callback function during the parallax depth search. +// But since we have it, we use it in some other functions as well rather than passing it around. +static float3 s_blendMaskFromVertexStream; + //! Returns the BlendMaskSource that will actually be used when rendering (not necessarily the same BlendMaskSource specified by the user) BlendMaskSource GetFinalBlendMaskSource() { @@ -151,40 +155,63 @@ BlendMaskSource GetFinalBlendMaskSource() } } -//! Return the final blend mask values to be used for rendering, based on the available data and configuration. -float3 GetBlendMaskValues(float2 uv, float3 vertexBlendMask) +//! Return the raw blend source values directly from the blend mask or vertex colors, depending on the available data and configuration. +//! layer1 is an implicit base layer +//! layer2 is weighted by r +//! layer3 is weighted by g +//! b is reserved for perhaps a dedicated puddle layer +float3 GetBlendSourceValues(float2 uv) { - float3 blendMaskValues; + float3 blendSourceValues = float3(0,0,0); switch(GetFinalBlendMaskSource()) { case BlendMaskSource::TextureMap: - blendMaskValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, uv).rgb; + blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, uv).rgb; break; case BlendMaskSource::VertexColors: - blendMaskValues = vertexBlendMask; - break; - case BlendMaskSource::Fallback: - blendMaskValues = float3(1,1,1); + blendSourceValues = s_blendMaskFromVertexStream; break; } - blendMaskValues = blendMaskValues / (blendMaskValues.r + blendMaskValues.g + blendMaskValues.b); - - return blendMaskValues; + return blendSourceValues; } -float BlendLayers(float layer1, float layer2, float layer3, float3 blendMaskValues) +//! Return the final blend mask values to be used for rendering, based on the available data and configuration. +//! @return The blend weights for each layer. +//! Even though layer1 not explicitly specified in the blend source data, it is explicitly included with the returned values. +//! layer1 = r +//! layer2 = g +//! layer3 = b +float3 GetBlendWeights(float2 uv) { - return dot(float3(layer1, layer2, layer3), blendMaskValues); + float3 blendSourceValues = GetBlendSourceValues(uv); + + // Calculate blend weights such that multiplying and adding them with layer data is equivalent + // to lerping between each layer. + // final = lerp(final, layer1, blendWeights.r) + // final = lerp(final, layer2, blendWeights.g) + // final = lerp(final, layer3, blendWeights.b) + + float3 blendWeights; + blendWeights.b = blendSourceValues.g; + blendWeights.g = (1.0 - blendSourceValues.g) * blendSourceValues.r; + blendWeights.r = (1.0 - blendSourceValues.g) * (1.0 - blendSourceValues.r); + + return blendWeights; } -float2 BlendLayers(float2 layer1, float2 layer2, float2 layer3, float3 blendMaskValues) + +float BlendLayers(float layer1, float layer2, float layer3, float3 blendWeights) { - return layer1 * blendMaskValues.r + layer2 * blendMaskValues.g + layer3 * blendMaskValues.b; + return dot(float3(layer1, layer2, layer3), blendWeights); } -float3 BlendLayers(float3 layer1, float3 layer2, float3 layer3, float3 blendMaskValues) +float2 BlendLayers(float2 layer1, float2 layer2, float2 layer3, float3 blendWeights) { - return layer1 * blendMaskValues.r + layer2 * blendMaskValues.g + layer3 * blendMaskValues.b; + return layer1 * blendWeights.r + layer2 * blendWeights.g + layer3 * blendWeights.b; +} +float3 BlendLayers(float3 layer1, float3 layer2, float3 layer3, float3 blendWeights) +{ + return layer1 * blendWeights.r + layer2 * blendWeights.g + layer3 * blendWeights.b; } // ------ Parallax Utilities ---------------------------------------- @@ -203,16 +230,6 @@ bool ShouldHandleParallaxInDepthShaders() return ShouldHandleParallax() && o_parallax_enablePixelDepthOffset; } -// These static values are used to pass extra data to the GetDepth callback function during the parallax depth search. -static float3 s_blendMaskFromVertexStream; - -//! Setup static variables that are needed by the GetDepth callback function -//! @param vertexBlendMask the blend mask values from the vertex input stream. -void GetDepth_Setup(float3 vertexBlendMask) -{ - s_blendMaskFromVertexStream = vertexBlendMask; -} - // Callback function for ParallaxMapping.azsli DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) { @@ -260,8 +277,8 @@ DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) // Note, when the blend source is BlendMaskSource::VertexColors, parallax will not be able to blend correctly between layers. It will end up using the same blend mask values // for every UV position when searching for the intersection. This leads to smearing artifacts at the transition point, but these won't be so noticeable as long as // you have a small depth factor relative to the size of the blend transition. - float3 blendMaskValues = GetBlendMaskValues(uv, s_blendMaskFromVertexStream); + float3 blendWeights = GetBlendWeights(uv); - float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendMaskValues); + float depth = BlendLayers(layerDepthValues.r, layerDepthValues.g, layerDepthValues.b, blendWeights); return DepthResultAbsolute(depth); } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl index ae156d7313..178deca8a2 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_DepthPass_WithPS.azsl @@ -108,7 +108,7 @@ PSDepthOutput MainPS(VSDepthOutput IN, bool isFrontFace : SV_IsFrontFace) float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - GetDepth_Setup(IN.m_blendMask); + s_blendMaskFromVertexStream = IN.m_blendMask; float depth; diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index a83ea629e4..a690c0569a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -134,6 +134,8 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float { depth = IN.m_position.z; + s_blendMaskFromVertexStream = IN.m_blendMask; + // ------- Tangents & Bitangets ------- // We support two UV streams, but only a single stream of tangent/bitangent. So for UV[1+] we generated the tangent/bitangent in screen-space. @@ -156,15 +158,14 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Debug Modes ------- - if(o_debugDrawMode == DebugDrawMode::BlendMaskValues) + if(o_debugDrawMode == DebugDrawMode::BlendSource) { - float3 blendMaskValues = GetBlendMaskValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); - return DebugOutput(blendMaskValues); + float3 blendSource = GetBlendSourceValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); + return DebugOutput(blendSource); } if(o_debugDrawMode == DebugDrawMode::DepthMaps) { - GetDepth_Setup(IN.m_blendMask); float depth = GetNormalizedDepth(-MaterialSrg::m_displacementMax, -MaterialSrg::m_displacementMin, IN.m_uv[MaterialSrg::m_parallaxUvIndex], float2(0,0), float2(0,0)); return DebugOutput(float3(depth,depth,depth)); } @@ -176,8 +177,6 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled if(ShouldHandleParallax()) { - GetDepth_Setup(IN.m_blendMask); - float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); float3x3 uvMatrixInverse = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrixInverse : CreateIdentity3x3(); @@ -218,13 +217,13 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Calculate Layer Blend Mask Values ------- // Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax. - float3 blendMaskValues = GetBlendMaskValues(IN.m_uv[MaterialSrg::m_blendMaskUvIndex], IN.m_blendMask); + float3 blendWeights = GetBlendWeights(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); // ------- Normal ------- - float3 layer1_normalFactor = MaterialSrg::m_layer1_m_normalFactor * blendMaskValues.r; - float3 layer2_normalFactor = MaterialSrg::m_layer2_m_normalFactor * blendMaskValues.g; - float3 layer3_normalFactor = MaterialSrg::m_layer3_m_normalFactor * blendMaskValues.b; + float3 layer1_normalFactor = MaterialSrg::m_layer1_m_normalFactor * blendWeights.r; + float3 layer2_normalFactor = MaterialSrg::m_layer2_m_normalFactor * blendWeights.g; + float3 layer3_normalFactor = MaterialSrg::m_layer3_m_normalFactor * blendWeights.b; float3x3 layer1_uvMatrix = MaterialSrg::m_layer1_m_normalMapUvIndex == 0 ? MaterialSrg::m_layer1_m_uvMatrix : CreateIdentity3x3(); float3x3 layer2_uvMatrix = MaterialSrg::m_layer2_m_normalMapUvIndex == 0 ? MaterialSrg::m_layer2_m_uvMatrix : CreateIdentity3x3(); float3x3 layer3_uvMatrix = MaterialSrg::m_layer3_m_normalMapUvIndex == 0 ? MaterialSrg::m_layer3_m_uvMatrix : CreateIdentity3x3(); @@ -249,7 +248,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3 layer1_baseColor = BlendBaseColor(layer1_sampledColor, MaterialSrg::m_layer1_m_baseColor.rgb, MaterialSrg::m_layer1_m_baseColorFactor, o_layer1_o_baseColorTextureBlendMode, o_layer1_o_baseColor_useTexture); float3 layer2_baseColor = BlendBaseColor(layer2_sampledColor, MaterialSrg::m_layer2_m_baseColor.rgb, MaterialSrg::m_layer2_m_baseColorFactor, o_layer2_o_baseColorTextureBlendMode, o_layer2_o_baseColor_useTexture); float3 layer3_baseColor = BlendBaseColor(layer3_sampledColor, MaterialSrg::m_layer3_m_baseColor.rgb, MaterialSrg::m_layer3_m_baseColorFactor, o_layer3_o_baseColorTextureBlendMode, o_layer3_o_baseColor_useTexture); - float3 baseColor = BlendLayers(layer1_baseColor, layer2_baseColor, layer3_baseColor, blendMaskValues); + float3 baseColor = BlendLayers(layer1_baseColor, layer2_baseColor, layer3_baseColor, blendWeights); if(o_parallax_highlightClipping && displacementIsClipped) { @@ -264,7 +263,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float layer1_metallic = GetMetallicInput(MaterialSrg::m_layer1_m_metallicMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_metallicMapUvIndex], MaterialSrg::m_layer1_m_metallicFactor, o_layer1_o_metallic_useTexture); float layer2_metallic = GetMetallicInput(MaterialSrg::m_layer2_m_metallicMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_metallicMapUvIndex], MaterialSrg::m_layer2_m_metallicFactor, o_layer2_o_metallic_useTexture); float layer3_metallic = GetMetallicInput(MaterialSrg::m_layer3_m_metallicMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_metallicMapUvIndex], MaterialSrg::m_layer3_m_metallicFactor, o_layer3_o_metallic_useTexture); - metallic = BlendLayers(layer1_metallic, layer2_metallic, layer3_metallic, blendMaskValues); + metallic = BlendLayers(layer1_metallic, layer2_metallic, layer3_metallic, blendWeights); } // ------- Specular ------- @@ -272,7 +271,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float layer1_specularF0Factor = GetSpecularInput(MaterialSrg::m_layer1_m_specularF0Map, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_specularF0MapUvIndex], MaterialSrg::m_layer1_m_specularF0Factor, o_layer1_o_specularF0_useTexture); float layer2_specularF0Factor = GetSpecularInput(MaterialSrg::m_layer2_m_specularF0Map, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_specularF0MapUvIndex], MaterialSrg::m_layer2_m_specularF0Factor, o_layer2_o_specularF0_useTexture); float layer3_specularF0Factor = GetSpecularInput(MaterialSrg::m_layer3_m_specularF0Map, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_specularF0MapUvIndex], MaterialSrg::m_layer3_m_specularF0Factor, o_layer3_o_specularF0_useTexture); - float specularF0Factor = BlendLayers(layer1_specularF0Factor, layer2_specularF0Factor, layer3_specularF0Factor, blendMaskValues); + float specularF0Factor = BlendLayers(layer1_specularF0Factor, layer2_specularF0Factor, layer3_specularF0Factor, blendWeights); surface.SetAlbedoAndSpecularF0(baseColor, specularF0Factor, metallic); @@ -281,7 +280,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float layer1_roughness = GetRoughnessInput(MaterialSrg::m_layer1_m_roughnessMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_roughnessMapUvIndex], MaterialSrg::m_layer1_m_roughnessFactor, MaterialSrg::m_layer1_m_roughnessLowerBound, MaterialSrg::m_layer1_m_roughnessUpperBound, o_layer1_o_roughness_useTexture); float layer2_roughness = GetRoughnessInput(MaterialSrg::m_layer2_m_roughnessMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_roughnessMapUvIndex], MaterialSrg::m_layer2_m_roughnessFactor, MaterialSrg::m_layer2_m_roughnessLowerBound, MaterialSrg::m_layer2_m_roughnessUpperBound, o_layer2_o_roughness_useTexture); float layer3_roughness = GetRoughnessInput(MaterialSrg::m_layer3_m_roughnessMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_roughnessMapUvIndex], MaterialSrg::m_layer3_m_roughnessFactor, MaterialSrg::m_layer3_m_roughnessLowerBound, MaterialSrg::m_layer3_m_roughnessUpperBound, o_layer3_o_roughness_useTexture); - surface.roughnessLinear = BlendLayers(layer1_roughness, layer2_roughness, layer3_roughness, blendMaskValues); + surface.roughnessLinear = BlendLayers(layer1_roughness, layer2_roughness, layer3_roughness, blendWeights); surface.CalculateRoughnessA(); @@ -314,19 +313,19 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float float3 layer1_emissive = GetEmissiveInput(MaterialSrg::m_layer1_m_emissiveMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_emissiveMapUvIndex], MaterialSrg::m_layer1_m_emissiveIntensity, MaterialSrg::m_layer1_m_emissiveColor.rgb, o_layer1_o_emissiveEnabled, o_layer1_o_emissive_useTexture); float3 layer2_emissive = GetEmissiveInput(MaterialSrg::m_layer2_m_emissiveMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_emissiveMapUvIndex], MaterialSrg::m_layer2_m_emissiveIntensity, MaterialSrg::m_layer2_m_emissiveColor.rgb, o_layer2_o_emissiveEnabled, o_layer2_o_emissive_useTexture); float3 layer3_emissive = GetEmissiveInput(MaterialSrg::m_layer3_m_emissiveMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_emissiveMapUvIndex], MaterialSrg::m_layer3_m_emissiveIntensity, MaterialSrg::m_layer3_m_emissiveColor.rgb, o_layer3_o_emissiveEnabled, o_layer3_o_emissive_useTexture); - lightingData.emissiveLighting = BlendLayers(layer1_emissive, layer2_emissive, layer3_emissive, blendMaskValues); + lightingData.emissiveLighting = BlendLayers(layer1_emissive, layer2_emissive, layer3_emissive, blendWeights); // ------- Occlusion ------- float layer1_diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_layer1_m_diffuseOcclusionMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_diffuseOcclusionMapUvIndex], MaterialSrg::m_layer1_m_diffuseOcclusionFactor, o_layer1_o_diffuseOcclusion_useTexture); float layer2_diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_layer2_m_diffuseOcclusionMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_diffuseOcclusionMapUvIndex], MaterialSrg::m_layer2_m_diffuseOcclusionFactor, o_layer2_o_diffuseOcclusion_useTexture); float layer3_diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_layer3_m_diffuseOcclusionMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_diffuseOcclusionMapUvIndex], MaterialSrg::m_layer3_m_diffuseOcclusionFactor, o_layer3_o_diffuseOcclusion_useTexture); - lightingData.diffuseAmbientOcclusion = BlendLayers(layer1_diffuseAmbientOcclusion, layer2_diffuseAmbientOcclusion, layer3_diffuseAmbientOcclusion, blendMaskValues); + lightingData.diffuseAmbientOcclusion = BlendLayers(layer1_diffuseAmbientOcclusion, layer2_diffuseAmbientOcclusion, layer3_diffuseAmbientOcclusion, blendWeights); float layer1_specularOcclusion = GetOcclusionInput(MaterialSrg::m_layer1_m_specularOcclusionMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_specularOcclusionMapUvIndex], MaterialSrg::m_layer1_m_specularOcclusionFactor, o_layer1_o_specularOcclusion_useTexture); float layer2_specularOcclusion = GetOcclusionInput(MaterialSrg::m_layer2_m_specularOcclusionMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_specularOcclusionMapUvIndex], MaterialSrg::m_layer2_m_specularOcclusionFactor, o_layer2_o_specularOcclusion_useTexture); float layer3_specularOcclusion = GetOcclusionInput(MaterialSrg::m_layer3_m_specularOcclusionMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_specularOcclusionMapUvIndex], MaterialSrg::m_layer3_m_specularOcclusionFactor, o_layer3_o_specularOcclusion_useTexture); - lightingData.specularOcclusion = BlendLayers(layer1_specularOcclusion, layer2_specularOcclusion, layer3_specularOcclusion, blendMaskValues); + lightingData.specularOcclusion = BlendLayers(layer1_specularOcclusion, layer2_specularOcclusion, layer3_specularOcclusion, blendWeights); // ------- Clearcoat ------- @@ -385,11 +384,11 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // --- Blend Layers --- - surface.clearCoat.factor = BlendLayers(layer1_clearCoatFactor, layer2_clearCoatFactor, layer3_clearCoatFactor, blendMaskValues); - surface.clearCoat.roughness = BlendLayers(layer1_clearCoatRoughness, layer2_clearCoatRoughness, layer3_clearCoatRoughness, blendMaskValues); + surface.clearCoat.factor = BlendLayers(layer1_clearCoatFactor, layer2_clearCoatFactor, layer3_clearCoatFactor, blendWeights); + surface.clearCoat.roughness = BlendLayers(layer1_clearCoatRoughness, layer2_clearCoatRoughness, layer3_clearCoatRoughness, blendWeights); // [GFX TODO][ATOM-14592] This is not the right way to blend the normals. We need to use ReorientTangentSpaceNormal(), and that requires GetClearCoatInputs() to return the normal in TS instead of WS. - surface.clearCoat.normal = BlendLayers(layer1_clearCoatNormal, layer2_clearCoatNormal, layer3_clearCoatNormal, blendMaskValues); + surface.clearCoat.normal = BlendLayers(layer1_clearCoatNormal, layer2_clearCoatNormal, layer3_clearCoatNormal, blendWeights); surface.clearCoat.normal = normalize(surface.clearCoat.normal); // manipulate base layer f0 if clear coat is enabled diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl index 325937b228..2ab4c50841 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl @@ -107,7 +107,7 @@ PSDepthOutput MainPS(VertexOutput IN, bool isFrontFace : SV_IsFrontFace) float3 bitangents[UvSetCount] = { IN.m_bitangent.xyz, float3(0, 0, 0) }; PrepareGeneratedTangent(IN.m_normal, IN.m_worldPosition, isFrontFace, IN.m_uv, UvSetCount, tangents, bitangents, 1); - GetDepth_Setup(IN.m_blendMask); + s_blendMaskFromVertexStream = IN.m_blendMask; float depth; diff --git a/Gems/Atom/Feature/Common/Assets/Textures/DefaultBlendMask_layers.png b/Gems/Atom/Feature/Common/Assets/Textures/DefaultBlendMask_layers.png index d1606516af..5e60261dd7 100644 --- a/Gems/Atom/Feature/Common/Assets/Textures/DefaultBlendMask_layers.png +++ b/Gems/Atom/Feature/Common/Assets/Textures/DefaultBlendMask_layers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f74ffab6ee15906158d27cbd2e5556e8fcdfd7820c0d0fd4b403de8a7af81662 -size 5651 +oid sha256:6660fa05dbf1e90298472fb41d99fff80a80de64ee88d17af4d0df3bdafb1ff6 +size 52877 diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material index b2a3eac890..b85705fcb8 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material @@ -134,8 +134,14 @@ "enable": true }, "uv": { - "offsetU": -0.2800000011920929, - "rotateDegrees": 39.599998474121097 + "center": [ + 0.10000000149011612, + 0.20000000298023225 + ], + "offsetU": 0.23000000417232514, + "offsetV": -0.23999999463558198, + "rotateDegrees": 39.599998474121097, + "scale": 1.100000023841858 } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material index e7903a8c91..f6c881aee5 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material @@ -30,6 +30,7 @@ "layer2_parallax": { "enable": true, "factor": 0.05299999937415123, + "offset": -0.024000000208616258, "textureMap": "TestData/Textures/cc0/Rock030_2K_Displacement.jpg" }, "layer2_roughness": { @@ -49,4 +50,4 @@ "pdo": true } } -} +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMaskValues.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material similarity index 86% rename from Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMaskValues.material rename to Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material index 94a1ec6a30..cdd21212c9 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendMaskValues.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/003_Debug_BlendSource.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "general": { - "debugDrawMode": "BlendMaskValues" + "debugDrawMode": "BlendSource" } } } diff --git a/Gems/Atom/TestData/TestData/Objects/PaintedPlane.fbx b/Gems/Atom/TestData/TestData/Objects/PaintedPlane.fbx new file mode 100644 index 0000000000..be30ca4639 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Objects/PaintedPlane.fbx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:053a7cd73b37c815900f87abd524830e6f23eee75d3b72fb866e86498d528159 +size 36156 From 9faf35b529d942dffe1b9fce7d9310be8de9edf8 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Tue, 11 May 2021 17:09:41 -0700 Subject: [PATCH 04/48] Refactor in prepration for ATOM-14688 "Disable Individual Layers" Refactored StandardMultilayerPBR to collate all the code for each layer into a couple structs and utility functions. This makes the code easier to maintain, and in particular will make it easy for me to add Enable flags for the layers in a subsequent commit. Also removed subsurface scattering and translucency from StandardMultilayerPBR, according to ATOM-4120 "Stabilize Standard PBR Regarding Subsurface and Translucency". Squashed commit of the following: commit a6052d6ad4f70183d0ce72e84c7dc5512dc24d5e Author: Chris Santora Date: Tue May 11 16:32:15 2021 -0700 Got the refactor finally working. I had change it to blend the baseColor, spec factor, and metalness before converting to albedo and spec, in order to get exactly the same results as before. commit 42d6da7f405097dea07b6ed0426d6a662b61440d Author: Chris Santora Date: Tue May 11 15:58:38 2021 -0700 Fixed clear coat issue due to LightingData initialized too late. commit 358194a5caf6f9eb99b0e5345ad5f7768b244a93 Author: Chris Santora Date: Tue May 11 15:18:30 2021 -0700 Fixed a couple issues. commit adb431f8113b945057959db288a7ee2dd825dd69 Author: Chris Santora Date: Tue May 11 12:42:12 2021 -0700 WIP refactor of StandardMultilayerPBR to collate the code for each layer. Also removed subsurface scattering from multilayer. --- .../Types/StandardMultilayerPBR.materialtype | 207 --------- .../Types/StandardMultilayerPBR_Common.azsli | 18 - .../StandardMultilayerPBR_ForwardPass.azsl | 398 ++++++++++-------- .../PBR/Surfaces/StandardSurface.azsli | 14 + 4 files changed, 244 insertions(+), 393 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index 3e95846b6e..90a599c6f9 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -23,11 +23,6 @@ "displayName": "UVs", "description": "Properties for configuring UV transforms for the entire material, including the blend masks." }, - { - "id": "subsurfaceScattering", - "displayName": "Subsurface Scattering", - "description": "Properties for configuring subsurface scattering effects." - }, { // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader "id": "irradiance", @@ -467,183 +462,6 @@ "step": 0.1 } ], - "subsurfaceScattering": [ - { - "id": "enableSubsurfaceScattering", - "displayName": "Subsurface Scattering", - "description": "Enable subsurface scattering feature, this will disable metallic and parallax mapping property due to incompatibility", - "type": "Bool", - "defaultValue": false, - "connection": { - "type": "ShaderOption", - "id": "o_enableSubsurfaceScattering" - } - }, - { - "id": "subsurfaceScatterFactor", - "displayName": " Factor", - "description": "Strength factor for scaling percentage of subsurface scattering effect applied", - "type": "float", - "defaultValue": 1.0, - "min": 0.0, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringFactor" - } - }, - { - "id": "influenceMap", - "displayName": " Influence Map", - "description": "Use texture map to control the strength of subsurface scattering", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMap" - } - }, - { - "id": "useInfluenceMap", - "displayName": " Use Influence Map", - "description": "Whether to use the texture map as influence mask.", - "type": "Bool", - "defaultValue": true - }, - { - "id": "influenceMapUv", - "displayName": " UV", - "description": "Influence map UV set", - "type": "Enum", - "enumIsUv": true, - "defaultValue": "Tiled", - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringInfluenceMapUvIndex" - } - }, - { - "id": "scatterColor", - "displayName": " Scatter color", - "description": "Color of volume light traveled through", - "type": "Color", - "defaultValue": [ 1.0, 0.27, 0.13 ] - }, - { - "id": "scatterDistance", - "displayName": " Scatter distance", - "description": "How far light traveled inside the volume", - "type": "float", - "defaultValue": 8, - "min": 0.0, - "softMax": 20.0 - }, - { - "id": "quality", - "displayName": " Quality", - "description": "How much percent of sample will be used for each pixel, more samples improve quality and reduce artifacts, especially when the scatter distance is relatively large, but slow down computation time, 1.0 = full set 200 samples per pixel", - "type": "float", - "defaultValue": 0.4, - "min": 0.2, - "max": 1.0, - "connection": { - "type": "ShaderInput", - "id": "m_subsurfaceScatteringQuality" - } - }, - { - "id": "transmissionMode", - "displayName": "Transmission", - "description": "Algorithm used for calculating transmission", - "type": "Enum", - "enumValues": [ "None", "ThickObject", "ThinObject" ], - "defaultValue": "None", - "connection": { - "type": "ShaderOption", - "id": "o_transmission_mode" - } - }, - { - "id": "thickness", - "displayName": " Thickness", - "description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel", - "type": "float", - "defaultValue": 0.5, - "min": 0.0, - "max": 1.0 - }, - { - "id": "thicknessMap", - "displayName": " Thickness Map", - "description": "Use a greyscale texture for per pixel thickness", - "type": "Image", - "connection": { - "type": "ShaderInput", - "id": "m_transmissionThicknessMap" - } - }, - { - "id": "useThicknessMap", - "displayName": " Use Thickness Map", - "description": "Whether to use the thickness map", - "type": "Bool", - "defaultValue": true - }, - { - "id": "thicknessMapUv", - "displayName": " UV", - "description": "Thickness map UV set", - "type": "Enum", - "enumIsUv": true, - "defaultValue": "Tiled", - "connection": { - "type": "ShaderInput", - "id": "m_transmissionThicknessMapUvIndex" - } - }, - { - "id": "transmissionTint", - "displayName": " Transmission Tint", - "description": "Color of the volume light travelling through", - "type": "Color", - "defaultValue": [ 1.0, 0.8, 0.6 ] - }, - { - "id": "transmissionPower", - "displayName": " Power", - "description": "How much transmitted light scatter radially ", - "type": "float", - "defaultValue": 6.0, - "min": 0.0, - "softMax": 20.0 - }, - { - "id": "transmissionDistortion", - "displayName": " Distortion", - "description": "How much light direction distorted towards surface normal", - "type": "float", - "defaultValue": 0.1, - "min": 0.0, - "max": 1.0 - }, - { - "id": "transmissionAttenuation", - "displayName": " Attenuation", - "description": "How fast transmitted light fade with thickness", - "type": "float", - "defaultValue": 4.0, - "min": 0.0, - "softMax": 20.0 - }, - { - "id": "transmissionScale", - "displayName": " Scale", - "description": "Strength of transmission", - "type": "float", - "defaultValue": 3.0, - "min": 0.0, - "softMax": 20.0 - } - ], "irradiance": [ // Note: this property group is used in the DiffuseGlobalIllumination pass, it is not read by the StandardPBR shader { @@ -2844,25 +2662,6 @@ "file": "StandardMultilayerPBR_ShaderEnable.lua" } }, - { - // Preprocess & build parameter set for subsurface scattering and translucency - "type": "HandleSubsurfaceScatteringParameters", - "args": { - "mode": "subsurfaceScattering.transmissionMode", - "scale" : "subsurfaceScattering.transmissionScale", - "power" : "subsurfaceScattering.transmissionPower", - "distortion" : "subsurfaceScattering.transmissionDistortion", - "attenuation" : "subsurfaceScattering.transmissionAttenuation", - "tintColor" : "subsurfaceScattering.transmissionTint", - "thickness" : "subsurfaceScattering.thickness", - "enabled": "subsurfaceScattering.enableSubsurfaceScattering", - "scatterDistanceColor" : "subsurfaceScattering.scatterColor", - "scatterDistanceIntensity" : "subsurfaceScattering.scatterDistance", - "scatterDistanceShaderInput" : "m_scatterDistance", - "parametersShaderInput" : "m_transmissionParams", - "tintThickenssShaderInput" : "m_transmissionTintThickness" - } - }, { "type": "Lua", "args": { @@ -2875,12 +2674,6 @@ "file": "StandardMultilayerPBR_Parallax.lua" } }, - { - "type": "Lua", - "args": { - "file": "StandardPBR_SubsurfaceState.lua" - } - }, //############################################################################################## // Layer 1 Functors //############################################################################################## diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli index f1f6d90e14..8c7b49214b 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -91,24 +91,6 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial MipFilter = Linear; }; - // Parameters for subsurface scattering - float m_subsurfaceScatteringFactor; - float m_subsurfaceScatteringQuality; - float3 m_scatterDistance; - Texture2D m_subsurfaceScatteringInfluenceMap; - uint m_subsurfaceScatteringInfluenceMapUvIndex; - - // Parameters for transmission - - // Elements of m_transmissionParams: - // Thick object mode: (attenuation coefficient, power, distortion, scale) - // Thin object mode: (float3 scatter distance, scale) - float4 m_transmissionParams; - - // (float3 TintColor, thickness) - float4 m_transmissionTintThickness; - Texture2D m_transmissionThicknessMap; - uint m_transmissionThicknessMapUvIndex; } // ------ Shader Options ---------------------------------------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index a690c0569a..9e5a2e623a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -55,7 +55,6 @@ DEFINE_LAYER_OPTIONS(o_layer1_) DEFINE_LAYER_OPTIONS(o_layer2_) DEFINE_LAYER_OPTIONS(o_layer3_) -#include "MaterialInputs/SubsurfaceInput.azsli" #include "MaterialInputs/TransmissionInput.azsli" #include "StandardMultilayerPBR_Common.azsli" @@ -127,6 +126,181 @@ VSOutput ForwardPassVS(VSInput IN) return OUT; } +//! Collects all the raw Standard material inputs for a single layer. See ProcessStandardMaterialInputs(). +struct StandardMaterialInputs +{ + COMMON_SRG_INPUTS_BASE_COLOR() + COMMON_SRG_INPUTS_ROUGHNESS() + COMMON_SRG_INPUTS_METALLIC() + COMMON_SRG_INPUTS_SPECULAR_F0() + COMMON_SRG_INPUTS_NORMAL() + COMMON_SRG_INPUTS_CLEAR_COAT() + COMMON_SRG_INPUTS_OCCLUSION() + COMMON_SRG_INPUTS_EMISSIVE() + // Note parallax is omitted here because that requires special handling. + + bool m_normal_useTexture; + bool m_baseColor_useTexture; + bool m_metallic_useTexture; + bool m_specularF0_useTexture; + bool m_roughness_useTexture; + bool m_emissiveEnabled; + bool m_emissive_useTexture; + bool m_diffuseOcclusion_useTexture; + bool m_specularOcclusion_useTexture; + bool m_clearCoatEnabled; + bool m_clearCoat_factor_useTexture; + bool m_clearCoat_roughness_useTexture; + bool m_clearCoat_normal_useTexture; + + TextureBlendMode m_baseColorTextureBlendMode; + + float2 m_vertexUv[UvSetCount]; + float3x3 m_uvMatrix; + float m_normal; + float3 m_tangents[UvSetCount]; + float3 m_bitangents[UvSetCount]; + + sampler m_sampler; + + bool m_isFrontFace; +}; + +//! Holds the final processed material inputs, after all flags have been checked, textures have been sampled, factors have been applied, etc. +//! This data is ready to be copied into a Surface and/or LightingData struct for the lighting system to consume. +class ProcessedMaterialInputs +{ + float3 m_normalTS; //!< Normal in tangent-space + float3 m_baseColor; + float3 m_specularF0Factor; + float m_metallic; + float m_roughness; + float3 m_emissiveLighting; + float m_diffuseAmbientOcclusion; + float m_specularOcclusion; + ClearCoatSurfaceData m_clearCoat; + + void InitializeToZero() + { + m_normalTS = float3(0,0,0); + m_baseColor = float3(0,0,0); + m_specularF0Factor = float3(0,0,0); + m_metallic = 0.0f; + m_roughness = 0.0f; + m_emissiveLighting = float3(0,0,0); + m_diffuseAmbientOcclusion = 0; + m_specularOcclusion = 0; + m_clearCoat.InitializeToZero(); + } +}; + +//! Processes the set of Standard material inputs for a single layer. +//! The FILL_STANDARD_MATERIAL_INPUTS() macro below can be used to fill the StandardMaterialInputs struct. +ProcessedMaterialInputs ProcessStandardMaterialInputs(StandardMaterialInputs inputs) +{ + ProcessedMaterialInputs result; + + float2 transformedUv[UvSetCount]; + transformedUv[0] = mul(inputs.m_uvMatrix, float3(inputs.m_vertexUv[0], 1.0)).xy; + transformedUv[1] = inputs.m_vertexUv[1]; + + float3x3 normalUvMatrix = inputs.m_normalMapUvIndex == 0 ? inputs.m_uvMatrix : CreateIdentity3x3(); + result.m_normalTS = GetNormalInputTS(inputs.m_normalMap, inputs.m_sampler, transformedUv[inputs.m_normalMapUvIndex], inputs.m_flipNormalX, inputs.m_flipNormalY, normalUvMatrix, inputs.m_normal_useTexture, inputs.m_normalFactor); + + float3 sampledBaseColor = GetBaseColorInput(inputs.m_baseColorMap, inputs.m_sampler, transformedUv[inputs.m_baseColorMapUvIndex], inputs.m_baseColor.rgb, inputs.m_baseColor_useTexture); + result.m_baseColor = BlendBaseColor(sampledBaseColor, inputs.m_baseColor.rgb, inputs.m_baseColorFactor, inputs.m_baseColorTextureBlendMode, inputs.m_baseColor_useTexture); + result.m_specularF0Factor = GetSpecularInput(inputs.m_specularF0Map, inputs.m_sampler, transformedUv[inputs.m_specularF0MapUvIndex], inputs.m_specularF0Factor, inputs.m_specularF0_useTexture); + result.m_metallic = GetMetallicInput(inputs.m_metallicMap, inputs.m_sampler, transformedUv[inputs.m_metallicMapUvIndex], inputs.m_metallicFactor, inputs.m_metallic_useTexture); + result.m_roughness = GetRoughnessInput(inputs.m_roughnessMap, MaterialSrg::m_sampler, transformedUv[inputs.m_roughnessMapUvIndex], inputs.m_roughnessFactor, inputs.m_roughnessLowerBound, inputs.m_roughnessUpperBound, inputs.m_roughness_useTexture); + + result.m_emissiveLighting = GetEmissiveInput(inputs.m_emissiveMap, inputs.m_sampler, transformedUv[inputs.m_emissiveMapUvIndex], inputs.m_emissiveIntensity, inputs.m_emissiveColor.rgb, inputs.m_emissiveEnabled, inputs.m_emissive_useTexture); + result.m_diffuseAmbientOcclusion = GetOcclusionInput(inputs.m_diffuseOcclusionMap, inputs.m_sampler, transformedUv[inputs.m_diffuseOcclusionMapUvIndex], inputs.m_diffuseOcclusionFactor, inputs.m_diffuseOcclusion_useTexture); + result.m_specularOcclusion = GetOcclusionInput(inputs.m_specularOcclusionMap, MaterialSrg::m_sampler, transformedUv[inputs.m_specularOcclusionMapUvIndex], inputs.m_specularOcclusionFactor, inputs.m_specularOcclusion_useTexture); + + result.m_clearCoat.InitializeToZero(); + if(inputs.m_clearCoatEnabled) + { + float3x3 clearCoatUvMatrix = inputs.m_clearCoatNormalMapUvIndex == 0 ? inputs.m_uvMatrix : CreateIdentity3x3(); + + GetClearCoatInputs(inputs.m_clearCoatInfluenceMap, transformedUv[inputs.m_clearCoatInfluenceMapUvIndex], inputs.m_clearCoatFactor, inputs.m_clearCoat_factor_useTexture, + inputs.m_clearCoatRoughnessMap, transformedUv[inputs.m_clearCoatRoughnessMapUvIndex], inputs.m_clearCoatRoughness, inputs.m_clearCoat_roughness_useTexture, + inputs.m_clearCoatNormalMap, transformedUv[inputs.m_clearCoatNormalMapUvIndex], inputs.m_normal, inputs.m_clearCoat_normal_useTexture, inputs.m_clearCoatNormalStrength, + clearCoatUvMatrix, inputs.m_tangents[inputs.m_clearCoatNormalMapUvIndex], inputs.m_bitangents[inputs.m_clearCoatNormalMapUvIndex], + inputs.m_sampler, inputs.m_isFrontFace, + result.m_clearCoat.factor, result.m_clearCoat.roughness, result.m_clearCoat.normal); + } + + return result; +} + +//! Fills a StandardMaterialInputs struct with data from the MaterialSrg, shader options, and local vertex data. +#define FILL_STANDARD_MATERIAL_INPUTS(inputs, srgLayerPrefix, optionsLayerPrefix, blendWeight) \ + inputs.m_sampler = MaterialSrg::m_sampler; \ + inputs.m_vertexUv = IN.m_uv; \ + inputs.m_uvMatrix = srgLayerPrefix##m_uvMatrix; \ + inputs.m_normal = IN.m_normal; \ + inputs.m_tangents = tangents; \ + inputs.m_bitangents = bitangents; \ + inputs.m_isFrontFace = isFrontFace; \ + \ + inputs.m_normalMapUvIndex = srgLayerPrefix##m_normalMapUvIndex; \ + inputs.m_normalMap = srgLayerPrefix##m_normalMap; \ + inputs.m_flipNormalX = srgLayerPrefix##m_flipNormalX; \ + inputs.m_flipNormalY = srgLayerPrefix##m_flipNormalY; \ + inputs.m_normal_useTexture = optionsLayerPrefix##o_normal_useTexture; \ + inputs.m_normalFactor = srgLayerPrefix##m_normalFactor * blendWeight; \ + inputs.m_baseColorMap = srgLayerPrefix##m_baseColorMap; \ + inputs.m_baseColorMapUvIndex = srgLayerPrefix##m_baseColorMapUvIndex; \ + inputs.m_baseColor = srgLayerPrefix##m_baseColor; \ + inputs.m_baseColor_useTexture = optionsLayerPrefix##o_baseColor_useTexture; \ + inputs.m_baseColorFactor = srgLayerPrefix##m_baseColorFactor; \ + inputs.m_baseColorTextureBlendMode = optionsLayerPrefix##o_baseColorTextureBlendMode; \ + inputs.m_metallicMap = srgLayerPrefix##m_metallicMap; \ + inputs.m_metallicMapUvIndex = srgLayerPrefix##m_metallicMapUvIndex; \ + inputs.m_metallicFactor = srgLayerPrefix##m_metallicFactor; \ + inputs.m_metallic_useTexture = optionsLayerPrefix##o_metallic_useTexture; \ + inputs.m_specularF0Map = srgLayerPrefix##m_specularF0Map; \ + inputs.m_specularF0MapUvIndex = srgLayerPrefix##m_specularF0MapUvIndex; \ + inputs.m_specularF0Factor = srgLayerPrefix##m_specularF0Factor; \ + inputs.m_specularF0_useTexture = optionsLayerPrefix##o_specularF0_useTexture; \ + inputs.m_roughnessMap = srgLayerPrefix##m_roughnessMap; \ + inputs.m_roughnessMapUvIndex = srgLayerPrefix##m_roughnessMapUvIndex; \ + inputs.m_roughnessFactor = srgLayerPrefix##m_roughnessFactor; \ + inputs.m_roughnessLowerBound = srgLayerPrefix##m_roughnessLowerBound; \ + inputs.m_roughnessUpperBound = srgLayerPrefix##m_roughnessUpperBound; \ + inputs.m_roughness_useTexture = optionsLayerPrefix##o_roughness_useTexture; \ + \ + inputs.m_emissiveMap = srgLayerPrefix##m_emissiveMap; \ + inputs.m_emissiveMapUvIndex = srgLayerPrefix##m_emissiveMapUvIndex; \ + inputs.m_emissiveIntensity = srgLayerPrefix##m_emissiveIntensity; \ + inputs.m_emissiveColor = srgLayerPrefix##m_emissiveColor; \ + inputs.m_emissiveEnabled = optionsLayerPrefix##o_emissiveEnabled; \ + inputs.m_emissive_useTexture = optionsLayerPrefix##o_emissive_useTexture; \ + \ + inputs.m_diffuseOcclusionMap = srgLayerPrefix##m_diffuseOcclusionMap; \ + inputs.m_diffuseOcclusionMapUvIndex = srgLayerPrefix##m_diffuseOcclusionMapUvIndex; \ + inputs.m_diffuseOcclusionFactor = srgLayerPrefix##m_diffuseOcclusionFactor; \ + inputs.m_diffuseOcclusion_useTexture = optionsLayerPrefix##o_diffuseOcclusion_useTexture; \ + \ + inputs.m_specularOcclusionMap = srgLayerPrefix##m_specularOcclusionMap; \ + inputs.m_specularOcclusionMapUvIndex = srgLayerPrefix##m_specularOcclusionMapUvIndex; \ + inputs.m_specularOcclusionFactor = srgLayerPrefix##m_specularOcclusionFactor; \ + inputs.m_specularOcclusion_useTexture = optionsLayerPrefix##o_specularOcclusion_useTexture; \ + \ + inputs.m_clearCoatEnabled = o_clearCoat_feature_enabled && optionsLayerPrefix##o_clearCoat_enabled; \ + inputs.m_clearCoatInfluenceMap = srgLayerPrefix##m_clearCoatInfluenceMap; \ + inputs.m_clearCoatInfluenceMapUvIndex = srgLayerPrefix##m_clearCoatInfluenceMapUvIndex; \ + inputs.m_clearCoatFactor = srgLayerPrefix##m_clearCoatFactor; \ + inputs.m_clearCoat_factor_useTexture = optionsLayerPrefix##o_clearCoat_factor_useTexture; \ + inputs.m_clearCoatRoughnessMap = srgLayerPrefix##m_clearCoatRoughnessMap; \ + inputs.m_clearCoatRoughnessMapUvIndex = srgLayerPrefix##m_clearCoatRoughnessMapUvIndex; \ + inputs.m_clearCoatRoughness = srgLayerPrefix##m_clearCoatRoughness; \ + inputs.m_clearCoat_roughness_useTexture = optionsLayerPrefix##o_clearCoat_roughness_useTexture; \ + inputs.m_clearCoatNormalMap = srgLayerPrefix##m_clearCoatNormalMap; \ + inputs.m_clearCoatNormalMapUvIndex = srgLayerPrefix##m_clearCoatNormalMapUvIndex; \ + inputs.m_clearCoat_normal_useTexture = optionsLayerPrefix##o_clearCoat_normal_useTexture; \ + inputs.m_clearCoatNormalStrength = srgLayerPrefix##m_clearCoatNormalStrength; + // ---------- Pixel Shader ---------- @@ -174,7 +348,6 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float bool displacementIsClipped = false; - // Parallax mapping's non uniform uv transformations break screen space subsurface scattering, disable it when subsurface scatteirng is enabled if(ShouldHandleParallax()) { float3x3 uvMatrix = MaterialSrg::m_parallaxUvIndex == 0 ? MaterialSrg::m_uvMatrix : CreateIdentity3x3(); @@ -197,108 +370,70 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float } } - Surface surface; - surface.position = IN.m_worldPosition; - - // ------- Setup the per-layer UV transforms ------- - - float2 uvLayer1[UvSetCount]; - float2 uvLayer2[UvSetCount]; - float2 uvLayer3[UvSetCount]; - - // Only UV0 will be applied transforms from each layer. - uvLayer1[0] = mul(MaterialSrg::m_layer1_m_uvMatrix, float3(IN.m_uv[0], 1.0)).xy; - uvLayer2[0] = mul(MaterialSrg::m_layer2_m_uvMatrix, float3(IN.m_uv[0], 1.0)).xy; - uvLayer3[0] = mul(MaterialSrg::m_layer3_m_uvMatrix, float3(IN.m_uv[0], 1.0)).xy; - uvLayer1[1] = IN.m_uv[1]; - uvLayer2[1] = IN.m_uv[1]; - uvLayer3[1] = IN.m_uv[1]; - // ------- Calculate Layer Blend Mask Values ------- // Now that any parallax has been calculated, we calculate the blend factors for any layers that are impacted by the parallax. float3 blendWeights = GetBlendWeights(IN.m_uv[MaterialSrg::m_blendMaskUvIndex]); - // ------- Normal ------- + // ------- Layer 1 (base layer) ----------- + + ProcessedMaterialInputs lightingInputLayer1; + { + StandardMaterialInputs inputs; + FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer1_, o_layer1_, blendWeights.r) + lightingInputLayer1 = ProcessStandardMaterialInputs(inputs); + } - float3 layer1_normalFactor = MaterialSrg::m_layer1_m_normalFactor * blendWeights.r; - float3 layer2_normalFactor = MaterialSrg::m_layer2_m_normalFactor * blendWeights.g; - float3 layer3_normalFactor = MaterialSrg::m_layer3_m_normalFactor * blendWeights.b; - float3x3 layer1_uvMatrix = MaterialSrg::m_layer1_m_normalMapUvIndex == 0 ? MaterialSrg::m_layer1_m_uvMatrix : CreateIdentity3x3(); - float3x3 layer2_uvMatrix = MaterialSrg::m_layer2_m_normalMapUvIndex == 0 ? MaterialSrg::m_layer2_m_uvMatrix : CreateIdentity3x3(); - float3x3 layer3_uvMatrix = MaterialSrg::m_layer3_m_normalMapUvIndex == 0 ? MaterialSrg::m_layer3_m_uvMatrix : CreateIdentity3x3(); - float3 layer1_normalTS = GetNormalInputTS(MaterialSrg::m_layer1_m_normalMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_normalMapUvIndex], MaterialSrg::m_layer1_m_flipNormalX, MaterialSrg::m_layer1_m_flipNormalY, layer1_uvMatrix, o_layer1_o_normal_useTexture, layer1_normalFactor); - float3 layer2_normalTS = GetNormalInputTS(MaterialSrg::m_layer2_m_normalMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_normalMapUvIndex], MaterialSrg::m_layer2_m_flipNormalX, MaterialSrg::m_layer2_m_flipNormalY, layer2_uvMatrix, o_layer2_o_normal_useTexture, layer2_normalFactor); - float3 layer3_normalTS = GetNormalInputTS(MaterialSrg::m_layer3_m_normalMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_normalMapUvIndex], MaterialSrg::m_layer3_m_flipNormalX, MaterialSrg::m_layer3_m_flipNormalY, layer3_uvMatrix, o_layer3_o_normal_useTexture, layer3_normalFactor); + // ----------- Layer 2 ----------- + + ProcessedMaterialInputs lightingInputLayer2; + { + StandardMaterialInputs inputs; + FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer2_, o_layer2_, blendWeights.g) + lightingInputLayer2 = ProcessStandardMaterialInputs(inputs); + } - float3 normalTS = ReorientTangentSpaceNormal(layer1_normalTS, layer2_normalTS); - normalTS = ReorientTangentSpaceNormal(normalTS, layer3_normalTS); + // ----------- Layer 3 ----------- + + ProcessedMaterialInputs lightingInputLayer3; + { + StandardMaterialInputs inputs; + FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer3_, o_layer3_, blendWeights.b) + lightingInputLayer3 = ProcessStandardMaterialInputs(inputs); + } + + // ------- Combine all layers --------- + + Surface surface; + surface.position = IN.m_worldPosition; + surface.transmission.InitializeToZero(); + + // ------- Combine Normals --------- + + float3 normalTS = lightingInputLayer1.m_normalTS; + normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer2.m_normalTS); + normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); // [GFX TODO][ATOM-14591]: This will only work if the normal maps all use the same UV stream. We would need to add support for having them in different UV streams. surface.normal = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex])); - - // ------- Base Color ------- - - float2 layer1_baseColorUv = uvLayer1[MaterialSrg::m_layer1_m_baseColorMapUvIndex]; - float2 layer2_baseColorUv = uvLayer2[MaterialSrg::m_layer2_m_baseColorMapUvIndex]; - float2 layer3_baseColorUv = uvLayer3[MaterialSrg::m_layer3_m_baseColorMapUvIndex]; + + // ------- Combine Albedo, roughness, specular, roughness --------- - float3 layer1_sampledColor = GetBaseColorInput(MaterialSrg::m_layer1_m_baseColorMap, MaterialSrg::m_sampler, layer1_baseColorUv, MaterialSrg::m_layer1_m_baseColor.rgb, o_layer1_o_baseColor_useTexture); - float3 layer2_sampledColor = GetBaseColorInput(MaterialSrg::m_layer2_m_baseColorMap, MaterialSrg::m_sampler, layer2_baseColorUv, MaterialSrg::m_layer2_m_baseColor.rgb, o_layer2_o_baseColor_useTexture); - float3 layer3_sampledColor = GetBaseColorInput(MaterialSrg::m_layer3_m_baseColorMap, MaterialSrg::m_sampler, layer3_baseColorUv, MaterialSrg::m_layer3_m_baseColor.rgb, o_layer3_o_baseColor_useTexture); - float3 layer1_baseColor = BlendBaseColor(layer1_sampledColor, MaterialSrg::m_layer1_m_baseColor.rgb, MaterialSrg::m_layer1_m_baseColorFactor, o_layer1_o_baseColorTextureBlendMode, o_layer1_o_baseColor_useTexture); - float3 layer2_baseColor = BlendBaseColor(layer2_sampledColor, MaterialSrg::m_layer2_m_baseColor.rgb, MaterialSrg::m_layer2_m_baseColorFactor, o_layer2_o_baseColorTextureBlendMode, o_layer2_o_baseColor_useTexture); - float3 layer3_baseColor = BlendBaseColor(layer3_sampledColor, MaterialSrg::m_layer3_m_baseColor.rgb, MaterialSrg::m_layer3_m_baseColorFactor, o_layer3_o_baseColorTextureBlendMode, o_layer3_o_baseColor_useTexture); - float3 baseColor = BlendLayers(layer1_baseColor, layer2_baseColor, layer3_baseColor, blendWeights); + float3 baseColor = BlendLayers(lightingInputLayer1.m_baseColor, lightingInputLayer2.m_baseColor, lightingInputLayer3.m_baseColor, blendWeights); + float3 specularF0Factor = BlendLayers(lightingInputLayer1.m_specularF0Factor, lightingInputLayer2.m_specularF0Factor, lightingInputLayer3.m_specularF0Factor, blendWeights); + float3 metallic = BlendLayers(lightingInputLayer1.m_metallic, lightingInputLayer2.m_metallic, lightingInputLayer3.m_metallic, blendWeights); if(o_parallax_highlightClipping && displacementIsClipped) { ApplyParallaxClippingHighlight(baseColor); } - // ------- Metallic ------- - - float metallic = 0; - if(!o_enableSubsurfaceScattering) // If subsurface scattering is enabled skip texture lookup for metallic, as this quantity won't be used anyway - { - float layer1_metallic = GetMetallicInput(MaterialSrg::m_layer1_m_metallicMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_metallicMapUvIndex], MaterialSrg::m_layer1_m_metallicFactor, o_layer1_o_metallic_useTexture); - float layer2_metallic = GetMetallicInput(MaterialSrg::m_layer2_m_metallicMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_metallicMapUvIndex], MaterialSrg::m_layer2_m_metallicFactor, o_layer2_o_metallic_useTexture); - float layer3_metallic = GetMetallicInput(MaterialSrg::m_layer3_m_metallicMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_metallicMapUvIndex], MaterialSrg::m_layer3_m_metallicFactor, o_layer3_o_metallic_useTexture); - metallic = BlendLayers(layer1_metallic, layer2_metallic, layer3_metallic, blendWeights); - } - - // ------- Specular ------- - - float layer1_specularF0Factor = GetSpecularInput(MaterialSrg::m_layer1_m_specularF0Map, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_specularF0MapUvIndex], MaterialSrg::m_layer1_m_specularF0Factor, o_layer1_o_specularF0_useTexture); - float layer2_specularF0Factor = GetSpecularInput(MaterialSrg::m_layer2_m_specularF0Map, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_specularF0MapUvIndex], MaterialSrg::m_layer2_m_specularF0Factor, o_layer2_o_specularF0_useTexture); - float layer3_specularF0Factor = GetSpecularInput(MaterialSrg::m_layer3_m_specularF0Map, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_specularF0MapUvIndex], MaterialSrg::m_layer3_m_specularF0Factor, o_layer3_o_specularF0_useTexture); - float specularF0Factor = BlendLayers(layer1_specularF0Factor, layer2_specularF0Factor, layer3_specularF0Factor, blendWeights); - surface.SetAlbedoAndSpecularF0(baseColor, specularF0Factor, metallic); - // ------- Roughness ------- - - float layer1_roughness = GetRoughnessInput(MaterialSrg::m_layer1_m_roughnessMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_roughnessMapUvIndex], MaterialSrg::m_layer1_m_roughnessFactor, MaterialSrg::m_layer1_m_roughnessLowerBound, MaterialSrg::m_layer1_m_roughnessUpperBound, o_layer1_o_roughness_useTexture); - float layer2_roughness = GetRoughnessInput(MaterialSrg::m_layer2_m_roughnessMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_roughnessMapUvIndex], MaterialSrg::m_layer2_m_roughnessFactor, MaterialSrg::m_layer2_m_roughnessLowerBound, MaterialSrg::m_layer2_m_roughnessUpperBound, o_layer2_o_roughness_useTexture); - float layer3_roughness = GetRoughnessInput(MaterialSrg::m_layer3_m_roughnessMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_roughnessMapUvIndex], MaterialSrg::m_layer3_m_roughnessFactor, MaterialSrg::m_layer3_m_roughnessLowerBound, MaterialSrg::m_layer3_m_roughnessUpperBound, o_layer3_o_roughness_useTexture); - surface.roughnessLinear = BlendLayers(layer1_roughness, layer2_roughness, layer3_roughness, blendWeights); - + surface.roughnessLinear = BlendLayers(lightingInputLayer1.m_roughness, lightingInputLayer2.m_roughness, lightingInputLayer3.m_roughness, blendWeights); surface.CalculateRoughnessA(); - - // ------- Subsurface ------- - - float2 subsurfaceUv = IN.m_uv[MaterialSrg::m_subsurfaceScatteringInfluenceMapUvIndex]; - float surfaceScatteringFactor = GetSubsurfaceInput(MaterialSrg::m_subsurfaceScatteringInfluenceMap, MaterialSrg::m_sampler, subsurfaceUv, MaterialSrg::m_subsurfaceScatteringFactor); - - // ------- Transmission ------- - - float2 transmissionUv = IN.m_uv[MaterialSrg::m_transmissionThicknessMapUvIndex]; - float4 transmissionTintThickness = GeTransmissionInput(MaterialSrg::m_transmissionThicknessMap, MaterialSrg::m_sampler, transmissionUv, MaterialSrg::m_transmissionTintThickness); - surface.transmission.tint = transmissionTintThickness.rgb; - surface.transmission.thickness = transmissionTintThickness.w; - surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams; - // ------- Lighting Data ------- - + // ------- Init and Combine Lighting Data ------- + LightingData lightingData; // Light iterator @@ -307,88 +442,22 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Directional light shadow coordinates lightingData.shadowCoords = IN.m_shadowCoords; - - // ------- Emissive ------- - float3 layer1_emissive = GetEmissiveInput(MaterialSrg::m_layer1_m_emissiveMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_emissiveMapUvIndex], MaterialSrg::m_layer1_m_emissiveIntensity, MaterialSrg::m_layer1_m_emissiveColor.rgb, o_layer1_o_emissiveEnabled, o_layer1_o_emissive_useTexture); - float3 layer2_emissive = GetEmissiveInput(MaterialSrg::m_layer2_m_emissiveMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_emissiveMapUvIndex], MaterialSrg::m_layer2_m_emissiveIntensity, MaterialSrg::m_layer2_m_emissiveColor.rgb, o_layer2_o_emissiveEnabled, o_layer2_o_emissive_useTexture); - float3 layer3_emissive = GetEmissiveInput(MaterialSrg::m_layer3_m_emissiveMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_emissiveMapUvIndex], MaterialSrg::m_layer3_m_emissiveIntensity, MaterialSrg::m_layer3_m_emissiveColor.rgb, o_layer3_o_emissiveEnabled, o_layer3_o_emissive_useTexture); - lightingData.emissiveLighting = BlendLayers(layer1_emissive, layer2_emissive, layer3_emissive, blendWeights); + lightingData.emissiveLighting = BlendLayers(lightingInputLayer1.m_emissiveLighting, lightingInputLayer2.m_emissiveLighting, lightingInputLayer3.m_emissiveLighting, blendWeights); + lightingData.specularOcclusion = BlendLayers(lightingInputLayer1.m_specularOcclusion, lightingInputLayer2.m_specularOcclusion, lightingInputLayer3.m_specularOcclusion, blendWeights); + lightingData.diffuseAmbientOcclusion = BlendLayers(lightingInputLayer1.m_diffuseAmbientOcclusion, lightingInputLayer2.m_diffuseAmbientOcclusion, lightingInputLayer3.m_diffuseAmbientOcclusion, blendWeights); - // ------- Occlusion ------- - - float layer1_diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_layer1_m_diffuseOcclusionMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_diffuseOcclusionMapUvIndex], MaterialSrg::m_layer1_m_diffuseOcclusionFactor, o_layer1_o_diffuseOcclusion_useTexture); - float layer2_diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_layer2_m_diffuseOcclusionMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_diffuseOcclusionMapUvIndex], MaterialSrg::m_layer2_m_diffuseOcclusionFactor, o_layer2_o_diffuseOcclusion_useTexture); - float layer3_diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_layer3_m_diffuseOcclusionMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_diffuseOcclusionMapUvIndex], MaterialSrg::m_layer3_m_diffuseOcclusionFactor, o_layer3_o_diffuseOcclusion_useTexture); - lightingData.diffuseAmbientOcclusion = BlendLayers(layer1_diffuseAmbientOcclusion, layer2_diffuseAmbientOcclusion, layer3_diffuseAmbientOcclusion, blendWeights); + lightingData.CalculateMultiscatterCompensation(surface.specularF0, o_specularF0_enableMultiScatterCompensation); - float layer1_specularOcclusion = GetOcclusionInput(MaterialSrg::m_layer1_m_specularOcclusionMap, MaterialSrg::m_sampler, uvLayer1[MaterialSrg::m_layer1_m_specularOcclusionMapUvIndex], MaterialSrg::m_layer1_m_specularOcclusionFactor, o_layer1_o_specularOcclusion_useTexture); - float layer2_specularOcclusion = GetOcclusionInput(MaterialSrg::m_layer2_m_specularOcclusionMap, MaterialSrg::m_sampler, uvLayer2[MaterialSrg::m_layer2_m_specularOcclusionMapUvIndex], MaterialSrg::m_layer2_m_specularOcclusionFactor, o_layer2_o_specularOcclusion_useTexture); - float layer3_specularOcclusion = GetOcclusionInput(MaterialSrg::m_layer3_m_specularOcclusionMap, MaterialSrg::m_sampler, uvLayer3[MaterialSrg::m_layer3_m_specularOcclusionMapUvIndex], MaterialSrg::m_layer3_m_specularOcclusionFactor, o_layer3_o_specularOcclusion_useTexture); - lightingData.specularOcclusion = BlendLayers(layer1_specularOcclusion, layer2_specularOcclusion, layer3_specularOcclusion, blendWeights); - - // ------- Clearcoat ------- + // ------- Combine Clearcoat ------- if(o_clearCoat_feature_enabled) { - // --- Layer 1 --- - - float layer1_clearCoatFactor = 0.0f; - float layer1_clearCoatRoughness = 0.0f; - float3 layer1_clearCoatNormal = float3(0.0, 0.0, 0.0); - if(o_layer1_o_clearCoat_enabled) - { - float3x3 layer1_uvMatrix = MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex == 0 ? MaterialSrg::m_layer1_m_uvMatrix : CreateIdentity3x3(); - - GetClearCoatInputs(MaterialSrg::m_layer1_m_clearCoatInfluenceMap, uvLayer1[MaterialSrg::m_layer1_m_clearCoatInfluenceMapUvIndex], MaterialSrg::m_layer1_m_clearCoatFactor, o_layer1_o_clearCoat_factor_useTexture, - MaterialSrg::m_layer1_m_clearCoatRoughnessMap, uvLayer1[MaterialSrg::m_layer1_m_clearCoatRoughnessMapUvIndex], MaterialSrg::m_layer1_m_clearCoatRoughness, o_layer1_o_clearCoat_roughness_useTexture, - MaterialSrg::m_layer1_m_clearCoatNormalMap, uvLayer1[MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex], IN.m_normal, o_layer1_o_clearCoat_normal_useTexture, MaterialSrg::m_layer1_m_clearCoatNormalStrength, - layer1_uvMatrix, tangents[MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex], bitangents[MaterialSrg::m_layer1_m_clearCoatNormalMapUvIndex], - MaterialSrg::m_sampler, isFrontFace, - layer1_clearCoatFactor, layer1_clearCoatRoughness, layer1_clearCoatNormal); - } - - // --- Layer 2 --- - - float layer2_clearCoatFactor = 0.0f; - float layer2_clearCoatRoughness = 0.0f; - float3 layer2_clearCoatNormal = float3(0.0, 0.0, 0.0); - if(o_layer2_o_clearCoat_enabled) - { - float3x3 layer2_uvMatrix = MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex == 0 ? MaterialSrg::m_layer2_m_uvMatrix : CreateIdentity3x3(); - - GetClearCoatInputs(MaterialSrg::m_layer2_m_clearCoatInfluenceMap, uvLayer2[MaterialSrg::m_layer2_m_clearCoatInfluenceMapUvIndex], MaterialSrg::m_layer2_m_clearCoatFactor, o_layer2_o_clearCoat_factor_useTexture, - MaterialSrg::m_layer2_m_clearCoatRoughnessMap, uvLayer2[MaterialSrg::m_layer2_m_clearCoatRoughnessMapUvIndex], MaterialSrg::m_layer2_m_clearCoatRoughness, o_layer2_o_clearCoat_roughness_useTexture, - MaterialSrg::m_layer2_m_clearCoatNormalMap, uvLayer2[MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex], IN.m_normal, o_layer2_o_clearCoat_normal_useTexture, MaterialSrg::m_layer2_m_clearCoatNormalStrength, - layer2_uvMatrix, tangents[MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex], bitangents[MaterialSrg::m_layer2_m_clearCoatNormalMapUvIndex], - MaterialSrg::m_sampler, isFrontFace, - layer2_clearCoatFactor, layer2_clearCoatRoughness, layer2_clearCoatNormal); - } - - // --- Layer 3 --- - - float layer3_clearCoatFactor = 0.0f; - float layer3_clearCoatRoughness = 0.0f; - float3 layer3_clearCoatNormal = float3(0.0, 0.0, 0.0); - if(o_layer3_o_clearCoat_enabled) - { - float3x3 layer3_uvMatrix = MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex == 0 ? MaterialSrg::m_layer3_m_uvMatrix : CreateIdentity3x3(); - - GetClearCoatInputs(MaterialSrg::m_layer3_m_clearCoatInfluenceMap, uvLayer3[MaterialSrg::m_layer3_m_clearCoatInfluenceMapUvIndex], MaterialSrg::m_layer3_m_clearCoatFactor, o_layer3_o_clearCoat_factor_useTexture, - MaterialSrg::m_layer3_m_clearCoatRoughnessMap, uvLayer3[MaterialSrg::m_layer3_m_clearCoatRoughnessMapUvIndex], MaterialSrg::m_layer3_m_clearCoatRoughness, o_layer3_o_clearCoat_roughness_useTexture, - MaterialSrg::m_layer3_m_clearCoatNormalMap, uvLayer3[MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex], IN.m_normal, o_layer3_o_clearCoat_normal_useTexture, MaterialSrg::m_layer3_m_clearCoatNormalStrength, - layer3_uvMatrix, tangents[MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex], bitangents[MaterialSrg::m_layer3_m_clearCoatNormalMapUvIndex], - MaterialSrg::m_sampler, isFrontFace, - layer3_clearCoatFactor, layer3_clearCoatRoughness, layer3_clearCoatNormal); - } - - // --- Blend Layers --- - - surface.clearCoat.factor = BlendLayers(layer1_clearCoatFactor, layer2_clearCoatFactor, layer3_clearCoatFactor, blendWeights); - surface.clearCoat.roughness = BlendLayers(layer1_clearCoatRoughness, layer2_clearCoatRoughness, layer3_clearCoatRoughness, blendWeights); + surface.clearCoat.factor = BlendLayers(lightingInputLayer1.m_clearCoat.factor, lightingInputLayer2.m_clearCoat.factor, lightingInputLayer3.m_clearCoat.factor, blendWeights); + surface.clearCoat.roughness = BlendLayers(lightingInputLayer1.m_clearCoat.roughness, lightingInputLayer2.m_clearCoat.roughness, lightingInputLayer3.m_clearCoat.roughness, blendWeights); // [GFX TODO][ATOM-14592] This is not the right way to blend the normals. We need to use ReorientTangentSpaceNormal(), and that requires GetClearCoatInputs() to return the normal in TS instead of WS. - surface.clearCoat.normal = BlendLayers(layer1_clearCoatNormal, layer2_clearCoatNormal, layer3_clearCoatNormal, blendWeights); + surface.clearCoat.normal = BlendLayers(lightingInputLayer1.m_clearCoat.normal, lightingInputLayer2.m_clearCoat.normal, lightingInputLayer3.m_clearCoat.normal, blendWeights); surface.clearCoat.normal = normalize(surface.clearCoat.normal); // manipulate base layer f0 if clear coat is enabled @@ -408,11 +477,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // Clear coat layer has fixed IOR = 1.5 and transparent => F0 = (1.5 - 1)^2 / (1.5 + 1)^2 = 0.04 lightingData.diffuseResponse *= 1.0 - (FresnelSchlickWithRoughness(lightingData.NdotV, float3(0.04, 0.04, 0.04), surface.clearCoat.roughness) * surface.clearCoat.factor); } - - // ------- Multiscatter ------- - - lightingData.CalculateMultiscatterCompensation(surface.specularF0, o_specularF0_enableMultiScatterCompensation); - + // ------- Lighting Calculation ------- // Apply Decals @@ -425,17 +490,14 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float ApplyIBL(surface, lightingData); // Finalize Lighting - lightingData.FinalizeLighting(surface.transmission.tint); + lightingData.FinalizeLighting(0); const float alpha = 1.0; PbrLightingOutput lightingOutput = GetPbrLightingOutput(surface, lightingData, alpha); - // Pack factor and quality, drawback: because of precision limit of float16 cannot represent exact 1, maximum representable value is 0.9961 - uint factorAndQuality = dot(round(float2(saturate(surfaceScatteringFactor), MaterialSrg::m_subsurfaceScatteringQuality) * 255), float2(256, 1)); - lightingOutput.m_diffuseColor.w = factorAndQuality * (o_enableSubsurfaceScattering ? 1.0 : -1.0); - + lightingOutput.m_diffuseColor.w = -1; // Disable subsurface scattering return lightingOutput; } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli index bb63d27df0..85d9370d2b 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli @@ -32,6 +32,8 @@ class Surface float roughnessA; //!< Actual roughness value ( a.k.a. "alpha roughness") to be used in microfacet calculations float roughnessA2; //!< Alpha roughness ^ 2 (i.e. roughnessA * roughnessA), used in GGX, cached here for perfromance + void InitializeToZero(); + //! Applies specular anti-aliasing to roughnessA2 void ApplySpecularAA(); @@ -43,6 +45,18 @@ class Surface }; +void Surface::InitializeToZero() +{ + clearCoat.InitializeToZero(); + transmission.InitializeToZero(); + position = float3(0,0,0); + normal = float3(0,0,0); + albedo = float3(0,0,0); + specularF0 = float3(0,0,0); + roughnessLinear = 0.0f; + roughnessA = 0.0f; + roughnessA2 = 0.0f; +} // Specular Anti-Aliasing technique from this paper: // http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf From 275bb1bfeccabb816880960c6afe61ff80e9fb58 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Tue, 11 May 2021 17:25:53 -0700 Subject: [PATCH 05/48] ATOM-14688 Disable Individual Layers Added flags for enabling StandardMultilayerPBR layers 2 and 3. This makes it easy to create a two-layer material, or to flip layers off and on for debugging. --- .../Types/StandardMultilayerPBR.materialtype | 22 +++++++ .../Types/StandardMultilayerPBR_Common.azsli | 64 +++++++++++++------ .../StandardMultilayerPBR_ForwardPass.azsl | 20 +++++- .../001_ManyFeatures.material | 4 ++ .../001_ManyFeatures_Layer2Off.material | 11 ++++ .../001_ManyFeatures_Layer3Off.material | 11 ++++ .../002_ParallaxPdo.material | 4 ++ 7 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer2Off.material create mode 100644 Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer3Off.material diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index 90a599c6f9..b49c66346f 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -295,6 +295,28 @@ } ], "blend": [ + { + "id": "enableLayer2", + "displayName": "Enable Layer 2", + "description": "Whether to enable layer 2.", + "type": "Bool", + "defaultValue": false, + "connection": { + "type": "ShaderOption", + "id": "o_layer2_enabled" + } + }, + { + "id": "enableLayer3", + "displayName": "Enable Layer 3", + "description": "Whether to enable layer 3.", + "type": "Bool", + "defaultValue": false, + "connection": { + "type": "ShaderOption", + "id": "o_layer3_enabled" + } + }, { "id": "blendSource", "displayName": "Blend Source", diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli index 8c7b49214b..d2314efefe 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_Common.azsli @@ -95,6 +95,9 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial // ------ Shader Options ---------------------------------------- +option bool o_layer2_enabled; +option bool o_layer3_enabled; + enum class DebugDrawMode { None, BlendSource, DepthMaps }; option DebugDrawMode o_debugDrawMode; @@ -146,14 +149,27 @@ float3 GetBlendSourceValues(float2 uv) { float3 blendSourceValues = float3(0,0,0); - switch(GetFinalBlendMaskSource()) + if(o_layer2_enabled || o_layer3_enabled) { - case BlendMaskSource::TextureMap: - blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, uv).rgb; - break; - case BlendMaskSource::VertexColors: - blendSourceValues = s_blendMaskFromVertexStream; - break; + switch(GetFinalBlendMaskSource()) + { + case BlendMaskSource::TextureMap: + blendSourceValues = MaterialSrg::m_blendMaskTexture.Sample(MaterialSrg::m_sampler, uv).rgb; + break; + case BlendMaskSource::VertexColors: + blendSourceValues = s_blendMaskFromVertexStream; + break; + } + + if(!o_layer2_enabled) + { + blendSourceValues.r = 0.0; + } + + if(!o_layer3_enabled) + { + blendSourceValues.g = 0.0; + } } return blendSourceValues; @@ -167,18 +183,26 @@ float3 GetBlendSourceValues(float2 uv) //! layer3 = b float3 GetBlendWeights(float2 uv) { - float3 blendSourceValues = GetBlendSourceValues(uv); - - // Calculate blend weights such that multiplying and adding them with layer data is equivalent - // to lerping between each layer. - // final = lerp(final, layer1, blendWeights.r) - // final = lerp(final, layer2, blendWeights.g) - // final = lerp(final, layer3, blendWeights.b) - float3 blendWeights; - blendWeights.b = blendSourceValues.g; - blendWeights.g = (1.0 - blendSourceValues.g) * blendSourceValues.r; - blendWeights.r = (1.0 - blendSourceValues.g) * (1.0 - blendSourceValues.r); + + if(o_layer2_enabled || o_layer3_enabled) + { + float3 blendSourceValues = GetBlendSourceValues(uv); + + // Calculate blend weights such that multiplying and adding them with layer data is equivalent + // to lerping between each layer. + // final = lerp(final, layer1, blendWeights.r) + // final = lerp(final, layer2, blendWeights.g) + // final = lerp(final, layer3, blendWeights.b) + + blendWeights.b = blendSourceValues.g; + blendWeights.g = (1.0 - blendSourceValues.g) * blendSourceValues.r; + blendWeights.r = (1.0 - blendSourceValues.g) * (1.0 - blendSourceValues.r); + } + else + { + blendWeights = float3(1,0,0); + } return blendWeights; } @@ -230,7 +254,7 @@ DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) layerDepthValues.r -= MaterialSrg::m_layer1_m_depthOffset; } - if(o_layer2_o_useDepthMap) + if(o_layer2_enabled && o_layer2_o_useDepthMap) { float2 layerUv = uv; if(MaterialSrg::m_parallaxUvIndex == 0) @@ -243,7 +267,7 @@ DepthResult GetDepth(float2 uv, float2 uv_ddx, float2 uv_ddy) layerDepthValues.g -= MaterialSrg::m_layer2_m_depthOffset; } - if(o_layer3_o_useDepthMap) + if(o_layer3_enabled && o_layer3_o_useDepthMap) { float2 layerUv = uv; if(MaterialSrg::m_parallaxUvIndex == 0) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl index 9e5a2e623a..8d5d32940d 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ForwardPass.azsl @@ -387,20 +387,30 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ----------- Layer 2 ----------- ProcessedMaterialInputs lightingInputLayer2; + if(o_layer2_enabled) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer2_, o_layer2_, blendWeights.g) lightingInputLayer2 = ProcessStandardMaterialInputs(inputs); } + else + { + lightingInputLayer2.InitializeToZero(); + } // ----------- Layer 3 ----------- ProcessedMaterialInputs lightingInputLayer3; + if(o_layer3_enabled) { StandardMaterialInputs inputs; FILL_STANDARD_MATERIAL_INPUTS(inputs, MaterialSrg::m_layer3_, o_layer3_, blendWeights.b) lightingInputLayer3 = ProcessStandardMaterialInputs(inputs); } + else + { + lightingInputLayer3.InitializeToZero(); + } // ------- Combine all layers --------- @@ -411,8 +421,14 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float // ------- Combine Normals --------- float3 normalTS = lightingInputLayer1.m_normalTS; - normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer2.m_normalTS); - normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); + if(o_layer2_enabled) + { + normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer2.m_normalTS); + } + if(o_layer3_enabled) + { + normalTS = ReorientTangentSpaceNormal(normalTS, lightingInputLayer3.m_normalTS); + } // [GFX TODO][ATOM-14591]: This will only work if the normal maps all use the same UV stream. We would need to add support for having them in different UV streams. surface.normal = normalize(TangentSpaceToWorld(normalTS, IN.m_normal, tangents[MaterialSrg::m_parallaxUvIndex], bitangents[MaterialSrg::m_parallaxUvIndex])); diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material index b85705fcb8..d9a4aabe2a 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "blend": { + "enableLayer2": true, + "enableLayer3": true + }, "layer1_baseColor": { "color": [ 0.3495536744594574, diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer2Off.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer2Off.material new file mode 100644 index 0000000000..d91cfb34eb --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer2Off.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "enableLayer2": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer3Off.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer3Off.material new file mode 100644 index 0000000000..3ee48df612 --- /dev/null +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures_Layer3Off.material @@ -0,0 +1,11 @@ +{ + "description": "", + "materialType": "Materials/Types/StandardMultilayerPBR.materialtype", + "parentMaterial": "TestData/Materials/StandardMultilayerPbrTestCases/001_ManyFeatures.material", + "propertyLayoutVersion": 3, + "properties": { + "blend": { + "enableLayer3": false + } + } +} \ No newline at end of file diff --git a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material index f6c881aee5..0bf4177db9 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardMultilayerPbrTestCases/002_ParallaxPdo.material @@ -4,6 +4,10 @@ "parentMaterial": "", "propertyLayoutVersion": 3, "properties": { + "blend": { + "enableLayer2": true, + "enableLayer3": true + }, "layer1_baseColor": { "textureMap": "TestData/Textures/cc0/bark1_col.jpg" }, From 53188a12da7d3ce90de64a0d184b6a5f9df613d8 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Wed, 12 May 2021 17:06:57 -0700 Subject: [PATCH 06/48] Made StandardMultilayerPBR hide a layer's property groups when that layer is disabled. ATOM-14688 Disable Individual Layers - Added new SetMaterialPropertyGroupVisibility functions to the material functors. - Updated the MaterialFunctor::EditorContext to include parameters for handling material property group metadata. - Updated the material inspector(s) to apply the property group visiblity changes from the material functor, to hide or show the property groups. - Moved some code from MaterialPropertyDescriptor.h/cpp to a new MaterialDynamicMetadata.h/cpp, since these aren't really related to the MaterialPropertyDescriptor code. It's more for material functors to use. - Also fixed the casing for the "GetMaterialPropertyValue_Image" lua function, since I was already in this code (ATOM-14793 "Fix Inconsistent Casing For LuaMaterialFunctorRuntimeContext") Tested in MaterialEditor and in in the main Editor's MaterialComponent property override inspector. --- .../DetailMapsCommonFunctor.lua | 8 +- .../Materials/Types/Skin_WrinkleMaps.lua | 4 +- .../Types/StandardMultilayerPBR.materialtype | 6 ++ .../StandardMultilayerPBR_LayerEnable.lua | 49 +++++++++ ...StandardMultilayerPBR_ParallaxPerLayer.lua | 4 +- .../Types/StandardPBR_ClearCoatState.lua | 4 +- .../Types/StandardPBR_EmissiveState.lua | 4 +- .../Types/StandardPBR_HandleOpacityMode.lua | 2 +- .../Types/StandardPBR_ParallaxState.lua | 4 +- .../Materials/Types/StandardPBR_Roughness.lua | 4 +- .../Types/StandardPBR_SubsurfaceState.lua | 4 +- .../RPI.Reflect/Material/LuaMaterialFunctor.h | 2 + .../Material/MaterialDynamicMetadata.h | 100 ++++++++++++++++++ .../RPI.Reflect/Material/MaterialFunctor.h | 20 +++- .../Material/MaterialPropertyDescriptor.h | 47 -------- .../RPI.Public/Material/MaterialSystem.cpp | 1 + .../Material/LuaMaterialFunctor.cpp | 17 ++- .../Material/MaterialDynamicMetadata.cpp | 50 +++++++++ .../RPI.Reflect/Material/MaterialFunctor.cpp | 96 ++++++++++++----- .../Material/MaterialPropertyDescriptor.cpp | 14 --- .../Material/LuaMaterialFunctorTests.cpp | 76 ++++++++++++- .../MaterialPropertySerializerTests.cpp | 1 + .../RPI/Code/atom_rpi_reflect_files.cmake | 2 + .../DynamicProperty/DynamicProperty.h | 6 +- .../Inspector/InspectorRequestBus.h | 6 ++ .../Inspector/InspectorWidget.h | 3 + .../Code/Source/Inspector/InspectorWidget.cpp | 27 +++++ .../MaterialDocumentNotificationBus.h | 7 ++ .../Document/MaterialDocumentRequestBus.h | 5 + .../Code/Source/Document/MaterialDocument.cpp | 58 ++++++++-- .../Code/Source/Document/MaterialDocument.h | 21 +++- .../MaterialInspector/MaterialInspector.cpp | 16 ++- .../MaterialInspector/MaterialInspector.h | 1 + .../EditorMaterialComponentInspector.cpp | 25 +++-- 34 files changed, 553 insertions(+), 141 deletions(-) create mode 100644 Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_LayerEnable.lua create mode 100644 Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialDynamicMetadata.h create mode 100644 Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialDynamicMetadata.cpp diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/DetailMapsCommonFunctor.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/DetailMapsCommonFunctor.lua index 15e4b4f416..e1a3dd6f29 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/DetailMapsCommonFunctor.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/MaterialInputs/DetailMapsCommonFunctor.lua @@ -35,16 +35,16 @@ end function Process(context) local isFeatureEnabled = context:GetMaterialPropertyValue_bool("detailLayerGroup.enableDetailLayer") - local blendMaskTexture = context:GetMaterialPropertyValue_image("detailLayerGroup.blendDetailMask") + local blendMaskTexture = context:GetMaterialPropertyValue_Image("detailLayerGroup.blendDetailMask") local blendMaskTextureEnabled = context:GetMaterialPropertyValue_bool("detailLayerGroup.enableDetailMaskTexture") context:SetShaderOptionValue_bool("o_detail_blendMask_useTexture", isFeatureEnabled and blendMaskTextureEnabled and blendMaskTexture ~= nil) local baseColorDetailEnabled = context:GetMaterialPropertyValue_bool("detailLayerGroup.enableBaseColor") - local baseColorDetailTexture = context:GetMaterialPropertyValue_image("detailLayerGroup.baseColorDetailMap") + local baseColorDetailTexture = context:GetMaterialPropertyValue_Image("detailLayerGroup.baseColorDetailMap") context:SetShaderOptionValue_bool("o_detail_baseColor_useTexture", isFeatureEnabled and baseColorDetailEnabled and baseColorDetailTexture ~= nil) local normalDetailEnabled = context:GetMaterialPropertyValue_bool("detailLayerGroup.enableNormals") - local normalDetailTexture = context:GetMaterialPropertyValue_image("detailLayerGroup.normalDetailMap") + local normalDetailTexture = context:GetMaterialPropertyValue_Image("detailLayerGroup.normalDetailMap") context:SetShaderOptionValue_bool("o_detail_normal_useTexture", isFeatureEnabled and normalDetailEnabled and normalDetailTexture ~= nil) end @@ -78,7 +78,7 @@ function ProcessEditor(context) context:SetMaterialPropertyVisibility("detailUV.rotateDegrees", mainVisibility) context:SetMaterialPropertyVisibility("detailUV.scale", mainVisibility) - local blendMaskTexture = context:GetMaterialPropertyValue_image("detailLayerGroup.blendDetailMask") + local blendMaskTexture = context:GetMaterialPropertyValue_Image("detailLayerGroup.blendDetailMask") if(nil == blendMaskTexture) then context:SetMaterialPropertyVisibility("detailLayerGroup.enableDetailMaskTexture", MaterialPropertyVisibility_Hidden) context:SetMaterialPropertyVisibility("detailLayerGroup.blendDetailMaskUv", MaterialPropertyVisibility_Hidden) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_WrinkleMaps.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_WrinkleMaps.lua index d77918f521..9e1bc3763a 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_WrinkleMaps.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_WrinkleMaps.lua @@ -65,8 +65,8 @@ function Process(context) for i=1,MAX_WRINKLE_LAYER_COUNT do if(i <= count) then - isBaseColorTextureMissing = isBaseColorEnabled and nil == context:GetMaterialPropertyValue_image("wrinkleLayers.baseColorMap" .. i) - isNormalTextureMissing = isNormalEnabled and nil == context:GetMaterialPropertyValue_image("wrinkleLayers.normalMap" .. i) + isBaseColorTextureMissing = isBaseColorEnabled and nil == context:GetMaterialPropertyValue_Image("wrinkleLayers.baseColorMap" .. i) + isNormalTextureMissing = isNormalEnabled and nil == context:GetMaterialPropertyValue_Image("wrinkleLayers.normalMap" .. i) context:SetShaderOptionValue_bool("o_wrinkleLayers_baseColor_useTexture" .. i, not isBaseColorTextureMissing) context:SetShaderOptionValue_bool("o_wrinkleLayers_normal_useTexture" .. i, not isNormalTextureMissing) else diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype index b49c66346f..a2854e8902 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR.materialtype @@ -2684,6 +2684,12 @@ "file": "StandardMultilayerPBR_ShaderEnable.lua" } }, + { + "type": "Lua", + "args": { + "file": "StandardMultilayerPBR_LayerEnable.lua" + } + }, { "type": "Lua", "args": { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_LayerEnable.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_LayerEnable.lua new file mode 100644 index 0000000000..f60aac6149 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_LayerEnable.lua @@ -0,0 +1,49 @@ +-------------------------------------------------------------------------------------- +-- +-- 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 functor hides the properties for disabled material layers. + +function GetMaterialPropertyDependencies() + return { + "blend.enableLayer2", + "blend.enableLayer3" + } +end + +function SetLayerVisibility(context, layerNamePrefix, isVisible) + + local visibility = MaterialPropertyGroupVisibility_Enabled + if(not isVisible) then + visibility = MaterialPropertyGroupVisibility_Hidden + end + + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "baseColor", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "metallic", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "roughness", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "specularF0", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "normal", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "clearCoat", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "occlusion", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "emissive", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "parallax", visibility) + context:SetMaterialPropertyGroupVisibility(layerNamePrefix .. "uv", visibility) +end + +function ProcessEditor(context) + local enableLayer2 = context:GetMaterialPropertyValue_bool("blend.enableLayer2") + local enableLayer3 = context:GetMaterialPropertyValue_bool("blend.enableLayer3") + + SetLayerVisibility(context, "layer2_", context:GetMaterialPropertyValue_bool("blend.enableLayer2")) + SetLayerVisibility(context, "layer3_", context:GetMaterialPropertyValue_bool("blend.enableLayer3")) +end diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua index 119dfed436..bd56292229 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua @@ -24,7 +24,7 @@ end function Process(context) local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - local textureMap = context:GetMaterialPropertyValue_image("parallax.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") context:SetShaderOptionValue_bool("o_useDepthMap", enable and textureMap ~= nil) end @@ -37,7 +37,7 @@ function ProcessEditor(context) context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Hidden) end - local textureMap = context:GetMaterialPropertyValue_image("parallax.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") local visibility = MaterialPropertyVisibility_Enabled if(not enable or textureMap == nil) then visibility = MaterialPropertyVisibility_Hidden diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ClearCoatState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ClearCoatState.lua index 8289291ef4..ffa00d7efd 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ClearCoatState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ClearCoatState.lua @@ -34,7 +34,7 @@ function GetShaderOptionDependencies() end function UpdateUseTextureState(context, clearCoatEnabled, textureMapPropertyName, useTexturePropertyName, shaderOptionName) - local textureMap = context:GetMaterialPropertyValue_image(textureMapPropertyName) + local textureMap = context:GetMaterialPropertyValue_Image(textureMapPropertyName) local useTextureMap = context:GetMaterialPropertyValue_bool(useTexturePropertyName) context:SetShaderOptionValue_bool(shaderOptionName, clearCoatEnabled and useTextureMap and textureMap ~= nil) end @@ -50,7 +50,7 @@ end -- Note this logic matches that of the UseTextureFunctor class. function UpdateTextureDependentPropertyVisibility(context, textureMapPropertyName, useTexturePropertyName, uvPropertyName) - local textureMap = context:GetMaterialPropertyValue_image(textureMapPropertyName) + local textureMap = context:GetMaterialPropertyValue_Image(textureMapPropertyName) local useTexture = context:GetMaterialPropertyValue_bool(useTexturePropertyName) if(textureMap == nil) then diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_EmissiveState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_EmissiveState.lua index a8ac2e8a4a..7ee5876adb 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_EmissiveState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_EmissiveState.lua @@ -22,7 +22,7 @@ end function Process(context) local enable = context:GetMaterialPropertyValue_bool("emissive.enable") - local textureMap = context:GetMaterialPropertyValue_image("emissive.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("emissive.textureMap") local useTextureMap = context:GetMaterialPropertyValue_bool("emissive.useTexture") context:SetShaderOptionValue_bool("o_emissiveEnabled", enable) @@ -47,7 +47,7 @@ function ProcessEditor(context) context:SetMaterialPropertyVisibility("emissive.textureMapUv", mainVisibility) if(enable) then - local textureMap = context:GetMaterialPropertyValue_image("emissive.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("emissive.textureMap") local useTextureMap = context:GetMaterialPropertyValue_bool("emissive.useTexture") if(textureMap == nil) then diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua index 6cc595d712..541b1ac1ce 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_HandleOpacityMode.lua @@ -90,7 +90,7 @@ function ProcessEditor(context) context:SetMaterialPropertyVisibility("opacity.textureMap", MaterialPropertyVisibility_Hidden) context:SetMaterialPropertyVisibility("opacity.textureMapUv", MaterialPropertyVisibility_Hidden) else - local textureMap = context:GetMaterialPropertyValue_image("opacity.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("opacity.textureMap") if(nil == textureMap) then context:SetMaterialPropertyVisibility("opacity.textureMapUv", MaterialPropertyVisibility_Disabled) diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua index 0287e1105e..e6689da327 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ParallaxState.lua @@ -22,7 +22,7 @@ end function Process(context) local enable = context:GetMaterialPropertyValue_bool("parallax.enable") - local textureMap = context:GetMaterialPropertyValue_image("parallax.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") context:SetShaderOptionValue_bool("o_parallax_feature_enabled", enable) context:SetShaderOptionValue_bool("o_useDepthMap", enable and textureMap ~= nil) end @@ -36,7 +36,7 @@ function ProcessEditor(context) context:SetMaterialPropertyVisibility("parallax.textureMap", MaterialPropertyVisibility_Hidden) end - local textureMap = context:GetMaterialPropertyValue_image("parallax.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("parallax.textureMap") local visibility = MaterialPropertyVisibility_Enabled if(not enable or textureMap == nil) then visibility = MaterialPropertyVisibility_Hidden diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Roughness.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Roughness.lua index 4887bc47e8..222e69cd3d 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Roughness.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_Roughness.lua @@ -21,13 +21,13 @@ function GetShaderOptionDependencies() end function Process(context) - local textureMap = context:GetMaterialPropertyValue_image("roughness.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("roughness.textureMap") local useTexture = context:GetMaterialPropertyValue_bool("roughness.useTexture") context:SetShaderOptionValue_bool("o_roughness_useTexture", useTexture and textureMap ~= nil) end function ProcessEditor(context) - local textureMap = context:GetMaterialPropertyValue_image("roughness.textureMap") + local textureMap = context:GetMaterialPropertyValue_Image("roughness.textureMap") local useTexture = context:GetMaterialPropertyValue_bool("roughness.useTexture") if(nil == textureMap) then diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_SubsurfaceState.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_SubsurfaceState.lua index fb07ac89a3..d8a69ba355 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_SubsurfaceState.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_SubsurfaceState.lua @@ -35,7 +35,7 @@ TransmissionMode_ThickObject = 1 TransmissionMode_ThinObject = 2 function UpdateUseTextureState(context, subsurfaceScatteringEnabled, textureMapPropertyName, useTexturePropertyName, shaderOptionName) - local textureMap = context:GetMaterialPropertyValue_image(textureMapPropertyName) + local textureMap = context:GetMaterialPropertyValue_Image(textureMapPropertyName) local useTextureMap = context:GetMaterialPropertyValue_bool(useTexturePropertyName) context:SetShaderOptionValue_bool(shaderOptionName, subsurfaceScatteringEnabled and useTextureMap and textureMap ~= nil) end @@ -53,7 +53,7 @@ function UpdateTextureDependentPropertyVisibility(context, featureEnabled, textu context:SetMaterialPropertyVisibility(useTexturePropertyName, MaterialPropertyVisibility_Hidden) context:SetMaterialPropertyVisibility(uvPropertyName, MaterialPropertyVisibility_Hidden) else - local textureMap = context:GetMaterialPropertyValue_image(textureMapPropertyName) + local textureMap = context:GetMaterialPropertyValue_Image(textureMapPropertyName) local useTextureMap = context:GetMaterialPropertyValue_bool(useTexturePropertyName) if(textureMap == nil) then diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h index 44de79ff22..396ba14810 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/LuaMaterialFunctor.h @@ -329,6 +329,8 @@ namespace AZ bool SetMaterialPropertySoftMaxValue(const char* name, Type value); bool SetMaterialPropertyDescription(const char* name, const char* description); + + bool SetMaterialPropertyGroupVisibility(const char* name, MaterialPropertyGroupVisibility visibility); private: diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialDynamicMetadata.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialDynamicMetadata.h new file mode 100644 index 0000000000..b74e330665 --- /dev/null +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialDynamicMetadata.h @@ -0,0 +1,100 @@ +/* +* 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 + +namespace AZ +{ + namespace RPI + { + // Normally we wouldn't want editor-related code mixed in with runtime code, but + // since this data can be modified dynamically, keeping it in the runtime makes + // the overall material functor design simpler and more user-friendly. + + + //! Visibility for each material property. + //! If the data field is empty, use default as editable. + enum class MaterialPropertyVisibility : uint32_t + { + Enabled, //!< The property is visible and editable + Disabled, //!< The property is visible but non-editable + Hidden, //!< The property is invisible + + Default = Enabled + }; + + struct MaterialPropertyRange + { + MaterialPropertyRange() = default; + MaterialPropertyRange( + const MaterialPropertyValue& max, + const MaterialPropertyValue& min, + const MaterialPropertyValue& softMax, + const MaterialPropertyValue& softMin + ) + : m_max(max) + , m_min(min) + , m_softMax(softMax) + , m_softMin(softMin) + {} + + MaterialPropertyValue m_max; + MaterialPropertyValue m_min; + MaterialPropertyValue m_softMax; + MaterialPropertyValue m_softMin; + }; + + //! Used by material functors to dynamically control property metadata in tools. + //! For example, show/hide a property based on some other 'enable' flag property. + struct MaterialPropertyDynamicMetadata + { + AZ_TYPE_INFO(MaterialPropertyDynamicMetadata, "{A89F215F-3235-499F-896C-9E63ACC1D657}"); + + AZ::RPI::MaterialPropertyVisibility m_visibility; + AZStd::string m_description; + AZ::RPI::MaterialPropertyRange m_propertyRange; + }; + + //! Visibility for each material property group. + enum class MaterialPropertyGroupVisibility : uint32_t + { + // Note it's helpful to keep these values aligned with MaterialPropertyVisibility in part because in lua it would be easy to accidentally use + // MaterialPropertyVisibility instead of MaterialPropertyGroupVisibility resulting in sneaky bugs. Also, if the enums end up being the same in + // the future, we could just merge them into one. + + Enabled, //!< The property is visible and editable + //Disabled, //!< The property is visible but non-editable (reserved for possible future use, to match MaterialPropertyVisibility) + Hidden=2, //!< The property is invisible + + Default = Enabled + }; + + //! Used by material functors to dynamically control property group metadata in tools. + //! For example, show/hide an entire property group based on some 'enable' flag property. + struct MaterialPropertyGroupDynamicMetadata + { + AZ_TYPE_INFO(MaterialPropertyGroupDynamicMetadata, "{F94009F7-48A3-4CE0-AF64-D5A86890ACD4}"); + + AZ::RPI::MaterialPropertyGroupVisibility m_visibility; + }; + + void ReflectMaterialDynamicMetadata(ReflectContext* context); + + } // namespace RPI + + AZ_TYPE_INFO_SPECIALIZE(RPI::MaterialPropertyVisibility, "{318B43A2-79E3-4502-8FD0-5815209EA123}"); + AZ_TYPE_INFO_SPECIALIZE(RPI::MaterialPropertyGroupVisibility, "{B803958B-DE64-4FBF-AC00-CF781611BE37}"); +} // namespace AZ + diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h index d17c7634a2..6b9b34aa1b 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace AZ { @@ -147,6 +148,8 @@ namespace AZ public: const MaterialPropertyDynamicMetadata* GetMaterialPropertyMetadata(const Name& propertyName) const; const MaterialPropertyDynamicMetadata* GetMaterialPropertyMetadata(const MaterialPropertyIndex& index) const; + + const MaterialPropertyGroupDynamicMetadata* GetMaterialPropertyGroupMetadata(const Name& propertyName) const; //! Get the property value. The type must be one of those in MaterialPropertyValue. //! Otherwise, a compile error will be reported. @@ -178,6 +181,8 @@ namespace AZ bool SetMaterialPropertySoftMaxValue(const Name& propertyName, const MaterialPropertyValue& max); bool SetMaterialPropertySoftMaxValue(const MaterialPropertyIndex& index, const MaterialPropertyValue& max); + + bool SetMaterialPropertyGroupVisibility(const Name& propertyGroupName, MaterialPropertyGroupVisibility visibility); // [GFX TODO][ATOM-4168] Replace the workaround for unlink-able RPI.Public classes in MaterialFunctor // const AZStd::vector&, AZStd::unordered_map&, RHI::ConstPtr @@ -185,18 +190,23 @@ namespace AZ EditorContext( const AZStd::vector& propertyValues, RHI::ConstPtr materialPropertiesLayout, - AZStd::unordered_map& metadata, - AZStd::unordered_set& outChangedProperties, + AZStd::unordered_map& propertyMetadata, + AZStd::unordered_map& propertyGroupMetadata, + AZStd::unordered_set& updatedPropertiesOut, + AZStd::unordered_set& updatedPropertyGroupsOut, const MaterialPropertyFlags* materialPropertyDependencies ); private: - AZStd::list_iterator> QueryMaterialMetadata(const Name& propertyName) const; + AZStd::list_iterator> QueryMaterialPropertyMetadata(const Name& propertyName) const; + AZStd::list_iterator> QueryMaterialPropertyGroupMetadata(const Name& propertyGroupName) const; const AZStd::vector& m_materialPropertyValues; RHI::ConstPtr m_materialPropertiesLayout; - AZStd::unordered_map& m_metadata; - AZStd::unordered_set& m_outChangedProperties; + AZStd::unordered_map& m_propertyMetadata; + AZStd::unordered_map& m_propertyGroupMetadata; + AZStd::unordered_set& m_updatedPropertiesOut; + AZStd::unordered_set& m_updatedPropertyGroupsOut; const MaterialPropertyFlags* m_materialPropertyDependencies = nullptr; }; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h index d73ce2334b..fab72d5f96 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h @@ -81,52 +81,6 @@ namespace AZ AZStd::string GetMaterialPropertyDataTypeString(AZ::TypeId typeId); - //! Visibility for each material property. - //! If the data field is empty, use default as editable. - enum class MaterialPropertyVisibility : uint32_t - { - Enabled, //< The property is visible and editable - Disabled, //< The property is visible but non-editable - Hidden, //< The property is invisible - - Default = Enabled - }; - - struct MaterialPropertyRange - { - MaterialPropertyRange() = default; - MaterialPropertyRange( - const MaterialPropertyValue& max, - const MaterialPropertyValue& min, - const MaterialPropertyValue& softMax, - const MaterialPropertyValue& softMin - ) - : m_max(max) - , m_min(min) - , m_softMax(softMax) - , m_softMin(softMin) - {} - - MaterialPropertyValue m_max; - MaterialPropertyValue m_min; - MaterialPropertyValue m_softMax; - MaterialPropertyValue m_softMin; - }; - - //! Used by material functors to dynamically control property metadata in tools. - //! For example, show/hide a property based on some other 'enable' flag property. - //! Normally we wouldn't want editor-related code mixed in with runtime code, but - //! since this data can be modified dynamically, keeping it in the runtime makes - //! the overall material functor design simpler and more user-friendly. - struct MaterialPropertyDynamicMetadata - { - AZ_TYPE_INFO(MaterialPropertyDynamicMetadata, "{A89F215F-3235-499F-896C-9E63ACC1D657}"); - - AZ::RPI::MaterialPropertyVisibility m_visibility; - AZStd::string m_description; - AZ::RPI::MaterialPropertyRange m_propertyRange; - }; - //! A material property is any data input to a material, like a bool, float, Vector, Image, Buffer, etc. //! This descriptor defines a single input property, including it's name ID, and how it maps //! to the shader system. @@ -171,7 +125,6 @@ namespace AZ } // namespace RPI AZ_TYPE_INFO_SPECIALIZE(RPI::MaterialPropertyOutputType, "{42A6E5E8-0FE6-4D7B-884A-1F478E4ADD97}"); - AZ_TYPE_INFO_SPECIALIZE(RPI::MaterialPropertyVisibility, "{318B43A2-79E3-4502-8FD0-5815209EA123}"); AZ_TYPE_INFO_SPECIALIZE(RPI::MaterialPropertyDataType, "{3D903D5C-C6AA-452E-A2F8-8948D30833FF}"); } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/MaterialSystem.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/MaterialSystem.cpp index e7e04b271d..f1751a02e2 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Material/MaterialSystem.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Material/MaterialSystem.cpp @@ -32,6 +32,7 @@ namespace AZ MaterialPropertiesLayout::Reflect(context); MaterialFunctor::Reflect(context); LuaMaterialFunctor::Reflect(context); + ReflectMaterialDynamicMetadata(context); } void MaterialSystem::GetAssetHandlers(AssetHandlerPtrList& assetHandlers) diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp index 7ab3489666..7db5f12560 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp @@ -63,6 +63,7 @@ namespace AZ behaviorContext->Class(); MaterialPropertyDescriptor::Reflect(behaviorContext); + ReflectMaterialDynamicMetadata(behaviorContext); LuaMaterialFunctorRenderStates::Reflect(behaviorContext); LuaMaterialFunctorShaderItem::Reflect(behaviorContext); @@ -255,7 +256,7 @@ namespace AZ // Specialize for type Image* because that will be more intuitive within Lua. // The script can then check the result for nil without calling "get()". - // For example, "GetMaterialPropertyValue_image(name) == nil" rather than "GetMaterialPropertyValue_image(name):get() == nil" + // For example, "GetMaterialPropertyValue_Image(name) == nil" rather than "GetMaterialPropertyValue_Image(name):get() == nil" template<> Image* LuaMaterialFunctorCommonContext::GetMaterialPropertyValue(const char* name) const { @@ -278,7 +279,7 @@ namespace AZ ->Method("GetMaterialPropertyValue_Vector3", &LuaMaterialFunctorRuntimeContext::GetMaterialPropertyValue) ->Method("GetMaterialPropertyValue_Vector4", &LuaMaterialFunctorRuntimeContext::GetMaterialPropertyValue) ->Method("GetMaterialPropertyValue_Color", &LuaMaterialFunctorRuntimeContext::GetMaterialPropertyValue) - ->Method("GetMaterialPropertyValue_image", &LuaMaterialFunctorRuntimeContext::GetMaterialPropertyValue) + ->Method("GetMaterialPropertyValue_Image", &LuaMaterialFunctorRuntimeContext::GetMaterialPropertyValue) ->Method("SetShaderConstant_bool", &LuaMaterialFunctorRuntimeContext::SetShaderConstant) ->Method("SetShaderConstant_int", &LuaMaterialFunctorRuntimeContext::SetShaderConstant) ->Method("SetShaderConstant_uint", &LuaMaterialFunctorRuntimeContext::SetShaderConstant) @@ -436,7 +437,7 @@ namespace AZ ->Method("GetMaterialPropertyValue_Vector3", &LuaMaterialFunctorEditorContext::GetMaterialPropertyValue) ->Method("GetMaterialPropertyValue_Vector4", &LuaMaterialFunctorEditorContext::GetMaterialPropertyValue) ->Method("GetMaterialPropertyValue_Color", &LuaMaterialFunctorEditorContext::GetMaterialPropertyValue) - ->Method("GetMaterialPropertyValue_image", &LuaMaterialFunctorEditorContext::GetMaterialPropertyValue) + ->Method("GetMaterialPropertyValue_Image", &LuaMaterialFunctorEditorContext::GetMaterialPropertyValue) ->Method("SetMaterialPropertyVisibility", &LuaMaterialFunctorEditorContext::SetMaterialPropertyVisibility) ->Method("SetMaterialPropertyDescription", &LuaMaterialFunctorEditorContext::SetMaterialPropertyDescription) ->Method("SetMaterialPropertyMinValue_int", &LuaMaterialFunctorEditorContext::SetMaterialPropertyMinValue) @@ -451,6 +452,7 @@ namespace AZ ->Method("SetMaterialPropertySoftMaxValue_int", &LuaMaterialFunctorEditorContext::SetMaterialPropertySoftMaxValue) ->Method("SetMaterialPropertySoftMaxValue_uint", &LuaMaterialFunctorEditorContext::SetMaterialPropertySoftMaxValue) ->Method("SetMaterialPropertySoftMaxValue_float", &LuaMaterialFunctorEditorContext::SetMaterialPropertySoftMaxValue) + ->Method("SetMaterialPropertyGroupVisibility", &LuaMaterialFunctorEditorContext::SetMaterialPropertyGroupVisibility) ; } @@ -524,6 +526,15 @@ namespace AZ return m_editorContextImpl->SetMaterialPropertySoftMaxValue(index, value); } + + bool LuaMaterialFunctorEditorContext::SetMaterialPropertyGroupVisibility(const char* name, MaterialPropertyGroupVisibility visibility) + { + if (m_editorContextImpl) + { + return m_editorContextImpl->SetMaterialPropertyGroupVisibility(Name{m_propertyNamePrefix + name}, visibility); + } + return false; + } bool LuaMaterialFunctorEditorContext::SetMaterialPropertyVisibility(const char* name, MaterialPropertyVisibility visibility) { diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialDynamicMetadata.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialDynamicMetadata.cpp new file mode 100644 index 0000000000..5c8f4ccc02 --- /dev/null +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialDynamicMetadata.cpp @@ -0,0 +1,50 @@ +/* +* 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 +#include + +namespace AZ +{ + namespace RPI + { + void ReflectMaterialDynamicMetadata(ReflectContext* context) + { + if (auto* serializeContext = azrtti_cast(context)) + { + serializeContext->Enum() + ->Value("Enabled", MaterialPropertyVisibility::Enabled) + ->Value("Disabled", MaterialPropertyVisibility::Disabled) + ->Value("Hidden", MaterialPropertyVisibility::Hidden) + ; + + serializeContext->Enum() + ->Value("Enabled", MaterialPropertyGroupVisibility::Enabled) + ->Value("Hidden", MaterialPropertyGroupVisibility::Hidden) + ; + } + + if (auto* behaviorContext = azrtti_cast(context)) + { + behaviorContext + ->Enum<(int)MaterialPropertyVisibility::Enabled>("MaterialPropertyVisibility_Enabled") + ->Enum<(int)MaterialPropertyVisibility::Disabled>("MaterialPropertyVisibility_Disabled") + ->Enum<(int)MaterialPropertyVisibility::Hidden>("MaterialPropertyVisibility_Hidden"); + + behaviorContext + ->Enum<(int)MaterialPropertyGroupVisibility::Enabled>("MaterialPropertyGroupVisibility_Enabled") + ->Enum<(int)MaterialPropertyGroupVisibility::Hidden>("MaterialPropertyGroupVisibility_Hidden"); + } + } + + } // namespace RPI +} // namespace AZ 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 32772e9dd5..ab18a1fb66 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp @@ -142,21 +142,25 @@ namespace AZ MaterialFunctor::EditorContext::EditorContext( const AZStd::vector& propertyValues, RHI::ConstPtr materialPropertiesLayout, - AZStd::unordered_map& metadata, - AZStd::unordered_set& outChangedProperties, + AZStd::unordered_map& propertyMetadata, + AZStd::unordered_map& propertyGroupMetadata, + AZStd::unordered_set& updatedPropertiesOut, + AZStd::unordered_set& updatedPropertyGroupsOut, const MaterialPropertyFlags* materialPropertyDependencies ) : m_materialPropertyValues(propertyValues) , m_materialPropertiesLayout(materialPropertiesLayout) - , m_metadata(metadata) - , m_outChangedProperties(outChangedProperties) + , m_propertyMetadata(propertyMetadata) + , m_propertyGroupMetadata(propertyGroupMetadata) + , m_updatedPropertiesOut(updatedPropertiesOut) + , m_updatedPropertyGroupsOut(updatedPropertyGroupsOut) , m_materialPropertyDependencies(materialPropertyDependencies) {} const MaterialPropertyDynamicMetadata* MaterialFunctor::EditorContext::GetMaterialPropertyMetadata(const Name& propertyName) const { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return nullptr; } @@ -168,11 +172,38 @@ namespace AZ const Name& name = m_materialPropertiesLayout->GetPropertyDescriptor(index)->GetName(); return GetMaterialPropertyMetadata(name); } + + const MaterialPropertyGroupDynamicMetadata* MaterialFunctor::EditorContext::GetMaterialPropertyGroupMetadata(const Name& propertyName) const + { + auto it = QueryMaterialPropertyGroupMetadata(propertyName); + if (it == m_propertyGroupMetadata.end()) + { + return nullptr; + } + return &(it->second); + } + + bool MaterialFunctor::EditorContext::SetMaterialPropertyGroupVisibility(const Name& propertyGroupName, MaterialPropertyGroupVisibility visibility) + { + auto it = QueryMaterialPropertyGroupMetadata(propertyGroupName); + if (it == m_propertyGroupMetadata.end()) + { + return false; + } + MaterialPropertyGroupVisibility originValue = it->second.m_visibility; + it->second.m_visibility = visibility; + if (originValue != visibility) + { + m_updatedPropertyGroupsOut.insert(propertyGroupName); + } + + return true; + } bool MaterialFunctor::EditorContext::SetMaterialPropertyVisibility(const Name& propertyName, MaterialPropertyVisibility visibility) { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return false; } @@ -180,7 +211,7 @@ namespace AZ it->second.m_visibility = visibility; if (originValue != visibility) { - m_outChangedProperties.insert(propertyName); + m_updatedPropertiesOut.insert(propertyName); } return true; @@ -194,8 +225,8 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyDescription(const Name& propertyName, AZStd::string description) { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return false; } @@ -204,7 +235,7 @@ namespace AZ it->second.m_description = description; if (origin != description) { - m_outChangedProperties.insert(propertyName); + m_updatedPropertiesOut.insert(propertyName); } return true; @@ -218,8 +249,8 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyMinValue(const Name& propertyName, const MaterialPropertyValue& min) { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return false; } @@ -229,7 +260,7 @@ namespace AZ if(origin != min) { - m_outChangedProperties.insert(propertyName); + m_updatedPropertiesOut.insert(propertyName); } return true; @@ -243,8 +274,8 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyMaxValue(const Name& propertyName, const MaterialPropertyValue& max) { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return false; } @@ -254,7 +285,7 @@ namespace AZ if (origin != max) { - m_outChangedProperties.insert(propertyName); + m_updatedPropertiesOut.insert(propertyName); } return true; @@ -268,8 +299,8 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertySoftMinValue(const Name& propertyName, const MaterialPropertyValue& min) { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return false; } @@ -279,7 +310,7 @@ namespace AZ if (origin != min) { - m_outChangedProperties.insert(propertyName); + m_updatedPropertiesOut.insert(propertyName); } return true; @@ -293,8 +324,8 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertySoftMaxValue(const Name& propertyName, const MaterialPropertyValue& max) { - auto it = QueryMaterialMetadata(propertyName); - if (it == m_metadata.end()) + auto it = QueryMaterialPropertyMetadata(propertyName); + if (it == m_propertyMetadata.end()) { return false; } @@ -304,7 +335,7 @@ namespace AZ if (origin != max) { - m_outChangedProperties.insert(propertyName); + m_updatedPropertiesOut.insert(propertyName); } return true; @@ -316,16 +347,27 @@ namespace AZ return SetMaterialPropertySoftMaxValue(name, max); } - AZStd::list_iterator> MaterialFunctor::EditorContext::QueryMaterialMetadata(const Name& propertyName) const + AZStd::list_iterator> MaterialFunctor::EditorContext::QueryMaterialPropertyMetadata(const Name& propertyName) const { - auto it = m_metadata.find(propertyName); - if (it == m_metadata.end()) + auto it = m_propertyMetadata.find(propertyName); + if (it == m_propertyMetadata.end()) { AZ_Error("MaterialFunctor", false, "Couldn't find metadata for material property: %s.", propertyName.GetCStr()); } return it; } + + AZStd::list_iterator> MaterialFunctor::EditorContext::QueryMaterialPropertyGroupMetadata(const Name& propertyGroupName) const + { + auto it = m_propertyGroupMetadata.find(propertyGroupName); + if (it == m_propertyGroupMetadata.end()) + { + AZ_Error("MaterialFunctor", false, "Couldn't find metadata for material property group: %s.", propertyGroupName.GetCStr()); + } + + return it; + } template const Type& MaterialFunctor::RuntimeContext::GetMaterialPropertyValue(const MaterialPropertyIndex& index) const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp index 367d95357b..72d658db63 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp @@ -124,12 +124,6 @@ namespace AZ ->Value(ToString(MaterialPropertyOutputType::ShaderOption), MaterialPropertyOutputType::ShaderOption) ; - serializeContext->Enum() - ->Value("Enabled", MaterialPropertyVisibility::Enabled) - ->Value("Disabled", MaterialPropertyVisibility::Disabled) - ->Value("Hidden", MaterialPropertyVisibility::Hidden) - ; - serializeContext->Enum() ->Value(ToString(MaterialPropertyDataType::Invalid), MaterialPropertyDataType::Invalid) ->Value(ToString(MaterialPropertyDataType::Bool), MaterialPropertyDataType::Bool) @@ -153,14 +147,6 @@ namespace AZ ; } - if (auto* behaviorContext = azrtti_cast(context)) - { - behaviorContext - ->Enum<(int)MaterialPropertyVisibility::Enabled>("MaterialPropertyVisibility_Enabled") - ->Enum<(int)MaterialPropertyVisibility::Disabled>("MaterialPropertyVisibility_Disabled") - ->Enum<(int)MaterialPropertyVisibility::Hidden>("MaterialPropertyVisibility_Hidden"); - } - MaterialPropertyIndex::Reflect(context); } diff --git a/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp index 76fa6bfb0e..3dcbdfce07 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/LuaMaterialFunctorTests.cpp @@ -748,12 +748,15 @@ namespace UnitTest MaterialPropertyDataType::UInt, "general.mode", MaterialPropertyDataType::Float, "general.value", functorScript); - + AZStd::unordered_set changedPropertyNames; - AZStd::unordered_map propertyDynamicMetadata; propertyDynamicMetadata[Name{"general.mode"}] = {}; propertyDynamicMetadata[Name{"general.value"}] = {}; + + AZStd::unordered_set changedPropertyGroupNames; + AZStd::unordered_map propertyGroupDynamicMetadata; + propertyGroupDynamicMetadata[Name{"general"}] = {}; Ptr functor = testData.GetMaterialTypeAsset()->GetMaterialFunctors()[0]; @@ -761,7 +764,9 @@ namespace UnitTest testData.GetMaterial()->GetPropertyValues(), testData.GetMaterial()->GetMaterialPropertiesLayout(), propertyDynamicMetadata, + propertyGroupDynamicMetadata, changedPropertyNames, + changedPropertyGroupNames, &functor->GetMaterialPropertyDependencies() ); @@ -814,10 +819,13 @@ namespace UnitTest functorScript); AZStd::unordered_set changedPropertyNames; - AZStd::unordered_map propertyDynamicMetadata; propertyDynamicMetadata[Name{"general.units"}] = {}; propertyDynamicMetadata[Name{"general.distance"}] = {}; + + AZStd::unordered_set changedPropertyGroupNames; + AZStd::unordered_map propertyGroupDynamicMetadata; + propertyGroupDynamicMetadata[Name{"general"}] = {}; Ptr functor = testData.GetMaterialTypeAsset()->GetMaterialFunctors()[0]; @@ -825,7 +833,9 @@ namespace UnitTest testData.GetMaterial()->GetPropertyValues(), testData.GetMaterial()->GetMaterialPropertiesLayout(), propertyDynamicMetadata, + propertyGroupDynamicMetadata, changedPropertyNames, + changedPropertyGroupNames, &functor->GetMaterialPropertyDependencies() ); @@ -845,6 +855,66 @@ namespace UnitTest EXPECT_EQ(-100.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_softMin.GetValue()); EXPECT_EQ(100.0f, propertyDynamicMetadata[Name{"general.distance"}].m_propertyRange.m_softMax.GetValue()); } + + TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_EditorContext_SetMaterialPropertyGroupVisibility) + { + using namespace AZ::RPI; + + const char* functorScript = + R"( + function GetMaterialPropertyDependencies() + return { "general.mode" } + end + + function ProcessEditor(context) + local mode = context:GetMaterialPropertyValue_uint("general.mode") + + if (mode == 1) then + context:SetMaterialPropertyGroupVisibility("otherGroup", MaterialPropertyGroupVisibility_Enabled) + else + context:SetMaterialPropertyGroupVisibility("otherGroup", MaterialPropertyGroupVisibility_Hidden) + end + end + )"; + + TestMaterialData testData; + testData.Setup( + MaterialPropertyDataType::UInt, "general.mode", + MaterialPropertyDataType::Float, "otherGroup.value", + functorScript); + + AZStd::unordered_set changedPropertyNames; + AZStd::unordered_map propertyDynamicMetadata; + propertyDynamicMetadata[Name{"general.mode"}] = {}; + propertyDynamicMetadata[Name{"otherGroup.value"}] = {}; + + AZStd::unordered_set changedPropertyGroupNames; + AZStd::unordered_map propertyGroupDynamicMetadata; + propertyGroupDynamicMetadata[Name{"general"}] = {}; + propertyGroupDynamicMetadata[Name{"otherGroup"}] = {}; + + Ptr functor = testData.GetMaterialTypeAsset()->GetMaterialFunctors()[0]; + + AZ::RPI::MaterialFunctor::EditorContext context = AZ::RPI::MaterialFunctor::EditorContext( + testData.GetMaterial()->GetPropertyValues(), + testData.GetMaterial()->GetMaterialPropertiesLayout(), + propertyDynamicMetadata, + propertyGroupDynamicMetadata, + changedPropertyNames, + changedPropertyGroupNames, + &functor->GetMaterialPropertyDependencies() + ); + + testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{0u}); + functor->Process(context); + EXPECT_EQ(MaterialPropertyGroupVisibility::Enabled, propertyGroupDynamicMetadata[Name{"general"}].m_visibility); + EXPECT_EQ(MaterialPropertyGroupVisibility::Hidden, propertyGroupDynamicMetadata[Name{"otherGroup"}].m_visibility); + + testData.GetMaterial()->SetPropertyValue(testData.GetMaterialPropertyIndex(), MaterialPropertyValue{1u}); + functor->Process(context); + EXPECT_EQ(MaterialPropertyGroupVisibility::Enabled, propertyGroupDynamicMetadata[Name{"general"}].m_visibility); + EXPECT_EQ(MaterialPropertyGroupVisibility::Enabled, propertyGroupDynamicMetadata[Name{"otherGroup"}].m_visibility); + } TEST_F(LuaMaterialFunctorTests, LuaMaterialFunctor_RuntimeContext_SetRenderStates) { diff --git a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp index 5a81ebb74d..d32c285780 100644 --- a/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp +++ b/Gems/Atom/RPI/Code/Tests/Material/MaterialPropertySerializerTests.cpp @@ -29,6 +29,7 @@ namespace JsonSerializationTests { AZ::RPI::MaterialTypeSourceData::Reflect(context.get()); AZ::RPI::MaterialPropertyDescriptor::Reflect(context.get()); + AZ::RPI::ReflectMaterialDynamicMetadata(context.get()); } void Reflect(AZStd::unique_ptr& context) diff --git a/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake b/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake index d1db00aa34..307f3a046b 100644 --- a/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake +++ b/Gems/Atom/RPI/Code/atom_rpi_reflect_files.cmake @@ -55,6 +55,7 @@ set(FILES Include/Atom/RPI.Reflect/Material/MaterialAsset.h Include/Atom/RPI.Reflect/Material/MaterialAssetCreatorCommon.h Include/Atom/RPI.Reflect/Material/MaterialAssetCreator.h + Include/Atom/RPI.Reflect/Material/MaterialDynamicMetadata.h Include/Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h Include/Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h Include/Atom/RPI.Reflect/Material/MaterialPropertyValue.h @@ -135,6 +136,7 @@ set(FILES Source/RPI.Reflect/Material/MaterialAssetCreatorCommon.cpp Source/RPI.Reflect/Material/MaterialAssetCreator.cpp Source/RPI.Reflect/Material/LuaMaterialFunctor.cpp + Source/RPI.Reflect/Material/MaterialDynamicMetadata.cpp Source/RPI.Reflect/Material/MaterialPropertyDescriptor.cpp Source/RPI.Reflect/Material/MaterialPropertiesLayout.cpp Source/RPI.Reflect/Material/MaterialTypeAsset.cpp diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h index b17a25a675..17ca1c8d04 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/DynamicProperty/DynamicProperty.h @@ -38,8 +38,8 @@ namespace AtomToolsFramework Count }; - // Configures the initial state, data type, attributes, and values that describe - // the dynamic property and how it is presented + //! Configures the initial state, data type, attributes, and values that describe + //! the dynamic property and how it is presented struct DynamicPropertyConfig { AZ_TYPE_INFO(DynamicPropertyConfig, "{9CA40E92-7F03-42BE-B6AA-51F30EE5796C}"); @@ -98,7 +98,7 @@ namespace AtomToolsFramework //! Returns true if the property has a valid value. bool IsValid() const; - //! Returns the ID of the property. + //! Returns the ID of the property. const AZ::Name GetId() const; //! Returns the current property visibility. diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h index 81b512faf2..0403c06f87 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h @@ -44,6 +44,12 @@ namespace AtomToolsFramework const AZStd::string& groupDescription, QWidget* groupWidget) = 0; + //! Sets the visibility of a specific property group. This impacts both the header and the widget. + virtual void SetGroupVisible(const AZStd::string& groupNameId, bool visible) = 0; + + //! Returns the visibility of a specific property group. + virtual bool IsGroupVisible(const AZStd::string& groupNameId) const = 0; + //! Calls Refresh for a specific InspectorGroupWidget, allowing for non-destructive UI changes virtual void RefreshGroup(const AZStd::string& groupNameId) = 0; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h index b0932b3b04..5559b51462 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h @@ -57,6 +57,9 @@ namespace AtomToolsFramework const AZStd::string& groupDescription, QWidget* groupWidget) override; + void SetGroupVisible(const AZStd::string& groupNameId, bool visible) override; + bool IsGroupVisible(const AZStd::string& groupNameId) const override; + void RefreshGroup(const AZStd::string& groupNameId) override; void RebuildGroup(const AZStd::string& groupNameId) override; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp index ec5f9893bd..c1ccfb4616 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp @@ -69,6 +69,7 @@ namespace AtomToolsFramework InspectorGroupHeaderWidget* groupHeader = new InspectorGroupHeaderWidget(m_ui->m_propertyContent); groupHeader->setText(groupDisplayName.c_str()); groupHeader->setToolTip(groupDescription.c_str()); + groupHeader->setObjectName(groupNameId.c_str()); m_layout->addWidget(groupHeader); m_headers.push_back(groupHeader); @@ -81,6 +82,32 @@ namespace AtomToolsFramework OnHeaderClicked(event, groupHeader, groupWidget); }); } + + void InspectorWidget::SetGroupVisible(const AZStd::string& groupNameId, bool visible) + { + for (size_t i = 0; i < m_groups.size(); ++i) + { + if (m_groups[i]->objectName() == groupNameId.c_str()) + { + m_headers[i]->setVisible(visible); + m_groups[i]->setVisible(visible && m_headers[i]->IsExpanded()); + break; + } + } + } + + bool InspectorWidget::IsGroupVisible(const AZStd::string& groupNameId) const + { + for (auto& header : m_headers) + { + if (header->objectName() == groupNameId.c_str()) + { + return header->isVisible(); + } + } + + return false; + } void InspectorWidget::RefreshGroup(const AZStd::string& groupNameId) { diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentNotificationBus.h b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentNotificationBus.h index 3c3c77a628..80e8054ec5 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentNotificationBus.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentNotificationBus.h @@ -18,6 +18,7 @@ #include #include +#include namespace MaterialEditor { @@ -77,6 +78,12 @@ namespace MaterialEditor //! @param documentId unique id of material document for which the notification is sent //! @param property object containing the property value and configuration that was modified virtual void OnDocumentPropertyConfigModified([[maybe_unused]] const AZ::Uuid& documentId, [[maybe_unused]] const AtomToolsFramework::DynamicProperty& property) {} + + //! Signal that the property group visibility has been changed. + //! @param documentId unique id of material document for which the notification is sent + //! @param groupId id of the group that changed + //! @param visible whether the property group is visible + virtual void OnDocumentPropertyGroupVisibilityChanged([[maybe_unused]] const AZ::Uuid& documentId, [[maybe_unused]] const AZ::Name& groupId, [[maybe_unused]] bool visible) {} }; using MaterialDocumentNotificationBus = AZ::EBus; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h index dad21a6348..c71d500d8c 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Include/Atom/Document/MaterialDocumentRequestBus.h @@ -19,6 +19,7 @@ #include #include +#include namespace AZ { @@ -67,6 +68,10 @@ namespace MaterialEditor //! Returns a property object //! If the document is not open or the id can't be found, an invalid property is returned. virtual const AtomToolsFramework::DynamicProperty& GetProperty(const AZ::Name& propertyFullName) const = 0; + + //! Returns whether a property group is visible + //! If the document is not open or the id can't be found, returns false. + virtual bool IsPropertyGroupVisible(const AZ::Name& propertyGroupFullName) const = 0; //! Modify material property value virtual void SetPropertyValue(const AZ::Name& propertyFullName, const AZStd::any& value) = 0; diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp index 288530a4e4..fec70763dd 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp @@ -117,6 +117,24 @@ namespace MaterialEditor const AtomToolsFramework::DynamicProperty& property = it->second; return property; } + + bool MaterialDocument::IsPropertyGroupVisible(const AZ::Name& propertyGroupFullName) const + { + if (!IsOpen()) + { + AZ_Error("MaterialDocument", false, "Material document is not open."); + return false; + } + + const auto it = m_propertyGroupVisibility.find(propertyGroupFullName); + if (it == m_propertyGroupVisibility.end()) + { + AZ_Error("MaterialDocument", false, "Material document property group could not be found: '%s'.", propertyGroupFullName.GetCStr()); + return false; + } + + return it->second; + } void MaterialDocument::SetPropertyValue(const AZ::Name& propertyFullName, const AZStd::any& value) { @@ -153,8 +171,12 @@ namespace MaterialEditor Recompile(); - AZStd::unordered_set changedPropertyNames = RunEditorMaterialFunctors(dirtyFlags); - for (const Name& changedPropertyName : changedPropertyNames) + EditorMaterialFunctorResult result = RunEditorMaterialFunctors(dirtyFlags); + for (const Name& changedPropertyGroupName : result.m_updatedPropertyGroups) + { + MaterialDocumentNotificationBus::Broadcast(&MaterialDocumentNotificationBus::Events::OnDocumentPropertyGroupVisibilityChanged, m_id, changedPropertyGroupName, IsPropertyGroupVisible(changedPropertyGroupName)); + } + for (const Name& changedPropertyName : result.m_updatedProperties) { MaterialDocumentNotificationBus::Broadcast(&MaterialDocumentNotificationBus::Events::OnDocumentPropertyConfigModified, m_id, GetProperty(changedPropertyName)); } @@ -782,6 +804,12 @@ namespace MaterialEditor return true; }); + // Populate the property group visibility map + for (MaterialTypeSourceData::GroupDefinition& group : m_materialTypeSourceData.GetGroupDefinitionsInDisplayOrder()) + { + m_propertyGroupVisibility[AZ::Name{group.m_nameId}] = true; + } + // Adding properties for material type and parent as part of making dynamic // properties and the inspector more general purpose. // This allows the read only properties to appear in the inspector like any @@ -914,16 +942,26 @@ namespace MaterialEditor } } - AZStd::unordered_set MaterialDocument::RunEditorMaterialFunctors(AZ::RPI::MaterialPropertyFlags dirtyFlags) + MaterialDocument::EditorMaterialFunctorResult MaterialDocument::RunEditorMaterialFunctors(AZ::RPI::MaterialPropertyFlags dirtyFlags) { - AZStd::unordered_set changedPropertyNames; + EditorMaterialFunctorResult result; + AZStd::unordered_map propertyDynamicMetadata; + AZStd::unordered_map propertyGroupDynamicMetadata; for (auto& propertyPair : m_properties) { AtomToolsFramework::DynamicProperty& property = propertyPair.second; AtomToolsFramework::ConvertToPropertyMetaData(propertyDynamicMetadata[property.GetId()], property.GetConfig()); } + for (auto& groupPair : m_propertyGroupVisibility) + { + AZ::RPI::MaterialPropertyGroupDynamicMetadata& metadata = propertyGroupDynamicMetadata[AZ::Name{groupPair.first}]; + bool visible = groupPair.second; + metadata.m_visibility = visible ? + AZ::RPI::MaterialPropertyGroupVisibility::Enabled : AZ::RPI::MaterialPropertyGroupVisibility::Hidden; + } + for (AZ::RPI::Ptr& functor : m_editorFunctors) { const AZ::RPI::MaterialPropertyFlags& materialPropertyDependencies = functor->GetMaterialPropertyDependencies(); @@ -935,7 +973,9 @@ namespace MaterialEditor m_materialInstance->GetPropertyValues(), m_materialInstance->GetMaterialPropertiesLayout(), propertyDynamicMetadata, - changedPropertyNames, + propertyGroupDynamicMetadata, + result.m_updatedProperties, + result.m_updatedPropertyGroups, &materialPropertyDependencies ); functor->Process(context); @@ -950,7 +990,13 @@ namespace MaterialEditor property.SetConfig(propertyConfig); } - return changedPropertyNames; + for (auto& updatedPropertyGroup : result.m_updatedPropertyGroups) + { + bool visible = propertyGroupDynamicMetadata[updatedPropertyGroup].m_visibility == AZ::RPI::MaterialPropertyGroupVisibility::Enabled; + m_propertyGroupVisibility[updatedPropertyGroup] = visible; + } + + return result; } } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h index 3959e45ad7..42f48c95cd 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h @@ -56,6 +56,7 @@ namespace MaterialEditor const AZ::RPI::MaterialTypeSourceData* GetMaterialTypeSourceData() const override; const AZStd::any& GetPropertyValue(const AZ::Name& propertyFullName) const override; const AtomToolsFramework::DynamicProperty& GetProperty(const AZ::Name& propertyFullName) const override; + bool IsPropertyGroupVisible(const AZ::Name& propertyGroupFullName) const override; void SetPropertyValue(const AZ::Name& propertyFullName, const AZStd::any& value) override; bool Open(AZStd::string_view loadPath) override; bool Rebuild() override; @@ -79,11 +80,14 @@ namespace MaterialEditor // Predicate for evaluating properties using PropertyFilterFunction = AZStd::function; - // Map of documenmt's property + // Map of document's properties using PropertyMap = AZStd::unordered_map; // Map of raw property values for undo/redo comparison and storage using PropertyValueMap = AZStd::unordered_map; + + // Map of document's property group visibility flags + using PropertyGroupVisibilityMap = AZStd::unordered_map; // Function to be bound for undo and redo using UndoRedoFunction = AZStd::function; @@ -119,10 +123,16 @@ namespace MaterialEditor void RestorePropertyValues(const PropertyValueMap& propertyValues); + struct EditorMaterialFunctorResult + { + AZStd::unordered_set m_updatedProperties; + AZStd::unordered_set m_updatedPropertyGroups; + }; + // Run editor material functor to update editor metadata. // @param dirtyFlags indicates which properties have changed, and thus which MaterialFunctors need to be run. - // @return names for the set of properties that have been changed or need update. - AZStd::unordered_set RunEditorMaterialFunctors(AZ::RPI::MaterialPropertyFlags dirtyFlags); + // @return names for the set of properties and groups that have been changed or need update. + EditorMaterialFunctorResult RunEditorMaterialFunctors(AZ::RPI::MaterialPropertyFlags dirtyFlags); // Unique id of this material document AZ::Uuid m_id = AZ::Uuid::CreateRandom(); @@ -153,6 +163,9 @@ namespace MaterialEditor // Collection of all material's properties PropertyMap m_properties; + + // Collection of all material's property groups + PropertyGroupVisibilityMap m_propertyGroupVisibility; // Material functors that run in editor. See MaterialFunctor.h for details. AZStd::vector> m_editorFunctors; @@ -175,7 +188,7 @@ namespace MaterialEditor int m_undoHistoryIndex = 0; AZStd::any m_invalidValue; - + AtomToolsFramework::DynamicProperty m_invalidProperty; }; } // namespace MaterialEditor diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index b066c3c7dd..0fd3a8e2a8 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -185,7 +185,7 @@ namespace MaterialEditor } } - void MaterialInspector::OnDocumentPropertyConfigModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property) + void MaterialInspector::OnDocumentPropertyConfigModified(const AZ::Uuid&, const AtomToolsFramework::DynamicProperty& property) { for (auto& groupPair : m_groups) { @@ -197,18 +197,28 @@ namespace MaterialEditor if (reflectedProperty.GetVisibility() != property.GetVisibility()) { reflectedProperty.SetConfig(property.GetConfig()); - AtomToolsFramework::InspectorRequestBus::Event(documentId, &AtomToolsFramework::InspectorRequestBus::Events::RebuildGroup, groupPair.first); + RebuildGroup(groupPair.first); } else { reflectedProperty.SetConfig(property.GetConfig()); - AtomToolsFramework::InspectorRequestBus::Event(documentId, &AtomToolsFramework::InspectorRequestBus::Events::RefreshGroup, groupPair.first); + RefreshGroup(groupPair.first); } return; } } } } + + void MaterialInspector::OnDocumentPropertyGroupVisibilityChanged(const AZ::Uuid&, const AZ::Name& groupId, bool visible) + { + auto groupIter = m_groups.find(groupId.GetStringView()); + + if(groupIter != m_groups.end()) + { + SetGroupVisible(groupIter->first, visible); + } + } void MaterialInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) { diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h index efe979e5ea..65de41f240 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h @@ -50,6 +50,7 @@ namespace MaterialEditor void OnDocumentOpened(const AZ::Uuid& documentId) override; void OnDocumentPropertyValueModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property) override; void OnDocumentPropertyConfigModified(const AZ::Uuid& documentId, const AtomToolsFramework::DynamicProperty& property) override; + void OnDocumentPropertyGroupVisibilityChanged(const AZ::Uuid& documentId, const AZ::Name& groupId, bool visible) override; // AzToolsFramework::IPropertyEditorNotify overrides... void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 03533e142f..62f8a58fab 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -288,15 +288,17 @@ namespace AZ void MaterialPropertyInspector::RunEditorMaterialFunctors() { AZStd::unordered_set changedPropertyNames; + AZStd::unordered_set changedPropertyGroupNames; // Convert editor property configuration data into material property meta data so that it can be used to execute functors AZStd::unordered_map propertyDynamicMetadata; - for (auto& group : m_groups) + AZStd::unordered_map propertyGroupDynamicMetadata; + for (auto& groupPair : m_groups) { - for (auto& property : group.second.m_properties) - { - AtomToolsFramework::ConvertToPropertyMetaData(propertyDynamicMetadata[property.GetId()], property.GetConfig()); - } + AZ::RPI::MaterialPropertyGroupDynamicMetadata& metadata = propertyGroupDynamicMetadata[AZ::Name{groupPair.first}]; + + metadata.m_visibility = IsGroupVisible(groupPair.first) ? + AZ::RPI::MaterialPropertyGroupVisibility::Enabled : AZ::RPI::MaterialPropertyGroupVisibility::Hidden; } for (AZ::RPI::Ptr& functor : m_editorFunctors) @@ -310,7 +312,9 @@ namespace AZ m_materialInstance->GetPropertyValues(), m_materialInstance->GetMaterialPropertiesLayout(), propertyDynamicMetadata, + propertyGroupDynamicMetadata, changedPropertyNames, + changedPropertyGroupNames, &materialPropertyDependencies ); functor->Process(context); @@ -319,9 +323,16 @@ namespace AZ m_dirtyPropertyFlags.reset(); // Apply any changes to material property meta data back to the editor property configurations - for (auto& group : m_groups) + for (auto& groupPair : m_groups) { - for (auto& property : group.second.m_properties) + AZ::Name groupName{groupPair.first}; + + if (changedPropertyGroupNames.find(groupName) != changedPropertyGroupNames.end()) + { + SetGroupVisible(groupPair.first, propertyGroupDynamicMetadata[groupName].m_visibility == AZ::RPI::MaterialPropertyGroupVisibility::Enabled); + } + + for (auto& property : groupPair.second.m_properties) { AtomToolsFramework::DynamicPropertyConfig propertyConfig = property.GetConfig(); From b225dcfa79a4ee9bfe48456899dba0632e632e24 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 12 May 2021 19:37:44 -0700 Subject: [PATCH 07/48] 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 e429c8e06a1638eb8ac2e29ae4fa5d5d0c2ec1c7 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Wed, 12 May 2021 23:28:05 -0700 Subject: [PATCH 08/48] Fixed issues after merging latest main, as well as some edge cases I didn't notice before. The structure of InspectorWidget::m_groups changed, so I had to update my new code accordingly. Updated the InspectorWidget::m_groups code a bit to be more readable. Discovered the initial property group visiblity state wasn't being set correctly when a material was first opened, so groups weren't initially hidden when they should have been. This had to be fixed in different ways for MaterialEditor's inspector and MaterialComponent's inspector. ATOM-14688 Disable Individual Layers --- .../Inspector/InspectorRequestBus.h | 7 ++- .../Inspector/InspectorWidget.h | 10 +++- .../Code/Source/Inspector/InspectorWidget.cpp | 50 +++++++++++-------- .../MaterialInspector/MaterialInspector.cpp | 5 ++ .../EditorMaterialComponentInspector.cpp | 7 ++- 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h index 442ff90f5b..d9b626f631 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorRequestBus.h @@ -47,8 +47,13 @@ namespace AtomToolsFramework //! Sets the visibility of a specific property group. This impacts both the header and the widget. virtual void SetGroupVisible(const AZStd::string& groupNameId, bool visible) = 0; - //! Returns the visibility of a specific property group. + //! Returns whether a specific property is visible. + //! Note this follows the same rules as QWidget::isVisible(), meaning a group could be not visible due to the widget's parents being not visible. virtual bool IsGroupVisible(const AZStd::string& groupNameId) const = 0; + + //! Returns whether a specific property is explicitly hidden. + //! Note this follows the same rules as QWidget::isHidden(), meaning a group that is hidden will not become visible automatically when the parent becomes visible. + virtual bool IsGroupHidden(const AZStd::string& groupNameId) const = 0; //! Calls Refresh for a specific InspectorGroupWidget, allowing for non-destructive UI changes virtual void RefreshGroup(const AZStd::string& groupNameId) = 0; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h index 4e94de952c..5e41121b37 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Inspector/InspectorWidget.h @@ -59,6 +59,7 @@ namespace AtomToolsFramework void SetGroupVisible(const AZStd::string& groupNameId, bool visible) override; bool IsGroupVisible(const AZStd::string& groupNameId) const override; + bool IsGroupHidden(const AZStd::string& groupNameId) const override; void RefreshGroup(const AZStd::string& groupNameId) override; void RebuildGroup(const AZStd::string& groupNameId) override; @@ -82,6 +83,13 @@ namespace AtomToolsFramework private: QVBoxLayout* m_layout = nullptr; QScopedPointer m_ui; - AZStd::unordered_map> m_groups; + + struct GroupWidgetPair + { + InspectorGroupHeaderWidget* m_header; + QWidget* m_panel; + }; + + AZStd::unordered_map m_groups; }; } // namespace AtomToolsFramework diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp index 94d4d95c24..83aed3c2ae 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp @@ -75,7 +75,7 @@ namespace AtomToolsFramework groupWidget->setParent(m_ui->m_propertyContent); m_layout->addWidget(groupWidget); - m_groups[groupNameId] = AZStd::make_pair(groupHeader, groupWidget); + m_groups[groupNameId] = {groupHeader, groupWidget}; connect(groupHeader, &InspectorGroupHeaderWidget::clicked, this, [this, groupNameId](QMouseEvent* event) { OnHeaderClicked(groupNameId, event); @@ -95,25 +95,31 @@ namespace AtomToolsFramework void InspectorWidget::SetGroupVisible(const AZStd::string& groupNameId, bool visible) { - for (size_t i = 0; i < m_groups.size(); ++i) + auto groupItr = m_groups.find(groupNameId); + if (groupItr != m_groups.end()) { - if (m_groups[i]->objectName() == groupNameId.c_str()) - { - m_headers[i]->setVisible(visible); - m_groups[i]->setVisible(visible && m_headers[i]->IsExpanded()); - break; - } + groupItr->second.m_header->setVisible(visible); + groupItr->second.m_panel->setVisible(visible && groupItr->second.m_header->IsExpanded()); } } bool InspectorWidget::IsGroupVisible(const AZStd::string& groupNameId) const { - for (auto& header : m_headers) + auto groupItr = m_groups.find(groupNameId); + if (groupItr != m_groups.end()) { - if (header->objectName() == groupNameId.c_str()) - { - return header->isVisible(); - } + return groupItr->second.m_header->isVisible(); + } + + return false; + } + + bool InspectorWidget::IsGroupHidden(const AZStd::string& groupNameId) const + { + auto groupItr = m_groups.find(groupNameId); + if (groupItr != m_groups.end()) + { + return groupItr->second.m_header->isHidden(); } return false; @@ -156,8 +162,8 @@ namespace AtomToolsFramework auto groupItr = m_groups.find(groupNameId); if (groupItr != m_groups.end()) { - groupItr->second.first->SetExpanded(true); - groupItr->second.second->setVisible(true); + groupItr->second.m_header->SetExpanded(true); + groupItr->second.m_panel->setVisible(true); } } @@ -166,23 +172,23 @@ namespace AtomToolsFramework auto groupItr = m_groups.find(groupNameId); if (groupItr != m_groups.end()) { - groupItr->second.first->SetExpanded(false); - groupItr->second.second->setVisible(false); + groupItr->second.m_header->SetExpanded(false); + groupItr->second.m_panel->setVisible(false); } } bool InspectorWidget::IsGroupExpanded(const AZStd::string& groupNameId) const { auto groupItr = m_groups.find(groupNameId); - return groupItr != m_groups.end() ? groupItr->second.first->IsExpanded() : false; + return groupItr != m_groups.end() ? groupItr->second.m_header->IsExpanded() : false; } void InspectorWidget::ExpandAll() { for (auto& groupPair : m_groups) { - groupPair.second.first->SetExpanded(true); - groupPair.second.second->setVisible(true); + groupPair.second.m_header->SetExpanded(true); + groupPair.second.m_panel->setVisible(true); } } @@ -190,8 +196,8 @@ namespace AtomToolsFramework { for (auto& groupPair : m_groups) { - groupPair.second.first->SetExpanded(false); - groupPair.second.second->setVisible(false); + groupPair.second.m_header->SetExpanded(false); + groupPair.second.m_panel->setVisible(false); } } diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 0492c9c150..19dfc6154c 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -198,6 +198,11 @@ namespace MaterialEditor &group, &group, group.TYPEINFO_Uuid(), this, this, GetGroupSaveStateKey(groupNameId), [this](const auto source, const auto target) { return CompareInstanceNodeProperties(source, target); }); AddGroup(groupNameId, groupDisplayName, groupDescription, propertyGroupWidget); + + bool isGroupVisible = false; + MaterialDocumentRequestBus::EventResult( + isGroupVisible, m_documentId, &MaterialDocumentRequestBus::Events::IsPropertyGroupVisible, AZ::Name{groupNameId}); + SetGroupVisible(groupNameId, isGroupVisible); } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp index 59e29d29f1..31d69569db 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp @@ -305,8 +305,11 @@ namespace AZ { AZ::RPI::MaterialPropertyGroupDynamicMetadata& metadata = propertyGroupDynamicMetadata[AZ::Name{groupPair.first}]; - metadata.m_visibility = IsGroupVisible(groupPair.first) ? - AZ::RPI::MaterialPropertyGroupVisibility::Enabled : AZ::RPI::MaterialPropertyGroupVisibility::Hidden; + // 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 + // of the material property inspector. + metadata.m_visibility = IsGroupHidden(groupPair.first) ? + AZ::RPI::MaterialPropertyGroupVisibility::Hidden : AZ::RPI::MaterialPropertyGroupVisibility::Enabled; } for (AZ::RPI::Ptr& functor : m_editorFunctors) From f478340376267f1623f7138fe2b06024d050687c Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed, 12 May 2021 16:04:25 -0500 Subject: [PATCH 09/48] Data driven asset importer. Need to fix reflection --- AssetImporterSettings.json | 7 +++ .../FbxImportRequestHandler.cpp | 60 +++++++++++++++++-- .../FbxSceneBuilder/FbxImportRequestHandler.h | 16 ++++- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 AssetImporterSettings.json diff --git a/AssetImporterSettings.json b/AssetImporterSettings.json new file mode 100644 index 0000000000..134484cf8d --- /dev/null +++ b/AssetImporterSettings.json @@ -0,0 +1,7 @@ +{ + "SupportedFileTypeExtensions" : [ + ".fbx", + ".stl", + ".stp" + ] +} \ No newline at end of file diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index 155209f1b5..2210abfaf7 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -10,7 +10,14 @@ * */ +#include +#include +#include +#include #include +#include +#include +#include #include #include #include @@ -23,10 +30,47 @@ namespace AZ { namespace FbxSceneImporter { - const char* FbxImportRequestHandler::s_extension = ".fbx"; + AssetImporterSettings::AssetImporterSettings() + { + // Default supported extension in case the settings file isn't found + m_supportedFileTypeExtensions.emplace(".fbx"); + } + + void AssetImporterSettings::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context); serializeContext) + { + serializeContext->Class() + ->Version(1) + ->Field("SupportedFileTypeExtensions", &AssetImporterSettings::m_supportedFileTypeExtensions); + } + } void FbxImportRequestHandler::Activate() { + // Attempt to load the Slice Builder Settings file + AZ::IO::LocalFileIO localFileIO; + + // This will point to @assets@/SettingsFilename, which loads from the cache + // We don't really want this but it works for now + // Trying to use AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath at this point + // would fail because components seem to activate before the AP has populated its file list + AZ::IO::Path sliceBuilderSettingsIoPath(SettingsFilename); + auto result = AzFramework::FileFunc::ReadJsonFile(sliceBuilderSettingsIoPath, &localFileIO); + if (result.IsSuccess()) + { + AZ::JsonSerializationResult::ResultCode serializationResult = + AZ::JsonSerialization::Load(m_settings, result.GetValue()); + if (serializationResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) + { + AZ_Warning("", false, "Error in Asset Importer Settings file.\nUsing default settings."); + } + } + else + { + AZ_Warning("", false, "Failed to load Asset Importer Settings file.\nUsing default settings."); + } + BusConnect(); } @@ -37,21 +81,29 @@ namespace AZ void FbxImportRequestHandler::Reflect(ReflectContext* context) { + AssetImporterSettings::Reflect(context); + SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(1); + serializeContext->Class()->Version(1)->Attribute( + AZ::Edit::Attributes::SystemComponentTags, + AZStd::vector({AssetBuilderSDK::ComponentTags::AssetBuilder})); + } } void FbxImportRequestHandler::GetSupportedFileExtensions(AZStd::unordered_set& extensions) { - extensions.insert(s_extension); + extensions.insert(m_settings.m_supportedFileTypeExtensions.begin(), m_settings.m_supportedFileTypeExtensions.end()); } Events::LoadingResult FbxImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester) { - if (!AzFramework::StringFunc::Path::IsExtension(path.c_str(), s_extension)) + AZStd::string extension; + AzFramework::StringFunc::Path::GetExtension(path.c_str(), extension); + + if (!m_settings.m_supportedFileTypeExtensions.contains(extension)) { return Events::LoadingResult::Ignored; } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h index 8b33051f1e..3ef2823e24 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h @@ -21,6 +21,17 @@ namespace AZ { namespace FbxSceneImporter { + struct AssetImporterSettings + { + AZ_TYPE_INFO(AssetImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}"); + + AssetImporterSettings(); + + static void Reflect(AZ::ReflectContext* context); + + AZStd::unordered_set m_supportedFileTypeExtensions; + }; + class FbxImportRequestHandler : public SceneCore::BehaviorComponent , public Events::AssetImportRequestBus::Handler @@ -39,7 +50,10 @@ namespace AZ RequestingApplication requester) override; private: - static const char* s_extension; + + AssetImporterSettings m_settings; + + static constexpr const char* SettingsFilename = "AssetImporterSettings.json"; }; } // namespace FbxSceneImporter } // namespace SceneAPI From 2b538c9921ce2be2ee4fc9c9877c1dd0272c8f58 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 13 May 2021 09:47:21 -0500 Subject: [PATCH 10/48] Switch to using settings registry # Conflicts: # Assets/Engine/Registry/assetimporter.setreg --- AssetImporterSettings.json | 7 ---- .../SceneAPI/FbxSceneBuilder/DllMain.cpp | 6 +--- .../FbxImportRequestHandler.cpp | 35 +++++-------------- .../FbxSceneBuilder/FbxImportRequestHandler.h | 6 ++-- .../SceneBuilder/SceneBuilderComponent.cpp | 6 +++- .../SceneBuilder/SceneBuilderComponent.h | 2 ++ Registry/assetimporter.setreg | 17 +++++++++ 7 files changed, 37 insertions(+), 42 deletions(-) delete mode 100644 AssetImporterSettings.json create mode 100644 Registry/assetimporter.setreg diff --git a/AssetImporterSettings.json b/AssetImporterSettings.json deleted file mode 100644 index 134484cf8d..0000000000 --- a/AssetImporterSettings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "SupportedFileTypeExtensions" : [ - ".fbx", - ".stl", - ".stp" - ] -} \ No newline at end of file diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp index 3dc14814de..d3d7b38663 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp @@ -46,11 +46,6 @@ namespace AZ // Currently it's still needed to explicitly create an instance of this instead of letting // it be a normal component. This is because ResourceCompilerScene needs to return // the list of available extensions before it can start the application. - if (!g_fbxImporter) - { - g_fbxImporter = aznew AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler(); - g_fbxImporter->Activate(); - } } void Reflect(AZ::SerializeContext* /*context*/) @@ -64,6 +59,7 @@ namespace AZ { // Global importer and behavior g_componentDescriptors.push_back(FbxSceneBuilder::FbxImporter::CreateDescriptor()); + g_componentDescriptors.push_back(FbxSceneImporter::FbxImportRequestHandler::CreateDescriptor()); // Node and attribute importers g_componentDescriptors.push_back(AssImpBitangentStreamImporter::CreateDescriptor()); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index 2210abfaf7..d3962cce60 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -30,12 +30,6 @@ namespace AZ { namespace FbxSceneImporter { - AssetImporterSettings::AssetImporterSettings() - { - // Default supported extension in case the settings file isn't found - m_supportedFileTypeExtensions.emplace(".fbx"); - } - void AssetImporterSettings::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context); serializeContext) @@ -48,27 +42,11 @@ namespace AZ void FbxImportRequestHandler::Activate() { - // Attempt to load the Slice Builder Settings file - AZ::IO::LocalFileIO localFileIO; - - // This will point to @assets@/SettingsFilename, which loads from the cache - // We don't really want this but it works for now - // Trying to use AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath at this point - // would fail because components seem to activate before the AP has populated its file list - AZ::IO::Path sliceBuilderSettingsIoPath(SettingsFilename); - auto result = AzFramework::FileFunc::ReadJsonFile(sliceBuilderSettingsIoPath, &localFileIO); - if (result.IsSuccess()) + auto settingsRegistry = AZ::SettingsRegistry::Get(); + + if (settingsRegistry) { - AZ::JsonSerializationResult::ResultCode serializationResult = - AZ::JsonSerialization::Load(m_settings, result.GetValue()); - if (serializationResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted) - { - AZ_Warning("", false, "Error in Asset Importer Settings file.\nUsing default settings."); - } - } - else - { - AZ_Warning("", false, "Failed to load Asset Importer Settings file.\nUsing default settings."); + settingsRegistry->GetObject(m_settings, "/O3DE/SceneAPI/AssetImporter"); } BusConnect(); @@ -125,6 +103,11 @@ namespace AZ return Events::LoadingResult::AssetFailure; } } + + void FbxImportRequestHandler::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided) + { + provided.emplace_back(AZ_CRC_CE("AssetImportRequestHandler")); + } } // namespace Import } // namespace SceneAPI } // namespace AZ diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h index 3ef2823e24..99d2061229 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h @@ -24,9 +24,7 @@ namespace AZ struct AssetImporterSettings { AZ_TYPE_INFO(AssetImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}"); - - AssetImporterSettings(); - + static void Reflect(AZ::ReflectContext* context); AZStd::unordered_set m_supportedFileTypeExtensions; @@ -49,6 +47,8 @@ namespace AZ Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, RequestingApplication requester) override; + static void GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided); + private: AssetImporterSettings m_settings; diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp index e71a5207d0..25faca3667 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.cpp @@ -72,6 +72,11 @@ namespace SceneBuilder m_sceneBuilder.BusDisconnect(); } + void BuilderPluginComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + required.emplace_back(AZ_CRC_CE("AssetImportRequestHandler")); + } + void BuilderPluginComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); @@ -81,5 +86,4 @@ namespace SceneBuilder ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })); } } - } // namespace SceneBuilder diff --git a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h index c1fc6ebb36..aed5e1b026 100644 --- a/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h +++ b/Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderComponent.h @@ -32,6 +32,8 @@ namespace SceneBuilder void Activate() override; void Deactivate() override; + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + private: SceneBuilderWorker m_sceneBuilder; }; diff --git a/Registry/assetimporter.setreg b/Registry/assetimporter.setreg new file mode 100644 index 0000000000..e0b0f00f6c --- /dev/null +++ b/Registry/assetimporter.setreg @@ -0,0 +1,17 @@ +{ + "O3DE": + { + "SceneAPI": + { + "AssetImporter": + { + "SupportedFileTypeExtensions": + [ + ".fbx", + ".stl", + ".stp" + ] + } + } + } +} \ No newline at end of file From 70c8ef99ef4a9b3bc2d7c69a1262304522085177 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Thu, 13 May 2021 09:39:01 -0700 Subject: [PATCH 11/48] Updates in response to code review, from gadams3. Cleaned up code around MaterialFunctor's QueryMaterialPropertyMetadata and QueryMaterialPropertyGroupMetadata. Removed unnecessary "groupHeader->setObjectName(...)" Simplified code in MaterialInspector::OnDocumentPropertyGroupVisibilityChanged. --- .../RPI.Reflect/Material/MaterialFunctor.h | 4 +- .../RPI.Reflect/Material/MaterialFunctor.cpp | 83 +++++++++---------- .../Code/Source/Inspector/InspectorWidget.cpp | 1 - .../MaterialInspector/MaterialInspector.cpp | 7 +- 4 files changed, 42 insertions(+), 53 deletions(-) diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h index 6b9b34aa1b..83472ade77 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Material/MaterialFunctor.h @@ -198,8 +198,8 @@ namespace AZ ); private: - AZStd::list_iterator> QueryMaterialPropertyMetadata(const Name& propertyName) const; - AZStd::list_iterator> QueryMaterialPropertyGroupMetadata(const Name& propertyGroupName) const; + MaterialPropertyDynamicMetadata* QueryMaterialPropertyMetadata(const Name& propertyName) const; + MaterialPropertyGroupDynamicMetadata* QueryMaterialPropertyGroupMetadata(const Name& propertyGroupName) const; const AZStd::vector& m_materialPropertyValues; RHI::ConstPtr m_materialPropertiesLayout; 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 ab18a1fb66..17e55309fb 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp @@ -159,12 +159,7 @@ namespace AZ const MaterialPropertyDynamicMetadata* MaterialFunctor::EditorContext::GetMaterialPropertyMetadata(const Name& propertyName) const { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) - { - return nullptr; - } - return &(it->second); + return QueryMaterialPropertyMetadata(propertyName); } const MaterialPropertyDynamicMetadata* MaterialFunctor::EditorContext::GetMaterialPropertyMetadata(const MaterialPropertyIndex& index) const @@ -175,23 +170,20 @@ namespace AZ const MaterialPropertyGroupDynamicMetadata* MaterialFunctor::EditorContext::GetMaterialPropertyGroupMetadata(const Name& propertyName) const { - auto it = QueryMaterialPropertyGroupMetadata(propertyName); - if (it == m_propertyGroupMetadata.end()) - { - return nullptr; - } - return &(it->second); + return QueryMaterialPropertyGroupMetadata(propertyName); } bool MaterialFunctor::EditorContext::SetMaterialPropertyGroupVisibility(const Name& propertyGroupName, MaterialPropertyGroupVisibility visibility) { - auto it = QueryMaterialPropertyGroupMetadata(propertyGroupName); - if (it == m_propertyGroupMetadata.end()) + MaterialPropertyGroupDynamicMetadata* metadata = QueryMaterialPropertyGroupMetadata(propertyGroupName); + if (!metadata) { return false; } - MaterialPropertyGroupVisibility originValue = it->second.m_visibility; - it->second.m_visibility = visibility; + + MaterialPropertyGroupVisibility originValue = metadata->m_visibility; + metadata->m_visibility = visibility; + if (originValue != visibility) { m_updatedPropertyGroupsOut.insert(propertyGroupName); @@ -202,13 +194,15 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyVisibility(const Name& propertyName, MaterialPropertyVisibility visibility) { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) + MaterialPropertyDynamicMetadata* metadata = QueryMaterialPropertyMetadata(propertyName); + if (!metadata) { return false; } - MaterialPropertyVisibility originValue = it->second.m_visibility; - it->second.m_visibility = visibility; + + MaterialPropertyVisibility originValue = metadata->m_visibility; + metadata->m_visibility = visibility; + if (originValue != visibility) { m_updatedPropertiesOut.insert(propertyName); @@ -225,14 +219,15 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyDescription(const Name& propertyName, AZStd::string description) { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) + MaterialPropertyDynamicMetadata* metadata = QueryMaterialPropertyMetadata(propertyName); + if (!metadata) { return false; } - AZStd::string origin = it->second.m_description; - it->second.m_description = description; + AZStd::string origin = metadata->m_description; + metadata->m_description = description; + if (origin != description) { m_updatedPropertiesOut.insert(propertyName); @@ -249,14 +244,14 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyMinValue(const Name& propertyName, const MaterialPropertyValue& min) { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) + MaterialPropertyDynamicMetadata* metadata = QueryMaterialPropertyMetadata(propertyName); + if (!metadata) { return false; } - MaterialPropertyValue origin = it->second.m_propertyRange.m_min; - it->second.m_propertyRange.m_min = min; + MaterialPropertyValue origin = metadata->m_propertyRange.m_min; + metadata->m_propertyRange.m_min = min; if(origin != min) { @@ -274,14 +269,14 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertyMaxValue(const Name& propertyName, const MaterialPropertyValue& max) { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) + MaterialPropertyDynamicMetadata* metadata = QueryMaterialPropertyMetadata(propertyName); + if (!metadata) { return false; } - MaterialPropertyValue origin = it->second.m_propertyRange.m_max; - it->second.m_propertyRange.m_max = max; + MaterialPropertyValue origin = metadata->m_propertyRange.m_max; + metadata->m_propertyRange.m_max = max; if (origin != max) { @@ -299,14 +294,14 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertySoftMinValue(const Name& propertyName, const MaterialPropertyValue& min) { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) + MaterialPropertyDynamicMetadata* metadata = QueryMaterialPropertyMetadata(propertyName); + if (!metadata) { return false; } - MaterialPropertyValue origin = it->second.m_propertyRange.m_softMin; - it->second.m_propertyRange.m_softMin = min; + MaterialPropertyValue origin = metadata->m_propertyRange.m_softMin; + metadata->m_propertyRange.m_softMin = min; if (origin != min) { @@ -324,14 +319,14 @@ namespace AZ bool MaterialFunctor::EditorContext::SetMaterialPropertySoftMaxValue(const Name& propertyName, const MaterialPropertyValue& max) { - auto it = QueryMaterialPropertyMetadata(propertyName); - if (it == m_propertyMetadata.end()) + MaterialPropertyDynamicMetadata* metadata = QueryMaterialPropertyMetadata(propertyName); + if (!metadata) { return false; } - MaterialPropertyValue origin = it->second.m_propertyRange.m_softMax; - it->second.m_propertyRange.m_softMax = max; + MaterialPropertyValue origin = metadata->m_propertyRange.m_softMax; + metadata->m_propertyRange.m_softMax = max; if (origin != max) { @@ -347,7 +342,7 @@ namespace AZ return SetMaterialPropertySoftMaxValue(name, max); } - AZStd::list_iterator> MaterialFunctor::EditorContext::QueryMaterialPropertyMetadata(const Name& propertyName) const + MaterialPropertyDynamicMetadata* MaterialFunctor::EditorContext::QueryMaterialPropertyMetadata(const Name& propertyName) const { auto it = m_propertyMetadata.find(propertyName); if (it == m_propertyMetadata.end()) @@ -355,10 +350,10 @@ namespace AZ AZ_Error("MaterialFunctor", false, "Couldn't find metadata for material property: %s.", propertyName.GetCStr()); } - return it; + return &it->second; } - AZStd::list_iterator> MaterialFunctor::EditorContext::QueryMaterialPropertyGroupMetadata(const Name& propertyGroupName) const + MaterialPropertyGroupDynamicMetadata* MaterialFunctor::EditorContext::QueryMaterialPropertyGroupMetadata(const Name& propertyGroupName) const { auto it = m_propertyGroupMetadata.find(propertyGroupName); if (it == m_propertyGroupMetadata.end()) @@ -366,7 +361,7 @@ namespace AZ AZ_Error("MaterialFunctor", false, "Couldn't find metadata for material property group: %s.", propertyGroupName.GetCStr()); } - return it; + return &it->second; } template diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp index 83aed3c2ae..097a819e49 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Inspector/InspectorWidget.cpp @@ -68,7 +68,6 @@ namespace AtomToolsFramework InspectorGroupHeaderWidget* groupHeader = new InspectorGroupHeaderWidget(m_ui->m_propertyContent); groupHeader->setText(groupDisplayName.c_str()); groupHeader->setToolTip(groupDescription.c_str()); - groupHeader->setObjectName(groupNameId.c_str()); m_layout->addWidget(groupHeader); groupWidget->setObjectName(groupNameId.c_str()); diff --git a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp index 19dfc6154c..706d365027 100644 --- a/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp +++ b/Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp @@ -253,12 +253,7 @@ namespace MaterialEditor void MaterialInspector::OnDocumentPropertyGroupVisibilityChanged(const AZ::Uuid&, const AZ::Name& groupId, bool visible) { - auto groupIter = m_groups.find(groupId.GetStringView()); - - if(groupIter != m_groups.end()) - { - SetGroupVisible(groupIter->first, visible); - } + SetGroupVisible(groupId.GetStringView(), visible); } void MaterialInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) From 1684c338b23b8ae7f9aff43748533b79de2b7541 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Thu, 13 May 2021 15:16:37 -0700 Subject: [PATCH 12/48] More minor cleanup in MaterialFunctor.cpp --- .../RPI.Reflect/Material/MaterialFunctor.cpp | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) 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 17e55309fb..a41ab9eea6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Material/MaterialFunctor.cpp @@ -181,11 +181,9 @@ namespace AZ return false; } - MaterialPropertyGroupVisibility originValue = metadata->m_visibility; - metadata->m_visibility = visibility; - - if (originValue != visibility) + if (metadata->m_visibility != visibility) { + metadata->m_visibility = visibility; m_updatedPropertyGroupsOut.insert(propertyGroupName); } @@ -200,11 +198,9 @@ namespace AZ return false; } - MaterialPropertyVisibility originValue = metadata->m_visibility; - metadata->m_visibility = visibility; - - if (originValue != visibility) + if (metadata->m_visibility != visibility) { + metadata->m_visibility = visibility; m_updatedPropertiesOut.insert(propertyName); } @@ -225,11 +221,9 @@ namespace AZ return false; } - AZStd::string origin = metadata->m_description; - metadata->m_description = description; - - if (origin != description) + if (metadata->m_description != description) { + metadata->m_description = description; m_updatedPropertiesOut.insert(propertyName); } @@ -250,11 +244,9 @@ namespace AZ return false; } - MaterialPropertyValue origin = metadata->m_propertyRange.m_min; - metadata->m_propertyRange.m_min = min; - - if(origin != min) + if(metadata->m_propertyRange.m_min != min) { + metadata->m_propertyRange.m_min = min; m_updatedPropertiesOut.insert(propertyName); } @@ -275,11 +267,9 @@ namespace AZ return false; } - MaterialPropertyValue origin = metadata->m_propertyRange.m_max; - metadata->m_propertyRange.m_max = max; - - if (origin != max) + if (metadata->m_propertyRange.m_max != max) { + metadata->m_propertyRange.m_max = max; m_updatedPropertiesOut.insert(propertyName); } @@ -300,11 +290,9 @@ namespace AZ return false; } - MaterialPropertyValue origin = metadata->m_propertyRange.m_softMin; - metadata->m_propertyRange.m_softMin = min; - - if (origin != min) + if (metadata->m_propertyRange.m_softMin != min) { + metadata->m_propertyRange.m_softMin = min; m_updatedPropertiesOut.insert(propertyName); } @@ -325,11 +313,9 @@ namespace AZ return false; } - MaterialPropertyValue origin = metadata->m_propertyRange.m_softMax; - metadata->m_propertyRange.m_softMax = max; - - if (origin != max) + if (metadata->m_propertyRange.m_softMax != max) { + metadata->m_propertyRange.m_softMax = max; m_updatedPropertiesOut.insert(propertyName); } From 81067e38c9e6b6f489457baa0b5108f02ae7b397 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 13 May 2021 17:39:27 -0500 Subject: [PATCH 13/48] Remove stp --- Registry/assetimporter.setreg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Registry/assetimporter.setreg b/Registry/assetimporter.setreg index e0b0f00f6c..bd7c4d0705 100644 --- a/Registry/assetimporter.setreg +++ b/Registry/assetimporter.setreg @@ -8,8 +8,7 @@ "SupportedFileTypeExtensions": [ ".fbx", - ".stl", - ".stp" + ".stl" ] } } From b7891f4fb6e3fa5a596d5acd436fc9de12446588 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 13 May 2021 18:43:51 -0500 Subject: [PATCH 14/48] Code cleanup --- Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp | 7 ------- .../FbxSceneBuilder/FbxImportRequestHandler.cpp | 11 ++++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp index d3d7b38663..5ffe916d23 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp @@ -41,13 +41,6 @@ namespace AZ static AZ::SceneAPI::FbxSceneImporter::FbxImportRequestHandler* g_fbxImporter = nullptr; static AZStd::vector g_componentDescriptors; - void Initialize() - { - // Currently it's still needed to explicitly create an instance of this instead of letting - // it be a normal component. This is because ResourceCompilerScene needs to return - // the list of available extensions before it can start the application. - } - void Reflect(AZ::SerializeContext* /*context*/) { // Descriptor registration is done in Reflect instead of Initialize because the ResourceCompilerScene initializes the libraries before diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index d3962cce60..65bb8de4c7 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -10,19 +10,16 @@ * */ -#include #include -#include #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include -#include namespace AZ { @@ -79,7 +76,7 @@ namespace AZ Events::LoadingResult FbxImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid, [[maybe_unused]] RequestingApplication requester) { AZStd::string extension; - AzFramework::StringFunc::Path::GetExtension(path.c_str(), extension); + StringFunc::Path::GetExtension(path.c_str(), extension); if (!m_settings.m_supportedFileTypeExtensions.contains(extension)) { From ba4439c397ef8b2b3203b482f5a0fd56ebf88713 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Thu, 13 May 2021 19:52:00 -0500 Subject: [PATCH 15/48] Remove call to deleted init function --- Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp index 5ffe916d23..d2818f3653 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/DllMain.cpp @@ -114,7 +114,6 @@ namespace AZ extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env) { AZ::Environment::Attach(static_cast(env)); - AZ::SceneAPI::FbxSceneBuilder::Initialize(); } extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext* context) { From 70c968f82917b83528deed7cec2b316be2e5fc03 Mon Sep 17 00:00:00 2001 From: daimini Date: Fri, 14 May 2021 15:02:39 -0700 Subject: [PATCH 16/48] Fixes issue with relative positioning of children on Prefab creation. Makes CreateLink more generic to facilitate reuse. --- .../Prefab/PrefabPublicHandler.cpp | 83 ++++++++++--------- .../Prefab/PrefabPublicHandler.h | 7 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 5ecff637a1..4eb6580158 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -121,6 +121,41 @@ namespace AzToolsFramework } AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId(); + AZ::Entity* containerEntity = GetEntityById(containerEntityId); + + { + // Generate the transform for the container entity out of the top level entities, and set it + // This step needs to be done before anything is parented to the container, else children position will be wrong + Prefab::PrefabDom containerEntityDomBefore; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity); + + AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero()); + AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero()); + + // Set container entity to be child of common root + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId); + + // Set the transform (translation, rotation) of the container entity + GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation); + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation); + AZ::TransformBus::Event( + containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation); + + PrefabDom containerEntityDomAfter; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity); + + PrefabDom patch; + m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter); + m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId); + + // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes + m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter)); + + // Create a link between the templates of the newly created instance and the instance it's being parented under. + CreateLink( + instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), + undoBatch.GetUndoBatch(), patch); + } // Parent the entities to the container entity. Parenting the container entities of the instances passed to createPrefab // will be done during the creation of links below. @@ -144,16 +179,10 @@ namespace AzToolsFramework // These link creations shouldn't be undone because that would put the template in a non-usable state if a user // chooses to instantiate the template after undoing the creation. - CreateLink( - {&nestedInstanceContainerEntity->get()}, *nestedInstance, instanceToCreate->get().GetTemplateId(), - undoBatch.GetUndoBatch(), containerEntityId, false); + PrefabDom emptyPatch; + CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), emptyPatch, false); }); - // Create a link between the templates of the newly created instance and the instance it's being parented under. - CreateLink( - topLevelEntities, instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), - undoBatch.GetUndoBatch(), commonRootEntityId); - for (AZ::Entity* topLevelEntity : topLevelEntities) { AZ::EntityId topLevelEntityId = topLevelEntity->GetId(); @@ -227,8 +256,7 @@ namespace AzToolsFramework ScopedUndoBatch undoBatch("Instantiate Prefab"); PrefabDom instanceToParentUnderDomBeforeCreate; - m_instanceToTemplateInterface->GenerateDomForInstance( - instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get()); + m_instanceToTemplateInterface->GenerateDomForInstance(instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get()); // Instantiate the Prefab auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder); @@ -242,8 +270,8 @@ namespace AzToolsFramework PrefabUndoHelpers::UpdatePrefabInstance( instanceToParentUnder->get(), "Update prefab instance", instanceToParentUnderDomBeforeCreate, undoBatch.GetUndoBatch()); - CreateLink({}, instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), - undoBatch.GetUndoBatch(), parent); + PrefabDom emptyPatch; + CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), emptyPatch); AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId(); // Apply position @@ -314,33 +342,9 @@ namespace AzToolsFramework } void PrefabPublicHandler::CreateLink( - const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId, - UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded) + Instance& sourceInstance, TemplateId targetTemplateId, + UndoSystem::URSequencePoint* undoBatch, PrefabDom& patch, const bool isUndoRedoSupportNeeded) { - AZ::EntityId containerEntityId = sourceInstance.GetContainerEntityId(); - AZ::Entity* containerEntity = GetEntityById(containerEntityId); - Prefab::PrefabDom containerEntityDomBefore; - m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity); - - AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero()); - AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero()); - - // Set the transform (translation, rotation) of the container entity - GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation); - - // Set container entity to be child of common root - AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId); - - AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation); - AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation); - - PrefabDom containerEntityDomAfter; - m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity); - - PrefabDom patch; - m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter); - m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId); - LinkId linkId; if (isUndoRedoSupportNeeded) { @@ -356,9 +360,6 @@ namespace AzToolsFramework } sourceInstance.SetLinkId(linkId); - - // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes - m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter)); } void PrefabPublicHandler::RemoveLink( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 138bc84aa0..95f9cb8174 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -72,16 +72,15 @@ namespace AzToolsFramework /** * Creates a link between the templates of an instance and its parent. * - * \param topLevelEntities The list of entities that are immediate children to the container entity of the instance. * \param sourceInstance The instance that corresponds to the source template of the link. * \param targetInstance The id of the target template. * \param undoBatch The undo batch to set as parent for this create link action. - * \param commonRootEntityId The id of the entity that the source instance should be parented under. + * \param patch The patch to store in the newly created link dom. * \param isUndoRedoSupportNeeded The flag indicating whether the link should be created with undo/redo support or not. */ void CreateLink( - const EntityList& topLevelEntities, Instance& sourceInstance, TemplateId targetTemplateId, - UndoSystem::URSequencePoint* undoBatch, AZ::EntityId commonRootEntityId, const bool isUndoRedoSupportNeeded = true); + Instance& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch, + PrefabDom& patch, const bool isUndoRedoSupportNeeded = true); /** * Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId. From 12760ef6a302309ce8e658e724bb51a697f0a041 Mon Sep 17 00:00:00 2001 From: daimini Date: Fri, 14 May 2021 16:37:47 -0700 Subject: [PATCH 17/48] Remove unused container entity retrieval --- .../AzToolsFramework/Prefab/PrefabPublicHandler.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 4eb6580158..5323fd3d7f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -172,11 +172,6 @@ namespace AzToolsFramework } instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr& nestedInstance) { - AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created."); - EntityOptionalReference nestedInstanceContainerEntity = nestedInstance->GetContainerEntity(); - AZ_Assert( - nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation."); - // These link creations shouldn't be undone because that would put the template in a non-usable state if a user // chooses to instantiate the template after undoing the creation. PrefabDom emptyPatch; From 3e0ce61d0003a9ad618607364d6403be80afb507 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon, 17 May 2021 08:24:02 -0500 Subject: [PATCH 18/48] Rename setreg file to sceneassetimporter --- Registry/{assetimporter.setreg => sceneassetimporter.setreg} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Registry/{assetimporter.setreg => sceneassetimporter.setreg} (100%) diff --git a/Registry/assetimporter.setreg b/Registry/sceneassetimporter.setreg similarity index 100% rename from Registry/assetimporter.setreg rename to Registry/sceneassetimporter.setreg From ee4e9af46521fe06a08f910b0086790042fff8af Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Mon, 17 May 2021 08:27:04 -0500 Subject: [PATCH 19/48] Rename AssetImporterSettings to SceneImporterSettings --- .../SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp | 8 ++++---- .../SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index 65bb8de4c7..52ab184f66 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -27,13 +27,13 @@ namespace AZ { namespace FbxSceneImporter { - void AssetImporterSettings::Reflect(AZ::ReflectContext* context) + void SceneImporterSettings::Reflect(AZ::ReflectContext* context) { if (auto serializeContext = azrtti_cast(context); serializeContext) { - serializeContext->Class() + serializeContext->Class() ->Version(1) - ->Field("SupportedFileTypeExtensions", &AssetImporterSettings::m_supportedFileTypeExtensions); + ->Field("SupportedFileTypeExtensions", &SceneImporterSettings::m_supportedFileTypeExtensions); } } @@ -56,7 +56,7 @@ namespace AZ void FbxImportRequestHandler::Reflect(ReflectContext* context) { - AssetImporterSettings::Reflect(context); + SceneImporterSettings::Reflect(context); SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h index 99d2061229..f68b56314b 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h @@ -21,9 +21,9 @@ namespace AZ { namespace FbxSceneImporter { - struct AssetImporterSettings + struct SceneImporterSettings { - AZ_TYPE_INFO(AssetImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}"); + AZ_TYPE_INFO(SceneImporterSettings, "{8BB6C7AD-BF99-44DC-9DA1-E7AD3F03DC10}"); static void Reflect(AZ::ReflectContext* context); @@ -51,7 +51,7 @@ namespace AZ private: - AssetImporterSettings m_settings; + SceneImporterSettings m_settings; static constexpr const char* SettingsFilename = "AssetImporterSettings.json"; }; From bcaf4209d59a5aa054c0fb2d5030b09d57655ee9 Mon Sep 17 00:00:00 2001 From: daimini Date: Mon, 17 May 2021 15:39:25 -0700 Subject: [PATCH 20/48] Move some logic to a helper function. Revert order of link creation operations as they are not necessary to fix the bug, so it is preferred to leave them untouched. --- .../Prefab/PrefabPublicHandler.cpp | 84 ++++++++++--------- .../Prefab/PrefabPublicHandler.h | 5 +- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 47642e90f3..53bf515860 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -121,41 +121,9 @@ namespace AzToolsFramework } AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId(); - AZ::Entity* containerEntity = GetEntityById(containerEntityId); - { - // Generate the transform for the container entity out of the top level entities, and set it - // This step needs to be done before anything is parented to the container, else children position will be wrong - Prefab::PrefabDom containerEntityDomBefore; - m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity); - - AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero()); - AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero()); - - // Set container entity to be child of common root - AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId); - - // Set the transform (translation, rotation) of the container entity - GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation); - AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation); - AZ::TransformBus::Event( - containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation); - - PrefabDom containerEntityDomAfter; - m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity); - - PrefabDom patch; - m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter); - m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId); - - // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes - m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter)); - - // Create a link between the templates of the newly created instance and the instance it's being parented under. - CreateLink( - instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), - undoBatch.GetUndoBatch(), patch); - } + // Apply the correct transform to the container for the new instance, and store the patch for use when creating the link. + PrefabDom patch = ApplyContainerTransformAndGeneratePatch(containerEntityId, commonRootEntityId, topLevelEntities); // Parent the entities to the container entity. Parenting the container entities of the instances passed to createPrefab // will be done during the creation of links below. @@ -172,12 +140,18 @@ namespace AzToolsFramework } instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr& nestedInstance) { + AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created."); + // These link creations shouldn't be undone because that would put the template in a non-usable state if a user // chooses to instantiate the template after undoing the creation. - PrefabDom emptyPatch; - CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), emptyPatch, false); + CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), {}, false); }); + // Create a link between the templates of the newly created instance and the instance it's being parented under. + CreateLink( + instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(), + AZStd::move(patch)); + for (AZ::Entity* topLevelEntity : topLevelEntities) { AZ::EntityId topLevelEntityId = topLevelEntity->GetId(); @@ -206,6 +180,39 @@ namespace AzToolsFramework return AZ::Success(); } + PrefabDom PrefabPublicHandler::ApplyContainerTransformAndGeneratePatch(AZ::EntityId containerEntityId, AZ::EntityId commonRootEntityId, const EntityList& topLevelEntities) + { + AZ::Entity* containerEntity = GetEntityById(containerEntityId); + + // Generate the transform for the container entity out of the top level entities, and set it + // This step needs to be done before anything is parented to the container, else children position will be wrong + Prefab::PrefabDom containerEntityDomBefore; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity); + + AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero()); + AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero()); + + // Set container entity to be child of common root + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId); + + // Set the transform (translation, rotation) of the container entity + GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation); + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation); + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation); + + PrefabDom containerEntityDomAfter; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity); + + PrefabDom patch; + m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter); + m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId); + + // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes + m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter)); + + return AZStd::move(patch); + } + PrefabOperationResult PrefabPublicHandler::InstantiatePrefab( AZStd::string_view filePath, AZ::EntityId parent, const AZ::Vector3& position) { @@ -271,8 +278,7 @@ namespace AzToolsFramework PrefabUndoHelpers::UpdatePrefabInstance( instanceToParentUnder->get(), "Update prefab instance", instanceToParentUnderDomBeforeCreate, undoBatch.GetUndoBatch()); - PrefabDom emptyPatch; - CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), emptyPatch); + CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), {}); AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId(); // Apply position @@ -344,7 +350,7 @@ namespace AzToolsFramework void PrefabPublicHandler::CreateLink( Instance& sourceInstance, TemplateId targetTemplateId, - UndoSystem::URSequencePoint* undoBatch, PrefabDom& patch, const bool isUndoRedoSupportNeeded) + UndoSystem::URSequencePoint* undoBatch, PrefabDom patch, const bool isUndoRedoSupportNeeded) { LinkId linkId; if (isUndoRedoSupportNeeded) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 583def2219..d6763cab40 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -69,6 +69,9 @@ namespace AzToolsFramework InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; + PrefabDom ApplyContainerTransformAndGeneratePatch( + AZ::EntityId containerEntityId, AZ::EntityId commonRootEntityId, const EntityList& topLevelEntities); + /** * Creates a link between the templates of an instance and its parent. * @@ -80,7 +83,7 @@ namespace AzToolsFramework */ void CreateLink( Instance& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch, - PrefabDom& patch, const bool isUndoRedoSupportNeeded = true); + PrefabDom patch, const bool isUndoRedoSupportNeeded = true); /** * Removes the link between template of the sourceInstance and the template corresponding to targetTemplateId. From 83f29f4a3443de16c793e28d12848bc77889bae7 Mon Sep 17 00:00:00 2001 From: moudgils Date: Mon, 17 May 2021 20:39:39 -0700 Subject: [PATCH 21/48] 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 bb458254a2645ca75b4b1be216d82bc54c889fe6 Mon Sep 17 00:00:00 2001 From: daimini Date: Tue, 18 May 2021 14:52:30 -0700 Subject: [PATCH 22/48] Polish pass - rename arguments to be more generic, add comments, restore patches to links during instantiation that were mistakenly removed in previous changes. --- .../Prefab/PrefabPublicHandler.cpp | 34 +++++++++++++++---- .../Prefab/PrefabPublicHandler.h | 14 ++++++-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 53bf515860..7ddd9f16e1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -180,9 +180,10 @@ namespace AzToolsFramework return AZ::Success(); } - PrefabDom PrefabPublicHandler::ApplyContainerTransformAndGeneratePatch(AZ::EntityId containerEntityId, AZ::EntityId commonRootEntityId, const EntityList& topLevelEntities) + PrefabDom PrefabPublicHandler::ApplyContainerTransformAndGeneratePatch(AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const EntityList& childEntities) { AZ::Entity* containerEntity = GetEntityById(containerEntityId); + AZ_Assert(containerEntity, "Invalid container entity passed to ApplyContainerTransformAndGeneratePatch."); // Generate the transform for the container entity out of the top level entities, and set it // This step needs to be done before anything is parented to the container, else children position will be wrong @@ -193,10 +194,10 @@ namespace AzToolsFramework AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero()); // Set container entity to be child of common root - AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, commonRootEntityId); + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parentEntityId); // Set the transform (translation, rotation) of the container entity - GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation); + GenerateContainerEntityTransform(childEntities, containerEntityTranslation, containerEntityRotation); AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, containerEntityTranslation); AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, containerEntityRotation); @@ -263,10 +264,10 @@ namespace AzToolsFramework // Initialize Undo Batch object ScopedUndoBatch undoBatch("Instantiate Prefab"); + // Instantiate the Prefab PrefabDom instanceToParentUnderDomBeforeCreate; m_instanceToTemplateInterface->GenerateDomForInstance(instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get()); - // Instantiate the Prefab auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder); if (!instanceToCreate) @@ -278,11 +279,32 @@ namespace AzToolsFramework PrefabUndoHelpers::UpdatePrefabInstance( instanceToParentUnder->get(), "Update prefab instance", instanceToParentUnderDomBeforeCreate, undoBatch.GetUndoBatch()); - CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), {}); + // Create Link with correct container patches AZ::EntityId containerEntityId = instanceToCreate->get().GetContainerEntityId(); + AZ::Entity* containerEntity = GetEntityById(containerEntityId); + AZ_Assert(containerEntity, "Invalid container entity detected in InstantiatePrefab."); - // Apply position + Prefab::PrefabDom containerEntityDomBefore; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity); + + // Set container entity's parent + AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parent); + + // Set the position of the container entity AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetWorldTranslation, position); + + PrefabDom containerEntityDomAfter; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity); + + // Generate patch to be stored in the link + PrefabDom patch; + m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter); + m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId); + + CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch)); + + // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes + m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter)); } return AZ::Success(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index d6763cab40..5ad5b4a9cf 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -68,9 +68,19 @@ namespace AzToolsFramework InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const; bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const; - + + /** + * Applies the correct transform changes to the container entity based on the parent and child entities provided, and returns an appropriate patch. + * The container will be parented to parentId, moved to the average transform of the future direct children and its cache will be updated. + * This helper function won't support undo/redo, update the templates or create any links. All that needs to be done by the caller. + * + * \param containerEntityId The container to apply the changes to. + * \param parentEntityId The id of the entity the container should be parented to. + * \param childEntities A list of entities that will subsequently be parented to this container. + * \return The PrefabDom containing the patches that should be stored in the parent link. + */ PrefabDom ApplyContainerTransformAndGeneratePatch( - AZ::EntityId containerEntityId, AZ::EntityId commonRootEntityId, const EntityList& topLevelEntities); + AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const EntityList& childEntities); /** * Creates a link between the templates of an instance and its parent. From 0a2c95cc201f4094f316732e96f83e6ba07d812e Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 18 May 2021 17:42:41 -0700 Subject: [PATCH 23/48] 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 977030a27ab1fe1a0367c238e30045f3129aecbf Mon Sep 17 00:00:00 2001 From: daimini Date: Tue, 18 May 2021 17:53:27 -0700 Subject: [PATCH 24/48] On prefab creation, inherit the patches when moving the nested prefab instances and then update the link with the new parent if necessary. --- .../Prefab/PrefabPublicHandler.cpp | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 394349c378..dc9f34b0cb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -84,6 +84,7 @@ namespace AzToolsFramework AZStd::vector entities; AZStd::vector> instances; + AZStd::unordered_map nestedInstanceLinkPatches; // Retrieve all entities affected and identify Instances if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances)) @@ -96,6 +97,16 @@ namespace AzToolsFramework // target templates of the other instances. for (auto& nestedInstance : instances) { + auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId()); + + if (linkRef.has_value()) + { + PrefabDom oldLinkPatches; + oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator()); + + nestedInstanceLinkPatches.emplace(nestedInstance.get(), AZStd::move(oldLinkPatches)); + } + RemoveLink(nestedInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); } @@ -145,23 +156,48 @@ namespace AzToolsFramework instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr& nestedInstance) { AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created."); - AZ::EntityId parentId; - AZ::TransformBus::EventResult( - parentId, nestedInstanceContainerEntity->get().GetId(), &AZ::TransformBus::Events::GetParentId); + EntityOptionalReference nestedInstanceContainerEntity = nestedInstance->GetContainerEntity(); + AZ_Assert( + nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation."); - auto entityIterator = AZStd::find_if( - entities.begin(), entities.end(), [parentId](AZ::Entity* entity) { return entity->GetId() == parentId; }); + AZ::EntityId nestedInstanceContainerEntityId = nestedInstanceContainerEntity->get().GetId(); + PrefabDom previousPatch; - // If the previous parent entity of the nested instance is not part of the entities of the newly created prefab, - // then set the parent of the nested prefab as the container entity of the newly created prefab. - if (entityIterator == entities.end()) + // Retrieve the previous patch if it exists + if (nestedInstanceLinkPatches.contains(nestedInstance.get())) { - parentId = containerEntityId; + previousPatch = AZStd::move(nestedInstanceLinkPatches[nestedInstance.get()]); } // These link creations shouldn't be undone because that would put the template in a non-usable state if a user // chooses to instantiate the template after undoing the creation. - CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), {}, false); + CreateLink(*nestedInstance, instanceToCreate->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(previousPatch), false); + + // If this nested instance's container is a top level entity in the new prefab, re-parent it and apply the change. + if (AZStd::find(topLevelEntities.begin(), topLevelEntities.end(), &nestedInstanceContainerEntity->get()) != topLevelEntities.end()) + { + Prefab::PrefabDom containerEntityDomBefore; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *nestedInstanceContainerEntity); + + AZ::TransformBus::Event(nestedInstanceContainerEntityId, &AZ::TransformBus::Events::SetParent, containerEntityId); + + PrefabDom containerEntityDomAfter; + m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *nestedInstanceContainerEntity); + + PrefabDom reparentPatch; + m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter); + m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId); + + // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step + m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter)); + + // Save these changes as patches to the link + PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); + linkUpdate->SetParent(undoBatch.GetUndoBatch()); + linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId()); + + linkUpdate->Redo(); + } }); // Create a link between the templates of the newly created instance and the instance it's being parented under. From cc2e3aed58e8815c544595bb3bf7021b0fee3dbc Mon Sep 17 00:00:00 2001 From: moudgils Date: Tue, 18 May 2021 20:54:06 -0700 Subject: [PATCH 25/48] 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 26/48] 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 d5122b2829235a03c2d64b753a3b08cc089c1984 Mon Sep 17 00:00:00 2001 From: abrmich Date: Fri, 14 May 2021 12:53:03 -0700 Subject: [PATCH 27/48] Add image builder processing bus --- .../Atom/ImageProcessing/ImageProcessingBus.h | 41 ++++++++++ .../ImageProcessingEditorBus.h | 7 +- .../Code/Source/ImageBuilderComponent.cpp | 78 +++++++++++++++++++ .../Code/Source/ImageBuilderComponent.h | 20 +++++ 4 files changed, 145 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingBus.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingBus.h index b8deb50cc5..9f6b6e629f 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingBus.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingBus.h @@ -13,8 +13,14 @@ #pragma once #include +#include #include +namespace AssetBuilderSDK +{ + struct JobProduct; +} + namespace ImageProcessingAtom { class ImageProcessingRequests @@ -35,4 +41,39 @@ namespace ImageProcessingAtom virtual IImageObjectPtr LoadImagePreview(const AZStd::string& filePath) = 0; }; using ImageProcessingRequestBus = AZ::EBus; + + class ImageBuilderRequests + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ///////////////////////////////////////////////////////////////////////// + + //! Create an image object + virtual IImageObjectPtr CreateImage( + AZ::u32 width, + AZ::u32 height, + AZ::u32 maxMipCount, + EPixelFormat pixelFormat) = 0; + + //! Convert an image and save its products to the specified folder + virtual AZStd::vector ConvertImageObject( + IImageObjectPtr imageObject, + const AZStd::string& presetName, + const AZStd::string& platformName, + const AZStd::string& outputDir, + const AZ::Data::AssetId& sourceAssetId, + const AZStd::string& sourceAssetName) = 0; + + //! Return whether the specified platform is supported by the image builder + virtual bool DoesSupportPlatform(const AZStd::string& platformId) = 0; + + //! Return whether the specified preset requires an image to be square and a power of 2 + virtual bool IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName) = 0; + }; + + using ImageBuilderRequestBus = AZ::EBus; } // namespace ImageProcessingAtom diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h index acd1d7a2da..5340b0d380 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h @@ -13,10 +13,15 @@ #include -class QString; +namespace ImageProcessingAtom +{ + class IImageObject; +} namespace ImageProcessingAtomEditor { + typedef AZStd::shared_ptr IImageObjectPtr; + class ImageProcessingEditorRequests : public AZ::EBusTraits { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp index 59c799a601..a17c094fc7 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp @@ -88,11 +88,13 @@ namespace ImageProcessingAtom m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler()); ImageProcessingRequestBus::Handler::BusConnect(); + ImageBuilderRequestBus::Handler::BusConnect(); } void BuilderPluginComponent::Deactivate() { ImageProcessingRequestBus::Handler::BusDisconnect(); + ImageBuilderRequestBus::Handler::BusDisconnect(); m_imageBuilder.BusDisconnect(); BuilderSettingManager::DestroyInstance(); CPixelFormats::DestroyInstance(); @@ -146,6 +148,82 @@ namespace ImageProcessingAtom return image; } + IImageObjectPtr BuilderPluginComponent::CreateImage( + AZ::u32 width, + AZ::u32 height, + AZ::u32 maxMipCount, + EPixelFormat pixelFormat) + { + IImageObjectPtr image(IImageObject::CreateImage(width, height, maxMipCount, pixelFormat)); + return image; + } + + AZStd::vector BuilderPluginComponent::ConvertImageObject( + IImageObjectPtr imageObject, + const AZStd::string& presetName, + const AZStd::string& platformName, + const AZStd::string& outputDir, + const AZ::Data::AssetId& sourceAssetId, + const AZStd::string& sourceAssetName) + { + AZStd::vector outProducts; + + AZStd::string_view presetFilePath; + const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName, platformName, &presetFilePath); + if (preset == nullptr) + { + AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str()); + return outProducts; + } + + AZStd::unique_ptr desc = AZStd::make_unique(); + TextureSettings& textureSettings = desc->m_textureSetting; + textureSettings.m_preset = preset->m_uuid; + desc->m_inputImage = imageObject; + desc->m_presetSetting = *preset; + desc->m_isPreview = false; + desc->m_platform = platformName; + desc->m_filePath = presetFilePath; + desc->m_isStreaming = BuilderSettingManager::Instance()->GetBuilderSetting(platformName)->m_enableStreaming; + desc->m_imageName = sourceAssetName; + desc->m_outputFolder = outputDir; + desc->m_sourceAssetId = sourceAssetId; + + // Create an image convert process + ImageConvertProcess* process = new ImageConvertProcess(AZStd::move(desc)); + if (process) + { + process->ProcessAll(); + bool result = process->IsSucceed(); + if (result) + { + process->GetAppendOutputProducts(outProducts); + } + delete process; + } + + return outProducts; + } + + bool BuilderPluginComponent::DoesSupportPlatform(const AZStd::string& platformId) + { + return ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformId); + } + + bool BuilderPluginComponent::IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName) + { + AZStd::string_view filePath; + const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(presetName, platformName, &filePath); + if (preset == nullptr) + { + AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str()); + return false; + } + + const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(preset->m_pixelFormat); + return info->bSquarePow2; + } + void ImageBuilderWorker::ShutDown() { // it is important to note that this will be called on a different thread than your process job thread diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h index fd099eb08a..9723278380 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h @@ -17,6 +17,7 @@ #include #include #include +#include namespace ImageProcessingAtom { @@ -47,6 +48,7 @@ namespace ImageProcessingAtom class BuilderPluginComponent : public AZ::Component , protected ImageProcessingRequestBus::Handler + , protected ImageBuilderRequestBus::Handler { public: AZ_COMPONENT(BuilderPluginComponent, "{A227F803-D2E4-406E-93EC-121EF45A64A1}") @@ -71,6 +73,24 @@ namespace ImageProcessingAtom IImageObjectPtr LoadImagePreview(const AZStd::string& filePath) override; //////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + // ImageBuilderRequestBus interface implementation + IImageObjectPtr CreateImage( + AZ::u32 width, + AZ::u32 height, + AZ::u32 maxMipCount, + EPixelFormat pixelFormat) override; + AZStd::vector ConvertImageObject( + IImageObjectPtr imageObject, + const AZStd::string& presetName, + const AZStd::string& platformName, + const AZStd::string& outputDir, + const AZ::Data::AssetId& sourceAssetId, + const AZStd::string& sourceAssetName) override; + bool DoesSupportPlatform(const AZStd::string& platformId) override; + bool IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName) override; + //////////////////////////////////////////////////////////////////////// + private: BuilderPluginComponent(const BuilderPluginComponent&) = delete; From 1127235715f15ec981c22cb6d3a3ee1d6813c726 Mon Sep 17 00:00:00 2001 From: abrmich Date: Wed, 19 May 2021 11:36:44 -0700 Subject: [PATCH 28/48] Remove unnecessary heap allocation --- .../Code/Source/ImageBuilderComponent.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp index a17c094fc7..34d18a26eb 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp @@ -190,16 +190,12 @@ namespace ImageProcessingAtom desc->m_sourceAssetId = sourceAssetId; // Create an image convert process - ImageConvertProcess* process = new ImageConvertProcess(AZStd::move(desc)); - if (process) + ImageConvertProcess process(AZStd::move(desc)); + process.ProcessAll(); + bool result = process.IsSucceed(); + if (result) { - process->ProcessAll(); - bool result = process->IsSucceed(); - if (result) - { - process->GetAppendOutputProducts(outProducts); - } - delete process; + process.GetAppendOutputProducts(outProducts); } return outProducts; From bd74835f30eff9c705152f3f9f31ea7b346135d9 Mon Sep 17 00:00:00 2001 From: abrmich Date: Wed, 19 May 2021 11:46:02 -0700 Subject: [PATCH 29/48] Remove leftover code that's no longer needed --- .../Atom/ImageProcessing/ImageProcessingEditorBus.h | 7 ------- .../Code/Source/ImageBuilderComponent.h | 1 - 2 files changed, 8 deletions(-) diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h index 5340b0d380..bf5289bc45 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Include/Atom/ImageProcessing/ImageProcessingEditorBus.h @@ -13,15 +13,8 @@ #include -namespace ImageProcessingAtom -{ - class IImageObject; -} - namespace ImageProcessingAtomEditor { - typedef AZStd::shared_ptr IImageObjectPtr; - class ImageProcessingEditorRequests : public AZ::EBusTraits { diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h index 9723278380..a88ad3f587 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.h @@ -17,7 +17,6 @@ #include #include #include -#include namespace ImageProcessingAtom { From 2112d67b6b9b2e490adf14d9fc1d5375ebcd77d2 Mon Sep 17 00:00:00 2001 From: daimini Date: Wed, 19 May 2021 16:15:18 -0700 Subject: [PATCH 30/48] Rename variable to make it clearer it's a map --- .../AzToolsFramework/Prefab/PrefabPublicHandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index dc9f34b0cb..571aea5875 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -84,7 +84,7 @@ namespace AzToolsFramework AZStd::vector entities; AZStd::vector> instances; - AZStd::unordered_map nestedInstanceLinkPatches; + AZStd::unordered_map nestedInstanceLinkPatchesMap; // Retrieve all entities affected and identify Instances if (!RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonRootEntityOwningInstance->get(), entities, instances)) @@ -104,7 +104,7 @@ namespace AzToolsFramework PrefabDom oldLinkPatches; oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator()); - nestedInstanceLinkPatches.emplace(nestedInstance.get(), AZStd::move(oldLinkPatches)); + nestedInstanceLinkPatchesMap.emplace(nestedInstance.get(), AZStd::move(oldLinkPatches)); } RemoveLink(nestedInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); @@ -164,9 +164,9 @@ namespace AzToolsFramework PrefabDom previousPatch; // Retrieve the previous patch if it exists - if (nestedInstanceLinkPatches.contains(nestedInstance.get())) + if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get())) { - previousPatch = AZStd::move(nestedInstanceLinkPatches[nestedInstance.get()]); + previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]); } // These link creations shouldn't be undone because that would put the template in a non-usable state if a user From e265990a0c40a119b7320cb8e1f4bc2161dd9182 Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 19 May 2021 16:18:11 -0700 Subject: [PATCH 31/48] 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 bcf21e0930dda9254fd760f4bf6a1aca9ca7f452 Mon Sep 17 00:00:00 2001 From: Chris Santora Date: Wed, 19 May 2021 17:43:24 -0700 Subject: [PATCH 32/48] Removed unused InitializeZero function from StandardSurface --- .../Features/PBR/Surfaces/StandardSurface.azsli | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli index 85d9370d2b..1a74a68e96 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/StandardSurface.azsli @@ -32,8 +32,6 @@ class Surface float roughnessA; //!< Actual roughness value ( a.k.a. "alpha roughness") to be used in microfacet calculations float roughnessA2; //!< Alpha roughness ^ 2 (i.e. roughnessA * roughnessA), used in GGX, cached here for perfromance - void InitializeToZero(); - //! Applies specular anti-aliasing to roughnessA2 void ApplySpecularAA(); @@ -45,19 +43,6 @@ class Surface }; -void Surface::InitializeToZero() -{ - clearCoat.InitializeToZero(); - transmission.InitializeToZero(); - position = float3(0,0,0); - normal = float3(0,0,0); - albedo = float3(0,0,0); - specularF0 = float3(0,0,0); - roughnessLinear = 0.0f; - roughnessA = 0.0f; - roughnessA2 = 0.0f; -} - // Specular Anti-Aliasing technique from this paper: // http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf void Surface::ApplySpecularAA() From 8d4fb4d67dcd3c9c617036170c6e71b4f77476f1 Mon Sep 17 00:00:00 2001 From: nvsickle Date: Wed, 19 May 2021 19:11:23 -0700 Subject: [PATCH 33/48] Fix dummy rendering pipeline being created for the Editor -This ensures OnBootstrapSceneReady still fires even if the bootstrap system component doesn't create a default scene -This also disables default scene creation for non-game projects by default to ease tools development Tested with the Editor, AtomSampleViewer, the Material Editor, and the AtomTest launcher --- Code/Sandbox/Editor/EditorViewportWidget.cpp | 9 --------- .../Code/Source/BootstrapSystemComponent.cpp | 20 ++++++++++++++++--- .../Code/Source/BootstrapSystemComponent.h | 1 + 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 436d4bba63..6214ede3cc 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -456,15 +456,6 @@ void EditorViewportWidget::Update() return; } - static bool sentOnWindowCreated = false; - if (!sentOnWindowCreated && windowHandle()->isActive()) - { - sentOnWindowCreated = true; - AzFramework::WindowSystemNotificationBus::Broadcast( - &AzFramework::WindowSystemNotificationBus::Handler::OnWindowCreated, - reinterpret_cast(winId())); - } - m_updatingCameraPosition = true; if (!ed_useNewCameraSystem) { diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp index e3bdb28046..012a229fe8 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.cpp @@ -123,6 +123,12 @@ namespace AZ m_windowHandle = m_nativeWindow->GetWindowHandle(); } + else + { + // Disable default scene creation for non-games projects + // This can be manually overridden via the DefaultWindowBus. + m_createDefaultScene = false; + } AzFramework::AssetCatalogEventBus::Handler::BusConnect(); TickBus::Handler::BusConnect(); @@ -351,6 +357,17 @@ namespace AZ scene->AddRenderPipeline(brdfTexturePipeline); } + // Send notification when the scene and its pipeline are ready. + // Use the first created pipeline's scene as our default scene for now to allow + // consumers waiting on scene availability to initialize. + if (!m_defaultSceneReady) + { + m_defaultScene = scene; + Render::Bootstrap::NotificationBus::Broadcast( + &Render::Bootstrap::NotificationBus::Handler::OnBootstrapSceneReady, m_defaultScene.get()); + m_defaultSceneReady = true; + } + return true; } @@ -364,9 +381,6 @@ namespace AZ { m_renderPipelineId = pipeline->GetId(); } - - // Send notification when the scene and its pipeline are ready - Render::Bootstrap::NotificationBus::Broadcast(&Render::Bootstrap::NotificationBus::Handler::OnBootstrapSceneReady, m_defaultScene.get()); } void BootstrapSystemComponent::DestroyDefaultScene() diff --git a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h index 7323a54221..65390ff153 100644 --- a/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h +++ b/Gems/Atom/Bootstrap/Code/Source/BootstrapSystemComponent.h @@ -125,6 +125,7 @@ namespace AZ Data::Instance m_brdfTexture; bool m_createDefaultScene = true; + bool m_defaultSceneReady = false; // Maps AZ scenes to RPI scene weak pointers to allow looking up a ScenePtr instead of a raw Scene* AZStd::unordered_map> m_azSceneToAtomSceneMap; From 94a8c8258b7a679ce7725e18e609a6566b6fe5cc Mon Sep 17 00:00:00 2001 From: moudgils Date: Wed, 19 May 2021 19:22:35 -0700 Subject: [PATCH 34/48] 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 ca94c59e28d16704564ec97f1cbadb62b0845e23 Mon Sep 17 00:00:00 2001 From: amzn-mike <80125227+amzn-mike@users.noreply.github.com> Date: Wed, 19 May 2021 23:22:44 -0500 Subject: [PATCH 35/48] Switch FbxImportRequestHandler to inherit from AZ::Component instead of BehaviorComponent Previously FbxImportRequestHandler used to be activated as part of DllMain init and never had CreateDescriptor called, which meant reflect was not called. BehaviorComponents get created and Activated as part of special SceneCore logic. Since this component now needs to be activated as part of the normal flow, reflecting it caused it to be picked up by the SceneCore activate logic, causing it to be created/activated twice --- .../SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp | 2 +- Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp index 52ab184f66..a43f1e16b8 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.cpp @@ -61,7 +61,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(1)->Attribute( + serializeContext->Class()->Version(1)->Attribute( AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({AssetBuilderSDK::ComponentTags::AssetBuilder})); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h index f68b56314b..12c7c6f877 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/FbxImportRequestHandler.h @@ -31,11 +31,11 @@ namespace AZ }; class FbxImportRequestHandler - : public SceneCore::BehaviorComponent + : public AZ::Component , public Events::AssetImportRequestBus::Handler { public: - AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}", SceneCore::BehaviorComponent); + AZ_COMPONENT(FbxImportRequestHandler, "{9F4B189C-0A96-4F44-A5F0-E087FF1561F8}"); ~FbxImportRequestHandler() override = default; From 1f45e03a0c46c3995ddf81886a632417af959ec9 Mon Sep 17 00:00:00 2001 From: abrmich Date: Wed, 19 May 2021 22:25:07 -0700 Subject: [PATCH 36/48] Added Gem::AtomFont to tool and runtime dependencies --- AutomatedTesting/Gem/Code/runtime_dependencies.cmake | 1 + AutomatedTesting/Gem/Code/tool_dependencies.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake index 33c2bf8d5f..15715f2136 100644 --- a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake @@ -43,6 +43,7 @@ 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 c8eccab947..1c0db5753b 100644 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/tool_dependencies.cmake @@ -55,6 +55,7 @@ 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 From 0b4b0698c7be87763b6080979844b453ce46bbba Mon Sep 17 00:00:00 2001 From: amzn-sean <75276488+amzn-sean@users.noreply.github.com> Date: Thu, 20 May 2021 15:39:39 +0100 Subject: [PATCH 37/48] disable physics tick time warning in debug builds and make it less spammy (#827) --- Gems/PhysX/Code/Source/System/PhysXSystem.cpp | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp index 1622d04aae..8df9e9a86f 100644 --- a/Gems/PhysX/Code/Source/System/PhysXSystem.cpp +++ b/Gems/PhysX/Code/Source/System/PhysXSystem.cpp @@ -21,10 +21,25 @@ #include +// only enable physx timestep warning when not running debug or in Release +#if !defined(DEBUG) && !defined(RELEASE) +#define ENABLE_PHYSX_TIMESTEP_WARNING +#endif + namespace PhysX { AZ_CLASS_ALLOCATOR_IMPL(PhysXSystem, AZ::SystemAllocator, 0); +#ifdef ENABLE_PHYSX_TIMESTEP_WARNING + namespace FrameTimeWarning + { + static constexpr int MaxSamples = 1000; + static int NumSamples = 0; + static int NumSamplesOverLimit = 0; + static float LostTime = 0.0f; + } +#endif + PhysXSystem::MaterialLibraryAssetHelper::MaterialLibraryAssetHelper(PhysXSystem* physXSystem) : m_physXSystem(physXSystem) { @@ -140,9 +155,26 @@ namespace PhysX } }; - AZ_Warning("PhysXSystem", deltaTime <= m_systemConfig.m_maxTimestep, - "Frame delta time of [%.6f seconds] exceeds Physics max frame timestep, physics timestep will be clamped to [%.6f seconds].", - deltaTime, m_systemConfig.m_maxTimestep); +#ifdef ENABLE_PHYSX_TIMESTEP_WARNING + if (FrameTimeWarning::NumSamples < FrameTimeWarning::MaxSamples) + { + FrameTimeWarning::NumSamples++; + if (deltaTime > m_systemConfig.m_maxTimestep) + { + FrameTimeWarning::NumSamplesOverLimit++; + FrameTimeWarning::LostTime += deltaTime - m_systemConfig.m_maxTimestep; + } + } + else + { + AZ_Warning("PhysXSystem", FrameTimeWarning::NumSamplesOverLimit <= 0, + "[%d] of [%d] frames had a deltatime over the Max physics timestep[%.6f]. Physx timestep was clamped on those frames, losing [%.6f] seconds.", + FrameTimeWarning::NumSamplesOverLimit, FrameTimeWarning::NumSamples, m_systemConfig.m_maxTimestep, FrameTimeWarning::LostTime); + FrameTimeWarning::NumSamples = 0; + FrameTimeWarning::NumSamplesOverLimit = 0; + FrameTimeWarning::LostTime = 0.0f; + } +#endif deltaTime = AZ::GetClamp(deltaTime, 0.0f, m_systemConfig.m_maxTimestep); AZ_Assert(m_systemConfig.m_fixedTimestep >= 0.0f, "PhysXSystem - fixed timestep is negitive."); From eb31d90ad94da7cca7a13b8e1385f1edc4bc42b4 Mon Sep 17 00:00:00 2001 From: Tom Hulton-Harrop <82228511+hultonha@users.noreply.github.com> Date: Thu, 20 May 2021 15:54:36 +0100 Subject: [PATCH 38/48] Updates to fix BoxSelect when using Orbit with the new Camera (#825) * update camera controller to block box select during orbit * simplify update for modern viewport camera controller * wip working lmb box select with orbit * add test for changes to click detector * add unit test for camera system to validate events * remove debugging code, tidy-up changes for PR * small updates before posting PR * fix for linux build failure --- .../AzFramework/Viewport/CameraInput.cpp | 54 ++++++++--- .../AzFramework/Viewport/CameraInput.h | 31 +++++-- .../AzFramework/Viewport/ClickDetector.cpp | 21 +++-- .../AzFramework/Viewport/ClickDetector.h | 9 ++ .../Viewport/ViewportMessages.h | 21 +++++ .../ViewportSelection/EditorBoxSelect.cpp | 13 ++- .../ViewportSelection/EditorBoxSelect.h | 38 ++++---- .../EditorTransformComponentSelection.cpp | 17 +--- Code/Framework/Tests/CameraInputTests.cpp | 90 +++++++++++++++++++ Code/Framework/Tests/ClickDetectorTests.cpp | 17 ++++ .../Tests/frameworktests_files.cmake | 1 + .../Editor/ModernViewportCameraController.cpp | 23 ++++- .../Editor/ModernViewportCameraController.h | 4 +- 13 files changed, 269 insertions(+), 70 deletions(-) create mode 100644 Code/Framework/Tests/CameraInputTests.cpp diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index 79c1a28e5d..bd826544a1 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -193,13 +193,12 @@ namespace AzFramework bool handling = false; for (auto& cameraInput : m_activeCameraInputs) { - cameraInput->HandleEvents(event, cursorDelta, scrollDelta); - handling = !cameraInput->Idle() || handling; + handling = cameraInput->HandleEvents(event, cursorDelta, scrollDelta) || handling; } for (auto& cameraInput : m_idleCameraInputs) { - cameraInput->HandleEvents(event, cursorDelta, scrollDelta); + handling = cameraInput->HandleEvents(event, cursorDelta, scrollDelta) || handling; } return handling; @@ -262,17 +261,26 @@ namespace AzFramework { m_activeCameraInputs[i]->Reset(); m_idleCameraInputs.push_back(m_activeCameraInputs[i]); - m_activeCameraInputs[i] = m_activeCameraInputs[m_activeCameraInputs.size() - 1]; + using AZStd::swap; + swap(m_activeCameraInputs[i], m_activeCameraInputs[m_activeCameraInputs.size() - 1]); m_activeCameraInputs.pop_back(); } } + void Cameras::Clear() + { + Reset(); + AZ_Assert(m_activeCameraInputs.empty(), "Active Camera Inputs is not empty"); + + m_idleCameraInputs.clear(); + } + RotateCameraInput::RotateCameraInput(const InputChannelId rotateChannelId) : m_rotateChannelId(rotateChannelId) { } - void RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) + bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { const ClickDetector::ClickEvent clickEvent = [&event, this] { if (const auto& input = AZStd::get_if(&event)) @@ -304,6 +312,11 @@ namespace AzFramework // noop break; } + + // note - must also check !ending to ensure the mouse up (release) event + // is not consumed and can be propagated to other systems. + // (don't swallow mouse up events) + return !Idle() && !Ending(); } Camera RotateCameraInput::StepCamera( @@ -330,7 +343,7 @@ namespace AzFramework { } - void PanCameraInput::HandleEvents( + bool PanCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { if (const auto& input = AZStd::get_if(&event)) @@ -347,6 +360,8 @@ namespace AzFramework } } } + + return !Idle(); } Camera PanCameraInput::StepCamera( @@ -411,7 +426,7 @@ namespace AzFramework { } - void TranslateCameraInput::HandleEvents( + bool TranslateCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { if (const auto& input = AZStd::get_if(&event)) @@ -429,7 +444,8 @@ namespace AzFramework m_boost = true; } } - else if (input->m_state == InputChannel::State::Ended) + // ensure we don't process end events in the idle state + else if (input->m_state == InputChannel::State::Ended && !Idle()) { m_translation &= ~(translationFromKey(input->m_channelId)); if (m_translation == TranslationType::Nil) @@ -442,6 +458,8 @@ namespace AzFramework } } } + + return !Idle(); } Camera TranslateCameraInput::StepCamera( @@ -503,7 +521,7 @@ namespace AzFramework m_boost = false; } - void OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) + bool OrbitCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) { if (const auto* input = AZStd::get_if(&event)) { @@ -522,8 +540,10 @@ namespace AzFramework if (Active()) { - m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta); + return m_orbitCameras.HandleEvents(event, cursorDelta, scrollDelta); } + + return !Idle(); } Camera OrbitCameraInput::StepCamera( @@ -533,7 +553,7 @@ namespace AzFramework if (Beginning()) { - const auto hasLookAt = [&nextCamera, &targetCamera, lookAtFn = m_lookAtFn] { + const auto hasLookAt = [&nextCamera, &targetCamera, &lookAtFn = m_lookAtFn] { if (lookAtFn) { if (const auto lookAt = lookAtFn()) @@ -585,13 +605,15 @@ namespace AzFramework return nextCamera; } - void OrbitDollyScrollCameraInput::HandleEvents( + bool OrbitDollyScrollCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { if (const auto* scroll = AZStd::get_if(&event)) { BeginActivation(); } + + return !Idle(); } Camera OrbitDollyScrollCameraInput::StepCamera( @@ -609,7 +631,7 @@ namespace AzFramework { } - void OrbitDollyCursorMoveCameraInput::HandleEvents( + bool OrbitDollyCursorMoveCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { if (const auto& input = AZStd::get_if(&event)) @@ -626,6 +648,8 @@ namespace AzFramework } } } + + return !Idle(); } Camera OrbitDollyCursorMoveCameraInput::StepCamera( @@ -637,13 +661,15 @@ namespace AzFramework return nextCamera; } - void ScrollTranslationCameraInput::HandleEvents( + bool ScrollTranslationCameraInput::HandleEvents( const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) { if (const auto* scroll = AZStd::get_if(&event)) { BeginActivation(); } + + return !Idle(); } Camera ScrollTranslationCameraInput::StepCamera( diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index b6b2bc1e6a..582fb5a6de 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -149,7 +149,7 @@ namespace AzFramework ResetImpl(); } - virtual void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0; + virtual bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) = 0; virtual Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) = 0; virtual bool Exclusive() const @@ -171,16 +171,29 @@ namespace AzFramework class Cameras { public: - void AddCamera(AZStd::shared_ptr cameraInput); bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta); Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime); + + void AddCamera(AZStd::shared_ptr cameraInput); + //! Reset the state of all cameras. void Reset(); + //! Remove all cameras that were added. + void Clear(); + //! Is one of the cameras in the active camera inputs marked as 'exclusive'. + //! @note This implies no other sibling cameras can begin while the exclusive camera is running. + bool Exclusive() const; private: AZStd::vector> m_activeCameraInputs; AZStd::vector> m_idleCameraInputs; }; + inline bool Cameras::Exclusive() const + { + return AZStd::any_of( + m_activeCameraInputs.begin(), m_activeCameraInputs.end(), [](const auto& cameraInput) { return cameraInput->Exclusive(); }); + } + class CameraSystem { public: @@ -200,7 +213,7 @@ namespace AzFramework explicit RotateCameraInput(InputChannelId rotateChannelId); // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; private: @@ -241,7 +254,7 @@ namespace AzFramework PanCameraInput(InputChannelId panChannelId, PanAxesFn panAxesFn); // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; private: @@ -282,7 +295,7 @@ namespace AzFramework explicit TranslateCameraInput(TranslationAxesFn translationAxesFn); // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; void ResetImpl() override; @@ -352,7 +365,7 @@ namespace AzFramework { public: // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; }; @@ -362,7 +375,7 @@ namespace AzFramework explicit OrbitDollyCursorMoveCameraInput(InputChannelId dollyChannelId); // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; private: @@ -373,7 +386,7 @@ namespace AzFramework { public: // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; }; @@ -383,7 +396,7 @@ namespace AzFramework using LookAtFn = AZStd::function()>; // CameraInput overrides ... - void HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; bool Exclusive() const override; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp index 4b8fbca36a..c276463554 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.cpp @@ -17,6 +17,17 @@ namespace AzFramework { ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta) { + const auto previousDetectionState = m_detectionState; + if (previousDetectionState == DetectionState::WaitingForMove) + { + // only allow the action to begin if the mouse has been moved a small amount + m_moveAccumulator += ScreenVectorLength(cursorDelta); + if (m_moveAccumulator > m_deadZone) + { + m_detectionState = DetectionState::Moved; + } + } + if (clickEvent == ClickEvent::Down) { const auto now = std::chrono::steady_clock::now(); @@ -52,15 +63,9 @@ namespace AzFramework return clickOutcome; } - if (m_detectionState == DetectionState::WaitingForMove) + if (previousDetectionState == DetectionState::WaitingForMove && m_detectionState == DetectionState::Moved) { - // only allow the action to begin if the mouse has been moved a small amount - m_moveAccumulator += ScreenVectorLength(cursorDelta); - if (m_moveAccumulator > m_deadZone) - { - m_detectionState = DetectionState::Moved; - return ClickOutcome::Move; - } + return ClickOutcome::Move; } return ClickOutcome::Nil; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h index 997ccd07d9..a595735d28 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/ClickDetector.h @@ -50,7 +50,11 @@ namespace AzFramework //! Called from any type of 'handle event' function. ClickOutcome DetectClick(ClickEvent clickEvent, const ScreenVector& cursorDelta); + //! Override the default double click interval. + //! @note Default is 400ms - system default. void SetDoubleClickInterval(float doubleClickInterval); + //! Override the dead zone before a 'move' outcome will be triggered. + void SetDeadZone(float deadZone); private: //! Internal state of ClickDetector based on incoming events. @@ -72,4 +76,9 @@ namespace AzFramework { m_doubleClickInterval = doubleClickInterval; } + + inline void ClickDetector::SetDeadZone(const float deadZone) + { + m_deadZone = deadZone; + } } // namespace AzFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h index ee95412376..8e91dc945d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Viewport/ViewportMessages.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -304,4 +305,24 @@ namespace AzToolsFramework return entityContextId; } + + //! Maps a mouse interaction event to a ClickDetector event. + //! @note Function only cares about up or down events, all other events are mapped to Nil (ignored). + inline AzFramework::ClickDetector::ClickEvent ClickDetectorEventFromViewportInteraction( + const ViewportInteraction::MouseInteractionEvent& mouseInteraction) + { + if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left()) + { + if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down) + { + return AzFramework::ClickDetector::ClickEvent::Down; + } + + if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up) + { + return AzFramework::ClickDetector::ClickEvent::Up; + } + } + return AzFramework::ClickDetector::ClickEvent::Nil; + } } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.cpp index 2e467caa4c..531cffb561 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.cpp @@ -14,6 +14,7 @@ #include #include +#include #include @@ -27,8 +28,11 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() && - mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down) + m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); + + const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction); + const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta()); + if (clickOutcome == AzFramework::ClickDetector::ClickOutcome::Move) { if (m_leftMouseDown) { @@ -58,8 +62,7 @@ namespace AzToolsFramework } } - if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left() && - mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up) + if (clickOutcome == AzFramework::ClickDetector::ClickOutcome::Release) { if (m_leftMouseUp) { @@ -77,6 +80,8 @@ namespace AzToolsFramework { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); + m_cursorState.Update(); + if (m_boxSelectRegion) { debugDisplay.DepthTestOff(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.h index 7f50b16325..c115220755 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorBoxSelect.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include @@ -26,49 +28,49 @@ namespace AzFramework namespace AzToolsFramework { - /// Utility to provide box select (click and drag) support for viewport types. - /// Users can override the mouse event callbacks and display scene function to customize behavior. + //! Utility to provide box select (click and drag) support for viewport types. + //! Users can override the mouse event callbacks and display scene function to customize behavior. class EditorBoxSelect { public: EditorBoxSelect() = default; - /// Return if a box select action is currently taking place. + //! Return if a box select action is currently taking place. bool Active() const { return m_boxSelectRegion.has_value(); } - /// Update the box select for various mouse events. - /// Call HandleMouseInteraction from type/system implementing MouseViewportRequests interface. + //! Update the box select for various mouse events. + //! Call HandleMouseInteraction from type/system implementing MouseViewportRequests interface. void HandleMouseInteraction( const ViewportInteraction::MouseInteractionEvent& mouseInteraction); - /// Responsible for drawing the 2d box representing the selection in screen space. + //! Responsible for drawing the 2d box representing the selection in screen space. void Display2d(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay); - /// Custom drawing behavior to happen during a box select. + //! Custom drawing behavior to happen during a box select. void DisplayScene( const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay); - /// Set the left mouse down callback. + //! Set the left mouse down callback. void InstallLeftMouseDown( const AZStd::function& leftMouseDown); - /// Set the mouse move callback. + //! Set the mouse move callback. void InstallMouseMove( const AZStd::function& mouseMove); - /// Set the left mouse up callback. + //! Set the left mouse up callback. void InstallLeftMouseUp( const AZStd::function& leftMouseUp); - /// Set the display scene callback. + //! Set the display scene callback. void InstallDisplayScene( const AZStd::function& displayScene); - /// Return the box select region. - /// If a box selection is being made, return the current rectangle representing the area. - /// If there is currently no active box select, then the Maybe type will be empty (there will be no region/area). + //! Return the box select region. + //! If a box selection is being made, return the current rectangle representing the area. + //! If there is currently no active box select, then the Maybe type will be empty (there will be no region/area). const AZStd::optional& BoxRegion() const { return m_boxSelectRegion; } - /// Return the active modifiers from the previous frame. + //! Return the active modifiers from the previous frame. ViewportInteraction::KeyboardModifiers PreviousModifiers() const { return m_previousModifiers; } private: @@ -79,7 +81,9 @@ namespace AzToolsFramework AZStd::function m_displayScene; - AZStd::optional m_boxSelectRegion; ///< Maybe/optional value to store box select region while active. - ViewportInteraction::KeyboardModifiers m_previousModifiers; ///< Modifier keys active on the previous frame. + AZStd::optional m_boxSelectRegion; //!< Maybe/optional value to store box select region while active. + ViewportInteraction::KeyboardModifiers m_previousModifiers; //!< Modifier keys active on the previous frame. + AzFramework::ClickDetector m_clickDetector; //!< Utility type to detect if a mouse click or move has occurred. + AzFramework::CursorState m_cursorState; //!< Utility type to track the current cursor position (and movement/delta). }; } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 6e49f7c601..91644dc6ac 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -1782,22 +1782,7 @@ namespace AzToolsFramework m_cachedEntityIdUnderCursor = m_editorHelpers->HandleMouseInteraction(cameraState, mouseInteraction); - const AzFramework::ClickDetector::ClickEvent selectClickEvent = [&mouseInteraction] { - if (mouseInteraction.m_mouseInteraction.m_mouseButtons.Left()) - { - if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Down) - { - return AzFramework::ClickDetector::ClickEvent::Down; - } - - if (mouseInteraction.m_mouseEvent == ViewportInteraction::MouseEvent::Up) - { - return AzFramework::ClickDetector::ClickEvent::Up; - } - } - return AzFramework::ClickDetector::ClickEvent::Nil; - }(); - + const auto selectClickEvent = ClickDetectorEventFromViewportInteraction(mouseInteraction); m_cursorState.SetCurrentPosition(mouseInteraction.m_mouseInteraction.m_mousePick.m_screenCoordinates); const auto clickOutcome = m_clickDetector.DetectClick(selectClickEvent, m_cursorState.CursorDelta()); diff --git a/Code/Framework/Tests/CameraInputTests.cpp b/Code/Framework/Tests/CameraInputTests.cpp new file mode 100644 index 0000000000..6fe9837c22 --- /dev/null +++ b/Code/Framework/Tests/CameraInputTests.cpp @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include +#include +#include +#include + +namespace UnitTest +{ + class CameraInputFixture : public AllocatorsTestFixture + { + public: + AzFramework::Camera m_camera; + AzFramework::Camera m_targetCamera; + AZStd::shared_ptr m_cameraSystem; + + bool HandleEventAndUpdate(const AzFramework::InputEvent& event) + { + constexpr float deltaTime = 0.01666f; // 60fps + const bool consumed = m_cameraSystem->HandleEvents(event); + m_camera = m_cameraSystem->StepCamera(m_targetCamera, deltaTime); + return consumed; + } + + void SetUp() override + { + AllocatorsTestFixture::SetUp(); + + AzFramework::ReloadCameraKeyBindings(); + + m_cameraSystem = AZStd::make_shared(); + + auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::InputDeviceMouse::Button::Right); + auto firstPersonTranslateCamera = AZStd::make_shared(AzFramework::LookTranslation); + + auto orbitCamera = AZStd::make_shared(); + auto orbitRotateCamera = AZStd::make_shared(AzFramework::InputDeviceMouse::Button::Left); + auto orbitTranslateCamera = AZStd::make_shared(AzFramework::OrbitTranslation); + + orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera); + orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera); + + m_cameraSystem->m_cameras.AddCamera(firstPersonRotateCamera); + m_cameraSystem->m_cameras.AddCamera(firstPersonTranslateCamera); + m_cameraSystem->m_cameras.AddCamera(orbitCamera); + } + + void TearDown() override + { + m_cameraSystem->m_cameras.Clear(); + m_cameraSystem.reset(); + + AllocatorsTestFixture::TearDown(); + } + }; + + 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( + 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( + 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)}); + // end orbit (mouse up) - event is not consumed + const bool consumed5 = HandleEventAndUpdate( + AzFramework::DiscreteInputEvent{AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Ended}); + + const auto allConsumed = AZStd::vector{consumed1, consumed2, consumed3, consumed4, consumed5}; + + using ::testing::ElementsAre; + EXPECT_THAT(allConsumed, ElementsAre(false, true, false, true, false)); + } +} // namespace UnitTest diff --git a/Code/Framework/Tests/ClickDetectorTests.cpp b/Code/Framework/Tests/ClickDetectorTests.cpp index 7e6f9634c8..64f06ee66c 100644 --- a/Code/Framework/Tests/ClickDetectorTests.cpp +++ b/Code/Framework/Tests/ClickDetectorTests.cpp @@ -139,4 +139,21 @@ namespace UnitTest EXPECT_THAT(secondaryDownOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // ignored double click EXPECT_THAT(secondaryUpOutcome, Eq(ClickDetector::ClickOutcome::Nil)); // click not registered } + + // if the click detector registers a mouse down event, but then all intermediate calls are ignored + // (another system may start intercepting events and swallowing them) then when we do receive a mouse + // up event we should ensure we take into account the current delta - if the delta is large, then the + // outcome will be release + TEST_F(ClickDetectorFixture, ClickIsNotRegisteredAfterIgnoringMouseMovesBeforeMouseUpWithLargeDelta) + { + using ::testing::Eq; + + const ClickDetector::ClickOutcome downOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0)); + const ClickDetector::ClickOutcome upOutcome = + m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(50, 50)); + + EXPECT_THAT(downOutcome, Eq(ClickDetector::ClickOutcome::Nil)); + EXPECT_THAT(upOutcome, Eq(ClickDetector::ClickOutcome::Release)); + } } // namespace UnitTest diff --git a/Code/Framework/Tests/frameworktests_files.cmake b/Code/Framework/Tests/frameworktests_files.cmake index e249cf6e64..197bcc9fce 100644 --- a/Code/Framework/Tests/frameworktests_files.cmake +++ b/Code/Framework/Tests/frameworktests_files.cmake @@ -17,6 +17,7 @@ set(FILES BinToTextEncode.cpp ComponentAddRemove.cpp ComponentAdapterTests.cpp + CameraInputTests.cpp ClickDetectorTests.cpp CursorStateTests.cpp EntityContext.cpp diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.cpp b/Code/Sandbox/Editor/ModernViewportCameraController.cpp index af161af493..83ab2ef0b5 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.cpp +++ b/Code/Sandbox/Editor/ModernViewportCameraController.cpp @@ -97,17 +97,38 @@ namespace SandboxEditor AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect(); } + // should the camera system respond to this particular event + static bool ShouldHandle(const AzFramework::ViewportControllerPriority priority, const bool exclusive) + { + // ModernViewportCameraControllerInstance receives events at all priorities, it should only respond + // to normal priority events if it is not in 'exclusive' mode and when in 'exclusive' mode it should + // only respond to the highest priority events + return !exclusive && priority == AzFramework::ViewportControllerPriority::Normal || + exclusive && priority == AzFramework::ViewportControllerPriority::Highest; + } + bool ModernViewportCameraControllerInstance::HandleInputChannelEvent(const AzFramework::ViewportControllerInputEvent& event) { AzFramework::WindowSize windowSize; AzFramework::WindowRequestBus::EventResult( windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize); - return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize)); + if (ShouldHandle(event.m_priority, m_cameraSystem.m_cameras.Exclusive())) + { + return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize)); + } + + return false; } void ModernViewportCameraControllerInstance::UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) { + // only update for a single priority (normal is the default) + if (event.m_priority != AzFramework::ViewportControllerPriority::Normal) + { + return; + } + if (auto viewportContext = RetrieveViewportContext(GetViewportId())) { m_updatingTransform = true; diff --git a/Code/Sandbox/Editor/ModernViewportCameraController.h b/Code/Sandbox/Editor/ModernViewportCameraController.h index 066c8efaa8..39e3c9cbb3 100644 --- a/Code/Sandbox/Editor/ModernViewportCameraController.h +++ b/Code/Sandbox/Editor/ModernViewportCameraController.h @@ -22,7 +22,9 @@ namespace SandboxEditor { class ModernViewportCameraControllerInstance; - class ModernViewportCameraController : public AzFramework::MultiViewportController + class ModernViewportCameraController + : public AzFramework::MultiViewportController< + ModernViewportCameraControllerInstance, AzFramework::ViewportControllerPriority::DispatchToAllPriorities> { public: using CameraListBuilder = AZStd::function; From 22e893ccbe4467cc4db328fd9c3c408c94e1a569 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 20 May 2021 16:32:41 +0100 Subject: [PATCH 39/48] Added support for nested prefabs in multiplayer pipeline --- .../Pipeline/NetworkPrefabProcessor.cpp | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index 0bbfe17801..762219cce4 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -54,34 +54,15 @@ namespace Multiplayer } } - static AZStd::vector GetEntitiesFromInstance(AZStd::unique_ptr& instance) - { - AZStd::vector result; - - instance->GetNestedEntities([&result](const AZStd::unique_ptr& entity) { - result.emplace_back(entity.get()); - return true; - }); - - if (instance->HasContainerEntity()) - { - auto containerEntityReference = instance->GetContainerEntity(); - result.emplace_back(&containerEntityReference->get()); - } - - return result; - } - - void NetworkPrefabProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab) + static AZStd::unique_ptr LoadInstanceFromPrefab(const PrefabDom& prefab) { using namespace AzToolsFramework::Prefab; // convert Prefab DOM into Prefab Instance. AZStd::unique_ptr sourceInstance(aznew Instance()); - if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab, - PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) + if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab, PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) { - PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName); + PrefabDomValueConstReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName); AZStd::string errorMessage("NetworkPrefabProcessor: Failed to Load Prefab Instance from given Prefab Dom."); if (sourceReference.has_value() && sourceReference->get().IsString() && sourceReference->get().GetStringLength() != 0) @@ -90,6 +71,38 @@ namespace Multiplayer errorMessage += AZStd::string::format("Prefab Source: %.*s", AZ_STRING_ARG(source)); } AZ_Error("NetworkPrefabProcessor", false, errorMessage.c_str()); + return nullptr; + } + return sourceInstance; + } + + static void GatherNetEntities( + AzToolsFramework::Prefab::Instance* instance, + AZStd::vector>& output) + { + instance->GetEntities([instance, &output](AZStd::unique_ptr& prefabEntity) + { + if (prefabEntity->FindComponent()) + { + output.push_back(AZStd::make_pair(prefabEntity.get(), instance)); + } + return true; + }); + + instance->GetNestedInstances([&output](AZStd::unique_ptr& nestedInstance) + { + GatherNetEntities(nestedInstance.get(), output); + }); + } + + void NetworkPrefabProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab) + { + using namespace AzToolsFramework::Prefab; + + // convert Prefab DOM into Prefab Instance. + AZStd::unique_ptr sourceInstance = LoadInstanceFromPrefab(prefab); + if (!sourceInstance) + { return; } @@ -105,36 +118,37 @@ namespace Multiplayer auto&& [object, networkSpawnable] = ProcessedObjectStore::Create(uniqueName, context.GetSourceUuid(), AZStd::move(serializer)); - // grab all nested entities from the Instance as source entities. - AZStd::vector sourceEntities = GetEntitiesFromInstance(sourceInstance); - AZStd::vector networkedEntityIds; - networkedEntityIds.reserve(sourceEntities.size()); + // Grab all net entities with their corresponding Instances to handle nested prefabs correctly + AZStd::vector> netEntities; + GatherNetEntities(sourceInstance.get(), netEntities); - for (auto* sourceEntity : sourceEntities) - { - if (sourceEntity->FindComponent()) - { - networkedEntityIds.push_back(sourceEntity->GetId()); - } - } + // Instance container for net entities + AZStd::unique_ptr networkInstance(aznew Instance()); - if (networkedEntityIds.empty()) + // Create an asset for our future network spawnable: this allows us to put references to the asset in the components + AZ::Data::Asset networkSpawnableAsset; + networkSpawnableAsset.Create(networkSpawnable->GetId()); + networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); + + if (netEntities.empty()) { // No networked entities in the prefab, no need to do anything in this processor. return; } - AZStd::unique_ptr networkInstance(aznew Instance()); + // Each spawnable has a root meta-data entity at position 0, so starting net indices from 1 + size_t netEntitiesIndexCounter = 1; - AZ::Data::Asset networkSpawnableAsset; - networkSpawnableAsset.Create(networkSpawnable->GetId()); - networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); - - for (size_t entityIndex = 0; entityIndex < networkedEntityIds.size(); ++entityIndex) + for (auto& entityInstancePair : netEntities) { - AZ::EntityId entityId = networkedEntityIds[entityIndex]; + AZ::Entity* prefabEntity = entityInstancePair.first; + Instance* instance = entityInstancePair.second; + + AZ::EntityId entityId = prefabEntity->GetId(); + AZ::Entity* netEntity = instance->DetachEntity(entityId).release(); + AZ_Assert(netEntity, "Unable to detach entity %s [%s] from the source prefab instance", + prefabEntity->GetName().c_str(), entityId.ToString().c_str()); - AZ::Entity* netEntity = sourceInstance->DetachEntity(entityId).release(); // Net entity will need a new ID to avoid IDs collision netEntity->SetId(AZ::Entity::MakeId()); networkInstance->AddEntity(*netEntity); @@ -143,17 +157,21 @@ namespace Multiplayer AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName()); breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault()); + // Marker component is what is responsible to spawning entities based on the index. NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent(); - // Each spawnable has a root meta-data entity at position 0, so starting net indices from 1 - netBindMarkerComponent->SetNetEntityIndex(entityIndex + 1); + netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter); netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset); + + // Copy the transform component from the original entity to have the correct transform and parent-child relationship AzFramework::TransformComponent* transformComponent = netEntity->FindComponent(); breadcrumbEntity->CreateComponent(*transformComponent); - sourceInstance->AddEntity(*breadcrumbEntity); + instance->AddEntity(*breadcrumbEntity); + + netEntitiesIndexCounter++; } - // Add net spawnable asset holder + // Add net spawnable asset holder to the prefab root { EntityOptionalReference containerEntityRef = sourceInstance->GetContainerEntity(); if (containerEntityRef.has_value()) @@ -184,7 +202,6 @@ namespace Multiplayer return; } - bool result = SpawnableUtils::CreateSpawnable(*networkSpawnable, networkPrefab); if (result) { From 43d98ac98933bc02cecadd68377ddbb4a81ff043 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 20 May 2021 16:56:54 +0100 Subject: [PATCH 40/48] Fixed comment --- .../Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index 762219cce4..fafaa30c4f 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -157,7 +157,7 @@ namespace Multiplayer AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName()); breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault()); - // Marker component is what is responsible to spawning entities based on the index. + // Marker component is responsible to spawning entities based on the index. NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent(); netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter); netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset); From 525840fb2a77feb24d5b365d8fad4116825858cb Mon Sep 17 00:00:00 2001 From: amzn-hdoke <61443753+hdoke@users.noreply.github.com> Date: Thu, 20 May 2021 09:29:44 -0700 Subject: [PATCH 41/48] Adding password 1 factor sign in (#830) AWS automation test --- .../client_auth/test_password_signin.py | 97 + .../resource_mappings/resource_mappings.py | 5 + .../ClientAuthPasswordSignIn.ly | 3 + .../PasswordSignIn.scriptcanvas | 6642 +++++++++++++++++ .../AWS/ClientAuthPasswordSignIn/filelist.xml | 6 + .../AWS/ClientAuthPasswordSignIn/level.pak | 3 + .../AWS/ClientAuthPasswordSignIn/tags.txt | 12 + .../ClientAuthPasswordSignUp.ly | 3 + .../PasswordSignUp.scriptcanvas | 4408 +++++++++++ .../AWS/ClientAuthPasswordSignUp/filelist.xml | 6 + .../AWS/ClientAuthPasswordSignUp/level.pak | 3 + .../AWS/ClientAuthPasswordSignUp/tags.txt | 12 + .../Registry/authenticationProvider.setreg | 5 + .../cdk/cognito/cognito_user_pool.py | 2 +- 14 files changed, 11206 insertions(+), 1 deletion(-) create mode 100644 AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/ClientAuthPasswordSignIn.ly create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/PasswordSignIn.scriptcanvas create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/filelist.xml create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/level.pak create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/tags.txt create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/ClientAuthPasswordSignUp.ly create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/PasswordSignUp.scriptcanvas create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/filelist.xml create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/level.pak create mode 100644 AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/tags.txt create mode 100644 AutomatedTesting/Registry/authenticationProvider.setreg diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py new file mode 100644 index 0000000000..da4898b8a9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_password_signin.py @@ -0,0 +1,97 @@ +""" +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. +""" +import pytest +import os +import logging +import ly_test_tools.log.log_monitor + +from AWS.Windows.resource_mappings.resource_mappings import resource_mappings +from AWS.Windows.cdk.cdk import cdk +from AWS.common.aws_utils import aws_utils +from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor as asset_processor + +AWS_PROJECT_NAME = 'AWS-AutomationTest' +AWS_CLIENT_AUTH_FEATURE_NAME = 'AWSClientAuth' +AWS_CLIENT_AUTH_DEFAULT_PROFILE_NAME = 'default' + +GAME_LOG_NAME = 'Game.log' + +logger = logging.getLogger(__name__) + + +@pytest.mark.SUITE_periodic +@pytest.mark.usefixtures('automatic_process_killer') +@pytest.mark.usefixtures('asset_processor') +@pytest.mark.usefixtures('workspace') +@pytest.mark.parametrize('project', ['AutomatedTesting']) +@pytest.mark.usefixtures('cdk') +@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME]) +@pytest.mark.usefixtures('resource_mappings') +@pytest.mark.parametrize('resource_mappings_filename', ['aws_resource_mappings.json']) +@pytest.mark.usefixtures('aws_utils') +@pytest.mark.parametrize('region_name', ['us-west-2']) +@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests']) +@pytest.mark.parametrize('session_name', ['o3de-Automation-session']) +class TestAWSClientAuthPasswordSignIn(object): + """ + Test class to verify AWS Cognito IDP Password sign in and Cognito Identity pool authenticated authorization. + """ + + def test_password_signin_credentials(self, + launcher: pytest.fixture, + cdk: pytest.fixture, + resource_mappings: pytest.fixture, + workspace: pytest.fixture, + asset_processor: pytest.fixture, + aws_utils: pytest.fixture + ): + """ + Setup: Deploys cdk and updates resource mapping file. + Tests: Sign up new test user, admin confirm the user, sign in and get aws credentials. + Verification: Log monitor looks for success credentials log. + """ + logger.info(f'Cdk stack names:\n{cdk.list()}') + stacks = cdk.deploy() + resource_mappings.populate_output_keys(stacks) + asset_processor.start() + asset_processor.wait_for_idle() + + file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME) + log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor) + + launcher.args = ['+LoadLevel', 'AWS/ClientAuthPasswordSignUp'] + + with launcher.start(launch_ap=False): + result = log_monitor.monitor_log_for_lines( + expected_lines=['(Script) - Signup Success'], + unexpected_lines=['(Script) - Signup Fail'], + halt_on_unexpected=True, + ) + assert result, 'Sign Up Success.' + + launcher.stop() + + cognito_idp = aws_utils.client('cognito-idp') + user_pool_id = resource_mappings.get_resource_name_id(f'{AWS_CLIENT_AUTH_FEATURE_NAME}.CognitoUserPoolId') + print(f'UserPoolId:{user_pool_id}') + cognito_idp.admin_confirm_sign_up( + UserPoolId=user_pool_id, + Username='test1' + ) + + launcher.args = ['+LoadLevel', 'AWS/ClientAuthPasswordSignIn'] + + with launcher.start(launch_ap=False): + result = log_monitor.monitor_log_for_lines( + expected_lines=['(Script) - SignIn Success', '(Script) - Success credentials'], + unexpected_lines=['(Script) - SignIn Fail', '(Script) - Fail credentials'], + halt_on_unexpected=True, + ) + assert result, 'Sign in Success, fetched authenticated AWS temp credentials.' diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py index c8d8cff828..b3fa3011ce 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py @@ -39,6 +39,7 @@ class ResourceMappings: self._region = region self._feature_name = feature_name self._account_id = account_id + self._resource_mappings = {} assert os.path.exists(self._resource_mapping_file_path), \ f'Invalid resource mapping file path {self._resource_mapping_file_path}' @@ -79,6 +80,7 @@ class ResourceMappings: resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key]['Name/ID'] = output.get('OutputValue', 'InvalidId') + self._resource_mappings = resource_mappings with open(self._resource_mapping_file_path, 'w') as file_content: json.dump(resource_mappings, file_content, indent=4) @@ -103,6 +105,9 @@ class ResourceMappings: self._region = '' self._client = None + def get_resource_name_id(self, resource_key: str): + return self._resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key]['Name/ID'] + @pytest.fixture(scope='function') def resource_mappings( diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/ClientAuthPasswordSignIn.ly b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/ClientAuthPasswordSignIn.ly new file mode 100644 index 0000000000..24fe4f2482 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/ClientAuthPasswordSignIn.ly @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43b1a23b62fe2ffa05545ac99524f40b6fff49d6e35925b9d6138c00d8082e86 +size 9073 diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/PasswordSignIn.scriptcanvas b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/PasswordSignIn.scriptcanvas new file mode 100644 index 0000000000..ffc3064084 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/PasswordSignIn.scriptcanvas @@ -0,0 +1,6642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/filelist.xml b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/filelist.xml new file mode 100644 index 0000000000..454b94a80a --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/filelist.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/level.pak b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/level.pak new file mode 100644 index 0000000000..14e6b3274b --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/level.pak @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f583e0b1b7016a11583383e6c6fcd29f9e796c1a9cd4b6ddb10f7dc91deec17a +size 3557 diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/tags.txt b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/tags.txt new file mode 100644 index 0000000000..0d6c1880e7 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignIn/tags.txt @@ -0,0 +1,12 @@ +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/ClientAuthPasswordSignUp.ly b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/ClientAuthPasswordSignUp.ly new file mode 100644 index 0000000000..f853ec3890 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/ClientAuthPasswordSignUp.ly @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b948461412d201b3a80abafa60e916f860e46e28109333fbd263a2d5fc53c5a +size 9103 diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/PasswordSignUp.scriptcanvas b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/PasswordSignUp.scriptcanvas new file mode 100644 index 0000000000..632d27d5b0 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/PasswordSignUp.scriptcanvas @@ -0,0 +1,4408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/filelist.xml b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/filelist.xml new file mode 100644 index 0000000000..5e47a51414 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/filelist.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/level.pak b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/level.pak new file mode 100644 index 0000000000..72ac9c767f --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/level.pak @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cdb456f6eb348be27249d80e9d2262e1e0bdabf2c1ff02c1a64a5609dcd823c +size 3553 diff --git a/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/tags.txt b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/tags.txt new file mode 100644 index 0000000000..0d6c1880e7 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuthPasswordSignUp/tags.txt @@ -0,0 +1,12 @@ +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 +0,0,0,0,0,0 diff --git a/AutomatedTesting/Registry/authenticationProvider.setreg b/AutomatedTesting/Registry/authenticationProvider.setreg new file mode 100644 index 0000000000..c90433468c --- /dev/null +++ b/AutomatedTesting/Registry/authenticationProvider.setreg @@ -0,0 +1,5 @@ +{ + "AWS": + { + } +} \ No newline at end of file diff --git a/Gems/AWSClientAuth/cdk/cognito/cognito_user_pool.py b/Gems/AWSClientAuth/cdk/cognito/cognito_user_pool.py index a903217f40..f0a2acf208 100755 --- a/Gems/AWSClientAuth/cdk/cognito/cognito_user_pool.py +++ b/Gems/AWSClientAuth/cdk/cognito/cognito_user_pool.py @@ -76,7 +76,7 @@ class CognitoUserPool: scope, 'CognitoUserPoolId', description="Cognito User pool id", - value=self._user_pool.attr_provider_name) + value=self._user_pool.ref) core.CfnOutput( scope, From 03b41b620d64564130876a691432e536fe6c9761 Mon Sep 17 00:00:00 2001 From: Benjamin Jillich <43751992+amzn-jillich@users.noreply.github.com> Date: Thu, 20 May 2021 18:42:37 +0200 Subject: [PATCH 42/48] [LYN-2522] Preparation work for the filter pane (#799) * [LYN-2522] Preparation work for the filter pane * Added arrow up/down icons. * Extended the gem info with the type (Asset, Code, Tool). * Extended the model with the type and a helper for converting gem uuids into display names. * Extended the link widget to be clickable with a custom action, needed for the "Show all/less" for the filters. * Converting the uuids we get from Python to AZ::Uuids and then back to strings to have them all in the same format. --- .../Resources/ArrowDownLine.svg | 3 ++ .../ProjectManager/Resources/ArrowUpLine.svg | 3 ++ .../Source/GemCatalog/GemCatalogScreen.cpp | 7 --- .../Source/GemCatalog/GemInfo.cpp | 25 ++++++++-- .../Source/GemCatalog/GemInfo.h | 12 +++++ .../Source/GemCatalog/GemModel.cpp | 48 +++++++++++++++++-- .../Source/GemCatalog/GemModel.h | 12 ++++- .../ProjectManager/Source/LinkWidget.cpp | 7 ++- Code/Tools/ProjectManager/Source/LinkWidget.h | 8 +++- .../ProjectManager/Source/PythonBindings.cpp | 5 +- Code/Tools/ProjectManager/project_manager.qrc | 2 + 11 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 Code/Tools/ProjectManager/Resources/ArrowDownLine.svg create mode 100644 Code/Tools/ProjectManager/Resources/ArrowUpLine.svg diff --git a/Code/Tools/ProjectManager/Resources/ArrowDownLine.svg b/Code/Tools/ProjectManager/Resources/ArrowDownLine.svg new file mode 100644 index 0000000000..8418431f11 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowDownLine.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Resources/ArrowUpLine.svg b/Code/Tools/ProjectManager/Resources/ArrowUpLine.svg new file mode 100644 index 0000000000..d7f26fdad5 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/ArrowUpLine.svg @@ -0,0 +1,3 @@ + + + diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 5737a188de..3c221d6055 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -58,13 +58,6 @@ namespace O3DE::ProjectManager hLayout->addWidget(m_gemListView); hLayout->addWidget(m_gemInspector); - - - // Select the first entry after everything got correctly sized - QTimer::singleShot(100, [=]{ - QModelIndex firstModelIndex = m_gemListView->model()->index(0,0); - m_gemListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect); - }); } QVector GemCatalogScreen::GenerateTestData() diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp index 729935fc8e..5b7127bdbe 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.cpp @@ -32,21 +32,36 @@ namespace O3DE::ProjectManager { switch (platform) { - case O3DE::ProjectManager::GemInfo::Android: + case Android: return "Android"; - case O3DE::ProjectManager::GemInfo::iOS: + case iOS: return "iOS"; - case O3DE::ProjectManager::GemInfo::Linux: + case Linux: return "Linux"; - case O3DE::ProjectManager::GemInfo::macOS: + case macOS: return "macOS"; - case O3DE::ProjectManager::GemInfo::Windows: + case Windows: return "Windows"; default: return ""; } } + QString GemInfo::GetTypeString(Type type) + { + switch (type) + { + case Asset: + return "Asset"; + case Code: + return "Code"; + case Tool: + return "Tool"; + default: + return ""; + } + } + bool GemInfo::IsPlatformSupported(Platform platform) const { return (m_platforms & platform); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 7ee619702f..28b2fab451 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -36,6 +36,16 @@ namespace O3DE::ProjectManager Q_DECLARE_FLAGS(Platforms, Platform) static QString GetPlatformString(Platform platform); + enum Type + { + Asset = 1 << 0, + Code = 1 << 1, + Tool = 1 << 2, + NumTypes = 3 + }; + Q_DECLARE_FLAGS(Types, Type) + static QString GetTypeString(Type type); + GemInfo() = default; GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded); bool IsPlatformSupported(Platform platform) const; @@ -50,6 +60,7 @@ namespace O3DE::ProjectManager bool m_isAdded = false; //! Is the gem currently added and enabled in the project? QString m_summary; Platforms m_platforms; + Types m_types; //! Asset and/or Code and/or Tool QStringList m_features; QString m_directoryLink; QString m_documentationLink; @@ -62,3 +73,4 @@ namespace O3DE::ProjectManager } // namespace O3DE::ProjectManager Q_DECLARE_OPERATORS_FOR_FLAGS(O3DE::ProjectManager::GemInfo::Platforms) +Q_DECLARE_OPERATORS_FOR_FLAGS(O3DE::ProjectManager::GemInfo::Types) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp index 1112c656f3..addf59783d 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.cpp @@ -10,7 +10,8 @@ * */ -#include "GemModel.h" +#include +#include namespace O3DE::ProjectManager { @@ -32,8 +33,11 @@ namespace O3DE::ProjectManager item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setData(gemInfo.m_name, RoleName); + const QString uuidString = gemInfo.m_uuid.ToString().c_str(); + item->setData(uuidString, RoleUuid); item->setData(gemInfo.m_creator, RoleCreator); - item->setData(static_cast(gemInfo.m_platforms), RolePlatforms); + item->setData(aznumeric_cast(gemInfo.m_platforms), RolePlatforms); + item->setData(aznumeric_cast(gemInfo.m_types), RoleTypes); item->setData(gemInfo.m_summary, RoleSummary); item->setData(gemInfo.m_isAdded, RoleIsAdded); @@ -48,6 +52,8 @@ namespace O3DE::ProjectManager item->setData(gemInfo.m_features, RoleFeatures); appendRow(item); + + m_uuidToNameMap[uuidString] = gemInfo.m_displayName; } void GemModel::Clear() @@ -65,11 +71,21 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleCreator).toString(); } + QString GemModel::GetUuidString(const QModelIndex& modelIndex) + { + return modelIndex.data(RoleUuid).toString(); + } + GemInfo::Platforms GemModel::GetPlatforms(const QModelIndex& modelIndex) { return static_cast(modelIndex.data(RolePlatforms).toInt()); } + GemInfo::Types GemModel::GetTypes(const QModelIndex& modelIndex) + { + return static_cast(modelIndex.data(RoleTypes).toInt()); + } + QString GemModel::GetSummary(const QModelIndex& modelIndex) { return modelIndex.data(RoleSummary).toString(); @@ -90,9 +106,35 @@ namespace O3DE::ProjectManager return modelIndex.data(RoleDocLink).toString(); } + AZ::Outcome GemModel::FindGemNameByUuidString(const QString& uuidString) const + { + const auto iterator = m_uuidToNameMap.find(uuidString); + if (iterator != m_uuidToNameMap.end()) + { + return AZ::Success(iterator.value()); + } + + return AZ::Failure(); + } + QStringList GemModel::GetDependingGems(const QModelIndex& modelIndex) { - return modelIndex.data(RoleDependingGems).toStringList(); + QStringList result = modelIndex.data(RoleDependingGems).toStringList(); + if (result.isEmpty()) + { + return {}; + } + + for (QString& dependingGemString : result) + { + AZ::Outcome gemNameOutcome = FindGemNameByUuidString(dependingGemString); + if (gemNameOutcome.IsSuccess()) + { + dependingGemString = gemNameOutcome.GetValue(); + } + } + + return result; } QStringList GemModel::GetConflictingGems(const QModelIndex& modelIndex) diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index fba65e7009..76211b1f22 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -13,7 +13,8 @@ #pragma once #if !defined(Q_MOC_RUN) -#include "GemInfo.h" +#include +#include #include #include #include @@ -33,14 +34,18 @@ namespace O3DE::ProjectManager void AddGem(const GemInfo& gemInfo); void Clear(); + AZ::Outcome FindGemNameByUuidString(const QString& uuidString) const; + QStringList GetDependingGems(const QModelIndex& modelIndex); + static QString GetName(const QModelIndex& modelIndex); static QString GetCreator(const QModelIndex& modelIndex); + static QString GetUuidString(const QModelIndex& modelIndex); static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex); + static GemInfo::Types GetTypes(const QModelIndex& modelIndex); static QString GetSummary(const QModelIndex& modelIndex); static bool IsAdded(const QModelIndex& modelIndex); static QString GetDirectoryLink(const QModelIndex& modelIndex); static QString GetDocLink(const QModelIndex& modelIndex); - static QStringList GetDependingGems(const QModelIndex& modelIndex); static QStringList GetConflictingGems(const QModelIndex& modelIndex); static QString GetVersion(const QModelIndex& modelIndex); static QString GetLastUpdated(const QModelIndex& modelIndex); @@ -51,6 +56,7 @@ namespace O3DE::ProjectManager enum UserRole { RoleName = Qt::UserRole, + RoleUuid, RoleCreator, RolePlatforms, RoleSummary, @@ -63,8 +69,10 @@ namespace O3DE::ProjectManager RoleLastUpdated, RoleBinarySize, RoleFeatures, + RoleTypes }; + QHash m_uuidToNameMap; QItemSelectionModel* m_selectionModel = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.cpp b/Code/Tools/ProjectManager/Source/LinkWidget.cpp index fddc4cd8c9..a6308f6c62 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.cpp +++ b/Code/Tools/ProjectManager/Source/LinkWidget.cpp @@ -27,7 +27,12 @@ namespace O3DE::ProjectManager void LinkLabel::mousePressEvent([[maybe_unused]] QMouseEvent* event) { - QDesktopServices::openUrl(m_url); + if (m_url.isValid()) + { + QDesktopServices::openUrl(m_url); + } + + emit clicked(); } void LinkLabel::enterEvent([[maybe_unused]] QEvent* event) diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.h b/Code/Tools/ProjectManager/Source/LinkWidget.h index 7055dce2af..b3a34cd63a 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.h +++ b/Code/Tools/ProjectManager/Source/LinkWidget.h @@ -26,10 +26,16 @@ namespace O3DE::ProjectManager class LinkLabel : public QLabel { + Q_OBJECT // AUTOMOC + public: - LinkLabel(const QString& text, const QUrl& url = {}, QWidget* parent = nullptr); + LinkLabel(const QString& text = {}, const QUrl& url = {}, QWidget* parent = nullptr); void SetUrl(const QUrl& url); + + signals: + void clicked(); + private: void mousePressEvent(QMouseEvent* event) override; void enterEvent(QEvent* event) override; diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index e925c81032..2c2c143845 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -426,7 +426,7 @@ namespace O3DE::ProjectManager { // required gemInfo.m_name = Py_To_String(data["Name"]); - gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"])); + gemInfo.m_uuid = AZ::Uuid(Py_To_String(data["Uuid"])); // optional gemInfo.m_displayName = Py_To_String_Optional(data, "DisplayName", gemInfo.m_name); @@ -437,7 +437,8 @@ namespace O3DE::ProjectManager { for (auto dependency : data["Dependencies"]) { - gemInfo.m_dependingGemUuids.push_back(Py_To_String(dependency["Uuid"])); + const AZ::Uuid uuid = Py_To_String(dependency["Uuid"]); + gemInfo.m_dependingGemUuids.push_back(uuid.ToString().c_str()); } } if (data.contains("Tags")) diff --git a/Code/Tools/ProjectManager/project_manager.qrc b/Code/Tools/ProjectManager/project_manager.qrc index 3c23bc24ff..f36633142f 100644 --- a/Code/Tools/ProjectManager/project_manager.qrc +++ b/Code/Tools/ProjectManager/project_manager.qrc @@ -9,6 +9,8 @@ Resources/iOS.svg Resources/Linux.svg Resources/macOS.svg + Resources/ArrowDownLine.svg + Resources/ArrowUpLine.svg Resources/Backgrounds/FirstTimeBackgroundImage.jpg From 451b850f2748c3b69d478418c359e17864785518 Mon Sep 17 00:00:00 2001 From: abrmich Date: Wed, 12 May 2021 21:17:32 -0700 Subject: [PATCH 43/48] 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 44/48] 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 c47c45724a7883a5c9ccd49492be3c1c1891cac9 Mon Sep 17 00:00:00 2001 From: AMZN-stankowi Date: Thu, 20 May 2021 11:28:34 -0700 Subject: [PATCH 45/48] LYN-3772 : For non-PBR materials, apply the diffuse color to the base color field on the Atom material, so a color value set in the DCC tool still comes through. (#733) --- .../Material/MaterialConverterSystemComponent.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialConverterSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialConverterSystemComponent.cpp index dbbf666967..8397dcf9b3 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Material/MaterialConverterSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Material/MaterialConverterSystemComponent.cpp @@ -77,12 +77,16 @@ namespace AZ } }; + // If PBR material properties aren't in use, fall back to legacy properties. Don't do that if some PBR material properties are set, though. + bool anyPBRInUse = false; + handleTexture("specularF0", SceneAPI::DataTypes::IMaterialData::TextureMapType::Specular); handleTexture("normal", SceneAPI::DataTypes::IMaterialData::TextureMapType::Normal); AZStd::optional useColorMap = materialData.GetUseColorMap(); // If the useColorMap property exists, this is a PBR material and the color should be set to baseColor. if (useColorMap.has_value()) { + anyPBRInUse = true; handleTexture("baseColor", SceneAPI::DataTypes::IMaterialData::TextureMapType::BaseColor); } else @@ -97,17 +101,19 @@ namespace AZ AZStd::optional baseColor = materialData.GetBaseColor(); if (baseColor.has_value()) { + anyPBRInUse = true; sourceData.m_properties["baseColor"]["color"].m_value = toColor(baseColor.value()); } sourceData.m_properties["opacity"]["factor"].m_value = materialData.GetOpacity(); - auto applyOptionalPropertiesFunc = [&sourceData](const auto& propertyGroup, const auto& propertyName, const auto& propertyOptional) + auto applyOptionalPropertiesFunc = [&sourceData, &anyPBRInUse](const auto& propertyGroup, const auto& propertyName, const auto& propertyOptional) { // Only set PBR settings if they were specifically set in the scene's data. // Otherwise, leave them unset so the data driven default properties are used. if (propertyOptional.has_value()) { + anyPBRInUse = true; sourceData.m_properties[propertyGroup][propertyName].m_value = propertyOptional.value(); } }; @@ -127,6 +133,13 @@ namespace AZ handleTexture("ambientOcclusion", SceneAPI::DataTypes::IMaterialData::TextureMapType::AmbientOcclusion); applyOptionalPropertiesFunc("ambientOcclusion", "useTexture", materialData.GetUseAOMap()); + + if (!anyPBRInUse) + { + // If it doesn't have the useColorMap property, then it's a non-PBR material and the baseColor + // texture needs to be set to the diffuse color. + sourceData.m_properties["baseColor"]["color"].m_value = toColor(materialData.GetDiffuseColor()); + } return true; } From cb87b7cd1fa1031b66d4f5e70973bfa997a73699 Mon Sep 17 00:00:00 2001 From: pereslav Date: Thu, 20 May 2021 19:43:33 +0100 Subject: [PATCH 46/48] Moved early return case in prefab processing --- .../Code/Source/Pipeline/NetworkPrefabProcessor.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index fafaa30c4f..31884d4c94 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -122,6 +122,12 @@ namespace Multiplayer AZStd::vector> netEntities; GatherNetEntities(sourceInstance.get(), netEntities); + if (netEntities.empty()) + { + // No networked entities in the prefab, no need to do anything in this processor. + return; + } + // Instance container for net entities AZStd::unique_ptr networkInstance(aznew Instance()); @@ -129,12 +135,6 @@ namespace Multiplayer AZ::Data::Asset networkSpawnableAsset; networkSpawnableAsset.Create(networkSpawnable->GetId()); networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); - - if (netEntities.empty()) - { - // No networked entities in the prefab, no need to do anything in this processor. - return; - } // Each spawnable has a root meta-data entity at position 0, so starting net indices from 1 size_t netEntitiesIndexCounter = 1; 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 47/48] 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 | 103 +++++++----------- 1 file changed, 42 insertions(+), 61 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; + + for (AZ::EntityId entityId : selectedEntities) { - bool layerInSelection = false; - - for (AZ::EntityId entityId : selectedEntities) - { - if (!layerInSelection) - { - AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult( - layerInSelection, entityId, - &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer); - - 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.")); + AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult( + layerInSelection, entityId, + &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer); - QObject::connect(createAction, &QAction::triggered, createAction, [this, selectedEntities] { - ContextMenu_CreatePrefab(selectedEntities); - }); + 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.")); + + 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(); }); - } - - menu->addSeparator(); } + // 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(); }); + } + + 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 48/48] [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)