diff --git a/Assets/Engine/Shaders/DistanceClouds.ext b/Assets/Engine/Shaders/DistanceClouds.ext deleted file mode 100644 index 322acef5f3..0000000000 --- a/Assets/Engine/Shaders/DistanceClouds.ext +++ /dev/null @@ -1,51 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Particles shader extension used by the editor -// for automatic shader generation (based on "Particles" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %DIFFUSE - Mask = 0x1 - Hidden -} - -Property -{ - Name = %SIMPLE - Mask = 0x2 - Property (Simple distance clouds) - Description (Use distance clouds with no volumetric shading computations) -} - -Property -{ - Name = %ADVANCED - Mask = 0x4 - Property (Advanced distance clouds) - Description (Use distance clouds with more accurate shading computations) -} - -Property -{ - Name = %DEPTH_FADE - Mask = 0x8 - Property (Depth Fade) - Description (Fades the output based on closeness of objects behind it) -} diff --git a/Assets/Engine/Shaders/Eye.ext b/Assets/Engine/Shaders/Eye.ext deleted file mode 100644 index 28df961f79..0000000000 --- a/Assets/Engine/Shaders/Eye.ext +++ /dev/null @@ -1,74 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Eye shader extension used by the editor -// for automatic shader generation (based on "Eye" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %ENVIRONMENT_MAP - Mask = 0x2 - Property (Environment map) - Description (Use environment map as separate texture) - DependencyReset = $TEX_EnvCM -} - -Property -{ - Name = %EYE_AO_OVERLAY - Mask = 0x4 - Property (Ambient occlusion overlay) - Description (Use for ambient occlusion overlay rendering) -} - -Property -{ - Name = %EYE_SPECULAR_OVERLAY - Mask = 0x8 - Property (Specular overlay) - Description (Use for specular overlay rendering) -} - -Property -{ - Name = %VERTCOLORS - Mask = 0x400000 - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %TEMP_EYES - Mask = 0x80000000 - DependencySet = $UserEnabled - Hidden -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/Fur.ext b/Assets/Engine/Shaders/Fur.ext deleted file mode 100644 index 067c85d274..0000000000 --- a/Assets/Engine/Shaders/Fur.ext +++ /dev/null @@ -1,126 +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. -// - -// Description: Fur shader extension used by the editor -// for automatic shader generation (based on "Fur" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x2 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %CUSTOM_MODIFICATOR - Mask = 0x4 - Property (Call CustomModificator function in vertex shader) - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %FUR_VERT_COLORS - Mask = 0x8 - Property (Fur Color Data) - Description (Vertex color channel contains fur combing and scaling info) -} - -Property -{ - Name = %FUR_WIND_BENDING - Mask = 0x10 - Property (Wind bending) - Description (Enable wind bending for fur) -} - -Property -{ - Name = %FUR_BLENDLAYER - Mask = 0x20 - Property (Fur Blendlayer) - Description (Diffuse layer blended into fur as it grows from base to tip) -} - -Property -{ - Name = %FUR_BLENDCOLOR - Mask = 0x40 - Property (Fur Blend color) - Description (Specified color blended into fur diffuse as it grows from base to tip) -} - -Property -{ - Name = %FUR_LENGTH_SCALED - Mask = 0x80 - Property (Scale fur length) - Description (Fur length scales with object scale) -} - -Property -{ - Name = %MODEL_SPACE_Z_UP - Mask = 0x100 - Property (Model Space Z Up) - Description (Check if model was generated with Z up, if unchecked, assumes Y up) -} - -Property -{ - Name = %DEPTH_FIXUP - Mask = 0x200 - Property (Depth Fixup) - Description (Write depth for depth of field and postprocessing) - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %ZPASS_CUSTOM_DIFFUSE - Mask = 0x400 - Property (Calls GetZPassDiffuse function in z pass pixel shader for custom diffuse handling) - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %FUR_MULTI_LAYER_ALPHA_BLEND - Mask = 0x800 - Property(Enable OIT) - Description(Use OIT for accurate alpha blending - performance penalty expected) -} diff --git a/Assets/Engine/Shaders/GeometryBeam.ext b/Assets/Engine/Shaders/GeometryBeam.ext deleted file mode 100644 index 25e395f42d..0000000000 --- a/Assets/Engine/Shaders/GeometryBeam.ext +++ /dev/null @@ -1,43 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// - - -Property -{ - Name = %NOISE - Mask = 0x1 - Property (Dust & Turbulence) - Description (Add a dust overlay [spec map for dust, bump map for turbulence] ) -} - - -Property -{ - Name = %RECEIVE_SHADOWS - Mask = 0x2 - Property (Receive Shadows) - Description (Enable shadow receiving) -} - - -Property -{ - Name = %UV_VIGNETTING - Mask = 0x4 - Property (UV Vignetting) - Description (Enabling this will cause contents to fade out at UV boundaries) -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/Glass.ext b/Assets/Engine/Shaders/Glass.ext deleted file mode 100644 index 1070d4e5c9..0000000000 --- a/Assets/Engine/Shaders/Glass.ext +++ /dev/null @@ -1,111 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Glass shader extension used by the editor -// for automatic shader generation (based on "Glass" shader template) -// -//////////////////////////////////////////////////////////////////////////// - - -Version (2.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %DIRT_MAP - Mask = 0x100000 - Property (Use Diffuse map) - Description (Use Diffuse map for dirt, etc. Requires Alpha channel) -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x200000 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %ENVIRONMENT_MAP - Mask = 0x10 - Property (Environment map) - Description (Use environment map as separate texture) - DependencyReset = $TEX_EnvCM -} - - -Property -{ - Name = %TINT_MAP - Mask = 0x200 - Property (Tint map - Tint/Gloss/Spec) - Description (Use RGB Spec Map to control Tinting in Red channel / Cloudiness in Green channel / Specular in Blue channel) - -} - -Property -{ - Name = %TINT_COLOR_MAP - Mask = 0x400 - Property (Use Tint Color Map) - Description (Use Tint Color Map for multi-colored glass, goes in the custom Tint Color Map slot) - DependencyReset = $TEX_Custom - DependencySet = $TEX_Custom -} - -Property -{ - Name = %BLUR_REFRACTION - Mask = 0x2000 - Property (Blur refraction - PC Only) - Description (Blur objects seen through the glass) -} - -Property -{ - Name = %DEPTH_FOG - Mask = 0x4000 - Property (Depth Fog) - Description (Enables depth fog behind glass surface) -} - -Property -{ - Name = %UNLIT - Mask = 0x8000 - Property (Disable Lights) - Description (Disables the reflection of lights) -} - -Property -{ - Name = %DEPTH_FIXUP - Mask = 0x4000000 - Property (Depth Fixup) - Description (Write depth for depth of field and postprocessing) -} - -Property -{ - Name = %SAA_FILTERING - Mask = 0x80000000 - Property (Specular Antialiasing) - Description (Perform specular Antialiasing) -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/AuxGeom.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/AuxGeom.cfx deleted file mode 100644 index ac64ed293a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/AuxGeom.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d924c59739f63724999b1bf6cd326d10024b031bdfbf02fe8e09cb0b783dfe67 -size 2891 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Clouds.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Clouds.cfx deleted file mode 100644 index cf8ee86492..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Clouds.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:045f6506425364221f9dbf0b359e38877f276300770ef7a5ab694f1055eb0d02 -size 1569 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Common.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/Common.cfi deleted file mode 100644 index 644826685e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Common.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f25999b453c3e91c8237d28ccc1ef3baefaa2a7ec8f9aad65adb95042e25e435 -size 56582 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Common.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Common.cfx deleted file mode 100644 index d347b5c967..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Common.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:396a70d86a4678b16fa110b3eceacbd5b515aa85090d16bdb6a8562d7c94e4a8 -size 17205 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonDebugPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonDebugPass.cfi deleted file mode 100644 index 95abf99bbf..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonDebugPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b05841cd01ec853455ad792e80302cf5ffbbd104c5ecf46699f5ecd4b0624c09 -size 983 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonMotionBlurPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonMotionBlurPass.cfi deleted file mode 100644 index 54f160e01b..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonMotionBlurPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:725006c2f2844e49b691506cbc3d015466cdfbad19209a63964acc264b78c782 -size 4096 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonMotionBlurPassTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonMotionBlurPassTess.cfi deleted file mode 100644 index 4d74269d97..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonMotionBlurPassTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5886e0be4d4047a9b41431c141738cb0a13a3b3a4033cc262f173b5e9e195707 -size 4734 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonSVO.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonSVO.cfi deleted file mode 100644 index 023437ec3c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonSVO.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57682432ba4de87ddc5ba542b6630ad512f75dbaca2a0c1402fb2a59ed35804c -size 111615 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonShadowGenPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonShadowGenPass.cfi deleted file mode 100644 index 7907e3913d..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonShadowGenPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01bbe928766a8c75bc3d990747f91e5becfbed6a59d58b71d2a2922f6d14bc1f -size 6970 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonShadowGenPassTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonShadowGenPassTess.cfi deleted file mode 100644 index ccb63377d1..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonShadowGenPassTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c4c004afe3782ae58fa4ac90dd7f16e026d0c38e4bf66ed475beaa1ce3fcf98 -size 3363 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonTessellation.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonTessellation.cfi deleted file mode 100644 index 33aa6974cf..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonTessellation.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86f594a6f8fb6e1328b95e6b03a04109ea3e9e8b8642d30c674281ea916dbf41 -size 19522 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonViewsPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonViewsPass.cfi deleted file mode 100644 index 71876eddd2..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonViewsPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:04850ac23185147fd8e97303efad70cbb876c6589c71cb413b0429d267705cd3 -size 8020 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonViewsPassTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonViewsPassTess.cfi deleted file mode 100644 index 96cbad4a40..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonViewsPassTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c660b79e7532b5a9513341d3f37215a742d3ee99532ba0003e2c13381d55bc6 -size 4238 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPass.cfi deleted file mode 100644 index 8a9f7ef4f1..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8fe036b79e16737499920500c87997567d8ee32534748df4ea617c4118237cb5 -size 32052 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPassTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPassTess.cfi deleted file mode 100644 index adc03e7945..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPassTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a30f1c7b94eb1bcb0261e518062da6eb69987698d65e6dd786bfe29be32d749e -size 4995 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPrePass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPrePass.cfi deleted file mode 100644 index 43481f75fe..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/CommonZPrePass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c4ac5b9fa8a288f8fdfc4110599b25742b19ddd3a251d638e5e1da9284310d6 -size 2814 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DXTCompress.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DXTCompress.cfx deleted file mode 100644 index 26545c6531..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DXTCompress.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a2b214f2873a3a99bcaccc120d6a397c708a4b3309c9b953860f8f9cf93461c8 -size 8591 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Debug.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Debug.cfx deleted file mode 100644 index 71ab497cc5..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Debug.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f26436234a598c2aa906653e711eb0e8ae14f99b65fc25eea0d238d9f56d914a -size 4345 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DebugLight.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DebugLight.cfx deleted file mode 100644 index cc9a79a324..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DebugLight.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bb0a8212530051bd6e4ac5646f6ab2f0a59f43a349ce8e03e544f6cf6d87ba4b -size 1929 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredCaustics.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DeferredCaustics.cfx deleted file mode 100644 index f477baa00c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredCaustics.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:24e1f7c4242c708a638a1069908f258fa26aa77152330a2c8da72260dd192209 -size 14703 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredRain.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DeferredRain.cfx deleted file mode 100644 index 2e771c73bf..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredRain.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:77910f05ea15757e24201187a68a9a0db924f7951dadd4b7c7330f0845bd5944 -size 14043 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredShading.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DeferredShading.cfx deleted file mode 100644 index 9252e1b06e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredShading.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ab94af4462722c1a111f4199714fabd0addf095aba82840f92e6ef8b056d78c -size 94446 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredShadows.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/DeferredShadows.cfi deleted file mode 100644 index 63c8b33c21..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredShadows.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0641726ad6140704434896912f0e57e29e6f5e28ac2653bc97bbe7e2d444fbc2 -size 2828 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredSnow.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DeferredSnow.cfx deleted file mode 100644 index 0789202955..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DeferredSnow.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ea4c40161f4f628fbf9cd1a3b7df445615dfa8ca15761a4fec15d0c2276b369 -size 22312 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DepthOfField.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DepthOfField.cfx deleted file mode 100644 index 9a431fb20a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DepthOfField.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b67ece889d68d8ed6f891ca5cfb1f951a777616ea928b0e6f91bd541b35c62ff -size 13136 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/DistanceClouds.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/DistanceClouds.cfx deleted file mode 100644 index 7448f0304a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/DistanceClouds.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a06a4a972542f51bea726578b26ef0fc711c4b6f4a77a31a383690717c5ccba2 -size 10622 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Eye.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Eye.cfx deleted file mode 100644 index a3cb1227ea..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Eye.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fed5c2b72b2b4769954f2fad9573c6e35581c556e3a96685d449eea5576cf433 -size 19244 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FXConstantDefs.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/FXConstantDefs.cfi deleted file mode 100644 index 7b0d7202e7..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FXConstantDefs.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1b6aa7281d2174f24b510a7ac820b4ab308a215d6b3eae4a039bc01e34b131ec -size 9922 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FXSamplerDefs.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/FXSamplerDefs.cfi deleted file mode 100644 index 7f5b097148..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FXSamplerDefs.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:750ac5a4ef25f1379121c7346dad0230d389c7c889ffdccbe6281a511b021cea -size 4044 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FXStreamDefs.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/FXStreamDefs.cfi deleted file mode 100644 index 4d05f905c1..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FXStreamDefs.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:073303aa45adc5ca237f33f29fb4de52f8b3c935912298cdec611128c510399c -size 9279 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FallBack.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/FallBack.cfx deleted file mode 100644 index 321440401b..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FallBack.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:052a458b7bdef6d7e1fa1a26d4cb66f6e24f91b9e42451a8e48045d3779aef1c -size 2383 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FixedPipelineEmu.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/FixedPipelineEmu.cfx deleted file mode 100644 index 8b92aa7399..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FixedPipelineEmu.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ab757a0df8246a0e9d20839a89219a6c616d6df054d72b21b79e459b8503ade8 -size 10188 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FogVolume.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/FogVolume.cfx deleted file mode 100644 index 929d89ab5b..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FogVolume.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a37c2ab18b0ad1da90005c0ff9fea89d7c78ade9fd51b28b665cf42b814ecd8d -size 23690 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Fur.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Fur.cfx deleted file mode 100644 index 7d7405cc5e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Fur.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2f429a8537df95455e32da7fd480e79ef46751d6bc880c34b746c21515aa012a -size 31609 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FurFinPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/FurFinPass.cfi deleted file mode 100644 index 84149cb0f6..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FurFinPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e3f25ea475310f699442a160ffcc9f58c40e54b6570210dafbe41746a010b821 -size 15002 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FurObliteratePass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/FurObliteratePass.cfi deleted file mode 100644 index 3ffe779a54..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FurObliteratePass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f6246f93aa73fdab460c1c82a33f2588a0ae83a162acdcdb12cec86ddac332b6 -size 5142 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/FurZPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/FurZPass.cfi deleted file mode 100644 index 5735035e49..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/FurZPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb0d83cc48ebee33b3d3db2b1a5c27636d35dfb7ce720e5391a28ff70ca0d729 -size 9023 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticle.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticle.cfi deleted file mode 100644 index 6cd187c993..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticle.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b690c770f90e33dc78604b5beca3453aba4fbb4db8014b730680c33b5cef5120 -size 2299 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBegin.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBegin.cfx deleted file mode 100644 index 3bc0dd3e01..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBegin.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64c184691014423f2f6c518d34ff9657acc1dc8867e0e43d20b5526491d35d5b -size 462 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSort.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSort.cfx deleted file mode 100644 index 14c0b06c58..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSort.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a71929dda4a7b4516754286da5494546a0ea2075b9fde5cefef68f0a99d37381 -size 1520 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSortGlobal2048.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSortGlobal2048.cfx deleted file mode 100644 index b8ff2e6afc..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSortGlobal2048.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a50144544a4c292d172d4bb9586a6016de4cc5b0a39958abfb00bc720e8c23e9 -size 1989 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSortLocal.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSortLocal.cfx deleted file mode 100644 index 6ee0b7b645..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleBitonicSortLocal.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:25d51818120964a1576f35727c0f13bc10a01727ab793f5fdf79cb9c1f7bf563 -size 2055 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleCurves.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleCurves.cfi deleted file mode 100644 index a565d62cc4..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleCurves.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4308e81d4ee4c65480a1cf110f3f602d347f2899b4cf299f334c2659904329dd -size 553 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleEmit.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleEmit.cfx deleted file mode 100644 index 8d417c29ac..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleEmit.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bfe3a765c1057d5dfae514ba17d0ce99d6598693035df2037314da5f38e4c305 -size 19142 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleGatherSortDistance.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleGatherSortDistance.cfx deleted file mode 100644 index 0a223398ff..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleGatherSortDistance.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f94a71dd8cbe95080f2de1bfa8cbd9443bea18106cc7bcae2c5c7225d113f2b4 -size 644 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleHelpers.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleHelpers.cfi deleted file mode 100644 index a3b10d582b..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleHelpers.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cf39d59eefe2c6641437e9d535528edb29e1e0dad6d1b84b20662c7075928fe8 -size 6969 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleOddEvenSort.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleOddEvenSort.cfx deleted file mode 100644 index be1e36e5db..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleOddEvenSort.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5cbcdd1c83247233dce9fb5c32e9d4530b23b825b284591a921a0b7e81d230ea -size 1817 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleRenderNoGS.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleRenderNoGS.cfx deleted file mode 100644 index 2d8fc2a1d0..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleRenderNoGS.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cae99346703f5ebc9bb59edb61c17b86184ae7659af78395ec4cba11cb6da471 -size 37741 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleUpdate.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleUpdate.cfx deleted file mode 100644 index 7524f13c6f..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GPUParticleUpdate.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a3a7bb8ad18d99c7490d15ce24b3c4867d9668b1539014a31b3ba5e80ebb00e -size 25753 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/GeometryBeam.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/GeometryBeam.cfx deleted file mode 100644 index 364f613063..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/GeometryBeam.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:45e7f387bb7cc9d7a489916b27ea6bff816510664099008ebbeaa0d10a26faa7 -size 12612 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Glass.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Glass.cfx deleted file mode 100644 index 38226fedec..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Glass.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:49aa341aa2563ced85aa4f62f74865c558238c91ba9d8d62ad21392088755820 -size 26913 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HDRDolbyMetadataPass0.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/HDRDolbyMetadataPass0.cfx deleted file mode 100644 index b2df095b8a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HDRDolbyMetadataPass0.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c7f9297a483fd7d7b91a3f06f1a3eb83e77192b967691f4e2119d41b7a7afdd2 -size 2707 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HDRDolbyMetadataPass1.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/HDRDolbyMetadataPass1.cfx deleted file mode 100644 index 25c959c8f6..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HDRDolbyMetadataPass1.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:52d68438b25fbd2d88a7247295a021680a9b95a6f3ed07c3ae066cb3d44b25ed -size 2530 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HDRPostProcess.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/HDRPostProcess.cfx deleted file mode 100644 index 76e692fa63..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HDRPostProcess.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5cf8d27075f4aa76e5814d1c4a9718d7106df50229f33e1d8d7d7a4532d1e5f3 -size 40838 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HDRPostProcessDolby.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/HDRPostProcessDolby.cfi deleted file mode 100644 index b8c0c3043c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HDRPostProcessDolby.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2e62a2aa829af58b2ba19a9920bd87d64a27854d24de5a51842740c7ec1e9ae8 -size 33567 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Hair.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Hair.cfx deleted file mode 100644 index 4788d26a0e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Hair.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eb56b0070f3811f1b8780e70d4b9410a08f3aac9f8fa3e947194a4d902c9d8ef -size 33265 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Helper.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Helper.cfx deleted file mode 100644 index 5c19cc54e0..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Helper.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9764bb3ab6d2895a355466f3c53f735c3500006420bac211bc9a0b7c29a605c6 -size 2168 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Hud3D.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Hud3D.cfx deleted file mode 100644 index c460d0468c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Hud3D.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c63de566d2a5a69781a4c1e16958d64d187e057f3447afb4ec9aa47455ef7a5c -size 9617 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkin.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkin.cfx deleted file mode 100644 index ff2a2b894a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkin.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b81300b59d42b38b7669016cc8920db9adbf07b612f12d520d9480a6de59aa73 -size 18648 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkinTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkinTess.cfi deleted file mode 100644 index 8f7ce5f8c9..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkinTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a79996ad61013920dcccd254f23e05fb4089879d477d8f3ce00ff98ee04d6ec1 -size 7783 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkinValidations.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkinValidations.cfi deleted file mode 100644 index 9743568b4d..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/HumanSkinValidations.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:548f2cc401046358ef4f4edeb37532f06437b99b2fe5bff70100e66a6ecb281a -size 986 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Illum.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Illum.cfx deleted file mode 100644 index 6de2f49547..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Illum.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c110e0891fbe46a10f2fbcf4ffb8f82c0711ffaa943fac5ca92c96f7e63d051 -size 28796 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/IllumTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/IllumTess.cfi deleted file mode 100644 index ef66d513a9..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/IllumTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9a52e2256a43707dfab74c680e96249d27e7eb38a2984c9d4dcff83ffe7dd15b -size 5194 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/IllumValidations.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/IllumValidations.cfi deleted file mode 100644 index 7af7a4748c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/IllumValidations.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4155e44e6687796095b5e2401c93a09e527c61a48830e8a35d4ab16d849d8159 -size 2031 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/LensOptics.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/LensOptics.cfx deleted file mode 100644 index ab20cc3bc7..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/LensOptics.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7acdc1aaa28aea83d9af53547f65c5686b2fc43be988068d8f4f88c87c4359a7 -size 16847 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Light.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Light.cfx deleted file mode 100644 index ebbe8183bf..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Light.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4436686d0f3e735973df2dd56523269683c66b3ae01da67fa7f41e5f9e1b1234 -size 12760 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/LightBeam.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/LightBeam.cfx deleted file mode 100644 index e19414deef..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/LightBeam.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:13c69186729534dcd2676d1740a530fb5d5a3307d7b713f5c08704220186ba6d -size 14169 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/LightVolumes.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/LightVolumes.cfi deleted file mode 100644 index 4ab290bb32..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/LightVolumes.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f79e6c1e182e86afd295758f0afefc901407e69eb3f93422d8a3089451e49c7d -size 5526 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/MeshBaker.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/MeshBaker.cfi deleted file mode 100644 index 5f019eca9a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/MeshBaker.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ca1d710fd2b0937a04ded739ab4dc35ff48e3ff0c07d6a5d4df95f32a1947a4 -size 5032 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/MeshBakerDilate.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/MeshBakerDilate.cfx deleted file mode 100644 index 59ff9363a5..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/MeshBakerDilate.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a7db4ca9b7d87e4c3410df26f3045a18fb96a2535b88ab8afe00dcf1d621943 -size 4173 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ModificatorTC.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/ModificatorTC.cfi deleted file mode 100644 index 6d0fd6bb42..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ModificatorTC.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0eb9c63a60a3d2afb78d6e4077ac81322dbd1a0537f5551c638d57c353381e15 -size 5256 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ModificatorVT.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/ModificatorVT.cfi deleted file mode 100644 index fff9af0b52..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ModificatorVT.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dd0aecf5b1620c49a9b08c35865ea8de5487098f1eed8374fa4fc85e4a87e886 -size 52858 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Monitor.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Monitor.cfx deleted file mode 100644 index 840da6f035..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Monitor.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:758aa594628cd94f10d379f08a9a0a63478cc69eb9a093404d57c46e6830150a -size 19450 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/MotionBlur.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/MotionBlur.cfx deleted file mode 100644 index b6ba57f9f3..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/MotionBlur.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b4f6c44e52297c1dfe02040a7e63739aeed73f2c2c4930053f5a83c3c73f816 -size 8716 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/MultiLayerAlphaBlend.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/MultiLayerAlphaBlend.cfi deleted file mode 100644 index 751d38bde8..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/MultiLayerAlphaBlend.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:01f08e32bfb1f8690759da687114e66bf6652b6d17b208ed1b316315f0a66a1b -size 6435 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/NoDraw.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/NoDraw.cfx deleted file mode 100644 index a4a038ac28..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/NoDraw.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d183abe91463d2ab90f56c055a44aa8c3cad1d731f51c9f56bd6f9df3d7eba1 -size 180 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/OcclusionTest.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/OcclusionTest.cfx deleted file mode 100644 index 7f1b7bf522..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/OcclusionTest.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ceef3fd50e977ba168e2f80803bc0a09542aa3c55721e0e6e27bbab45454607 -size 1545 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ParticleImposter.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ParticleImposter.cfx deleted file mode 100644 index 0506b14d5e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ParticleImposter.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b67b9997024270cfdede2d057220078029db61ab5dff6d522a39c99d08420245 -size 8657 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ParticleVT.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/ParticleVT.cfi deleted file mode 100644 index bbe4d0c261..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ParticleVT.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:670e9dd98f131fa0375ddcc4df8fe850eeabb43f9771d302b5c8e2fd7022d78d -size 8534 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Particles.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/Particles.cfi deleted file mode 100644 index 6f38a8eae7..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Particles.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:98f62a7869360e63083eea9a784652ad0fca13a9cd741930940be75c10ca83e7 -size 40918 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Particles.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Particles.cfx deleted file mode 100644 index 728135d83c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Particles.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:55bcc8a634262b6001193fabf318cdf2960904dd47e915fa17de3a60c620639d -size 4613 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesCustomPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesCustomPass.cfi deleted file mode 100644 index 7f90f9f1a5..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesCustomPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:af77abd5a2d462e4b533e6e5b489bc8e39909527c64a9cd599bbf5312b3aa17a -size 6052 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesNoMat.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesNoMat.cfx deleted file mode 100644 index 3728b1f379..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesNoMat.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db6f21607b4ce4e0d0a48ffa0a7db81f29ac0546c76846dd39f1fc584f7d23e5 -size 1517 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesNoMatMirror.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesNoMatMirror.cfx deleted file mode 100644 index 955c99c3e9..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesNoMatMirror.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f8178b44fc27aacde3600b86bc06038aefbac6ca4fbf4fbe29f32ca6e8e0584 -size 1603 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesShadowPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesShadowPass.cfi deleted file mode 100644 index 461f4d40ac..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ParticlesShadowPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c7c5e176c6f0a5f689bc433dd23d5bb998f8f10d4185c30cb9788022ebb38a0 -size 6464 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/PostAA.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/PostAA.cfx deleted file mode 100644 index d8ae4939e4..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/PostAA.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a10320d3b31738cc0154de2c973fcaf912885209a7e729e3f6bd372f7633354 -size 67647 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/PostEffects.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/PostEffects.cfx deleted file mode 100644 index 95269fe06c..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/PostEffects.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ec164683d3fe45a512c0e9a2314c5b450602ee8c8a8281d99d43c1d76a761df3 -size 60596 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/PostEffectsGame.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/PostEffectsGame.cfx deleted file mode 100644 index efaa765189..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/PostEffectsGame.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6d66a0aed7f39e3848ee4763c408c390ec7021205cf2f0f07459b99d84f1d27 -size 80783 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/PostEffectsLib.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/PostEffectsLib.cfi deleted file mode 100644 index 90ce479d4a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/PostEffectsLib.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b73c3ae905c1833cade569500b647dc504e83b34f15a4584f72a372d254d884f -size 8502 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ReferenceImage.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ReferenceImage.cfx deleted file mode 100644 index 08b3c94d06..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ReferenceImage.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12d61af66003a6ab90e24574925fd94bc03622386da142fcaa655df21b0fd5bd -size 2033 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ReferenceImageHDR.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ReferenceImageHDR.cfx deleted file mode 100644 index aaa0a26a21..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ReferenceImageHDR.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1c4913b73fec9ebdcd5b8e7c86d495b7dc9a135e7f8a77ee90b9e62a78d3cc47 -size 1819 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Scopes.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Scopes.cfx deleted file mode 100644 index 7be1e74629..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Scopes.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4565c5415b0a68ced049335d803fcfdbf899db4ea914b8fbc5ba34620e4c4045 -size 10709 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ShadowBlur.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ShadowBlur.cfx deleted file mode 100644 index ad707f2932..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ShadowBlur.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a6eafe0f42be55811eda4ac6b164bd01e22b6cc2b77723a8693549b26939871 -size 10768 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ShadowCommon.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/ShadowCommon.cfi deleted file mode 100644 index fbaa4d7f65..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ShadowCommon.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ddac405a61300a0857cba4ea7dabcb6d5aef5253ada53d09ec4ab591ed3cfbce -size 25680 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/ShadowMaskGen.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/ShadowMaskGen.cfx deleted file mode 100644 index 36b7604c43..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/ShadowMaskGen.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2aec8020aa2454b8a0cd406198bd2c067f878cb697dad2e2059e88c7cb2e5e87 -size 32118 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Sketch.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Sketch.cfx deleted file mode 100644 index 60d4aa0f4a..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Sketch.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ba3cc8b5127ccb8a446e0ac85daf0b13c700092bcdcbdd92d5fbf45dfd430dc -size 11176 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/SketchTerrain.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/SketchTerrain.cfx deleted file mode 100644 index f8ff66d760..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/SketchTerrain.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b247b792e8e26abb50c2c01e9bc6c341216f6a84fc3fac67088294cf868428ab -size 2992 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Sky.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Sky.cfx deleted file mode 100644 index 06ec9fc6f1..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Sky.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7fec7735636fa7a4c2387e216918ec92414bc29ec6ecbd89605677214f829403 -size 4830 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/SkyHDR.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/SkyHDR.cfx deleted file mode 100644 index 48b47a9ae1..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/SkyHDR.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3195bdf90fa2a0b1539f0cac7471dc31ec7aa783d138f9c0455a32354ead3874 -size 8548 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/SoftOcclusionQuery.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/SoftOcclusionQuery.cfx deleted file mode 100644 index 425102e21d..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/SoftOcclusionQuery.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c82e35c6af179bb2405bbfe1d452ae2a8c5479934c0c9d8e8119d04db35d8cf5 -size 3546 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Stars.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Stars.cfx deleted file mode 100644 index 4fdbddb2c6..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Stars.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c5f191743c706e8531bada03abab8700c01cc9afbff0c9ea22c41d509b11ee76 -size 4075 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/StarterGame_GeometryBeamScaling.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/StarterGame_GeometryBeamScaling.cfx deleted file mode 100644 index a5a51340fc..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/StarterGame_GeometryBeamScaling.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:888486a516d3052e6b8b01cc0d36f01a68985ed482db683a25527c220290cc6c -size 13217 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Stereo.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Stereo.cfx deleted file mode 100644 index 8ef249a60f..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Stereo.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:761402eba9c7256a79fe78869b0287b95a8be8f810b48e41222049425b2eb535 -size 5422 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Sunshafts.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Sunshafts.cfx deleted file mode 100644 index 2274352ec3..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Sunshafts.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9a162b22e9ee5dc42559c8bcd4a6160c8b227bfeb6a5b367b62b8256109b99b5 -size 8884 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/TemplBeamProc.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/TemplBeamProc.cfx deleted file mode 100644 index 1a2de2a0dd..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/TemplBeamProc.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aed5eeb5e7467884319e268f985134f0d50482408cdbda6359873d211387737c -size 7182 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Terrain.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Terrain.cfx deleted file mode 100644 index 22abec6b24..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Terrain.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4d2246d81afaad43e267addffdc01ce201dc4032bf5e05131cfb9f56ebc5ed0 -size 19043 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/TerrainValidations.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/TerrainValidations.cfi deleted file mode 100644 index 1c8349f673..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/TerrainValidations.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3956d70a7eff854ab02d44f6c712cdb694045e87e9a0201ad8427436c693897a -size 707 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/TiledShading.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/TiledShading.cfi deleted file mode 100644 index ebc1521531..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/TiledShading.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:72448b07701ad745e35d98de2229f0675cbb74bd6d40095ec4625929da906c5f -size 50201 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Total_Illumination.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Total_Illumination.cfx deleted file mode 100644 index 9b2c8e38dc..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Total_Illumination.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c73e03d1e09efd2e8cdb9292debb5260a0690772c81a9dbb085502f822b7f91b -size 38227 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/UI.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/UI.cfx deleted file mode 100644 index e98a5a2fae..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/UI.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2b7a3ff846dde2ba481e46180e67e81528fa2a984ba6bb1a5c83304c301551d -size 7717 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Vegetation.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Vegetation.cfx deleted file mode 100644 index 0bca5a3e53..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Vegetation.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6c330b2ecb3d850e5eeb3673500cdebb95bee46b92eb93c3274291dbf96b2c98 -size 25807 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/VegetationTess.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/VegetationTess.cfi deleted file mode 100644 index 5abc5af104..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/VegetationTess.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:28aadc74c027782d5971e8145d3286f733fcbb571872447f638397df4f957765 -size 5746 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/VegetationValidations.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/VegetationValidations.cfi deleted file mode 100644 index 00d2e11b8f..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/VegetationValidations.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c05fb764a9fbc9cf7ea7b225680880c32847f4d355cab27f958564d5adcfc21b -size 1167 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Video.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Video.cfx deleted file mode 100644 index 966c5d92f6..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Video.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8c1c4e2eed305e2319127d12349754819350301b42d33cb109def7237c3591f1 -size 2123 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/VolumeLighting.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/VolumeLighting.cfi deleted file mode 100644 index 77cb40a98f..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/VolumeLighting.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:515e3218c9b23e8ff4d67a200ca11b3c833ad3999500f7f8be4ecdc5c01d5565 -size 82057 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/VolumeObject.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/VolumeObject.cfx deleted file mode 100644 index 2ca573741e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/VolumeObject.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a1fa34bd8cd7d8df9089739c7adf62b95c64f3d0993dcb2ee3892e05e118e83c -size 6700 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/VolumetricFog.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/VolumetricFog.cfi deleted file mode 100644 index 2556067f04..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/VolumetricFog.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3dd1721686abf6fe824df07a5e8ee1505b0fbcbaec6dc1a8ded98274d6ea4058 -size 25814 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Water.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Water.cfx deleted file mode 100644 index 175960ffc2..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Water.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d3ebab64f075124eb4cf426fee1c9ac446225ed647db3f10a28a28b1efaa5e63 -size 41727 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/WaterCausticsPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/WaterCausticsPass.cfi deleted file mode 100644 index 5aa4d40dff..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/WaterCausticsPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f73053b56ef452bf8ae72a4cc6fed59876562d9f4541172265ce6c8d5740711 -size 3340 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/WaterFogVolume.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/WaterFogVolume.cfx deleted file mode 100644 index eda54be33e..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/WaterFogVolume.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f9d26a36eb0fa05b58991aaaa6a073621309aa8a4a06a5bb4a6048b507a3fe1 -size 18306 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/WaterOceanBottom.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/WaterOceanBottom.cfx deleted file mode 100644 index 90cf67ba52..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/WaterOceanBottom.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5266c69503cb72e831e5c488332237fbfd343f96708ea3c75037780bd62fed60 -size 5280 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/WaterReflectionsPass.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/WaterReflectionsPass.cfi deleted file mode 100644 index a2ffd3d2a8..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/WaterReflectionsPass.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bb1ba5273a487655f813688f0a8500c662348639414ac53a0b0ee3d911010eab -size 10008 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/WaterVolume.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/WaterVolume.cfx deleted file mode 100644 index 7985e3dab0..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/WaterVolume.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:41c19dceb8daa6e9a9102b83c78208d6f0681066f5d6c2cf2e657a82e1ce695c -size 39737 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/Waterfall.cfx b/Assets/Engine/Shaders/HWScripts/CryFX/Waterfall.cfx deleted file mode 100644 index d09922f322..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/Waterfall.cfx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ebece6e3b0115174da434ed1b166b7209c012a6013e219e2421ffee5ebd6dda1 -size 11237 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/fragLib.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/fragLib.cfi deleted file mode 100644 index ffba873be1..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/fragLib.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3a340c8cbb76900b02bc171e680cfdc628900a5c38ecd3ce6a7b9eecbafa72ef -size 23148 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/shadeLib.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/shadeLib.cfi deleted file mode 100644 index b2633fd0da..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/shadeLib.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b33f5a65661f977c15047dea6b01809a1ebab5dbd6c7299a0e2888c2de8a911b -size 59794 diff --git a/Assets/Engine/Shaders/HWScripts/CryFX/vertexLib.cfi b/Assets/Engine/Shaders/HWScripts/CryFX/vertexLib.cfi deleted file mode 100644 index 66def251da..0000000000 --- a/Assets/Engine/Shaders/HWScripts/CryFX/vertexLib.cfi +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8b577a8c20b7cc94e1c97909487b3a0986b47882502e120fc518880883e04435 -size 15666 diff --git a/Assets/Engine/Shaders/Hair.ext b/Assets/Engine/Shaders/Hair.ext deleted file mode 100644 index 975e29323e..0000000000 --- a/Assets/Engine/Shaders/Hair.ext +++ /dev/null @@ -1,111 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Hair shader extension used by the editor -// for automatic shader generation (based on "Hair" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %VERTCOLORS - Mask = 0x10 - Property (Vertex Colors) - Description (Use vertex colors) -} - -Property -{ - Name = %HAIR_PASS - Mask = 0x20 - Property (Hair Pass) - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %ANISO_SPECULAR - Mask = 0x40 - Property (Anisotropic specular) - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %DIRECTION_MAP - Mask = 0x200 - Property (Direction map) - Description (Use direction map as separate texture) - DependencySet = $TEX_Detail - DependencyReset = $TEX_Detail - Hidden -} - -Property -{ - Name = %VIEW_ALIGNED_STRANDS - Mask = 0x800 - Property (View aligned strands) - Description (View aligned cards that get extruded from thin quads with texture u-coords 0 and 1) -} - -Property -{ - Name = %THIN_HAIR - Mask = 0x1000 - Property (Thin hair) - Description (Thin alpha-blended hair) -} - -Property -{ - Name = %HAIR_AMBIENT - Mask = 0x2000 - Property (Ambient cubemap) - Description (Use (nearest) cubemap specified in environment map slot for ambient lighting) - DependencyReset = $TEX_EnvCM -} - -Property -{ - Name = %ENFORCE_TILED_SHADING - Mask = 0x4000 - Property (Enforce tiled shading) - Description (Force hair to be fully affected by tiled shading. This can be expensive for dense hair meshes.) -} - -Property -{ - Name = %WIND_BENDING - Mask = 0x40000000 - Property (Wind bending) - Description (Gets affected by wind entities. Use extra shader parameters to tweak look.) -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/HumanSkin.ext b/Assets/Engine/Shaders/HumanSkin.ext deleted file mode 100644 index 92429316ea..0000000000 --- a/Assets/Engine/Shaders/HumanSkin.ext +++ /dev/null @@ -1,116 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Skin shader extension used by the editor -// for automatic shader generation (based on "Skin" shader template) -// -//////////////////////////////////////////////////////////////////////////// - - - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x2 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %WRINKLE_BLENDING - Mask = 0x200 - Property (Wrinkle blending) - Description (Use subsurface map alpha for wrinkle blending) - DependencyReset = $TEX_Custom - DependencyReset = $TEX_CustomSecondary -} - -Property -{ - Name = %TEMP_SKIN - Mask = 0x1000 - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %DECAL_MAP - Mask = 0x2000 - Property (Decal map) - Description (Use a decal map which is blended on top of the diffuse map) -} - -Property -{ - Name = %DETAIL_MAPPING - Mask = 0x20000 - Property (Detail normal-map) - Description (Tiled detail normal-map for pores and tiny details (_ddn)) -} - -Property -{ - Name = %SUBSURFACE_SCATTERING_MASK - Mask = 0x40000 - Property (Subsurface Scattering Mask) - Description (Use diffuse map alpha as subsurface scattering amount multiplier) -} - -#ifdef FEATURE_MESH_TESSELLATION -Property -{ - Name = %DISPLACEMENT_MAPPING - Mask = 0x10000000 - Property (Displacement mapping) - Description (Use displacement mapping (requires height map (_displ))) - //DependencySet = $TEX_Height - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %PHONG_TESSELLATION - Mask = 0x20000000 - Property (Phong tessellation) - Description (Use rough approximation of smooth surface subdivision) -} - -Property -{ - Name = %PN_TESSELLATION - Mask = 0x40000000 - Property (PN triangles tessellation) - Description (Use rough approximation of smooth surface subdivision) -} -#endif \ No newline at end of file diff --git a/Assets/Engine/Shaders/Illum.ext b/Assets/Engine/Shaders/Illum.ext deleted file mode 100644 index edce2900d2..0000000000 --- a/Assets/Engine/Shaders/Illum.ext +++ /dev/null @@ -1,243 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Illumination shader extension used by the editor -// for automatic shader generation (based on "Illumination" shader template) -// -//////////////////////////////////////////////////////////////////////////// - - - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x10 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %DETAIL_MAPPING - Mask = 0x4000 - Property (Detail mapping) - Description (Enables Detail Map texture to increase surface detail. Requires Detail map before enabling.) - DependencyReset = $TEX_Detail -} - -Property -{ - Name = %DETAIL_MAPPING_UV_SET_2 - Mask = 0x8000 - Property (Use uv set 2 for detail map) - Description (Detail map will be applied to second UV set on mesh) -} - -Property -{ - Name = %OFFSET_BUMP_MAPPING - Mask = 0x20000 - Property (Offset bump mapping) - Description (Simulates surface bump detail. Used in place of POM for lower spec configs. Requires height and normal maps before enabling.) - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %FX_DISSOLVE - Mask = 0x20 - Property (Dissolve FX) - Description (Enables the use of an animated dissolve effect on the material) -} - -Property -{ - Name = %VERTCOLORS - Mask = 0x400000 - Property (Vertex Colors) - Description (Enables the use of vertex colors added to the mesh in the DCC tool) -} - -Property -{ - Name = %DECAL - Mask = 0x2000000 - Property (Decal) - Description (Enables the decal opacity map and used to prevent flickering and z-fighting) -} - -Property -{ - Name = %PARALLAX_OCCLUSION_MAPPING - Mask = 0x8000000 - Property (Parallax occlusion mapping) - Description (Simulates surface depth by parallaxing bump detail from camera view. Requires height and normal maps before enabling.) - DependencyReset = $TEX_Normals -} - -#ifdef FEATURE_MESH_TESSELLATION -Property -{ - Name = %DISPLACEMENT_MAPPING - Mask = 0x10000000 - Property (Displacement mapping) - Description (Displaces the vertices on the mesh to add depth. Requires height and normal maps before enabling.) - //DependencySet = $TEX_Height - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %PHONG_TESSELLATION - Mask = 0x20000000 - Property (Phong tessellation) - Description (Tesselates geometry for smoother faces and displacement. Can suffer from inflation.) -} - -Property -{ - Name = %PN_TESSELLATION - Mask = 0x40000000 - Property (PN triangles tessellation) - Description (Best geometry tesselation for smoother faces and displacement at the cost of perfornmance) -} -#endif - -Property -{ - Name = %BLENDLAYER - Mask = 0x100 - Property (Blendlayer) - Description (Enables a second set of texture inputs and mask to be used for a layered material) -} - -Property -{ - Name = %BLENDLAYER_UV_SET_2 - Mask = 0x200 - Property (Use uv set 2 for blendlayer maps) - Description (Second blend layer maps will be applied to second UV set on mesh) -} - -Property -{ - Name = %EMITTANCE_MAP - Mask = 0x400 - Property (Emittance Map) - Description (Use emittance map texture) - DependencySet = $TEX_Emittance - DependencyReset = $TEX_Emittance - Hidden -} - -Property -{ - Name = %EMITTANCE_MAP_UV_SET_2 - Mask = 0x800 - Property (Use uv set 2 for emittance map) - Description (Emittance map will be applied to second UV set on mesh) -} - -Property -{ - Name = %ALPHAMASK_DETAILMAP - Mask = 0x800000 - Property (DetailMap mask in Diffuse alpha) - Description (Enables the diffuse map alpha to mask the detail map) -} - -Property -{ - Name = %SILHOUETTE_PARALLAX_OCCLUSION_MAPPING - Mask = 0x10000 - Property (Silhouette POM) - Description (Simulates surface depth by parallaxing bump detail and adds silhouette displacement to the mesh edge. Requires height and normal maps before enabling.) - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %ALLOW_SILHOUETTE_POM - Mask = 0x40000 - DependencySet = $HW_SilhouettePom - DependencyReset = $HW_SilhouettePom - Hidden -} - -Property -{ - Name = %SUBSURFACE_SCATTERING - Mask = 0x80000 - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %DEPTH_FIXUP - Mask = 0x4000000 - Property (Depth Fixup) - Description (Enables to write and control depth for post-processing like depth of field) -} - -Property -{ - Name = %SAA_FILTERING - Mask = 0x80000000 - Property (Specular Antialiasing) - Description (Reduces antialiasing on bright specular meshes) -} - -Property -{ - Name = %ALLOW_SPECULAR_ANTIALIASING - Mask = 0x200000000 - DependencySet = $HW_SpecularAntialiasing - DependencyReset = $HW_SpecularAntialiasing - Hidden -} - -Property -{ - Name = %OCCLUSION_MAP - Mask = 0x40 - Property (Occlusion Map) - Description (Adds an additional texture slot for ambient occlusion) -} - -Property -{ - Name = %APPLY_FORWARD_DYNAMIC_LIGHTING - Mask = 0x400000000 - Property(Dynamic Lighting for Transparency) - Description(Adds a full forward lighting pass for transparent objects - less performant) -} diff --git a/Assets/Engine/Shaders/LensOptics.ext b/Assets/Engine/Shaders/LensOptics.ext deleted file mode 100644 index 5bd18e3895..0000000000 --- a/Assets/Engine/Shaders/LensOptics.ext +++ /dev/null @@ -1,16 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// diff --git a/Assets/Engine/Shaders/LightBeam.ext b/Assets/Engine/Shaders/LightBeam.ext deleted file mode 100644 index 835a0dbc99..0000000000 --- a/Assets/Engine/Shaders/LightBeam.ext +++ /dev/null @@ -1,44 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: LightBeam shader extension used by the editor -// for automatic shader generation (based on "LightBeam" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %NOISE - Mask = 0x1 - Property (Noise map) - Description (Use 3D noise) -} - -Property -{ - Name = %FALLOFF - Mask = 0x2 - Property (Use Falloff) - Description (Use Falloff) -} - -Property -{ - Name = %DOUBLE_SAMPLING - Mask = 0x4 - Property (Extra Sampling) - Description (Add expense to reduce aliasing) -} diff --git a/Assets/Engine/Shaders/Monitor.ext b/Assets/Engine/Shaders/Monitor.ext deleted file mode 100644 index 2a20138c1f..0000000000 --- a/Assets/Engine/Shaders/Monitor.ext +++ /dev/null @@ -1,53 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Monitor shader extension used by the editor -// for automatic shader generation -// -//////////////////////////////////////////////////////////////////////////// - - - -Version (1.00) - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x2 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %PIXELIZE - Mask = 0x4 - Property (Pixelized) - Description (Pixelize the diffuse texture.) -} - diff --git a/Assets/Engine/Shaders/ParticleImposter.ext b/Assets/Engine/Shaders/ParticleImposter.ext deleted file mode 100644 index 650541cfa3..0000000000 --- a/Assets/Engine/Shaders/ParticleImposter.ext +++ /dev/null @@ -1,37 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SOFT_PARTICLE - Mask = 0x2 - Property (Soft Particle) - Description (Soften particle intersections with world) -} diff --git a/Assets/Engine/Shaders/Particles.ext b/Assets/Engine/Shaders/Particles.ext deleted file mode 100644 index 3f24ea84b7..0000000000 --- a/Assets/Engine/Shaders/Particles.ext +++ /dev/null @@ -1,102 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Particles shader extension used by the editor -// for automatic shader generation (based on "Particles" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %REFRACTION - Mask = 0x4 - Property (Refraction) - Description (Use normal-map texture as displacement for refraction) -} - -Property -{ - Name = %REFRACTION_TINTING - Mask = 0x800 - Property (Refraction Tinting) - Description (Use color texture to tint refraction) -} - -Property -{ - Name = %SCREEN_SPACE_DEFORMATION - Mask = 0x10 - Property (Screen space deformation) - Description (Use custom slot map for screen space particles deformation) - DependencyReset = $TEX_Custom -} - -Property -{ - Name = %DEFORMATION - Mask = 0x20 - Property (Deformation) - Description (Use custom slot map for per-particle deformation) - DependencyReset = $TEX_Custom -} - -Property -{ - Name = %COLOR_LOOKUP - Mask = 0x40 - Property (Color lookup) - Description (Use custom slot [1] map for applying color lookup) - DependencyReset = $TEX_CustomSecondary -} - -Property -{ - Name = %SPECULAR_LIGHTING - Mask = 0x100 - Property (Specular Lighting) - Description (Calculate specular lighting in addition to diffuse lighting) - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %DEPTH_FIXUP - Mask = 0x200 - Property (Depth Fixup) - Description (Write depth for depth of field and postprocessing) -} - -Property -{ - Name = %NORMAL_MAP - Mask = 0x400 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %GLOW_MAP - Mask = 0x1000 - Property (Emissive map) - Description (Use this map to mask the particle emissive intensity) - DependencySet = $TEX_Detail - DependencyReset = $TEX_Detail - Hidden -} diff --git a/Assets/Engine/Shaders/RunTime.ext b/Assets/Engine/Shaders/RunTime.ext deleted file mode 100644 index 9cc96769d5..0000000000 --- a/Assets/Engine/Shaders/RunTime.ext +++ /dev/null @@ -1,1259 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %_RT_FOG - Mask = 0x1 // 1 << 0 - Precache = GeneralPS - Precache = GeneralVS - Precache = GeneralGS - Precache = GeneralDS - Precache = GeneralHS - Precache = TerrainPS - Precache = TerrainVS - Precache = VegetationVS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationPS - Precache = SkinPS - Precache = SkinVS - Precache = HairPS - Precache = HairVS - Precache = EyePS - Precache = EyeVS - Precache = GlassPS - Precache = GlassVS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS - Precache = CustomRenderHS - Precache = CustomRenderDS - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - Precache = WaterFogVolume_VS - Precache = WaterFogVolume_PS - Precache = MeshBakerPS - Precache = ParticleImposterVS - Precache = ParticleImposterPS -} - -Property -{ - Name = %_RT_AMBIENT - Mask = 0x2 // 1 << 1 - Precache = GeneralPS - Precache = SkinPS - Precache = HairPS - Precache = EyePS - Precache = GlassPS - Precache = TerrainPS - Precache = VegetationPS - Precache = ParticlePS - Precache = MeshBakerPS -} - -Property -{ - Name = %_RT_OCEAN_PARTICLE - Mask = 0x4 // 1 << 2 - Precache = PostProcessGamePS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - Precache = WaterFogVolume_PS -} - -Property -{ - //Using the same mask as _RT_OCEAN_PARTICLE as we have run out of them. - //It should be safe to do this as this flag is not used in water rendering - Name = %_RT_DEPTHFIXUP - Mask = 0x4 // 1 << 2 - Precache = FurShellPS - Precache = GlassPS - Precache = IlluminationPS - Precache = GeneralPS -} - -Property -{ - Name = %_RT_DECAL_TEXGEN_2D - Mask = 0x8 // 1 << 3 - Precache = GeneralVS - Precache = GeneralDS - Precache = GeneralHS - Precache = ShadowGenVS - Precache = CausticsVS - Precache = GeneralPS - Precache = CustomRenderHS - Precache = CustomRenderDS - Precache = ZVS - Precache = ZPS - Precache = MeshBakerPS - Precache = TiledShadingCS -} - -Property -{ - Name = %_RT_DISSOLVE - Mask = 0x10 // 1 << 4 - Precache = ZPS - Precache = ZVS - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = GeneralPS - Precache = GeneralVS -} - -Property -{ - Name = %_RT_VOLUMETRIC_FOG - Mask = 0x20 // 1 << 5 - Precache = GeneralPS - Precache = GeneralVS - Precache = GeneralGS - Precache = GeneralDS - Precache = GeneralHS - Precache = TerrainPS - Precache = TerrainVS - Precache = VegetationVS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationPS - Precache = SkinPS - Precache = SkinVS - Precache = HairPS - Precache = HairVS - Precache = EyePS - Precache = EyeVS - Precache = GlassPS - Precache = GlassVS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS - Precache = WaterSurfacePS - Precache = WaterFogVolume_VS - Precache = WaterFogVolume_PS - Precache = MeshBakerPS - Precache = CloudVS - Precache = CloudPS - Precache = FogPostProcessPS - Precache = ParticleImposterVS - Precache = ParticleImposterPS -} - -Property -{ - Name = %_RT_NEAREST - Mask = 0x40 // 1 << 6 - Precache = ShadowGenVS - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS - Precache = ZVS - Precache = MotionBlurVS - Precache = SkinVS - Precache = GeneralVS - Precache = VegetationVS - Precache = CausticsVS - Precache = CustomRenderVS - Precache = DebugPassVS -} - -Property -{ - Name = %_RT_GLOBAL_ILLUMINATION - Mask = 0x80 // 1 << 7 - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_ALPHATEST - Mask = 0x100 // 1 << 8 - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = ZPS - Precache = ZVS - Precache = MotionBlurPS - Precache = CustomRenderPS - Precache = VegetationVS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationPS - Precache = GeneralPS - Precache = GlassPS - Precache = GlassVS - Precache = MotionBlurVS - Precache = MeshBakerPS -} - -Property -{ - Name = %_RT_SOFT_PARTICLE - Mask = 0x200 // 1 << 9 - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_HDR_MODE - Mask = 0x400 // 1 << 10 - Precache = GeneralPS - Precache = TerrainPS - Precache = VegetationPS - Precache = SkinPS - Precache = HairPS - Precache = EyePS - Precache = GlassPS - Precache = ParticlePS - Precache = WaterSurfacePS - Precache = ParticleImposterPS -} - -Property -{ - Name = %_RT_PARTICLE_SHADOW - Mask = 0x800 // 1 << 11 - Precache = ParticlePS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS -} - -Property -{ - Name = %_RT_SAMPLE1 - Mask = 0x1000 // 1 << 12 - Precache = CustomRenderHS - Precache = CustomRenderDS - Precache = CustomRenderVS - Precache = CustomRenderPS - Precache = SkinPS - Precache = HDRPostProcessPS - Precache = PostMotionBlurVS - Precache = PostMotionBlurPS - Precache = PostSunShaftsPS - Precache = PostProcessGamePS - Precache = PostDofPS - Precache = PostEffectsVS - Precache = PostEffectsPS - Precache = PostAA_PS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = PostHUD3D_VS - Precache = PostHUD3D_PS - Precache = ParticlePS - Precache = ParticleVS - Precache = DeferredRainPS - Precache = ShadowMaskGenPS - - Precache = WaterFogVolume_VS - Precache = WaterFogVolume_PS - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - - Precache = BeamPS - Precache = ParticleHS - Precache = ParticleDS - - Precache = TiledShadingCS - Precache = VolumeLightInjectionCS - Precache = ResolvePS - Precache = VideoPS -} - -Property -{ - Name = %_RT_SAMPLE2 - Mask = 0x2000 // 1 << 13 - - Precache = HDRPostProcessPS - Precache = PostMotionBlurVS - Precache = PostMotionBlurPS - Precache = PostAA_PS - Precache = PostSunShaftsPS - Precache = PostProcessGamePS - Precache = DeferredDecalPassPS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = CustomRenderPS - Precache = DeferredRainPS - - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS - Precache = ParticlePS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - - Precache = BeamPS - Precache = LensOpticsPS - Precache = WaterFogVolume_PS - Precache = TiledShadingCS - Precache = FogPostProcessPS - Precache = VideoPS -} - -Property -{ - Name = %_RT_SAMPLE3 - Mask = 0x4000 // 1 << 14 - Precache = ParticlePS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = PostAA_PS - Precache = HDRPostProcessPS - Precache = BeamPS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = DeferredRainPS - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS - Precache = ResolvePS - - Precache = LensOpticsPS - Precache = WaterFogVolume_PS - Precache = TiledShadingCS - Precache = VideoPS -} - -Property -{ - Name = %_RT_POINT_LIGHT - Mask = 0x8000 // 1 << 15 - Precache = FogPassVolShadowsInterleavePassPS - Precache = WaterFogVolume_PS - Precache = ConeTraceDiffusePS -} - -Property -{ - Name = %_RT_ALPHABLEND - Mask = 0x10000 // 1 << 16 - Precache = GeneralHS - Precache = GeneralDS - Precache = FogPassVolShadowsInterleavePassPS - Precache = ParticlePS - Precache = ParticleVS - Precache = HairVS - Precache = EyeVS - Precache = VegetationVS - Precache = GeneralVS - Precache = GlassVS - Precache = CustomRenderHS - Precache = CustomRenderDS - Precache = ZVS - Precache = ZPS - Precache = MotionBlurPS -} - -Property -{ - Name = %_RT_ANIM_BLEND - Mask = 0x20000 // 1 << 17 - Precache = ParticlePS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticleGS -} - -Property -{ - Name = %_RT_QUALITY - Mask = 0x40000 // 1 << 18 - AutoPrecache - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = ZVS - Precache = ZPS - Precache = FogPassVolShadowsInterleavePassPS - Precache = GeneralPS - Precache = GeneralVS - Precache = SkinPS - Precache = SkinVS - Precache = HairPS - Precache = HairVS - Precache = EyePS - Precache = EyeVS - Precache = GlassPS - Precache = GlassVS - Precache = TerrainPS - Precache = TerrainVS - Precache = VegetationPS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationVS - Precache = MotionBlurVS - Precache = MotionBlurPS - Precache = CausticsVS - Precache = ParticlePS - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - - Precache = PostMotionBlurVS - Precache = PostMotionBlurPS - Precache = PostSunShaftsPS - Precache = SpriteDilatePS - Precache = ShadowMaskGenPS - - Precache = HDRPostProcessVS - Precache = HDRPostProcessPS - Precache = FogPostProcessPS - Precache = PostProcessGameVS - Precache = PostProcessGamePS - - Precache = DistanceCloudsPS - Precache = DeferredLightPassPS - Precache = VolumeLightInjectionCS -} - -Property -{ - Name = %_RT_QUALITY1 - Mask = 0x80000 // 1 << 19 - AutoPrecache - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = ZVS - Precache = ZPS - Precache = FogPassVolShadowsInterleavePassPS - Precache = GeneralPS - Precache = GeneralVS - Precache = SkinPS - Precache = SkinVS - Precache = HairPS - Precache = HairVS - Precache = EyePS - Precache = EyeVS - Precache = GlassPS - Precache = GlassVS - Precache = TerrainPS - Precache = TerrainVS - Precache = VegetationPS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationVS - Precache = MotionBlurVS - Precache = MotionBlurPS - Precache = CausticsVS - Precache = ParticlePS - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - - Precache = PostMotionBlurVS - Precache = PostMotionBlurPS - Precache = PostSunShaftsPS - Precache = SpriteDilatePS - Precache = ShadowMaskGenPS - - Precache = HDRPostProcessVS - Precache = HDRPostProcessPS - Precache = FogPostProcessPS - Precache = PostProcessGameVS - Precache = PostProcessGamePS - - Precache = DistanceCloudsPS - Precache = DeferredLightPassPS - Precache = VolumeLightInjectionCS -} - -Property -{ - Name = %_RT_INSTANCING_ATTR - Mask = 0x100000 // 1 << 20 - Precache = GeneralVS - Precache = SkinVS - Precache = HairVS - Precache = EyeVS - Precache = GlassVS - Precache = VegetationVS - Precache = ShadowGenVS - Precache = ZVS - Precache = MotionBlurVS - Precache = CausticsVS - Precache = CustomRenderVS - Precache = DebugPassVS -} - -Property -{ - Name = %_RT_ENVIRONMENT_CUBEMAP - Mask = 0x200000 // 1 << 21 - Precache = ParticlePS -} - -Property -{ - Name = %_RT_TILED_SHADING - Mask = 0x400000 // 1 << 22 - Precache = EyePS - Precache = HairPS - Precache = GlassPS - Precache = IlluminationPS -} - -Property -{ - Name = %_RT_NO_TESSELLATION - Mask = 0x800000 // 1 << 23 - Precache = GeneralVS - Precache = GeneralPS - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = ZVS - Precache = ZPS - Precache = MotionBlurVS - Precache = MotionBlurPS - Precache = CustomRenderVS - Precache = SkinVS - Precache = SkinPS - Precache = VegetationVS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationPS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS - Precache = DebugPassVS -} - -Property -{ - Name = %_RT_APPLY_TOON_SHADING - Mask = 0x1000000 // 1 << 24 - Precache = DeferredLightPassPS - Precache = TiledShadingCS -} - - -Property -{ - Name = %_RT_LIGHT_TEX_PROJ - Mask = 0x2000000 // 1 << 25 - Precache = DeferredLightPassPS - Precache = ParticleVS - Precache = ParticleDS - Precache = ConeTraceDiffusePS -} - -Property -{ - Name = %_RT_VERTEX_VELOCITY - Mask = 0x4000000 // 1 << 26 - Precache = ZVS - Precache = MotionBlurVS -} - -Property -{ - Name = %_RT_SKINNING_DUAL_QUAT - Mask = 0x8000000 // 1 << 27 - Precache = GeneralVS - Precache = SkinVS - Precache = HairVS - Precache = EyeVS - Precache = GlassVS - Precache = VegetationVS - Precache = ShadowGenVS - Precache = ZVS - Precache = MotionBlurVS - Precache = CausticsVS - Precache = CustomRenderVS - Precache = DebugPassVS -} - -Property -{ - Name = %_RT_SKINNING_DQ_LINEAR - Mask = 0x10000000 // 1 << 28 - Precache = GeneralVS - Precache = SkinVS - Precache = HairVS - Precache = EyeVS - Precache = GlassVS - Precache = VegetationVS - Precache = ShadowGenVS - Precache = ZVS - Precache = MotionBlurVS - Precache = CausticsVS - Precache = CustomRenderVS - Precache = DebugPassVS -} - -Property -{ - Name = %_RT_BLEND_WITH_TERRAIN_COLOR - Mask = 0x20000000 // 1 << 29 - Precache = ZVS - Precache = ZPS - Precache = GeneralHS - Precache = GeneralDS - Precache = VegetationVS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationPS - Precache = ConeTraceDiffusePS -} - -Property -{ - Name = %_RT_MOTION_BLUR - Mask = 0x40000000 // 1 << 30 - Precache = ParticleVS - Precache = ParticleHS - Precache = GPUParticleCS - Precache = ParticleDS - Precache = ParticlePS - Precache = ZVS - Precache = ZGS - Precache = ZPS - Precache = GeneralHS - Precache = GeneralDS -} - -Property -{ - Name = %_RT_LIGHTVOLUME0 - Mask = 0x80000000 // 1 << 31 - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = VolumeLightInjectionCS - Precache = RenderDownscaledShadowMapPS -} - -Property -{ - //Using the same mask as _RT_LIGHTVOLUME0 as we have run out of them. - //It should be safe to do this as this flag is not used in the deferred lighting pass or particles. - - Name = %_RT_SRGB0 - Mask = 0x80000000 // 1 << 31 - Precache = PostAA_PS - Precache = ResolvePS - Precache = HDRPostProcessPS -} - -Property -{ - Name = %_RT_LIGHTVOLUME1 - Mask = 0x100000000 // 1 << 32 - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS - Precache = VolumeLightInjectionCS - Precache = RenderDownscaledShadowMapPS -} - -Property -{ - //Using the same mask as _RT_LIGHTVOLUME1 as we have run out of them. - //It should be safe to do this as this flag is not used in the deferred lighting pass or particles. - - Name = %_RT_SRGB1 - Mask = 0x100000000 // 1 << 32 - Precache = PostAA_PS - Precache = ResolvePS - Precache = HDRPostProcessPS -} - -Property -{ - Name = %_RT_NOZPASS - Mask = 0x200000000 // 1 << 33 - Precache = VegetationVS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationPS -} - -Property -{ - //Using the same mask as _RT_NOZPASS as we have run out of them. - //It should be safe to do this as this flag is not used in vegetation pass. - - Name = %_RT_SRGB2 - Mask = 0x200000000 // 1 << 33 - Precache = PostAA_PS - Precache = ResolvePS - Precache = HDRPostProcessPS -} - -Property -{ - Name = %_RT_SHADOW_MIXED_MAP_G16R16 - Mask = 0x400000000 // 1 << 34 - Precache = FogPassVolShadowsInterleavePassPS - Precache = WaterFogVolume_PS - - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS -} - -Property -{ - Name = %_RT_SHADOW_JITTERING - Mask = 0x800000000 // 1 << 35 - Precache = FogPassVolShadowsInterleavePassPS - Precache = TerrainPS - Precache = WaterFogVolume_PS - - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS - - Precache = HairPS - Precache = EyePS -} - -Property -{ - Name = %_RT_ADDITIVE_BLENDING - Mask = 0x1000000000 // 1 << 36 - Precache = GlassPS - Precache = GeneralPS - Precache = HairPS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_SAMPLE0 - Mask = 0x2000000000 // 1 << 37 - Precache = CustomRenderVS - Precache = CustomRenderPS - Precache = SkinPS - Precache = GlassPS - - Precache = FogPostProcessPS - Precache = HDRPostProcessPS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS - Precache = PostHUD3D_VS - Precache = PostHUD3D_PS - Precache = PostMotionBlurVS - Precache = PostMotionBlurPS - Precache = PostAA_PS - Precache = PostSunShaftsPS - Precache = PostProcessGameVS - Precache = PostProcessGamePS - Precache = PostDofPS - Precache = PostEffectsVS - Precache = PostEffectsPS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = SceneRainVS - Precache = SceneRainPS - Precache = DeferredRainPS - Precache = ResolveVS - Precache = ResolvePS - - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - Precache = WaterFogVolume_PS - - Precache = BeamPS - Precache = LensOpticsVS - Precache = TiledShadingCS - Precache = VolumeLightInjectionCS - Precache = VideoPS -} - -// Reserved for post processes/deferred - do not use for light/common shaders -Property -{ - Name = %_RT_SAMPLE5 - Mask = 0x4000000000 // 1 << 38 - - Precache = ZVS - Precache = GeneralVS - Precache = GeneralHS - Precache = GeneralDS - - Precache = HDRPostProcessPS - Precache = PostSunShaftsPS - Precache = PostProcessGamePS - Precache = DeferredPassPS - Precache = DeferredPassVS - Precache = DeferredLightPassPS - Precache = PostMotionBlurPS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS - Precache = ResolvePS - Precache = FogPassVolShadowsInterleavePassPS - Precache = CustomRenderHS - Precache = CustomRenderDS - Precache = CustomRenderPS - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - Precache = WaterFogVolume_PS - Precache = PostAA_PS - - Precache = BeamPS - Precache = LensOpticsVS - Precache = TiledShadingCS - Precache = VolumeLightInjectionCS - Precache = ReprojectVolumetricFogCS -} - -Property -{ - Name = %_RT_HW_PCF_COMPARE - Mask = 0x8000000000 // 1 << 39 - Precache = FogPassVolShadowsInterleavePassPS - Precache = ShadowGenVS - Precache = ShadowGenPS - - Precache = DeferredLightPassPS - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS - - Precache = WaterFogVolume_PS - Precache = ConeTraceDiffusePS -} - -Property -{ - Name = %_RT_REVERSE_DEPTH - Mask = 0x10000000000 // 1 << 40 - Precache = DistanceCloudsVS - Precache = PostProcessGamePS - Precache = TerrainVS - Precache = ZVS - Precache = ZPS - Precache = UnderwaterGodRays - Precache = WaterSurfaceVS - Precache = WaterFogVolume_VS - Precache = GeneralVS - Precache = LensOpticsVS -} - -Property -{ - Name = %_RT_DEBUG0 - Mask = 0x20000000000 // 1 << 41 - - Runtime -} - -Property -{ - Name = %_RT_DEBUG1 - Mask = 0x40000000000 // 1 << 42 - Runtime -} - -Property -{ - Name = %_RT_DEBUG2 - Mask = 0x80000000000 // 1 << 43 - Runtime -} - -Property -{ - Name = %_RT_DEBUG3 - Mask = 0x100000000000 // 1 << 44 - Runtime -} - -Property -{ - Name = %_RT_CUBEMAP0 - Mask = 0x200000000000 // 1 << 45 - Precache = ShadowGenPS - Precache = ShadowGenVS - - Precache = PostSunShaftsPS - Precache = PostProcessGamePS - - Precache = DeferredDecalPassVS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS - Precache = DeferredLightPassVS - Precache = DeferredLightPassPS - Precache = DeferredPassVS - Precache = DeferredPassPS - - Precache = WaterFogVolume_PS - Precache = TiledShadingCS -} - -Property -{ - Name = %_RT_SAMPLE4 - Mask = 0x400000000000 // 1 << 46 - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = PostAA_PS - - Precache = HDRPostProcessPS - Precache = PostProcessGamePS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = PostSunShaftsPS - Precache = PostMotionBlurPS - - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - Precache = WaterFogVolume_PS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS - - Precache = BeamPS - Precache = LensOpticsPS - - Precache = TiledShadingCS - - Precache = ConeTraceDiffusePS - Precache = VolumeLightInjectionCS -} - -Property -{ - Name = %_RT_SPRITE - Mask = 0x800000000000 // 1 << 47 - Precache = VegetationVS - Precache = VegetationPS - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_SHADOW_PASS - Mask = 0x1000000000000 // 1 << 48 - Precache = ParticleVS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_DEPTH_COLLISION - Mask = 0x2000000000000 // 1 << 49 - Precache = GPUParticleCS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_TURBULENCE - Mask = 0x4000000000000 // 1 << 50 - Precache = GPUParticleCS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_UV_ANIMATION - Mask = 0x8000000000000 // 1 << 51 - Precache = ParticleVS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_NORMAL_MAP - Mask = 0x10000000000000 // 1 << 52 - Precache = ParticleVS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_GLOW_MAP - Mask = 0x20000000000000 // 1 << 53 - Precache = ParticleVS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_CUBEMAP_DEPTH_COLLISION - Mask = 0x40000000000000 // 1 << 54 - Precache = GPUParticleCS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_WRITEBACK_DEATH_LOCATIONS - Mask = 0x80000000000000 // 1 << 55 - Precache = GPUParticleCS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_TARGET_ATTRACTION - Mask = 0x100000000000000 // 1<< 56 - Precache = GPUParticleCS -} - -Property -{ - // Using the same mask as _RT_GPU_PARTICLE_TARGET_ATTRACTION as we have run out of them. - //It should be safe to do this as this flag is not used in the deferred lighting pass. - - Name = %_RT_DEFERRED_RENDER_TARGET_OPTIMIZATION - Mask = 0x100000000000000 // 1<< 56 - - Precache = DeferredPassPS - Precache = DeferredPassVS - Precache = DeferredLightPassPS - Precache = TiledShadingCS - Precache = LightPassPS - Precache = LightPassGmemPS - Precache = ConeTraceDiffusePS - Precache = VolumeLightInjectionCS - Precache = CubemapPassPS - Precache = CubemapPassGmemPS - Precache = DeferredShadowGmemPS - Precache = DeferredShadingPassPS - Precache = DeferredShadingPassGmemPS - Precache = AmbientPS - Precache = ShadowMaskGenVS - Precache = ShadowMaskGenPS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_SHAPE_ANGLE - Mask = 0x200000000000000 // 1 << 57 - Precache = GPUParticleCS -} - -Property -{ - - // Using the same mask as _RT_GPU_PARTICLE_SHAPE_ANGLE as we have run out of them. - //It should be safe to do this as this flag is not used in the deferred lighting pass. - - Name = %_RT_SLIM_GBUFFER - Mask = 0x200000000000000 // 1 << 57 - AutoPrecache - Precache = ShadowGenVS - Precache = ShadowGenPS - Precache = ZVS - Precache = ZPS - Precache = FogPassVolShadowsInterleavePassPS - Precache = GeneralPS - Precache = GeneralVS - Precache = SkinPS - Precache = SkinVS - Precache = HairPS - Precache = HairVS - Precache = EyePS - Precache = EyeVS - Precache = GlassPS - Precache = GlassVS - Precache = TerrainPS - Precache = TerrainVS - Precache = VegetationPS - Precache = VegetationHS - Precache = VegetationDS - Precache = VegetationVS - Precache = MotionBlurVS - Precache = MotionBlurPS - Precache = CausticsVS - Precache = ParticlePS - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - - Precache = PostMotionBlurVS - Precache = PostMotionBlurPS - Precache = PostSunShaftsPS - Precache = SpriteDilatePS - Precache = ShadowMaskGenPS - - Precache = HDRPostProcessVS - Precache = HDRPostProcessPS - Precache = FogPostProcessPS - Precache = PostProcessGameVS - Precache = PostProcessGamePS - - Precache = DistanceCloudsPS - Precache = DeferredLightPassPS - Precache = VolumeLightInjectionCS - Precache = HDRPostProcessPS - Precache = PostProcessGamePS - Precache = DeferredLightPassPS - Precache = DeferredPassPS - Precache = PostSunShaftsPS - Precache = PostMotionBlurPS - - Precache = WaterSurfaceVS - Precache = WaterSurfacePS - Precache = WaterSurfaceHS - Precache = WaterSurfaceDS - Precache = WaterFogVolume_PS - Precache = DeferredDecalPassPS - Precache = DeferredDecalEmissivePassPS - - Precache = BeamPS - Precache = LensOpticsPS - - Precache = DeferredShadingPassPS - Precache = DeferredPassVS - Precache = DeferredLightPassPS - Precache = TiledShadingCS - Precache = LightPassPS - Precache = ConeTraceDiffusePS - Precache = VolumeLightInjectionCS - Precache = CubemapPassPS - Precache = CubemapPassGmemPS - - Precache = DeferredSnowPS - Precache = DeferredRainPS - Precache = SSRCompositionPS - Precache = SSRRaytracePS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_SHAPE_BOX - Mask = 0x400000000000000 // 1 << 58 - Precache = GPUParticleCS - Precache = ParticleVS -} - -Property -{ - // Using the same mask as _RT_GPU_PARTICLE_SHAPE_BOX as we have run out of them. - // It should be safe to do this as this (GPU Particles shouldn't have skinning) - - Name = %_RT_SKINNING_MATRIX - Mask = 0x400000000000000 // 1 << 58 - Precache = GeneralVS - Precache = SkinVS - Precache = HairVS - Precache = EyeVS - Precache = GlassVS - Precache = VegetationVS - Precache = ShadowGenVS - Precache = ZVS - Precache = MotionBlurVS - Precache = CausticsVS - Precache = CustomRenderVS - Precache = DebugPassVS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_SHAPE_POINT - Mask = 0x800000000000000 // 1 << 59 - Precache = GPUParticleCS -} - -Property -{ - //Using the same mask as _RT_GPU_PARTICLE_SHAPE_POINT as we have run out of them. - //It should be safe to do this as this flag is not used in the deferred lighting pass. - Name = %_RT_APPLY_SSDO - Mask = 0x800000000000000 // 1 << 59. - Precache = DeferredPassPS - Precache = DeferredPassVS - Precache = DeferredLightPassPS - Precache = TiledShadingCS -} - -//Using the same mask as _RT_APPLY_SSDO as we have run out of them. -//It should be safe to do this as this flag is not used in particle shaders. -Property -{ - Name = %_RT_FOG_VOLUME_HIGH_QUALITY_SHADER - Mask = 0x800000000000000 // 1 << 59 - Precache = ParticleVS - Precache = ParticleHS - Precache = ParticleDS - Precache = ParticlePS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_SHAPE_CIRCLE - Mask = 0x1000000000000000 // 1 << 60 - Precache = GPUParticleCS - Precache = ParticleVS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_SHAPE_SPHERE - Mask = 0x2000000000000000 // 1 << 61 - Precache = GPUParticleCS - Precache = ParticleVS -} - -Property -{ - Name = %_RT_GPU_PARTICLE_WIND - Mask = 0x4000000000000000 // 1 << 62 - Precache = GPUParticleCS - -} -Property -{ - Name = %_RT_MULTI_LAYER_ALPHA_BLEND - Mask = 0x8000000000000000 // 1 << 63 - Precache = FurShellPS - Precache = FurFinsPS - Precache = GlassPS - Precache = GeneralPS - Precache = HairPS - Precache = ParticlePS - Precache = MultiLayerAlphaBlendResolvePS -} - - diff --git a/Assets/Engine/Shaders/Scopes.ext b/Assets/Engine/Shaders/Scopes.ext deleted file mode 100644 index ae84db49ca..0000000000 --- a/Assets/Engine/Shaders/Scopes.ext +++ /dev/null @@ -1,42 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %REFLEX_SIGHT - Mask = 0x2 - Property (Reflex sight) - Description (New reflex sight version) -} - -Property -{ - Name = %SCOPE_ZOOMED_REFRACTION - Mask = 0x4 - Property (Scope zoomed refraction) - Description (Scope zoomed in refraction) -} - -Property -{ - Name = %HOLO_SIGHT - Mask = 0x8 - Property (Use holo sight depth) - Description (Holographic sight with depth modifier) -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/ShaderProfiles.txt b/Assets/Engine/Shaders/ShaderProfiles.txt deleted file mode 100644 index ae5178118e..0000000000 --- a/Assets/Engine/Shaders/ShaderProfiles.txt +++ /dev/null @@ -1,11 +0,0 @@ - -Version (1.00) - -Profile 'Low' -{ -} - -Profile 'High' -{ - UseNormalAlpha -} diff --git a/Assets/Engine/Shaders/ShadowMaskGen.ext b/Assets/Engine/Shaders/ShadowMaskGen.ext deleted file mode 100644 index 6c65742d63..0000000000 --- a/Assets/Engine/Shaders/ShadowMaskGen.ext +++ /dev/null @@ -1,21 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: -// -//////////////////////////////////////////////////////////////////////////// - - - -Version (1.00) diff --git a/Assets/Engine/Shaders/SketchTerrain.ext b/Assets/Engine/Shaders/SketchTerrain.ext deleted file mode 100644 index 3dfad9d1cf..0000000000 --- a/Assets/Engine/Shaders/SketchTerrain.ext +++ /dev/null @@ -1,27 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %TEMP_TERRAIN - Mask = 0x40000000 - DependencySet = $UserEnabled - Hidden -} diff --git a/Assets/Engine/Shaders/SkyHDR.ext b/Assets/Engine/Shaders/SkyHDR.ext deleted file mode 100644 index 87d1a00ca5..0000000000 --- a/Assets/Engine/Shaders/SkyHDR.ext +++ /dev/null @@ -1,39 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// -Version (1.00) - -Property -{ - Name = %NO_MOON - Mask = 0x02 - Property (No moon) - Description (Sky dome doesn't render moon) -} -Property -{ - Name = %NO_NIGHT_SKY_GRADIENT - Mask = 0x04 - Property (No night sky gradient) - Description (Sky dome doesn't render night sky gradient) -} -Property -{ - Name = %NO_DAY_SKY_GRADIENT - Mask = 0x08 - Property (No day sky gradient) - Description (Sky dome doesn't render day sky gradient) -} diff --git a/Assets/Engine/Shaders/StarterGame_GeometryBeamScaling.ext b/Assets/Engine/Shaders/StarterGame_GeometryBeamScaling.ext deleted file mode 100644 index 25e395f42d..0000000000 --- a/Assets/Engine/Shaders/StarterGame_GeometryBeamScaling.ext +++ /dev/null @@ -1,43 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// - - -Property -{ - Name = %NOISE - Mask = 0x1 - Property (Dust & Turbulence) - Description (Add a dust overlay [spec map for dust, bump map for turbulence] ) -} - - -Property -{ - Name = %RECEIVE_SHADOWS - Mask = 0x2 - Property (Receive Shadows) - Description (Enable shadow receiving) -} - - -Property -{ - Name = %UV_VIGNETTING - Mask = 0x4 - Property (UV Vignetting) - Description (Enabling this will cause contents to fade out at UV boundaries) -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/Statics.ext b/Assets/Engine/Shaders/Statics.ext deleted file mode 100644 index e7d93fbeaf..0000000000 --- a/Assets/Engine/Shaders/Statics.ext +++ /dev/null @@ -1,74 +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. -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %ST_GMEM_128BPP - Mask = 0x1 // 1 << 0 -} - -Property -{ - Name = %ST_GMEM_256BPP - Mask = 0x2 // 1 << 1 -} - -Property -{ - Name = %ST_GMEM_PLS - Mask = 0x4 // 1 << 2 -} - -Property -{ - Name = %ST_LLVM_DIRECTX_SHADER_COMPILER - Mask = 0x8 // 1 << 3 -} - -Property -{ - Name = %ST_FIXED_POINT - Mask = 0x10 // 1 << 4 -} - -Property -{ - Name = %ST_GMEM_RT_GREATER_FOUR - Mask = 0x20 // 1 << 5 -} - -Property -{ - Name = %ST_NO_DEPTH_CLIPPING - Mask = 0x40 // 1 << 6 -} - -Property -{ - Name = %ST_FEATURE_FETCH_DEPTHSTENCIL - Mask = 0x80 // 1 << 7 -} - -property -{ - Name = %ST_GMEM_VELOCITY_BUFFER - Mask = 0x100 // 1 << 8 -} - -property -{ - Name = %ST_GLES3_0 - Mask = 0x200 // 1 << 9 -} diff --git a/Assets/Engine/Shaders/TemplBeamProc.ext b/Assets/Engine/Shaders/TemplBeamProc.ext deleted file mode 100644 index 485702ace7..0000000000 --- a/Assets/Engine/Shaders/TemplBeamProc.ext +++ /dev/null @@ -1,34 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %NOISE - Mask = 0x1 - Property (Noise map) - Description (Use animated 3D noise) -} - -Property -{ - Name = %MUZZLEFLASH - Mask = 0x2 - Property (Muzzleflash) - Description (Use as muzzle flash) -} diff --git a/Assets/Engine/Shaders/Terrain.ext b/Assets/Engine/Shaders/Terrain.ext deleted file mode 100644 index 6e8b5dc6f3..0000000000 --- a/Assets/Engine/Shaders/Terrain.ext +++ /dev/null @@ -1,79 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: TerrainLayer shader extension used by the editor -// for automatic shader generation (based on "TerrainLayer" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x200 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %OFFSET_BUMP_MAPPING - Mask = 0x1000 - Property (Offset bump mapping) - Description (Use offset bump mapping (requires height map (_displ))) - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %DETAIL_MAPPING - Mask = 0x8000 - Property (Detail mapping) - Description (Use detail mapping) - DependencyReset = $TEX_Detail -} - -Property -{ - Name = %PARALLAX_OCCLUSION_MAPPING - Mask = 0x8000000 - Property (Parallax occlusion mapping) - Description (Use parallax occlusion mapping (requires height map (_displ))) - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %TEMP_TERRAIN - Mask = 0x40000000 - DependencySet = $UserEnabled - Hidden -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/Vegetation.ext b/Assets/Engine/Shaders/Vegetation.ext deleted file mode 100644 index 5f98ec49e6..0000000000 --- a/Assets/Engine/Shaders/Vegetation.ext +++ /dev/null @@ -1,137 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Vegetation extension used by the editor -// for automatic shader generation (based on "Vegetation" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -UsesCommonGlobalFlags - -Property -{ - Name = %NORMAL_MAP - Mask = 0x1 - Property (Normal map) - Description (Use normal-map texture) - DependencySet = $TEX_Normals - DependencyReset = $TEX_Normals - Hidden -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x10 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %LEAVES - Mask = 0x100 - Property (Leaves) - Description (Activate for leaves only ! Use leaf shading and leaves animation) -} - -Property -{ - Name = %GRASS - Mask = 0x2000 - Property (Grass) - Description (Activate for grass only ! Use simple grass rendering) -} - -Property -{ - Name = %SPEEDTREE_BILLBOARD - Mask = 0x40000 - Property (SpeedTree billboard) - Description (Activate for SpeedTree billboards only! Enables removal of non-camera-facing geometry from the billboard mesh) -} - -Property -{ - Name = %DETAIL_BENDING - Mask = 0x10000 - Property (Detail bending) - Description (Activate for leaves and grass only. Make sure to paint required vertex colors also) -} - -Property -{ - Name = %DETAIL_MAPPING - Mask = 0x20000 - Property (Detail mapping) - Description (Use detail mapping) - DependencyReset = $TEX_Detail -} - -Property -{ - Name = %BLENDLAYER - Mask = 0x80000 - Property (Blendlayer) - Description (Normal-mapped diffuse layer blended on top of base material) -} - -Property -{ - Name = %VERTCOLORS - Mask = 0x100000 - DependencySet = $UserEnabled - Hidden -} - -Property -{ - Name = %TEMP_VEGETATION - Mask = 0x8000000 - DependencySet = $UserEnabled - Hidden -} - -#ifdef FEATURE_MESH_TESSELLATION -Property -{ - Name = %DISPLACEMENT_MAPPING - Mask = 0x10000000 - Property (Displacement mapping) - Description (Use displacement mapping (requires height map (_displ))) - //DependencySet = $TEX_Height - DependencyReset = $TEX_Normals -} - -Property -{ - Name = %PHONG_TESSELLATION - Mask = 0x20000000 - Property (Phong tessellation) - Description (Use rough approximation of smooth surface subdivision) -} - -Property -{ - Name = %PN_TESSELLATION - Mask = 0x40000000 - Property (PN triangles tessellation) - Description (Use rough approximation of smooth surface subdivision) -} -#endif \ No newline at end of file diff --git a/Assets/Engine/Shaders/VolumeObject.ext b/Assets/Engine/Shaders/VolumeObject.ext deleted file mode 100644 index 8cab9b4270..0000000000 --- a/Assets/Engine/Shaders/VolumeObject.ext +++ /dev/null @@ -1,57 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: -// -//////////////////////////////////////////////////////////////////////////// - - - -Version (1.00) - -Property -{ - Name = %SOFT_OBJECT_INTERSECTION - Mask = 0x1 - Property (Soft Intersections) - Description (Enables soft intersections with opaque scene geometry) -} -Property -{ - Name = %BACK_LIGHTING - Mask = 0x2 - Property (Back Lighting) - Description (Adds back lighting to volume silhouette when viewing against sun) -} -Property -{ - Name = %JITTERING - Mask = 0x4 - Property (Jittering) - Description (Enables jittering on cloud steps) -} -Property -{ - Name = %SOFT_JITTERING - Mask = 0x8 - Property (Soft Jittering) - Description (Softens the effect of jittering on volume objects) -} -Property -{ - Name = %CUSTOM_SETTINGS - Mask = 0x10 - Property (Use TOD Settings) - Description (Use the custom cloud settings from the TOD for lighting) -} \ No newline at end of file diff --git a/Assets/Engine/Shaders/Water.ext b/Assets/Engine/Shaders/Water.ext deleted file mode 100644 index d44e3598e5..0000000000 --- a/Assets/Engine/Shaders/Water.ext +++ /dev/null @@ -1,62 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Water extension used by the editor -// for automatic shader generation (based on "Water" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %ENVIRONMENT_MAP - Mask = 0x4 - Property (Environment map) - Description (Use environment map instead of reflection) - DependencyReset = $TEX_EnvCM -} - -Property -{ - Name = %SUN_SHINE - Mask = 0x20 - Property (Sunshine) - Description (Activate for water sunshine) -} - -Property -{ - Name = %NO_REFRACTION_BUMP - Mask = 0x200 - Property (No refraction bump) - Description (Disables refraction bump) -} - -Property -{ - Name = %FOAM - Mask = 0x400 - Property (Foam) - Description (Enables foam) -} - -Property -{ - Name = %WATER_TESSELLATION_DX11 - Mask = 0x800 - DependencySet = $HW_WaterTessellation - DependencyReset = $HW_WaterTessellation - Hidden -} diff --git a/Assets/Engine/Shaders/WaterVolume.ext b/Assets/Engine/Shaders/WaterVolume.ext deleted file mode 100644 index dec9d1a531..0000000000 --- a/Assets/Engine/Shaders/WaterVolume.ext +++ /dev/null @@ -1,107 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Water extension used by the editor -// for automatic shader generation (based on "Water" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %SSREFL - Mask = 0x1 - Property (Realtime Reflection) - Description (Approximate realtime reflections) -} - -Property -{ - Name = %FLOW - Mask = 0x2 - Property (Water flow) - Description (Enable water to flow along geometry uvs) -} - -Property -{ - Name = %FLOW_MAP - Mask = 0x4 - Property (Water flow map) - Description (Enable water flow along a flow map) -} - -Property -{ - Name = %FLOW_MAP_STRENGTH - Mask = 0x100 - Property (Water flow map strength) - Description (Enable additional water flow strength controls - requires blue channel for strength) -} - -Property -{ - Name = %SUN_SPECULAR - Mask = 0x8 - Property (Sun specular) - Description (Activate for water sunshine) -} - -Property -{ - Name = %SPECULAR_MAP - Mask = 0x10 - Property (Specular map) - Description (Use specular map as separate texture) - DependencySet = $TEX_Specular - DependencyReset = $TEX_Specular - Hidden -} - -Property -{ - Name = %DEBUG_FLOW_MAP - Mask = 0x20 - Property (Debug flow map) - Description (Enable visualizing flow map) -} - -Property -{ - Name = %FOAM - Mask = 0x40 - Property (Foam) - Description (Enables foam) -} - -Property -{ - Name = %DECAL_MAP - Mask = 0x80 - Property (Decal map) - Description (Use tiling decal map as separate texture) - DependencySet = $TEX_Custom - DependencyReset = $TEX_Custom - Hidden -} - -Property -{ - Name = %WATER_TESSELLATION_DX11 - Mask = 0x80000000 - DependencySet = $HW_WaterTessellation - DependencyReset = $HW_WaterTessellation - Hidden -} diff --git a/Assets/Engine/Shaders/Waterfall.ext b/Assets/Engine/Shaders/Waterfall.ext deleted file mode 100644 index 8d0ff2cc73..0000000000 --- a/Assets/Engine/Shaders/Waterfall.ext +++ /dev/null @@ -1,45 +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. -// -// Original file Copyright Crytek GMBH or its affiliates, used under license. -// - -// Description: Water extension used by the editor -// for automatic shader generation (based on "Water" shader template) -// -//////////////////////////////////////////////////////////////////////////// - -Version (1.00) - -Property -{ - Name = %ENVIRONMENT_MAP - Mask = 0x1 - Property (Environment map) - Description (Use environment map as separate texture) - DependencyReset = $TEX_EnvCM -} - -Property -{ - Name = %SUN_SHADING - Mask = 0x2 - Property (Sun shading) - Description (Activate for water sunshading - for outdoors) -} - -Property -{ - Name = %FOAM - Mask = 0x4 - Property (Foam) - Description (Enables foam rendering) -} diff --git a/AutomatedTesting/Config/aws_resource_mappings.json b/AutomatedTesting/Config/aws_resource_mappings.json new file mode 100644 index 0000000000..03a611b749 --- /dev/null +++ b/AutomatedTesting/Config/aws_resource_mappings.json @@ -0,0 +1,6 @@ +{ + "AWSResourceMappings": {}, + "AccountId": "", + "Region": "us-west-2", + "Version": "1.0.0" +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake index 280c25bcf7..33c2bf8d5f 100644 --- a/AutomatedTesting/Gem/Code/runtime_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/runtime_dependencies.cmake @@ -45,4 +45,7 @@ set(GEM_DEPENDENCIES Gem::Atom_AtomBridge Gem::NvCloth Gem::Blast + Gem::AWSCore + Gem::AWSClientAuth + Gem::AWSMetrics ) diff --git a/AutomatedTesting/Gem/Code/tool_dependencies.cmake b/AutomatedTesting/Gem/Code/tool_dependencies.cmake index fc50707c12..c8eccab947 100644 --- a/AutomatedTesting/Gem/Code/tool_dependencies.cmake +++ b/AutomatedTesting/Gem/Code/tool_dependencies.cmake @@ -52,7 +52,12 @@ set(GEM_DEPENDENCIES Gem::LandscapeCanvas.Editor Gem::EMotionFX.Editor Gem::ImGui.Editor + Gem::Atom_RHI.Private + Gem::Atom_Feature_Common.Editor Gem::Atom_AtomBridge.Editor Gem::NvCloth.Editor Gem::Blast.Editor + Gem::AWSCore.Editor + Gem::AWSClientAuth + Gem::AWSMetrics ) diff --git a/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt new file mode 100644 index 0000000000..b406ea77de --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# 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. +# + +################################################################################ +# AWS Automated Tests +# Runs AWS Gems automation tests. +################################################################################ + +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS) + # Enable after installing NodeJS and CDK on jenkins Windows AMI. + #ly_add_pytest( + # NAME AutomatedTesting::AWSTests + # TEST_SUITE periodic + # TEST_SERIAL + # PATH ${CMAKE_CURRENT_LIST_DIR}/AWS/${PAL_PLATFORM_NAME}/ + # RUNTIME_DEPENDENCIES + # Legacy::Editor + # AZ::AssetProcessor + # AutomatedTesting.Assets + # COMPONENT + # AWS + #) +endif() diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py new file mode 100644 index 0000000000..455b3f94cb --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py @@ -0,0 +1,155 @@ +""" +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 os +import pytest +import boto3 + +import ly_test_tools.environment.process_utils as process_utils +from typing import List + + +class Cdk: + """ + Cdk class that provides methods to run cdk application commands. + Expects system to have NodeJS, AWS CLI and CDK installed globally and have their paths setup as env variables. + """ + def __init__(self, cdk_path: str, project: str, account_id: str, + workspace: pytest.fixture, session: boto3.session.Session): + """ + :param cdk_path: Path where cdk app.py is stored. + :param project: Project name used for cdk project name env variable. + :param account_id: AWS account id to use with cdk application. + :param workspace: ly_test_tools workspace fixture. + """ + self._cdk_env = os.environ.copy() + self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project + self._cdk_env['O3DE_AWS_DEPLOY_REGION'] = session.region_name + self._cdk_env['O3DE_AWS_DEPLOY_ACCOUNT'] = account_id + self._cdk_env['PATH'] = f'{workspace.paths.engine_root()}\\python;' + self._cdk_env['PATH'] + + credentials = session.get_credentials().get_frozen_credentials() + self._cdk_env['AWS_ACCESS_KEY_ID'] = credentials.access_key + self._cdk_env['AWS_SECRET_ACCESS_KEY'] = credentials.secret_key + self._cdk_env['AWS_SESSION_TOKEN'] = credentials.token + self._stacks = [] + self._cdk_path = cdk_path + + output = process_utils.check_output( + 'python -m pip install -r requirements.txt', + cwd=self._cdk_path, + env=self._cdk_env, + shell=True) + + def list(self) -> List[str]: + """ + lists cdk stack names + :return List of cdk stack names + """ + + if not self._cdk_path: + return [] + + list_cdk_application_cmd = ['cdk', 'list'] + output = process_utils.check_output( + list_cdk_application_cmd, + cwd=self._cdk_path, + env=self._cdk_env, + shell=True) + + return output.splitlines() + + def synthesize(self) -> None: + """ + Synthesizes all cdk stacks + """ + if not self._cdk_path: + return + + list_cdk_application_cmd = ['cdk', 'synth'] + + process_utils.check_output( + list_cdk_application_cmd, + cwd=self._cdk_path, + env=self._cdk_env, + shell=True) + + def deploy(self, context_variable: str = '') -> List[str]: + """ + Deploys all the CDK stacks. + :param context_variable: Context variable for enabling optional features. + :return List of deployed stack arns. + """ + if not self._cdk_path: + return [] + + deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never'] + if context_variable: + deploy_cdk_application_cmd.extend(['-c', f'{context_variable}']) + + output = process_utils.check_output( + deploy_cdk_application_cmd, + cwd=self._cdk_path, + env=self._cdk_env, + shell=True) + + stacks = [] + for line in output.splitlines(): + line_sections = line.split('/') + assert len(line_sections), 3 + stacks.append(line.split('/')[-2]) + + return stacks + + def destroy(self) -> None: + """ + Destroys the cdk application. + """ + destroy_cdk_application_cmd = ['cdk', 'destroy', '-f'] + process_utils.check_output( + destroy_cdk_application_cmd, + cwd=self._cdk_path, + env=self._cdk_env, + shell=True) + + self._stacks = [] + self._cdk_path = '' + + +@pytest.fixture(scope='function') +def cdk( + request: pytest.fixture, + project: str, + feature_name: str, + workspace: pytest.fixture, + aws_utils: pytest.fixture, + destroy_stacks_on_teardown: bool = True) -> Cdk: + """ + Fixture for setting up a Cdk + :param request: _pytest.fixtures.SubRequest class that handles getting + a pytest fixture from a pytest function/fixture. + :param project: Project name used for cdk project name env variable. + :param feature_name: Feature gem name to expect cdk folder in. + :param workspace: ly_test_tools workspace fixture. + :param aws_utils: aws_utils fixture. + :param destroy_stacks_on_teardown: option to control calling destroy ot the end of test. + :return Cdk class object. + """ + + cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk' + cdk_obj = Cdk(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session()) + + def teardown(): + if destroy_stacks_on_teardown: + cdk_obj.destroy() + request.addfinalizer(teardown) + + return cdk_obj diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py new file mode 100644 index 0000000000..5997701870 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/test_anonymous_credentials.py @@ -0,0 +1,78 @@ +""" +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.parametrize('level', ['AWS/ClientAuth']) +@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 TestAWSClientAuthAnonymousCredentials(object): + """ + Test class to verify AWS Cognito Identity pool anonymous authorization. + """ + + def test_anonymous_credentials(self, + level: str, + launcher: pytest.fixture, + cdk: pytest.fixture, + resource_mappings: pytest.fixture, + workspace: pytest.fixture, + asset_processor: pytest.fixture + ): + """ + Setup: Deploys cdk and updates resource mapping file. + Tests: Getting AWS credentials for no signed in user. + 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', level] + + with launcher.start(launch_ap=False): + result = log_monitor.monitor_log_for_lines( + expected_lines=['(Script) - Success anonymous credentials'], + unexpected_lines=['(Script) - Fail anonymous credentials'], + halt_on_unexpected=True, + ) + assert result, 'Anonymous credentials fetched successfully.' diff --git a/Tests/BuildSystems/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from Tests/BuildSystems/__init__.py rename to AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/__init__.py diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py new file mode 100644 index 0000000000..c8d8cff828 --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/resource_mappings/resource_mappings.py @@ -0,0 +1,137 @@ +""" +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 os +import pytest +import json + +AWS_RESOURCE_MAPPINGS_KEY = 'AWSResourceMappings' +AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY = 'AccountId' +AWS_RESOURCE_MAPPINGS_REGION_KEY = 'Region' + + +class ResourceMappings: + """ + ResourceMappings class that handles writing Cloud formation outputs to resource mappings json file in a project. + """ + + def __init__(self, file_path: str, region: str, feature_name: str, account_id: str, workspace: pytest.fixture, + cloud_formation_client): + """ + :param file_path: Path for the resource mapping file. + :param region: Region value for the resource mapping file. + :param feature_name: Feature gem name to use to append name to mappings key. + :param account_id: AWS account id value for the resource mapping file. + :param workspace: ly_test_tools workspace fixture. + :param cloud_formation_client: AWS cloud formation client. + """ + self._cdk_env = os.environ.copy() + self._cdk_env['PATH'] = f'{workspace.paths.engine_root()}\\python;' + self._cdk_env['PATH'] + self._resource_mapping_file_path = file_path + self._region = region + self._feature_name = feature_name + self._account_id = account_id + + assert os.path.exists(self._resource_mapping_file_path), \ + f'Invalid resource mapping file path {self._resource_mapping_file_path}' + self._client = cloud_formation_client + + def populate_output_keys(self, stacks=[]) -> None: + """ + Calls describe stacks on cloud formation service and persists outputs to resource mappings file. + :param stacks List of stack arns to describe and populate resource mappings with. + """ + for stack_name in stacks: + response = self._client.describe_stacks( + StackName=stack_name + ) + stacks = response.get('Stacks', []) + assert len(stacks) == 1, f'{stack_name} is invalid.' + + self.__write_resource_mappings(stacks[0].get('Outputs', [])) + + def __write_resource_mappings(self, outputs, append_feature_name = True) -> None: + with open(self._resource_mapping_file_path) as file_content: + resource_mappings = json.load(file_content) + + resource_mappings[AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY] = self._account_id + resource_mappings[AWS_RESOURCE_MAPPINGS_REGION_KEY] = self._region + + # Append new mappings. + resource_mappings[AWS_RESOURCE_MAPPINGS_KEY] = resource_mappings.get(AWS_RESOURCE_MAPPINGS_KEY, {}) + + for output in outputs: + if append_feature_name: + resource_key = f'{self._feature_name}.{output.get("OutputKey", "InvalidKey")}' + else: + resource_key = output.get("OutputKey", "InvalidKey") + resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key] = resource_mappings[ + AWS_RESOURCE_MAPPINGS_KEY].get(resource_key, {}) + resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key]['Type'] = 'AutomationTestType' + resource_mappings[AWS_RESOURCE_MAPPINGS_KEY][resource_key]['Name/ID'] = output.get('OutputValue', + 'InvalidId') + + with open(self._resource_mapping_file_path, 'w') as file_content: + json.dump(resource_mappings, file_content, indent=4) + + def clear_output_keys(self) -> None: + """ + Clears values of all resource mapping keys. Sets region to default to us-west-2 + """ + with open(self._resource_mapping_file_path) as file_content: + resource_mappings = json.load(file_content) + + resource_mappings[AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY] = '' + resource_mappings[AWS_RESOURCE_MAPPINGS_REGION_KEY] = 'us-west-2' + + # Append new mappings. + resource_mappings[AWS_RESOURCE_MAPPINGS_KEY] = resource_mappings.get(AWS_RESOURCE_MAPPINGS_KEY, {}) + resource_mappings[AWS_RESOURCE_MAPPINGS_KEY] = {} + + with open(self._resource_mapping_file_path, 'w') as file_content: + json.dump(resource_mappings, file_content, indent=4) + + self._resource_mapping_file_path = '' + self._region = '' + self._client = None + + +@pytest.fixture(scope='function') +def resource_mappings( + request: pytest.fixture, + project: str, + feature_name: str, + resource_mappings_filename: str, + workspace: pytest.fixture, + aws_utils: pytest.fixture) -> ResourceMappings: + """ + Fixture for setting up resource mappings file. + :param request: _pytest.fixtures.SubRequest class that handles getting + a pytest fixture from a pytest function/fixture. + :param project: Project to find resource mapping file. + :param feature_name: AWS Gem name that is prepended to resource mapping keys. + :param resource_mappings_filename: Name of resource mapping file. + :param workspace: ly_test_tools workspace fixture. + :param aws_utils: AWS utils fixture. + :return: ResourceMappings class object. + """ + + path = f'{workspace.paths.engine_root()}\\{project}\\Config\\{resource_mappings_filename}' + resource_mappings_obj = ResourceMappings(path, aws_utils.assume_session().region_name, feature_name, + aws_utils.assume_account_id(), workspace, + aws_utils.client('cloudformation')) + + def teardown(): + resource_mappings_obj.clear_output_keys() + + request.addfinalizer(teardown) + + return resource_mappings_obj diff --git a/Tests/BuildSystems/test_lib/__init__.py b/AutomatedTesting/Gem/PythonTests/AWS/__init__.py old mode 100755 new mode 100644 similarity index 99% rename from Tests/BuildSystems/test_lib/__init__.py rename to AutomatedTesting/Gem/PythonTests/AWS/__init__.py index 6ed3dc4bda..8caef52682 --- a/Tests/BuildSystems/test_lib/__init__.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/__init__.py @@ -7,4 +7,5 @@ distribution (the "License"). All use of this software is governed by the Licens 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. -""" \ No newline at end of file +""" + diff --git a/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py b/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py new file mode 100644 index 0000000000..7a15ba0abe --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/AWS/common/aws_utils.py @@ -0,0 +1,82 @@ +""" +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 boto3 +import pytest +import logging + +logger = logging.getLogger(__name__) + + +class AwsUtils: + + def __init__(self, arn: str, session_name: str, region_name: str): + local_session = boto3.Session(profile_name='default') + local_sts_client = local_session.client('sts') + self._local_account_id = local_sts_client.get_caller_identity()["Account"] + logger.info(f'Local Account Id: {self._local_account_id}') + + response = local_sts_client.assume_role(RoleArn=arn, RoleSessionName=session_name) + + self._assume_session = boto3.Session(aws_access_key_id=response['Credentials']['AccessKeyId'], + aws_secret_access_key=response['Credentials']['SecretAccessKey'], + aws_session_token=response['Credentials']['SessionToken'], + region_name=region_name) + + assume_sts_client = self._assume_session.client('sts') + assume_account_id = assume_sts_client.get_caller_identity()["Account"] + logger.info(f'Assume Account Id: {assume_account_id}') + self._assume_account_id = assume_account_id + + def client(self, service: str): + """ + Get the client for a specific AWS service from configured session + :return: Client for the AWS service. + """ + return self._assume_session.client(service) + + def assume_session(self): + return self._assume_session + + def local_account_id(self): + return self._local_account_id + + def assume_account_id(self): + return self._assume_account_id + + def destroy(self) -> None: + """ + clears stored session + """ + self._assume_session = None + + +@pytest.fixture(scope='function') +def aws_utils( + request: pytest.fixture, + assume_role_arn: str, + session_name: str, + region_name: str): + """ + Fixture for setting up a Cdk + :param request: _pytest.fixtures.SubRequest class that handles getting + a pytest fixture from a pytest function/fixture. + :param assume_role_arn: Role used to fetch temporary aws credentials, configure service clients with obtained credentials. + :param session_name: Session name to set. + :param region_name: AWS account region to set for session. + :return AWSUtils class object. + """ + aws_utils_obj = AwsUtils(assume_role_arn, session_name, region_name) + + def teardown(): + aws_utils_obj.destroy() + + request.addfinalizer(teardown) + + return aws_utils_obj diff --git a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt index d6f9ecff4b..c6ed6c7538 100644 --- a/AutomatedTesting/Gem/PythonTests/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/CMakeLists.txt @@ -56,5 +56,8 @@ add_subdirectory(editor) ## Streaming ## add_subdirectory(streaming) -## Streaming ## +## Smoke ## add_subdirectory(smoke) + +## AWS ## +add_subdirectory(AWS) diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_test_helper.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_test_helper.py index 74f34659c0..d375e69ac2 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_test_helper.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_test_helper.py @@ -98,11 +98,6 @@ class EditorTestHelper: self.test_success = False success = False - # Turn off any display info like FPS, as that will mess up our image comparisons - # Turn off antialiasing as well - general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") - general.idle_wait(1.0) return success # Test Teardown diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/pyside_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/pyside_utils.py index 10f060bba6..eed6a18614 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/pyside_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/pyside_utils.py @@ -18,7 +18,6 @@ from PySide2 import QtCore, QtWidgets, QtGui, QtTest from PySide2.QtWidgets import QAction, QWidget from PySide2.QtCore import Qt from PySide2.QtTest import QTest -import azlmbr.legacy.general as general import traceback import threading import types diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py index e701ff8d16..904075c747 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_AtomEditorComponents_AddedToEntity.py @@ -77,10 +77,10 @@ def run(): def verify_enter_exit_game_mode(component_name): general.enter_game_mode() - TestHelper.wait_for_condition(lambda: general.is_in_game_mode(), 1.0) + TestHelper.wait_for_condition(lambda: general.is_in_game_mode(), 2.0) general.log(f"{component_name}_test: Entered game mode: {general.is_in_game_mode()}") general.exit_game_mode() - TestHelper.wait_for_condition(lambda: not general.is_in_game_mode(), 1.0) + TestHelper.wait_for_condition(lambda: not general.is_in_game_mode(), 2.0) general.log(f"{component_name}_test: Exit game mode: {not general.is_in_game_mode()}") def verify_hide_unhide_entity(component_name, entity_obj): @@ -97,16 +97,16 @@ def run(): def verify_deletion_undo_redo(component_name, entity_obj): editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", entity_obj.id) - TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 1.0) + TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 2.0) general.log(f"{component_name}_test: Entity deleted: {not hydra.find_entity_by_name(entity_obj.name)}") general.undo() - TestHelper.wait_for_condition(lambda: hydra.find_entity_by_name(entity_obj.name) is not None, 1.0) + TestHelper.wait_for_condition(lambda: hydra.find_entity_by_name(entity_obj.name) is not None, 2.0) general.log(f"{component_name}_test: UNDO entity deletion works: " f"{hydra.find_entity_by_name(entity_obj.name) is not None}") general.redo() - TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 1.0) + TestHelper.wait_for_condition(lambda: not hydra.find_entity_by_name(entity_obj.name), 2.0) general.log(f"{component_name}_test: REDO entity deletion works: " f"{not hydra.find_entity_by_name(entity_obj.name)}") @@ -120,7 +120,7 @@ def run(): f"{not is_component_enabled(entity_obj.components[0])}") for component in components_to_add: entity_obj.add_component(component) - TestHelper.wait_for_condition(lambda: is_component_enabled(entity_obj.components[0]), 1.0) + TestHelper.wait_for_condition(lambda: is_component_enabled(entity_obj.components[0]), 2.0) general.log( f"{component_name}_test: Entity enabled after adding " f"required components: {is_component_enabled(entity_obj.components[0])}" @@ -135,7 +135,9 @@ def run(): # Delete all existing entities initially search_filter = azlmbr.entity.SearchFilter() all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter) + general.idle_wait_frames(1) editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities) + general.idle_wait_frames(1) class ComponentTests: """Test launcher for each component.""" @@ -147,9 +149,11 @@ def run(): def run_component_tests(self): # Run common and additional tests entity_obj = create_entity_undo_redo_component_addition(self.component_name) + general.idle_wait(0.5) # Enter/Exit game mode test verify_enter_exit_game_mode(self.component_name) + general.idle_wait(0.5) # Any additional tests are executed here for test in self.additional_tests: @@ -157,13 +161,16 @@ def run(): # Hide/Unhide entity test verify_hide_unhide_entity(self.component_name, entity_obj) + general.idle_wait(0.5) # Deletion/Undo/Redo test verify_deletion_undo_redo(self.component_name, entity_obj) + general.idle_wait(0.5) # DepthOfField Component camera_entity = hydra.Entity("camera_entity") camera_entity.create_entity(math.Vector3(512.0, 512.0, 34.0), ["Camera"]) + general.idle_wait(0.5) depth_of_field = "DepthOfField" ComponentTests( depth_of_field, diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py index 0b75760e05..b64a592c1d 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite.py @@ -163,7 +163,8 @@ class TestAtomEditorComponentsMain(object): ] unexpected_lines = [ - "failed to open", + "Trace::Assert", + "Trace::Error", "Traceback (most recent call last):", ] diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py index 208ef40d41..3eeb3ddf84 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_EditMenuOptions.py @@ -64,8 +64,6 @@ class TestEditMenuOptions(EditorTestHelper): ("Modify", "Transform Mode", "Move"), ("Modify", "Transform Mode", "Rotate"), ("Modify", "Transform Mode", "Scale"), - ("Lock Selection",), - ("Unlock All Entities",), ("Editor Settings", "Global Preferences"), ("Editor Settings", "Graphics Settings"), ("Editor Settings", "Editor Settings Manager"), diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py index 05ee802d19..173d658b3c 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_ViewMenuOptions.py @@ -50,7 +50,6 @@ class TestViewMenuOptions(EditorTestHelper): ("Center on Selection",), ("Show Quick Access Bar",), ("Viewport", "Wireframe"), - ("Viewport", "Grid Settings"), ("Viewport", "Go to Position"), ("Viewport", "Center on Selection"), ("Viewport", "Go to Location"), diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py b/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py index a75b3b36cd..c2d515e250 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Docking.py @@ -40,7 +40,7 @@ class TestDocking(object): @pytest.mark.test_case_id("C6376081") @pytest.mark.SUITE_periodic - def test_basic_docked_tools(self, request, editor, level, launcher_platform): + def test_Docking_BasicDockedTools(self, request, editor, level, launcher_platform): expected_lines = [ "The tools are all docked together in a tabbed widget", "Entity Outliner works when docked, can select an Entity", diff --git a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py index 251bd7cfed..70a22f9e2a 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py +++ b/AutomatedTesting/Gem/PythonTests/editor/test_Menus.py @@ -59,8 +59,6 @@ class TestMenus(object): "Move Action triggered", "Rotate Action triggered", "Scale Action triggered", - "Lock Selection Action triggered", - "Unlock All Entities Action triggered", "Global Preferences Action triggered", "Graphics Settings Action triggered", "Editor Settings Manager Action triggered", @@ -93,7 +91,6 @@ class TestMenus(object): "Center on Selection Action triggered", "Show Quick Access Bar Action triggered", "Wireframe Action triggered", - "Grid Settings Action triggered", "Go to Position Action triggered", "Center on Selection Action triggered", "Go to Location Action triggered", diff --git a/AutomatedTesting/Gem/PythonTests/scripting/Pane_PropertiesChanged_RetainsOnRestart.py b/AutomatedTesting/Gem/PythonTests/scripting/Pane_PropertiesChanged_RetainsOnRestart.py new file mode 100644 index 0000000000..3750fd290b --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/scripting/Pane_PropertiesChanged_RetainsOnRestart.py @@ -0,0 +1,161 @@ +""" +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. +""" + + +class Tests: + test_panes_visible = "All the test panes are opened" + close_pane_1 = "Test pane 1 is closed" + resize_pane_3 = "Test pane 3 resized successfully" + location_changed = "Location of test pane 2 changed successfully" + visiblity_retained = "Test pane retained its visiblity on Editor restart" + location_retained = "Test pane retained its location on Editor restart" + size_retained = "Test pane retained its size on Editor restart" + + +def Pane_PropertiesChanged_RetainsOnRestart(): + """ + Summary: + The Script Canvas window is opened to verify if Script canvas panes can retain its visibility, size and location + upon Editor restart. + + Expected Behavior: + The ScriptCanvas pane retain it's visiblity, size and location upon Editor restart. + + Test Steps: + 1) Open Script Canvas window (Tools > Script Canvas) + 2) Make sure test panes are open and visible + 3) Close test pane 1 + 4) Change dock location of test pane 2 + 5) Resize test pane 3 + 6) Restart Editor + 7) Verify if test pane 1 retain its visiblity + 8) Verify if location of test pane 2 is retained + 9) Verify if size of test pane 3 is retained + 10) Restore default layout and close SC window + + Note: + - This test file must be called from the Open 3D Engine Editor command terminal + - Any passed and failed tests are written to the Editor.log file. + Parsing the file or running a log_monitor are required to observe the test results. + + :return: None + """ + + import sys + + # Helper imports + from utils import Report + from utils import TestHelper as helper + import pyside_utils + + # Lumberyard Imports + import azlmbr.legacy.general as general + + # Pyside imports + from PySide2 import QtCore, QtWidgets + from PySide2.QtCore import Qt + + # Constants + TEST_CONDITION = sys.argv[1] + TEST_PANE_1 = "NodePalette" # pane used to test visibility + TEST_PANE_2 = "VariableManager" # pane used to test location + TEST_PANE_3 = "NodeInspector" # pane used to test size + SCALE_INT = 10 # Random resize scale integer + DOCKAREA = Qt.TopDockWidgetArea # Preferred top area since no widget is docked on top + + def click_menu_option(window, option_text): + action = pyside_utils.find_child_by_pattern(window, {"text": option_text, "type": QtWidgets.QAction}) + action.trigger() + + def find_pane(window, pane_name): + return window.findChild(QtWidgets.QDockWidget, pane_name) + + # Test starts here + general.idle_enable(True) + + # 1) Open Script Canvas window (Tools > Script Canvas) + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + + if TEST_CONDITION == "before_restart": + # 2) Make sure test panes are open and visible + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + click_menu_option(sc, "Restore Default Layout") + test_pane_1 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_1) + test_pane_2 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_2) + test_pane_3 = sc.findChild(QtWidgets.QDockWidget, TEST_PANE_3) + + result = test_pane_1.isVisible() and test_pane_2.isVisible() and test_pane_3.isVisible() + Report.info(f"{Tests.test_panes_visible}: {result}") + + # 3) Close test pane + test_pane_1.close() + Report.info(f"{Tests.close_pane_1}: {not test_pane_1.isVisible()}") + + # 4) Change dock location of test pane 2 + sc_main = sc.findChild(QtWidgets.QMainWindow) + sc_main.addDockWidget(DOCKAREA, find_pane(sc_main, TEST_PANE_2), QtCore.Qt.Vertical) + Report.info(f"{Tests.location_changed}: {sc_main.dockWidgetArea(find_pane(sc_main, TEST_PANE_2)) == DOCKAREA}") + + # 5) Resize test pane 3 + initial_size = test_pane_3.frameSize() + test_pane_3.resize(initial_size.width() + SCALE_INT, initial_size.height() + SCALE_INT) + new_size = test_pane_3.frameSize() + resize_success = ( + abs(initial_size.width() - new_size.width()) == abs(initial_size.height() - new_size.height()) == SCALE_INT + ) + Report.info(f"{Tests.resize_pane_3}: {resize_success}") + + if TEST_CONDITION == "after_restart": + try: + # 6) Restart Editor + # Restart is not possible through script and hence it is done by running the same file as 2 tests with a + # condition as before_test and after_test + + # 7) Verify if test pane 1 retain its visiblity + # This pane closed before restart and expected that pane should not be visible. + editor_window = pyside_utils.get_editor_main_window() + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + Report.info(f"{Tests.visiblity_retained}: {not find_pane(sc, TEST_PANE_1).isVisible()}") + + # 8) Verify if location of test pane 2 is retained + # This pane was set at DOCKAREA lcoation before restart + sc_main = sc.findChild(QtWidgets.QMainWindow) + Report.info( + f"{Tests.location_retained}: {sc_main.dockWidgetArea(find_pane(sc_main, TEST_PANE_2)) == DOCKAREA}" + ) + + # 9) Verify if size of test pane 3 is retained + # Verifying if size retained by checking current size not matching with default size + test_pane_3 = find_pane(sc, TEST_PANE_3) + retained_size = test_pane_3.frameSize() + click_menu_option(sc, "Restore Default Layout") + actual_size = test_pane_3.frameSize() + Report.info(f"{Tests.size_retained}: {retained_size != actual_size}") + + finally: + # 10) Restore default layout and close SC window + general.open_pane("Script Canvas") + helper.wait_for_condition(lambda: general.is_pane_visible("Script Canvas"), 5.0) + sc = editor_window.findChild(QtWidgets.QDockWidget, "Script Canvas") + click_menu_option(sc, "Restore Default Layout") + sc.close() + + +if __name__ == "__main__": + import ImportPathHelper as imports + + imports.init() + + from utils import Report + + Report.start_test(Pane_PropertiesChanged_RetainsOnRestart) diff --git a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py index c42c9f1a03..9180c1b44c 100755 --- a/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py +++ b/AutomatedTesting/Gem/PythonTests/scripting/TestSuite_Periodic.py @@ -76,14 +76,8 @@ class TestAutomation(TestAutomationBase): from . import ScriptCanvasComponent_OnEntityActivatedDeactivated_PrintMessage as test_module self._run_test(request, workspace, editor, test_module) -<<<<<<< HEAD def test_NodePalette_HappyPath_ClearSelection(self, request, workspace, editor, launcher_platform, project): from . import NodePalette_HappyPath_ClearSelection as test_module -======= - @pytest.mark.test_case_id("T92562993") - def test_NodePalette_ClearSelection(self, request, workspace, editor, launcher_platform, project): - from . import NodePalette_ClearSelection as test_module ->>>>>>> main self._run_test(request, workspace, editor, test_module) @pytest.mark.parametrize("level", ["tmp_level"]) @@ -119,7 +113,6 @@ class TestAutomation(TestAutomationBase): from . import Debugger_HappyPath_TargetMultipleGraphs as test_module self._run_test(request, workspace, editor, test_module) - @pytest.mark.test_case_id("T92569137") def test_Debugging_TargetMultipleGraphs(self, request, workspace, editor, launcher_platform, project): from . import Debugging_TargetMultipleGraphs as test_module self._run_test(request, workspace, editor, test_module) @@ -262,3 +255,37 @@ class TestScriptCanvasTests(object): auto_test_mode=False, timeout=60, ) + + @pytest.mark.parametrize( + "config", + [ + { + "cfg_args": "before_restart", + "expected_lines": [ + "All the test panes are opened: True", + "Test pane 1 is closed: True", + "Location of test pane 2 changed successfully: True", + "Test pane 3 resized successfully: True", + ], + }, + { + "cfg_args": "after_restart", + "expected_lines": [ + "Test pane retained its visiblity on Editor restart: True", + "Test pane retained its location on Editor restart: True", + "Test pane retained its size on Editor restart: True", + ], + }, + ], + ) + def test_Pane_PropertiesChanged_RetainsOnRestart(self, request, editor, config, project, launcher_platform): + hydra.launch_and_validate_results( + request, + TEST_DIRECTORY, + editor, + "Pane_PropertiesChanged_RetainsOnRestart.py", + config.get('expected_lines'), + cfg_args=[config.get('cfg_args')], + auto_test_mode=False, + timeout=60, + ) diff --git a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AssetBuilder_Works.py b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AssetBuilder_Works.py index 10df59e086..5381fd9fd8 100644 --- a/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AssetBuilder_Works.py +++ b/AutomatedTesting/Gem/PythonTests/smoke/test_CLITool_AssetBuilder_Works.py @@ -20,7 +20,6 @@ import subprocess @pytest.mark.SUITE_smoke class TestCLIToolAssetBuilderWorks(object): - @pytest.mark.xfail(reason="Ignoring failure temporarily - SPEC-6905") def test_CLITool_AssetBuilder_Works(self, build_directory): file_path = os.path.join(build_directory, "AssetBuilder") help_message = "AssetBuilder is part of the Asset Processor" diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/ClientAuth.ly b/AutomatedTesting/Levels/AWS/ClientAuth/ClientAuth.ly new file mode 100644 index 0000000000..af8a7f5c8e --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/ClientAuth.ly @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0f4d4e0155feaa76c80a14128000a0fd9570ab76e79f4847eaef9006324a4d2 +size 9084 diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/ConitoAnonymousAuthorization.scriptcanvas b/AutomatedTesting/Levels/AWS/ClientAuth/ConitoAnonymousAuthorization.scriptcanvas new file mode 100644 index 0000000000..ef03c66b16 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/ConitoAnonymousAuthorization.scriptcanvas @@ -0,0 +1,2313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/LevelData/Environment.xml b/AutomatedTesting/Levels/AWS/ClientAuth/LevelData/Environment.xml new file mode 100644 index 0000000000..d4e3d33551 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/LevelData/Environment.xml @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/LevelData/TimeOfDay.xml b/AutomatedTesting/Levels/AWS/ClientAuth/LevelData/TimeOfDay.xml new file mode 100644 index 0000000000..d827d4da29 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/LevelData/TimeOfDay.xml @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/filelist.xml b/AutomatedTesting/Levels/AWS/ClientAuth/filelist.xml new file mode 100644 index 0000000000..f69a99fe37 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/filelist.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/level.pak b/AutomatedTesting/Levels/AWS/ClientAuth/level.pak new file mode 100644 index 0000000000..1ae0bb1f7a --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/level.pak @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4900bdf28654e21032e69957f2762fa0a3b93a4b82163267a1f10f19f6d78692 +size 3795 diff --git a/AutomatedTesting/Levels/AWS/ClientAuth/tags.txt b/AutomatedTesting/Levels/AWS/ClientAuth/tags.txt new file mode 100644 index 0000000000..0d6c1880e7 --- /dev/null +++ b/AutomatedTesting/Levels/AWS/ClientAuth/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/awscoreconfiguration.setreg b/AutomatedTesting/Registry/awscoreconfiguration.setreg new file mode 100644 index 0000000000..ca110eb103 --- /dev/null +++ b/AutomatedTesting/Registry/awscoreconfiguration.setreg @@ -0,0 +1,10 @@ +{ + "Amazon": + { + "AWSCore": + { + "ProfileName": "default", + "ResourceMappingConfigFileName": "aws_resource_mappings.json" + } + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ee08a8d16..63177e9d60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,13 +124,13 @@ foreach(external_directory ${LY_EXTERNAL_SUBDIRS}) string(SUBSTRING ${full_directory_hash} 0 8 full_directory_hash) # Use the last directory as the suffix path to use for the Binary Directory get_filename_component(directory_name ${external_directory} NAME) - add_subdirectory(${external_directory} ${CMAKE_BINARY_DIR}/${directory_name}-${full_directory_hash}) + add_subdirectory(${external_directory} ${CMAKE_BINARY_DIR}/External/${directory_name}-${full_directory_hash}) endforeach() # The following steps have to be done after all targets are registered: # 1. generate a settings registry .setreg file for all ly_add_project_dependencies() and ly_add_target_dependencies() calls # to provide applications with the filenames of gem modules to load -# This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIE as MANUALLY_ADDED_DEPENDENCIES +# This must be done before ly_delayed_target_link_libraries() as that inserts BUILD_DEPENDENCIES as MANUALLY_ADDED_DEPENDENCIES # if the build dependency is a MODULE_LIBRARY. That would cause a false load dependency to be generated ly_delayed_generate_settings_registry() # 2. link targets where the dependency was yet not declared, we need to have the declaration so we do different diff --git a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp index 9dbe8f7264..7fd8a639c3 100644 --- a/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp +++ b/Code/Framework/AzCore/Platform/Common/UnixLike/AzCore/Utils/Utils_UnixLike.cpp @@ -41,10 +41,6 @@ namespace AZ if (const char* homePath = std::getenv("HOME"); homePath != nullptr) { AZ::IO::FixedMaxPath path{homePath}; - if (!path.empty()) - { - path /= ".o3de"; - } return path.Native(); } return {}; diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp index 497d67bef1..cd250b71a9 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.cpp @@ -21,7 +21,7 @@ namespace AzPhysics namespace { const float TimestepMin = 0.001f; //1000fps - const float TimestepMax = 0.05f; //20fps + const float TimestepMax = 0.1f; //10fps } AZ_CLASS_ALLOCATOR_IMPL(SystemConfiguration, AZ::SystemAllocator, 0); diff --git a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h index 3de4dafd8c..0a00d627a7 100644 --- a/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h +++ b/Code/Framework/AzFramework/AzFramework/Physics/Configuration/SystemConfiguration.h @@ -34,7 +34,7 @@ namespace AzPhysics static constexpr float DefaultFixedTimestep = 0.0166667f; //! Value represents 1/60th or 60 FPS. - float m_maxTimestep = 1.f / 20.f; //!< Maximum fixed timestep in seconds to run the physics update. + float m_maxTimestep = 0.1f; //!< Maximum fixed timestep in seconds to run the physics update (10FPS). float m_fixedTimestep = DefaultFixedTimestep; //!< Timestep in seconds to run the physics update. See DefaultFixedTimestep. AZ::u64 m_raycastBufferSize = 32; //!< Maximum number of hits that will be returned from a raycast. diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h new file mode 100644 index 0000000000..47388c56c3 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionHandlingRequests.h @@ -0,0 +1,78 @@ +/* + * 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 + +namespace AzFramework +{ + //! SessionConnectionConfig + //! The properties for handling join session request. + struct SessionConnectionConfig + { + // A unique identifier for registered player in session. + AZStd::string m_playerSessionId; + + // The DNS identifier assigned to the instance that is running the session. + AZStd::string m_dnsName; + + // The IP address of the session. + AZStd::string m_ipAddress; + + // The port number for the session. + uint16_t m_port; + }; + + //! SessionConnectionConfig + //! The properties for handling player connect/disconnect + struct PlayerConnectionConfig + { + // A unique identifier for player connection. + uint32_t m_playerConnectionId; + + // A unique identifier for registered player in session. + AZStd::string m_playerSessionId; + }; + + //! ISessionHandlingClientRequests + //! The session handling events to invoke multiplayer component handle the work on client side + class ISessionHandlingClientRequests + { + public: + // Handle the player join session process + // @param sessionConnectionConfig The required properties to handle the player join session process + // @return The result of player join session process + virtual bool HandlePlayerJoinSession(const SessionConnectionConfig& sessionConnectionConfig) = 0; + + // Handle the player leave session process + virtual void HandlePlayerLeaveSession() = 0; + }; + + //! ISessionHandlingServerRequests + //! The session handling events to invoke server provider handle the work on server side + class ISessionHandlingServerRequests + { + public: + // Handle the destroy session process + virtual void HandleDestroySession() = 0; + + // Validate the player join session process + // @param playerConnectionConfig The required properties to validate the player join session process + // @return The result of player join session validation + virtual bool ValidatePlayerJoinSession(const PlayerConnectionConfig& playerConnectionConfig) = 0; + + // Handle the player leave session process + // @param playerConnectionConfig The required properties to handle the player leave session process + virtual void HandlePlayerLeaveSession(const PlayerConnectionConfig& playerConnectionConfig) = 0; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp new file mode 100644 index 0000000000..4eb42ab815 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.cpp @@ -0,0 +1,130 @@ +/* + * 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 + +namespace AzFramework +{ + void CreateSessionRequest::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("creatorId", &CreateSessionRequest::m_creatorId) + ->Field("sessionProperties", &CreateSessionRequest::m_sessionProperties) + ->Field("sessionName", &CreateSessionRequest::m_sessionName) + ->Field("maxPlayer", &CreateSessionRequest::m_maxPlayer) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("CreateSessionRequest", "The container for CreateSession request parameters") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_creatorId, + "CreatorId", "A unique identifier for a player or entity creating the session") + ->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_sessionProperties, + "SessionProperties", "A collection of custom properties for a session") + ->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_sessionName, + "SessionName", "A descriptive label that is associated with a session") + ->DataElement(AZ::Edit::UIHandlers::Default, &CreateSessionRequest::m_maxPlayer, + "MaxPlayer", "The maximum number of players that can be connected simultaneously to the session") + ; + } + } + } + + void SearchSessionsRequest::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("filterExpression", &SearchSessionsRequest::m_filterExpression) + ->Field("sortExpression", &SearchSessionsRequest::m_sortExpression) + ->Field("maxResult", &SearchSessionsRequest::m_maxResult) + ->Field("nextToken", &SearchSessionsRequest::m_nextToken) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("SearchSessionsRequest", "The container for SearchSessions request parameters") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_filterExpression, + "FilterExpression", "String containing the search criteria for the session search") + ->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_sortExpression, + "SortExpression", "Instructions on how to sort the search results") + ->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_maxResult, + "MaxResult", "The maximum number of results to return") + ->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsRequest::m_nextToken, + "NextToken", "A token that indicates the start of the next sequential page of results") + ; + } + } + } + + void SearchSessionsResponse::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("sessionConfigs", &SearchSessionsResponse::m_sessionConfigs) + ->Field("nextToken", &SearchSessionsResponse::m_nextToken) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("SearchSessionsResponse", "The container for SearchSession request results") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsResponse::m_sessionConfigs, + "SessionConfigs", "A collection of sessions that match the search criteria and sorted in specific order") + ->DataElement(AZ::Edit::UIHandlers::Default, &SearchSessionsResponse::m_nextToken, + "NextToken", "A token that indicates the start of the next sequential page of results") + ; + } + } + } + + void JoinSessionRequest::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("sessionId", &JoinSessionRequest::m_sessionId) + ->Field("playerId", &JoinSessionRequest::m_playerId) + ->Field("playerData", &JoinSessionRequest::m_playerData) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("JoinSessionRequest", "The container for JoinSession request parameters") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &JoinSessionRequest::m_sessionId, + "SessionId", "A unique identifier for the session") + ->DataElement(AZ::Edit::UIHandlers::Default, &JoinSessionRequest::m_playerId, + "PlayerId", "A unique identifier for a player. Player IDs are developer-defined") + ->DataElement(AZ::Edit::UIHandlers::Default, &JoinSessionRequest::m_playerData, + "PlayerData", "Developer-defined information related to a player") + ; + } + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h new file mode 100644 index 0000000000..9d21a7f282 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/ISessionRequests.h @@ -0,0 +1,192 @@ +/* + * 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 AzFramework +{ + struct SessionConfig; + + //! CreateSessionRequest + //! The container for CreateSession request parameters. + struct CreateSessionRequest + { + AZ_RTTI(CreateSessionRequest, "{E39C2A45-89C9-4CFB-B337-9734DC798930}"); + static void Reflect(AZ::ReflectContext* context); + + CreateSessionRequest() = default; + virtual ~CreateSessionRequest() = default; + + // A unique identifier for a player or entity creating the session. + AZStd::string m_creatorId; + + // A collection of custom properties for a session. + AZStd::unordered_map m_sessionProperties; + + // A descriptive label that is associated with a session. + AZStd::string m_sessionName; + + // The maximum number of players that can be connected simultaneously to the session. + uint64_t m_maxPlayer; + }; + + //! SearchSessionsRequest + //! The container for SearchSessions request parameters. + struct SearchSessionsRequest + { + AZ_RTTI(SearchSessionsRequest, "{B49207A8-8549-4ADB-B7D9-D7A4932F9B4B}"); + static void Reflect(AZ::ReflectContext* context); + + SearchSessionsRequest() = default; + virtual ~SearchSessionsRequest() = default; + + // String containing the search criteria for the session search. If no filter expression is included, the request returns results + // for all active sessions. + AZStd::string m_filterExpression; + + // Instructions on how to sort the search results. If no sort expression is included, the request returns results in random order. + AZStd::string m_sortExpression; + + // The maximum number of results to return. + uint8_t m_maxResult; + + // A token that indicates the start of the next sequential page of results. + AZStd::string m_nextToken; + }; + + //! SearchSessionsResponse + //! The container for SearchSession request results. + struct SearchSessionsResponse + { + AZ_RTTI(SearchSessionsResponse, "{F93DE7DC-D381-4E08-8A3B-0B08F7C38714}"); + static void Reflect(AZ::ReflectContext* context); + + SearchSessionsResponse() = default; + virtual ~SearchSessionsResponse() = default; + + // A collection of sessions that match the search criteria and sorted in specific order. + AZStd::vector m_sessionConfigs; + + // A token that indicates the start of the next sequential page of results. + AZStd::string m_nextToken; + }; + + //! JoinSessionRequest + //! The container for JoinSession request parameters. + struct JoinSessionRequest + { + AZ_RTTI(JoinSessionRequest, "{519769E8-3CDE-4385-A0D7-24DBB3685657}"); + static void Reflect(AZ::ReflectContext* context); + + JoinSessionRequest() = default; + virtual ~JoinSessionRequest() = default; + + // A unique identifier for the session. + AZStd::string m_sessionId; + + // A unique identifier for a player. Player IDs are developer-defined. + AZStd::string m_playerId; + + // Developer-defined information related to a player. + AZStd::string m_playerData; + }; + + //! ISessionRequests + //! Pure virtual session interface class to abstract the details of session handling from application code. + class ISessionRequests + { + public: + AZ_RTTI(ISessionRequests, "{D6C41A71-DD8D-47FE-8515-FAF90670AE2F}"); + + ISessionRequests() = default; + virtual ~ISessionRequests() = default; + + // Create a session for players to find and join. + // @param createSessionRequest The request of CreateSession operation + // @return The request id if session creation request succeeds; empty if it fails + virtual AZStd::string CreateSession(const CreateSessionRequest& createSessionRequest) = 0; + + // Retrieve all active sessions that match the given search criteria and sorted in specific order. + // @param searchSessionsRequest The request of SearchSessions operation + // @return The response of SearchSessions operation + virtual SearchSessionsResponse SearchSessions(const SearchSessionsRequest& searchSessionsRequest) const = 0; + + // Reserve an open player slot in a session, and perform connection from client to server. + // @param joinSessionRequest The request of JoinSession operation + // @return True if joining session succeeds; False otherwise + virtual bool JoinSession(const JoinSessionRequest& joinSessionRequest) = 0; + + // Disconnect player from session. + virtual void LeaveSession() = 0; + }; + + //! ISessionAsyncRequests + //! Async version of ISessionRequests + class ISessionAsyncRequests + { + public: + AZ_RTTI(ISessionAsyncRequests, "{471542AF-96B9-4930-82FE-242A4E68432D}"); + + ISessionAsyncRequests() = default; + virtual ~ISessionAsyncRequests() = default; + + // CreateSession Async + // @param createSessionRequest The request of CreateSession operation + virtual void CreateSessionAsync(const CreateSessionRequest& createSessionRequest) = 0; + + // SearchSessions Async + // @param searchSessionsRequest The request of SearchSessions operation + virtual void SearchSessionsAsync(const SearchSessionsRequest& searchSessionsRequest) const = 0; + + // JoinSession Async + // @param joinSessionRequest The request of JoinSession operation + virtual void JoinSessionAsync(const JoinSessionRequest& joinSessionRequest) = 0; + + // LeaveSession Async + virtual void LeaveSessionAsync() = 0; + }; + + //! SessionAsyncRequestNotifications + //! The notifications correspond to session async requests + class SessionAsyncRequestNotifications + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + // OnCreateSessionAsyncComplete is fired once CreateSessionAsync completes + // @param createSessionResponse The request id if session creation request succeeds; empty if it fails + virtual void OnCreateSessionAsyncComplete(const AZStd::string& createSessionReponse) = 0; + + // OnSearchSessionsAsyncComplete is fired once SearchSessionsAsync completes + // @param searchSessionsResponse The response of SearchSessions call + virtual void OnSearchSessionsAsyncComplete(const SearchSessionsResponse& searchSessionsResponse) = 0; + + // OnJoinSessionAsyncComplete is fired once JoinSessionAsync completes + // @param joinSessionsResponse True if joining session succeeds; False otherwise + virtual void OnJoinSessionAsyncComplete(bool joinSessionsResponse) = 0; + + // OnLeaveSessionAsyncComplete is fired once LeaveSessionAsync completes + virtual void OnLeaveSessionAsyncComplete() = 0; + }; + using SessionAsyncRequestNotificationBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp new file mode 100644 index 0000000000..12c5163031 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.cpp @@ -0,0 +1,74 @@ +/* + * 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 + +namespace AzFramework +{ + void SessionConfig::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("creationTime", &SessionConfig::m_creationTime) + ->Field("terminationTime", &SessionConfig::m_terminationTime) + ->Field("creatorId", &SessionConfig::m_creatorId) + ->Field("sessionProperties", &SessionConfig::m_sessionProperties) + ->Field("sessionId", &SessionConfig::m_sessionId) + ->Field("sessionName", &SessionConfig::m_sessionName) + ->Field("dnsName", &SessionConfig::m_dnsName) + ->Field("ipAddress", &SessionConfig::m_ipAddress) + ->Field("port", &SessionConfig::m_port) + ->Field("maxPlayer", &SessionConfig::m_maxPlayer) + ->Field("currentPlayer", &SessionConfig::m_currentPlayer) + ->Field("status", &SessionConfig::m_status) + ->Field("statusReason", &SessionConfig::m_statusReason) + ; + + if (AZ::EditContext* editContext = serializeContext->GetEditContext()) + { + editContext->Class("SessionConfig", "Properties describing a session") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_creationTime, + "CreationTime", "A time stamp indicating when this session was created. Format is a number expressed in Unix time as milliseconds.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_terminationTime, + "TerminationTime", "A time stamp indicating when this data object was terminated. Same format as creation time.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_creatorId, + "CreatorId", "A unique identifier for a player or entity creating the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionProperties, + "SessionProperties", "A collection of custom properties for a session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionId, + "SessionId", "A unique identifier for the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_sessionName, + "SessionName", "A descriptive label that is associated with a session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_dnsName, + "DnsName", "The DNS identifier assigned to the instance that is running the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_ipAddress, + "IpAddress", "The IP address of the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_port, + "Port", "The port number for the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_maxPlayer, + "MaxPlayer", "The maximum number of players that can be connected simultaneously to the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_currentPlayer, + "CurrentPlayer", "Number of players currently in the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_status, + "Status", "Current status of the session.") + ->DataElement(AZ::Edit::UIHandlers::Default, &AzFramework::SessionConfig::m_statusReason, + "StatusReason", "Provides additional information about session status."); + } + } + } +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h new file mode 100644 index 0000000000..22d1c9e875 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionConfig.h @@ -0,0 +1,70 @@ +/* +* 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 AzFramework +{ + //! SessionConfig + //! Properties describing a session. + struct SessionConfig + { + AZ_RTTI(SessionConfig, "{992DD4BE-8BA5-4071-8818-B99FD2952086}"); + static void Reflect(AZ::ReflectContext* context); + + SessionConfig() = default; + virtual ~SessionConfig() = default; + + // A time stamp indicating when this session was created. Format is a number expressed in Unix time as milliseconds. + uint64_t m_creationTime; + + // A time stamp indicating when this data object was terminated. Same format as creation time. + uint64_t m_terminationTime; + + // A unique identifier for a player or entity creating the session. + AZStd::string m_creatorId; + + // A collection of custom properties for a session. + AZStd::unordered_map m_sessionProperties; + + // A unique identifier for the session. + AZStd::string m_sessionId; + + // A descriptive label that is associated with a session. + AZStd::string m_sessionName; + + // The DNS identifier assigned to the instance that is running the session. + AZStd::string m_dnsName; + + // The IP address of the session. + AZStd::string m_ipAddress; + + // The port number for the session. + uint16_t m_port; + + // The maximum number of players that can be connected simultaneously to the session. + uint64_t m_maxPlayer; + + // Number of players currently in the session. + uint64_t m_currentPlayer; + + // Current status of the session. + AZStd::string m_status; + + // Provides additional information about session status. + AZStd::string m_statusReason; + }; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h new file mode 100644 index 0000000000..a61c995db7 --- /dev/null +++ b/Code/Framework/AzFramework/AzFramework/Session/SessionNotifications.h @@ -0,0 +1,47 @@ +/* + * 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 + +namespace AzFramework +{ + struct SessionConfig; + + //! SessionNotifications + //! The session notifications to listen for performing required operations + class SessionNotifications + : public AZ::EBusTraits + { + public: + ////////////////////////////////////////////////////////////////////////// + // EBusTraits overrides + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; + ////////////////////////////////////////////////////////////////////////// + + // OnSessionHealthCheck is fired in health check process + // @return The result of all OnSessionHealthCheck + virtual bool OnSessionHealthCheck() = 0; + + // OnCreateSessionBegin is fired at the beginning of session creation + // @param sessionConfig The properties to describe a session + // @return The result of all OnCreateSessionBegin notifications + virtual bool OnCreateSessionBegin(const SessionConfig& sessionConfig) = 0; + + // OnDestroySessionBegin is fired at the beginning of session termination + // @return The result of all OnDestroySessionBegin notifications + virtual bool OnDestroySessionBegin() = 0; + }; + using SessionNotificationBus = AZ::EBus; +} // namespace AzFramework diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 6799ea9353..8045766686 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -215,6 +216,13 @@ namespace AzFramework return clone; } + AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, + EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext) + { + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, templateToCloneEntityIdMap, &serializeContext); + } + bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) { Ticket& ticket = GetTicketPayload(*request.m_ticket); @@ -230,48 +238,37 @@ namespace AzFramework const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); size_t entitiesToSpawnSize = entitiesToSpawn.size(); + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + // Reserve buffers spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); - ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesToSpawnSize); + spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); - // TEMP: To be replaced by IdUtils::Remapper - using EntityIdMap = AZStd::unordered_map; - EntityIdMap templateToCloneIdMap; - // \TEMP - - // Clone the entities from Spawnable + // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); - spawnedEntities.push_back(clone); + spawnedEntities.emplace_back(clone); spawnedEntityIndices.push_back(i); + } - // TEMP: To be replaced by IdUtils::Remapper - templateToCloneIdMap[entityTemplate.GetId()] = clone->GetId(); - - // Update TransformComponent parent Id. It is guaranteed for the entities array to be sorted from parent->child here. - auto* transformComponent = clone->FindComponent(); - AZ::EntityId parentId = transformComponent->GetParentId(); - if (parentId.IsValid()) - { - auto it = templateToCloneIdMap.find(parentId); - if (it != templateToCloneIdMap.end()) - { - transformComponent->SetParentRelative(it->second); - } - else - { - AZ_Warning( - "SpawnableEntitiesManager", false, "Entity %s doesn't have the parent entity %s present in the spawnable", - clone->GetName().c_str(), parentId.ToString().data()); - } - } - // \TEMP + // loadAll is true if every entity has been spawned only once + if (spawnedEntities.size() == entitiesToSpawnSize) + { + ticket.m_loadAll = true; + } + else + { + // Case where there were already spawns from a previous request + ticket.m_loadAll = false; } // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. @@ -437,10 +434,23 @@ namespace AzFramework // to load every, simply start over. ticket.m_spawnedEntityIndices.clear(); - size_t entitiesSize = entities.size(); - for (size_t i = 0; i < entitiesSize; ++i) + size_t entitiesToSpawnSize = entities.size(); + + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); + + // Mark all indices as spawned + for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[i], serializeContext)); + const AZ::Entity& entityTemplate = *entities[i]; + + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + + ticket.m_spawnedEntities.emplace_back(clone); ticket.m_spawnedEntityIndices.push_back(i); } } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index 54f48055fd..e20f58ac76 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -29,6 +29,8 @@ namespace AZ namespace AzFramework { + using EntityIdMap = AZStd::unordered_map; + class SpawnableEntitiesManager : public SpawnableEntitiesInterface::Registrar { @@ -142,7 +144,11 @@ namespace AzFramework using Requests = AZStd::variant; - AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext); + AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, + AZ::SerializeContext& serializeContext); + + AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, + EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext); diff --git a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake index 8cff479ec8..13dff43f68 100644 --- a/Code/Framework/AzFramework/AzFramework/azframework_files.cmake +++ b/Code/Framework/AzFramework/AzFramework/azframework_files.cmake @@ -188,6 +188,12 @@ set(FILES Script/ScriptDebugMsgReflection.h Script/ScriptRemoteDebugging.cpp Script/ScriptRemoteDebugging.h + Session/ISessionHandlingRequests.h + Session/ISessionRequests.cpp + Session/ISessionRequests.h + Session/SessionConfig.cpp + Session/SessionConfig.h + Session/SessionNotifications.h StreamingInstall/StreamingInstall.h StreamingInstall/StreamingInstall.cpp StreamingInstall/StreamingInstallRequests.h diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp index 8c371baf8c..890253b528 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.cpp @@ -27,6 +27,7 @@ AZ_PUSH_DISABLE_WARNING(4244 4251 4800, "-Wunknown-warning-option") // 4244: con #include #include #include +#include AZ_POP_DISABLE_WARNING static const int LabelColumnStretch = 2; @@ -121,6 +122,19 @@ namespace AzToolsFramework setLayout(m_mainLayout); } + void PropertyRowWidget::paintEvent(QPaintEvent* event) + { + QStylePainter p(this); + + if (CanBeReordered()) + { + const QPen linePen(QColor(0x3B3E3F)); + p.setPen(linePen); + int indent = m_treeDepth * m_treeIndentation; + p.drawLine(event->rect().topLeft() + QPoint(indent, 0), event->rect().topRight()); + } + } + bool PropertyRowWidget::HasChildWidgetAlready() const { return m_childWidget != nullptr; @@ -1661,6 +1675,20 @@ namespace AzToolsFramework m_nameLabel->setFilter(m_currentFilterString); } + bool PropertyRowWidget::CanChildrenBeReordered() const + { + return m_containerEditable; + } + + bool PropertyRowWidget::CanBeReordered() const + { + if (!m_parentRow) + { + return false; + } + + return m_parentRow->CanChildrenBeReordered(); + } } #include "UI/PropertyEditor/moc_PropertyRowWidget.cpp" diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx index e4b538ccdc..79121403c9 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx @@ -44,6 +44,7 @@ namespace AzToolsFramework Q_PROPERTY(bool hasChildRows READ HasChildRows); Q_PROPERTY(bool isTopLevel READ IsTopLevel); Q_PROPERTY(int getLevel READ GetLevel); + Q_PROPERTY(bool canBeReordered READ CanBeReordered); Q_PROPERTY(bool appendDefaultLabelToName READ GetAppendDefaultLabelToName WRITE AppendDefaultLabelToName) public: AZ_CLASS_ALLOCATOR(PropertyRowWidget, AZ::SystemAllocator, 0) @@ -126,6 +127,7 @@ namespace AzToolsFramework void SetSelectionEnabled(bool selectionEnabled); void SetSelected(bool selected); bool eventFilter(QObject *watched, QEvent *event) override; + void paintEvent(QPaintEvent*) override; /// Apply tooltip to widget and some of its children. void SetDescription(const QString& text); @@ -146,6 +148,9 @@ namespace AzToolsFramework QLabel* GetNameLabel() { return m_nameLabel; } void SetIndentSize(int w); void SetAsCustom(bool custom) { m_custom = custom; } + + bool CanChildrenBeReordered() const; + bool CanBeReordered() const; protected: int CalculateLabelWidth() const; diff --git a/Code/Sandbox/Editor/Style/Editor.qss b/Code/Sandbox/Editor/Style/Editor.qss index 0c3f64b85c..fa7d67dd43 100644 --- a/Code/Sandbox/Editor/Style/Editor.qss +++ b/Code/Sandbox/Editor/Style/Editor.qss @@ -38,6 +38,11 @@ AzToolsFramework--ComponentPaletteWidget > QTreeView background-color: #222222; } +AzToolsFramework--PropertyRowWidget[canBeReordered="true"] QLabel#Name +{ + font-weight: bold; +} + /* Style for visualizing property values overridden from their prefab values */ AzToolsFramework--PropertyRowWidget[IsOverridden=true] #Name QLabel, AzToolsFramework--ComponentEditorHeader #Title[IsOverridden="true"] diff --git a/Code/Tools/ProjectManager/Resources/Backgrounds/FirstTimeBackgroundImage.jpg b/Code/Tools/ProjectManager/Resources/Backgrounds/FirstTimeBackgroundImage.jpg new file mode 100644 index 0000000000..bfa5f83cf6 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Backgrounds/FirstTimeBackgroundImage.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7088e902885d98953f6a1715efab319c063a4ab8918fd0e810251c8ed82b8514 +size 542983 diff --git a/Code/Tools/ProjectManager/Resources/Backgrounds/LICENSE.TXT b/Code/Tools/ProjectManager/Resources/Backgrounds/LICENSE.TXT new file mode 100644 index 0000000000..5adf81a289 --- /dev/null +++ b/Code/Tools/ProjectManager/Resources/Backgrounds/LICENSE.TXT @@ -0,0 +1,4 @@ +SPDX-FileCopyrightText: Unsplash grants you an irrevocable, nonexclusive, worldwide copyright license to download, copy, +SPDX-FileCopyrightText: modify, distribute, perform, and use photos from Unsplash for free, including for commercial +SPDX-FileCopyrightText: purposes, without permission from or attributing the photographer or Unsplash. This license does +SPDX-FileCopyrightText: not include the right to compile photos from Unsplash to replicate a similar or competing service. \ No newline at end of file diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp index aa4dee1aa6..2c96078d43 100644 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp +++ b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.cpp @@ -12,18 +12,64 @@ #include -#include +#include +#include +#include +#include +#include +#include namespace O3DE::ProjectManager { + inline constexpr static int s_contentMargins = 80; + inline constexpr static int s_buttonSpacing = 30; + inline constexpr static int s_iconSize = 24; + inline constexpr static int s_spacerSize = 20; + inline constexpr static int s_boxButtonWidth = 210; + inline constexpr static int s_boxButtonHeight = 280; + FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent) : ScreenWidget(parent) - , m_ui(new Ui::FirstTimeUseClass()) { - m_ui->setupUi(this); + QVBoxLayout* vLayout = new QVBoxLayout(); + setLayout(vLayout); + vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins); - connect(m_ui->createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton); - connect(m_ui->openProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleOpenProjectButton); + QLabel* titleLabel = new QLabel(this); + titleLabel->setText(tr("Ready. Set. Create!")); + titleLabel->setStyleSheet("font-size: 60px"); + vLayout->addWidget(titleLabel); + + QLabel* introLabel = new QLabel(this); + introLabel->setTextFormat(Qt::AutoText); + introLabel->setText(tr("

Welcome to O3DE! Start something new by creating a project. Not sure what to create?

Explore what\342\200\231s available by downloading our sample project.

")); + introLabel->setStyleSheet("font-size: 14px"); + vLayout->addWidget(introLabel); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setSpacing(s_buttonSpacing); + + m_createProjectButton = CreateLargeBoxButton(QIcon(":/Resources/Add.svg"), tr("Create Project"), this); + m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); + buttonLayout->addWidget(m_createProjectButton); + + m_addProjectButton = CreateLargeBoxButton(QIcon(":/Resources/Select_Folder.svg"), tr("Add a Project"), this); + m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize)); + buttonLayout->addWidget(m_addProjectButton); + + QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum); + buttonLayout->addItem(buttonSpacer); + + vLayout->addItem(buttonLayout); + + QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding); + vLayout->addItem(verticalSpacer); + + // Using border-image allows for scaling options background-image does not support + setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Resources/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }"); + + connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton); + connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton); } ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum() @@ -36,9 +82,21 @@ namespace O3DE::ProjectManager emit ResetScreenRequest(ProjectManagerScreen::NewProjectSettingsCore); emit ChangeScreenRequest(ProjectManagerScreen::NewProjectSettingsCore); } - void FirstTimeUseScreen::HandleOpenProjectButton() + void FirstTimeUseScreen::HandleAddProjectButton() { emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome); } + QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent) + { + QPushButton* largeBoxButton = new QPushButton(icon, text, parent); + + largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight); + largeBoxButton->setFlat(true); + largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus); + largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }"); + + return largeBoxButton; + } + } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h index 4b4a99f16a..b6b57dc16b 100644 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h +++ b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.h @@ -15,10 +15,8 @@ #include #endif -namespace Ui -{ - class FirstTimeUseClass; -} +QT_FORWARD_DECLARE_CLASS(QIcon) +QT_FORWARD_DECLARE_CLASS(QPushButton) namespace O3DE::ProjectManager { @@ -32,10 +30,13 @@ namespace O3DE::ProjectManager protected slots: void HandleNewProjectButton(); - void HandleOpenProjectButton(); + void HandleAddProjectButton(); private: - QScopedPointer m_ui; + QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); + + QPushButton* m_createProjectButton; + QPushButton* m_addProjectButton; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.ui b/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.ui deleted file mode 100644 index fdc195731f..0000000000 --- a/Code/Tools/ProjectManager/Source/FirstTimeUseScreen.ui +++ /dev/null @@ -1,93 +0,0 @@ - - - FirstTimeUseClass - - - - 0 - 0 - 881 - 555 - - - - Form - - - - - - - - - 30 - - - - READY. SET. CREATE! - - - - - - - <html><head/><body><p>Welcome to O3DE! Start something new by creating a project. Not sure what to create? </p><p>Explore what’s available by downloading our sample project.</p></body></html> - - - Qt::AutoText - - - - - - - - - - - - 0 - 0 - - - - Create Project - - - - :/Resources/Add.svg:/Resources/Add.svg - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Open a Project - - - - :/Resources/Select_Folder.svg:/Resources/Select_Folder.svg - - - - - - - - - - - - diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp index 977667071f..6b9d268564 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.cpp @@ -27,6 +27,12 @@ namespace O3DE::ProjectManager , m_ui(new Ui::ProjectManagerWindowClass()) { m_ui->setupUi(this); + QLayout* layout = m_ui->centralWidget->layout(); + layout->setMargin(0); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + setFixedSize(this->geometry().width(), this->geometry().height()); m_pythonBindings = AZStd::make_unique(engineRootPath); diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui index 789dd1b656..a71ed3aabf 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui +++ b/Code/Tools/ProjectManager/Source/ProjectManagerWindow.ui @@ -6,10 +6,16 @@ 0 0 - 800 - 600 + 1200 + 800 + + + 0 + 0 + + O3DE Project Manager @@ -21,7 +27,7 @@ 0 0 - 800 + 1200 36 diff --git a/Code/Tools/ProjectManager/Source/ScreenWidget.h b/Code/Tools/ProjectManager/Source/ScreenWidget.h index ae235daf2b..483066e031 100644 --- a/Code/Tools/ProjectManager/Source/ScreenWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenWidget.h @@ -15,18 +15,20 @@ #include #include +#include +#include #endif namespace O3DE::ProjectManager { class ScreenWidget - : public QWidget + : public QFrame { Q_OBJECT public: explicit ScreenWidget(QWidget* parent = nullptr) - : QWidget(parent) + : QFrame(parent) { } ~ScreenWidget() = default; diff --git a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp index 69af09f496..b8a38ed155 100644 --- a/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/ScreensCtrl.cpp @@ -22,6 +22,9 @@ namespace O3DE::ProjectManager : QWidget(parent) { QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->setMargin(0); + vLayout->setSpacing(0); + vLayout->setContentsMargins(0, 0, 0, 0); setLayout(vLayout); m_screenStack = new QStackedWidget(); diff --git a/Code/Tools/ProjectManager/project_manager.qrc b/Code/Tools/ProjectManager/project_manager.qrc index 6509a9f940..3c23bc24ff 100644 --- a/Code/Tools/ProjectManager/project_manager.qrc +++ b/Code/Tools/ProjectManager/project_manager.qrc @@ -9,5 +9,6 @@ Resources/iOS.svg Resources/Linux.svg Resources/macOS.svg + Resources/Backgrounds/FirstTimeBackgroundImage.jpg diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 9ffdb6029d..3594d1e079 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -22,7 +22,6 @@ set(FILES Source/EngineInfo.cpp Source/FirstTimeUseScreen.h Source/FirstTimeUseScreen.cpp - Source/FirstTimeUseScreen.ui Source/ProjectManagerWindow.h Source/ProjectManagerWindow.cpp Source/ProjectTemplateInfo.h diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp index 0ee25195bc..38f7de89c6 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpAnimationImporter.cpp @@ -260,7 +260,7 @@ namespace AZ { AZ_TraceContext("Importer", "Animation"); - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); // Add check for animation layers at the scene level. @@ -387,11 +387,10 @@ namespace AZ } Events::ProcessingResultCombiner combinedAnimationResult; - for (AZ::u32 meshIndex = 0; meshIndex < currentNode->mNumMeshes; ++meshIndex) + if (context.m_sourceNode.ContainsMesh()) { - aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[meshIndex]]; - - if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(mesh->mName.C_Str()); + const aiMesh* firstMesh = scene->mMeshes[currentNode->mMeshes[0]]; + if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(firstMesh->mName.C_Str()); channelsForMeshName != meshMorphAnimations.end()) { const auto [nodeIterName, channels] = *channelsForMeshName; @@ -399,7 +398,7 @@ namespace AZ { const auto& [animation, morphAnimation] = animAndMorphAnim; combinedAnimationResult += ImportBlendShapeAnimation( - context, animation, morphAnimation, mesh); + context, animation, morphAnimation, firstMesh); } } } @@ -413,32 +412,39 @@ namespace AZ if (boneAnimations.empty() && !meshMorphAnimations.empty()) { const aiAnimation* animation = scene->mAnimations[0]; - - // Morph animations need a regular animation on the node, as well. - // If there is no bone animation on the current node, then generate one here. - AZStd::shared_ptr createdAnimationData = - AZStd::make_shared(); - - const size_t numKeyframes = animation->mDuration + 1; // +1 because we start at 0 and the last keyframe is at mDuration instead of mDuration-1 - createdAnimationData->ReserveKeyFrames(numKeyframes); - - const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond; - createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames); - - // Set every frame of the animation to the start location of the node. - aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode); - DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform); - context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform); - context.m_sourceSceneSystem.ConvertUnit(localTransform); - for (AZ::u32 time = 0; time <= animation->mDuration; ++time) + for (AZ::u32 channelIndex = 0; channelIndex < animation->mNumMorphMeshChannels; ++channelIndex) { - createdAnimationData->AddKeyFrame(localTransform); + const aiMeshMorphAnim* nodeAnim = animation->mMorphMeshChannels[channelIndex]; + // Morph animations need a regular animation on the node, as well. + // If there is no bone animation on the current node, then generate one here. + AZStd::shared_ptr createdAnimationData = + AZStd::make_shared(); + + const size_t numKeyframes = GetNumKeyFrames( + nodeAnim->mNumKeys, + animation->mDuration, + animation->mTicksPerSecond); + createdAnimationData->ReserveKeyFrames(numKeyframes); + + const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond; + createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames); + + // Set every frame of the animation to the start location of the node. + aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode); + DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform); + context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform); + context.m_sourceSceneSystem.ConvertUnit(localTransform); + for (AZ::u32 time = 0; time <= numKeyframes; ++time) + { + createdAnimationData->AddKeyFrame(localTransform); + } + + const AZStd::string stubBoneAnimForMorphName(AZStd::string::format("%s%s", nodeName.c_str(), nodeAnim->mName.C_Str())); + Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild( + context.m_currentGraphPosition, stubBoneAnimForMorphName.c_str(), AZStd::move(createdAnimationData)); + context.m_scene.GetGraph().MakeEndPoint(addNode); } - - Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild( - context.m_currentGraphPosition, nodeName.c_str(), AZStd::move(createdAnimationData)); - context.m_scene.GetGraph().MakeEndPoint(addNode); - + return combinedAnimationResult.GetResult(); } decltype(boneAnimations) parentFillerAnimations; @@ -446,8 +452,8 @@ namespace AZ // Go through all the animations and make sure we create animations for bones who's parents don't have an animation for (auto&& anim : boneAnimations) { - aiNode* node = scene->mRootNode->FindNode(anim.first.c_str()); - aiNode* parent = node->mParent; + const aiNode* node = scene->mRootNode->FindNode(anim.first.c_str()); + const aiNode* parent = node->mParent; while (parent && parent != scene->mRootNode) { @@ -598,7 +604,8 @@ namespace AZ // Keyframes generated for every single frame of the animation. typedef AZStd::map> ValueToKeyDataMap; ValueToKeyDataMap valueToKeyDataMap; - + // Key time can be less than zero, normalize to have zero be the lowest time. + double keyOffset = 0; for (int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++) { aiMeshMorphKey& key = meshMorphAnim->mKeys[keyIdx]; @@ -609,6 +616,10 @@ namespace AZ valueToKeyDataMap[currentValue].insert( AZStd::upper_bound(valueToKeyDataMap[currentValue].begin(), valueToKeyDataMap[currentValue].end(),thisKey), thisKey); + if (key.mTime < keyOffset) + { + keyOffset = key.mTime; + } } } @@ -631,7 +642,7 @@ namespace AZ const double time = GetTimeForFrame(frame, animation->mTicksPerSecond); float weight = 0; - if (!SampleKeyFrame(weight, keys, keys.size(), time, keyIdx)) + if (!SampleKeyFrame(weight, keys, keys.size(), time + keyOffset, keyIdx)) { return Events::ProcessingResult::Failure; } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp index c2b1f20035..2ce9bc14f4 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBitangentStreamImporter.cpp @@ -25,7 +25,6 @@ #include #include - namespace AZ { namespace SceneAPI @@ -44,7 +43,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(2); // LYN-2576 + serializeContext->Class()->Version(3); // LYN-3250 } } @@ -55,62 +54,79 @@ namespace AZ { return Events::ProcessingResult::Ignored; } - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context)); - if (!meshDataResult.IsSuccess()) + const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex) { - return meshDataResult.GetError(); - } - const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue()); + return scene->mMeshes[meshIndex]->HasTangentsAndBitangents(); + }; - size_t vertexCount = parentMeshData->GetVertexCount(); - - int sdkMeshIndex = parentMeshData->GetSdkMeshIndex(); - if (sdkMeshIndex < 0 || sdkMeshIndex >= currentNode->mNumMeshes) - { - AZ_Error(Utilities::ErrorWindow, false, - "Tried to construct bitangent stream attribute for invalid or non-mesh parent data, mesh index is invalid"); - return Events::ProcessingResult::Failure; - } - - aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; - - if (!mesh->HasTangentsAndBitangents()) + // If there are no bitangents on any meshes, there's nothing to import in this function. + const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); + if (!anyMeshHasTangentsAndBitangents) { return Events::ProcessingResult::Ignored; } + // AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material. + // This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes, + // and the engine has code to do this later. + const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); + if (!allMeshesHaveTangentsAndBitangents) + { + const char* mixedBitangentsError = + "Node with name %s has meshes with and without bitangents. " + "Placeholder incorrect bitangents will be generated to allow the data to process, " + "but the source art needs to be fixed to correct this. Either apply bitangents to all meshes on this node, " + "or remove all bitangents from all meshes on this node."; + AZ_Error( + Utilities::ErrorWindow, false, mixedBitangentsError, currentNode->mName.C_Str()); + } + + const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); + AZStd::shared_ptr bitangentStream = AZStd::make_shared(); - // AssImp only has one bitangentStream per mesh. bitangentStream->SetBitangentSetIndex(0); bitangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx); bitangentStream->ReserveContainerSpace(vertexCount); - - for (int v = 0; v < mesh->mNumVertices; ++v) + for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { - const Vector3 bitangent( - AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v])); - bitangentStream->AppendBitangent(bitangent); + const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; + + for (int v = 0; v < mesh->mNumVertices; ++v) + { + if (!mesh->HasTangentsAndBitangents()) + { + // This node has mixed meshes with and without bitangents. + // An error was already thrown above. Output stub bitangents so + // the mesh can still be output in some form, even if the data isn't correct. + // The bitangent count needs to match the vertex count on the associated mesh node. + bitangentStream->AppendBitangent(Vector3::CreateAxisY()); + } + else + { + const Vector3 bitangent( + AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mBitangents[v])); + bitangentStream->AppendBitangent(bitangent); + } + } } - AZStd::string nodeName(AZStd::string::format("%s",m_defaultNodeName)); Containers::SceneGraph::NodeIndex newIndex = - context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str()); + context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName); Events::ProcessingResult bitangentResults; - AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, nodeName.c_str()); + AssImpSceneAttributeDataPopulatedContext dataPopulated(context, bitangentStream, newIndex, m_defaultNodeName); bitangentResults = Events::Process(dataPopulated); if (bitangentResults != Events::ProcessingResult::Failure) { bitangentResults = AddAttributeDataNodeWithContexts(dataPopulated); } - return bitangentResults; } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp index c0399329d3..34266a3e29 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBlendShapeImporter.cpp @@ -74,37 +74,51 @@ namespace AZ { return meshDataResult.GetError(); } - const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue()); - int parentMeshIndex = parentMeshData->GetSdkMeshIndex(); Events::ProcessingResultCombiner combinedBlendShapeResult; + // 1. Loop through meshes & anims + // Create storage: Anim to meshes + // 2. Loop through anims & meshes + // Create an anim mesh for each anim, with meshes re-combined. + // AssImp separates meshes that have multiple materials. + // This code re-combines them to match previous FBX SDK behavior, + // so they can be separated by engine code instead. + AZStd::map>> animToMeshToAnimMeshIndices; for (int nodeMeshIdx = 0; nodeMeshIdx < numMesh; nodeMeshIdx++) { int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[nodeMeshIdx]; const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx]; - - // Each mesh gets its own node in the scene graph, so only generate - // morph targets for the current mesh. - if (parentMeshIndex != nodeMeshIdx || !aiMesh->mNumAnimMeshes) - { - continue; - } - for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++) { - AZStd::shared_ptr blendShapeData = - AZStd::make_shared(); - aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx]; - AZStd::string nodeName(aiAnimMesh->mName.C_Str()); - size_t dotIndex = nodeName.rfind('.'); - if (dotIndex != AZStd::string::npos) - { - nodeName.erase(0, dotIndex + 1); - } - RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape"); - AZ_TraceContext("Blend shape name", nodeName); + animToMeshToAnimMeshIndices[aiAnimMesh->mName.C_Str()].emplace_back(nodeMeshIdx, animIdx); + } + } + + for (const auto& animToMeshIndex : animToMeshToAnimMeshIndices) + { + AZStd::shared_ptr blendShapeData = + AZStd::make_shared(); + + // Some DCC tools, like Maya, include a full path separated by '.' in the node names. + // For example, "cone_skin_blendShapeNode.cone_squash" + // Downstream processing doesn't want anything but the last part of that node name, + // so find the last '.' and remove anything before it. + AZStd::string nodeName(animToMeshIndex.first); + size_t dotIndex = nodeName.rfind('.'); + if (dotIndex != AZStd::string::npos) + { + nodeName.erase(0, dotIndex + 1); + } + int vertexOffset = 0; + RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, "BlendShape"); + AZ_TraceContext("Blend shape name", nodeName); + for (const auto& meshIndex : animToMeshIndex.second) + { + int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[meshIndex.first]; + const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx]; + const aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[meshIndex.second]; AZStd::bitset uvSetUsedFlags; for (AZ::u8 uvSetIndex = 0; uvSetIndex < SceneData::GraphData::BlendShapeData::MaxNumUVSets; ++uvSetIndex) @@ -128,7 +142,7 @@ namespace AZ context.m_sourceSceneSystem.ConvertUnit(vertex); blendShapeData->AddPosition(vertex); - blendShapeData->SetVertexIndexToControlPointIndexMap(vertIdx, vertIdx); + blendShapeData->SetVertexIndexToControlPointIndexMap(vertIdx + vertexOffset, vertIdx + vertexOffset); // Add normals if (aiAnimMesh->HasNormals()) @@ -191,33 +205,36 @@ namespace AZ } for (int idx = 0; idx < face.mNumIndices; ++idx) { - blendFace.vertexIndex[idx] = face.mIndices[idx]; + blendFace.vertexIndex[idx] = face.mIndices[idx] + vertexOffset; } blendShapeData->AddFace(blendFace); } + vertexOffset += aiMesh->mNumVertices; - // Report problem if no vertex or face converted to MeshData - if (blendShapeData->GetVertexCount() <= 0 || blendShapeData->GetFaceCount() <= 0) - { - AZ_Error(Utilities::ErrorWindow, false, "Missing geometry data in blendshape node %s.", nodeName.c_str()); - return Events::ProcessingResult::Failure; - } - Containers::SceneGraph::NodeIndex newIndex = - context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str()); - - Events::ProcessingResult blendShapeResult; - AssImpSceneAttributeDataPopulatedContext dataPopulated(context, blendShapeData, newIndex, nodeName); - blendShapeResult = Events::Process(dataPopulated); - - if (blendShapeResult != Events::ProcessingResult::Failure) - { - blendShapeResult = AddAttributeDataNodeWithContexts(dataPopulated); - } - combinedBlendShapeResult += blendShapeResult; } + + // Report problem if no vertex or face converted to MeshData + if (blendShapeData->GetVertexCount() <= 0 || blendShapeData->GetFaceCount() <= 0) + { + AZ_Error(Utilities::ErrorWindow, false, "Missing geometry data in blendshape node %s.", nodeName.c_str()); + return Events::ProcessingResult::Failure; + } + + Containers::SceneGraph::NodeIndex newIndex = + context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str()); + + Events::ProcessingResult blendShapeResult; + AssImpSceneAttributeDataPopulatedContext dataPopulated(context, blendShapeData, newIndex, nodeName); + blendShapeResult = Events::Process(dataPopulated); + + if (blendShapeResult != Events::ProcessingResult::Failure) + { + blendShapeResult = AddAttributeDataNodeWithContexts(dataPopulated); + } + combinedBlendShapeResult += blendShapeResult; } return combinedBlendShapeResult.GetResult(); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp index 4467d6933b..5b43941715 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpBoneImporter.cpp @@ -46,8 +46,8 @@ namespace AZ } void EnumBonesInNode( - const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList, - AZStd::unordered_map& boneLookup) + const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList, + AZStd::unordered_map& boneLookup) { /* From AssImp Documentation a) Create a map or a similar container to store which nodes are necessary for the skeleton. Pre-initialise it for all nodes with a "no". @@ -62,14 +62,14 @@ namespace AZ for (unsigned meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex) { - aiMesh* mesh = scene->mMeshes[node->mMeshes[meshIndex]]; + const aiMesh* mesh = scene->mMeshes[node->mMeshes[meshIndex]]; for (unsigned boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) { - aiBone* bone = mesh->mBones[boneIndex]; + const aiBone* bone = mesh->mBones[boneIndex]; - aiNode* boneNode = scene->mRootNode->FindNode(bone->mName); - aiNode* boneParent = boneNode->mParent; + const aiNode* boneNode = scene->mRootNode->FindNode(bone->mName); + const aiNode* boneParent = boneNode->mParent; mainBoneList[bone->mName.C_Str()] = boneNode; boneLookup[bone->mName.C_Str()] = bone; @@ -85,8 +85,8 @@ namespace AZ } void EnumChildren( - const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList, - AZStd::unordered_map& boneLookup) + const aiScene* scene, const aiNode* node, AZStd::unordered_map& mainBoneList, + AZStd::unordered_map& boneLookup) { EnumBonesInNode(scene, node, mainBoneList, boneLookup); @@ -102,7 +102,7 @@ namespace AZ { AZ_TraceContext("Importer", "Bone"); - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); if (IsPivotNode(currentNode->mName)) @@ -118,8 +118,8 @@ namespace AZ } else { - AZStd::unordered_map mainBoneList; - AZStd::unordered_map boneLookup; + AZStd::unordered_map mainBoneList; + AZStd::unordered_map boneLookup; EnumChildren(scene, scene->mRootNode, mainBoneList, boneLookup); if (mainBoneList.find(currentNode->mName.C_Str()) != mainBoneList.end()) @@ -172,7 +172,7 @@ namespace AZ } aiMatrix4x4 transform = currentNode->mTransformation; - aiNode* parent = currentNode->mParent; + const aiNode* parent = currentNode->mParent; while (parent) { diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp index 7ebdb55363..75fa39105f 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpColorStreamImporter.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -44,7 +45,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(2); // LYN-2576 + serializeContext->Class()->Version(3); // LYN-3250 } } @@ -55,43 +56,64 @@ namespace AZ { return Events::ProcessingResult::Ignored; } - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context)); - if (!meshDataResult.IsSuccess()) - { - return meshDataResult.GetError(); - } - const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue()); + // This node has at least one mesh, verify that the color channel counts are the same for all meshes. + const int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels(); + const bool allMeshesHaveSameNumberOfColorChannels = + AZStd::all_of(currentNode->mMeshes + 1, currentNode->mMeshes + currentNode->mNumMeshes, [scene, expectedColorChannels](const unsigned int meshIndex) + { + return scene->mMeshes[meshIndex]->GetNumColorChannels() == expectedColorChannels; + }); - size_t vertexCount = parentMeshData->GetVertexCount(); + AZ_Error( + Utilities::ErrorWindow, + allMeshesHaveSameNumberOfColorChannels, + "Color channel counts for node %s has meshes with different color channel counts. " + "The color channel count for the first mesh will be used, and placeholder incorrect color values " + "will be generated to allow the data to process, but the source art needs to be fixed to correct this. " + "All meshes on this node should have the same number of color channels.", + currentNode->mName.C_Str()); - int sdkMeshIndex = parentMeshData->GetSdkMeshIndex(); - if (sdkMeshIndex < 0) + if (expectedColorChannels == 0) { - AZ_Error(Utilities::ErrorWindow, false, - "Tried to construct color stream attribute for invalid or non-mesh parent data, mesh index is missing"); - return Events::ProcessingResult::Failure; + return Events::ProcessingResult::Ignored; } - aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; + const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); Events::ProcessingResultCombiner combinedVertexColorResults; - for (int colorSetIndex = 0; colorSetIndex < mesh->GetNumColorChannels(); ++colorSetIndex) + for (int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex) { + AZStd::shared_ptr vertexColors = AZStd::make_shared(); vertexColors->ReserveContainerSpace(vertexCount); - for (int v = 0; v < mesh->mNumVertices; ++v) + for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { - AZ::SceneAPI::DataTypes::Color vertexColor( - AssImpSDKWrapper::AssImpTypeConverter::ToColor(mesh->mColors[colorSetIndex][v])); - vertexColors->AppendColor(vertexColor); + const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; + for (int v = 0; v < mesh->mNumVertices; ++v) + { + if (colorSetIndex < mesh->GetNumColorChannels()) + { + AZ::SceneAPI::DataTypes::Color vertexColor( + AssImpSDKWrapper::AssImpTypeConverter::ToColor(mesh->mColors[colorSetIndex][v])); + vertexColors->AppendColor(vertexColor); + } + else + { + // An error was already emitted if this mesh has less color channels + // than other meshes on the parent node. Append an arbitrary color value, fully opaque black, + // so the mesh can still be processed. + // It's better to let the engine load a partially valid mesh than to completely fail. + vertexColors->AppendColor(AZ::SceneAPI::DataTypes::Color(0.0f,0.0f,0.0f,1.0f)); + } + } } - AZStd::string nodeName(AZStd::string::format("%s%d",m_defaultNodeName,colorSetIndex)); + AZStd::string nodeName(AZStd::string::format("%s%d", m_defaultNodeName, colorSetIndex)); Containers::SceneGraph::NodeIndex newIndex = context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str()); @@ -106,9 +128,7 @@ namespace AZ combinedVertexColorResults += colorMapResults; } - return combinedVertexColorResults.GetResult(); - } } // namespace FbxSceneBuilder diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp index 80ac01ebdf..e79eaa09aa 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpImporterUtilities.cpp @@ -69,7 +69,7 @@ namespace AZ aiMatrix4x4 GetConcatenatedLocalTransform(const aiNode* currentNode) { - aiNode* parent = currentNode->mParent; + const aiNode* parent = currentNode->mParent; aiMatrix4x4 combinedTransform = currentNode->mTransformation; while (parent) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp index e314804ea1..a912b90e34 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMaterialImporter.cpp @@ -62,7 +62,7 @@ namespace AZ for (int idx = 0; idx < context.m_sourceNode.m_assImpNode->mNumMeshes; ++idx) { int meshIndex = context.m_sourceNode.m_assImpNode->mMeshes[idx]; - aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex]; + const aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex]; AZ_Assert(assImpMesh, "Asset Importer Mesh should not be null."); int materialIndex = assImpMesh->mMaterialIndex; AZ_TraceContext("Material Index", materialIndex); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp index cafb96934d..193a1f9fd5 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpMeshImporter.cpp @@ -45,7 +45,7 @@ namespace AZ { AZ_TraceContext("Importer", "Mesh"); - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); if (!context.m_sourceNode.ContainsMesh() || IsSkinnedMesh(*currentNode, *scene)) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp index 145dc9a457..f4a5fd0f93 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinImporter.cpp @@ -45,7 +45,7 @@ namespace AZ { AZ_TraceContext("Importer", "Skin"); - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); if (!context.m_sourceNode.ContainsMesh() || !IsSkinnedMesh(*currentNode, *scene)) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp index abcbf10b4a..d8503857cc 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.cpp @@ -51,7 +51,7 @@ namespace AZ { AZ_TraceContext("Importer", "Skin Weights"); - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); if(currentNode->mNumMeshes <= 0) @@ -59,35 +59,21 @@ namespace AZ return Events::ProcessingResult::Ignored; } - GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context)); - if (!meshDataResult.IsSuccess()) - { - return meshDataResult.GetError(); - } - const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue()); - - int parentMeshIndex = parentMeshData->GetSdkMeshIndex(); - Events::ProcessingResultCombiner combinedSkinWeightsResult; + // Don't create this until a bone with weights is encountered + Containers::SceneGraph::NodeIndex weightsIndexForMesh; + AZStd::string skinWeightName; + AZStd::shared_ptr skinWeightData; + + const uint64_t totalVertices = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); + + int vertexCount = 0; for(unsigned nodeMeshIndex = 0; nodeMeshIndex < currentNode->mNumMeshes; ++nodeMeshIndex) { - if (nodeMeshIndex != parentMeshIndex) - { - // Only generate skinning data for the parent mesh. - // Each AssImp mesh is assigned to a unique node, - // so the skinning data should be generated as a child node - // for the associated parent mesh. - continue; - } int sceneMeshIndex = currentNode->mMeshes[nodeMeshIndex]; const aiMesh* mesh = scene->mMeshes[sceneMeshIndex]; - // Don't create this until a bone with weights is encountered - Containers::SceneGraph::NodeIndex weightsIndexForMesh; - AZStd::string skinWeightName; - AZStd::shared_ptr skinWeightData; - for(unsigned b = 0; b < mesh->mNumBones; ++b) { const aiBone* bone = mesh->mBones[b]; @@ -100,7 +86,6 @@ namespace AZ if (!weightsIndexForMesh.IsValid()) { skinWeightName = s_skinWeightName; - skinWeightName += AZStd::to_string(nodeMeshIndex); RenamedNodesMap::SanitizeNodeName(skinWeightName, context.m_scene.GetGraph(), context.m_currentGraphPosition); weightsIndexForMesh = @@ -116,23 +101,25 @@ namespace AZ } Pending pending; pending.m_bone = bone; - pending.m_numVertices = mesh->mNumVertices; + pending.m_numVertices = totalVertices; pending.m_skinWeightData = skinWeightData; + pending.m_vertOffset = vertexCount; m_pendingSkinWeights.push_back(pending); } - - Events::ProcessingResult skinWeightsResult; - AssImpSceneAttributeDataPopulatedContext dataPopulated(context, skinWeightData, weightsIndexForMesh, skinWeightName); - skinWeightsResult = Events::Process(dataPopulated); - - if (skinWeightsResult != Events::ProcessingResult::Failure) - { - skinWeightsResult = AddAttributeDataNodeWithContexts(dataPopulated); - } - - combinedSkinWeightsResult += skinWeightsResult; + vertexCount += mesh->mNumVertices; } + Events::ProcessingResult skinWeightsResult; + AssImpSceneAttributeDataPopulatedContext dataPopulated(context, skinWeightData, weightsIndexForMesh, skinWeightName); + skinWeightsResult = Events::Process(dataPopulated); + + if (skinWeightsResult != Events::ProcessingResult::Failure) + { + skinWeightsResult = AddAttributeDataNodeWithContexts(dataPopulated); + } + + combinedSkinWeightsResult += skinWeightsResult; + return combinedSkinWeightsResult.GetResult(); } @@ -153,7 +140,7 @@ namespace AZ link.boneId = boneId; link.weight = it.m_bone->mWeights[weight].mWeight; - it.m_skinWeightData->AddAndSortLink(it.m_bone->mWeights[weight].mVertexId, link); + it.m_skinWeightData->AddAndSortLink(it.m_bone->mWeights[weight].mVertexId + it.m_vertOffset, link); } } const auto result = m_pendingSkinWeights.empty() ? Events::ProcessingResult::Ignored : Events::ProcessingResult::Success; diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h index f048fe0af2..655c838701 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpSkinWeightsImporter.h @@ -61,6 +61,7 @@ namespace AZ { const aiBone* m_bone = nullptr; unsigned m_numVertices = 0; + unsigned m_vertOffset = 0; AZStd::shared_ptr m_skinWeightData; }; diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp index 992a6a6ab1..47b7e410b4 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTangentStreamImporter.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -44,7 +45,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(2); // LYN-2576 + serializeContext->Class()->Version(3); // LYN-3250 } } @@ -55,62 +56,79 @@ namespace AZ { return Events::ProcessingResult::Ignored; } - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - - GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context)); - if (!meshDataResult.IsSuccess()) + + const auto meshHasTangentsAndBitangents = [&scene](const unsigned int meshIndex) { - return meshDataResult.GetError(); - } - const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue()); + return scene->mMeshes[meshIndex]->HasTangentsAndBitangents(); + }; - size_t vertexCount = parentMeshData->GetVertexCount(); - - int sdkMeshIndex = parentMeshData->GetSdkMeshIndex(); - if (sdkMeshIndex < 0 || sdkMeshIndex >= currentNode->mNumMeshes) - { - AZ_Error(Utilities::ErrorWindow, false, - "Tried to construct tangent stream attribute for invalid or non-mesh parent data, mesh index is invalid"); - return Events::ProcessingResult::Failure; - } - - aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; - - if (!mesh->HasTangentsAndBitangents()) + // If there are no tangents on any meshes, there's nothing to import in this function. + const bool anyMeshHasTangentsAndBitangents = AZStd::any_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); + if (!anyMeshHasTangentsAndBitangents) { return Events::ProcessingResult::Ignored; } + // AssImp nodes with multiple meshes on them occur when AssImp split a mesh on material. + // This logic recombines those meshes to minimize the changes needed to replace FBX SDK with AssImp, FBX SDK did not separate meshes, + // and the engine has code to do this later. + const bool allMeshesHaveTangentsAndBitangents = AZStd::all_of(currentNode->mMeshes, currentNode->mMeshes + currentNode->mNumMeshes, meshHasTangentsAndBitangents); + if (!allMeshesHaveTangentsAndBitangents) + { + const char* mixedTangentsError = + "Node with name %s has meshes with and without tangents. " + "Placeholder incorrect tangents will be generated to allow the data to process, " + "but the source art needs to be fixed to correct this. Either apply tangents to all meshes on this node, " + "or remove all tangents from all meshes on this node."; + AZ_Error( + Utilities::ErrorWindow, false, mixedTangentsError, currentNode->mName.C_Str()); + } + + const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); + AZStd::shared_ptr tangentStream = AZStd::make_shared(); - // AssImp only has one tangentStream per mesh. tangentStream->SetTangentSetIndex(0); tangentStream->SetTangentSpace(AZ::SceneAPI::DataTypes::TangentSpace::FromFbx); tangentStream->ReserveContainerSpace(vertexCount); - - for (int v = 0; v < mesh->mNumVertices; ++v) + for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { - // Vector4's constructor that takes in a vector3 sets w to 1.0f automatically. - const Vector4 tangent(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mTangents[v])); - tangentStream->AppendTangent(tangent); + const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; + + for (int v = 0; v < mesh->mNumVertices; ++v) + { + if (!mesh->HasTangentsAndBitangents()) + { + // This node has mixed meshes with and without tangents. + // An error was already thrown above. Output stub tangents so + // the mesh can still be output in some form, even if the data isn't correct. + // The tangent count needs to match the vertex count on the associated mesh node. + tangentStream->AppendTangent(Vector4(0.f, 1.f, 0.f, 1.f)); + } + else + { + const Vector4 tangent( + AssImpSDKWrapper::AssImpTypeConverter::ToVector3(mesh->mTangents[v])); + tangentStream->AppendTangent(tangent); + } + } } - AZStd::string nodeName(AZStd::string::format("%s", m_defaultNodeName)); Containers::SceneGraph::NodeIndex newIndex = - context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, nodeName.c_str()); + context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, m_defaultNodeName); Events::ProcessingResult tangentResults; - AssImpSceneAttributeDataPopulatedContext dataPopulated(context, tangentStream, newIndex, nodeName.c_str()); + AssImpSceneAttributeDataPopulatedContext dataPopulated(context, tangentStream, newIndex, m_defaultNodeName); tangentResults = Events::Process(dataPopulated); if (tangentResults != Events::ProcessingResult::Failure) { tangentResults = AddAttributeDataNodeWithContexts(dataPopulated); } - return tangentResults; } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp index 84c0e3e18c..5357c32fa9 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpTransformImporter.cpp @@ -50,7 +50,7 @@ namespace AZ Events::ProcessingResult AssImpTransformImporter::ImportTransform(AssImpSceneNodeAppendedContext& context) { AZ_TraceContext("Importer", "transform"); - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); if (currentNode == scene->mRootNode || IsPivotNode(currentNode->mName)) diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp index f5f47b233d..e37a4f4285 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/AssImpUvMapImporter.cpp @@ -12,17 +12,19 @@ #include #include +#include +#include #include #include #include #include #include #include -#include -#include +#include #include #include -#include +#include +#include #include #include @@ -45,7 +47,7 @@ namespace AZ SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { - serializeContext->Class()->Version(3); // LYN-2506 + serializeContext->Class()->Version(4); // LYN-3250 } } @@ -56,28 +58,53 @@ namespace AZ { return Events::ProcessingResult::Ignored; } - aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); + const aiNode* currentNode = context.m_sourceNode.GetAssImpNode(); const aiScene* scene = context.m_sourceScene.GetAssImpScene(); - GetMeshDataFromParentResult meshDataResult(GetMeshDataFromParent(context)); - if (!meshDataResult.IsSuccess()) + // AssImp separates meshes that have multiple materials. + // This code re-combines them to match previous FBX SDK behavior, + // so they can be separated by engine code instead. + bool foundTextureCoordinates = false; + AZStd::array meshesPerTextureCoordinateIndex = {}; + for (int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex) { - return meshDataResult.GetError(); + aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[localMeshIndex]]; + for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex) + { + if (!mesh->mTextureCoords[texCoordIndex]) + { + continue; + } + ++meshesPerTextureCoordinateIndex[texCoordIndex]; + foundTextureCoordinates = true; + } } - const SceneData::GraphData::MeshData* const parentMeshData(meshDataResult.GetValue()); - size_t vertexCount = parentMeshData->GetVertexCount(); + if (!foundTextureCoordinates) + { + return Events::ProcessingResult::Ignored; + } - int sdkMeshIndex = parentMeshData->GetSdkMeshIndex(); - AZ_Assert(sdkMeshIndex >= 0, - "Tried to construct uv stream attribute for invalid or non-mesh parent data, mesh index is missing"); + const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene); - aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; + for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex) + { + int meshesWithIndex = meshesPerTextureCoordinateIndex[texCoordIndex]; + AZ_Error( + Utilities::ErrorWindow, + meshesWithIndex == 0 || meshesWithIndex == currentNode->mNumMeshes, + "Texture coordinate index %d for node %s is not on all meshes on this node. " + "Placeholder arbitrary texture values will be generated to allow the data to process, but the source art " + "needs to be fixed to correct this. All meshes on this node should have the same number of texture coordinate channels.", + texCoordIndex, + currentNode->mName.C_Str()); + } Events::ProcessingResultCombiner combinedUvMapResults; - for (int texCoordIndex = 0; texCoordIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++texCoordIndex) + for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex) { - if (!mesh->mTextureCoords[texCoordIndex]) + // No meshes have this texture coordinate index, skip it. + if (meshesPerTextureCoordinateIndex[texCoordIndex] == 0) { continue; } @@ -85,24 +112,55 @@ namespace AZ AZStd::shared_ptr uvMap = AZStd::make_shared(); uvMap->ReserveContainerSpace(vertexCount); - + bool customNameFound = false; AZStd::string name(AZStd::string::format("%s%d", m_defaultNodeName, texCoordIndex)); - if (mesh->mTextureCoordsNames[texCoordIndex].length) + for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex) { - name = mesh->mTextureCoordsNames[texCoordIndex].C_Str(); + const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]]; + if(mesh->mTextureCoords[texCoordIndex]) + { + if (mesh->mTextureCoordsNames[texCoordIndex].length > 0) + { + if (!customNameFound) + { + name = mesh->mTextureCoordsNames[texCoordIndex].C_Str(); + customNameFound = true; + } + else + { + AZ_Warning(Utilities::WarningWindow, + strcmp(name.c_str(), mesh->mTextureCoordsNames[texCoordIndex].C_Str()) == 0, + "Node %s has conflicting mesh coordinate names at index %d, %s and %s. Using %s.", + currentNode->mName.C_Str(), + texCoordIndex, + name.c_str(), + mesh->mTextureCoordsNames[texCoordIndex].C_Str(), + name.c_str()); + } + } + } + + for (int v = 0; v < mesh->mNumVertices; ++v) + { + if (mesh->mTextureCoords[texCoordIndex]) + { + AZ::Vector2 vertexUV( + mesh->mTextureCoords[texCoordIndex][v].x, + // The engine's V coordinate is reverse of how it's stored in the FBX file. + 1.0f - mesh->mTextureCoords[texCoordIndex][v].y); + uvMap->AppendUV(vertexUV); + } + else + { + // An error was already emitted if the UV channels for all meshes on this node do not match. + // Append an arbitrary UV value so that the mesh can still be processed. + // It's better to let the engine load a partially valid mesh than to completely fail. + uvMap->AppendUV(AZ::Vector2::CreateZero()); + } + } } uvMap->SetCustomName(name.c_str()); - - for (int v = 0; v < mesh->mNumVertices; ++v) - { - AZ::Vector2 vertexUV( - mesh->mTextureCoords[texCoordIndex][v].x, - // The engine's V coordinate is reverse of how it's stored in the FBX file. - 1.0f - mesh->mTextureCoords[texCoordIndex][v].y); - uvMap->AppendUV(vertexUV); - } - Containers::SceneGraph::NodeIndex newIndex = context.m_scene.GetGraph().AddChild(context.m_currentGraphPosition, name.c_str()); @@ -116,6 +174,7 @@ namespace AZ } combinedUvMapResults += uvMapResults; + } return combinedUvMapResults.GetResult(); diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp index 59821336e0..c90fe7d1f3 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,7 +25,7 @@ namespace AZ::SceneAPI::FbxSceneBuilder { - bool BuildSceneMeshFromAssImpMesh(aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes, + bool BuildSceneMeshFromAssImpMesh(const aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes, const AZStd::function()>& makeMeshFunc) { AZStd::unordered_map assImpMatIndexToLYIndex; @@ -34,17 +35,18 @@ namespace AZ::SceneAPI::FbxSceneBuilder { return false; } + auto newMesh = makeMeshFunc(); + newMesh->SetUnitSizeInMeters(sceneSystem.GetUnitSizeInMeters()); + newMesh->SetOriginalUnitSizeInMeters(sceneSystem.GetOriginalUnitSizeInMeters()); + + // AssImp separates meshes that have multiple materials. + // This code re-combines them to match previous FBX SDK behavior, + // so they can be separated by engine code instead. + int vertOffset = 0; for (int m = 0; m < currentNode->mNumMeshes; ++m) { - auto newMesh = makeMeshFunc(); - - newMesh->SetUnitSizeInMeters(sceneSystem.GetUnitSizeInMeters()); - newMesh->SetOriginalUnitSizeInMeters(sceneSystem.GetOriginalUnitSizeInMeters()); - - newMesh->SetSdkMeshIndex(m); - - aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]]; + const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]]; // Lumberyard materials are created in order based on mesh references in the scene if (assImpMatIndexToLYIndex.find(mesh->mMaterialIndex) == assImpMatIndexToLYIndex.end()) @@ -59,7 +61,7 @@ namespace AZ::SceneAPI::FbxSceneBuilder sceneSystem.SwapVec3ForUpAxis(vertex); sceneSystem.ConvertUnit(vertex); newMesh->AddPosition(vertex); - newMesh->SetVertexIndexToControlPointIndexMap(vertIdx, vertIdx); + newMesh->SetVertexIndexToControlPointIndexMap(vertIdx + vertOffset, vertIdx + vertOffset); if (mesh->HasNormals()) { @@ -86,14 +88,15 @@ namespace AZ::SceneAPI::FbxSceneBuilder } for (int idx = 0; idx < face.mNumIndices; ++idx) { - meshFace.vertexIndex[idx] = face.mIndices[idx]; + meshFace.vertexIndex[idx] = face.mIndices[idx] + vertOffset; } newMesh->AddFace(meshFace, assImpMatIndexToLYIndex[mesh->mMaterialIndex]); } + vertOffset += mesh->mNumVertices; - meshes.push_back(newMesh); } + meshes.push_back(newMesh); return true; } @@ -127,4 +130,13 @@ namespace AZ::SceneAPI::FbxSceneBuilder azrtti_cast(parentData); return AZ::Success(parentMeshData); } + + uint64_t GetVertexCountForAllMeshesOnNode(const aiNode& node, const aiScene& scene) + { + return AZStd::accumulate(node.mMeshes, node.mMeshes + node.mNumMeshes, uint64_t{ 0u }, + [&scene](auto runningTotal, unsigned int meshIndex) + { + return runningTotal + scene.mMeshes[meshIndex]->mNumVertices; + }); + } } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h index c0b5c044cb..3c7d3d2102 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/AssImpMeshImporterUtilities.h @@ -44,11 +44,16 @@ namespace AZ namespace FbxSceneBuilder { - bool BuildSceneMeshFromAssImpMesh(aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes, + bool BuildSceneMeshFromAssImpMesh(const aiNode* currentNode, const aiScene* scene, const FbxSceneSystem& sceneSystem, AZStd::vector>& meshes, const AZStd::function()>& makeMeshFunc); typedef AZ::Outcome GetMeshDataFromParentResult; GetMeshDataFromParentResult GetMeshDataFromParent(AssImpSceneNodeAppendedContext& context); + + // If a node in the original scene file has a mesh with multiple materials on it, the associated AssImp + // node will have multiple meshes on it, broken apart per material. This returns the total number + // of vertices on all meshes on the given node. + uint64_t GetVertexCountForAllMeshesOnNode(const aiNode& node, const aiScene& scene); } } } diff --git a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp index abc095d583..b67696e3cc 100644 --- a/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp +++ b/Code/Tools/SceneAPI/FbxSceneBuilder/Importers/Utilities/RenamedNodesMap.cpp @@ -27,6 +27,7 @@ namespace AZ Containers::SceneGraph::NodeIndex parentNode, const char* defaultName) { AZ_TraceContext("Node name", name); + const AZStd::string originalNodeName(name); bool isNameUpdated = false; // Nodes can't have an empty name, except of the root, otherwise nodes can't be referenced. @@ -56,7 +57,7 @@ namespace AZ // can't reference the same parent in that case. This is to make sure the node can be quickly found as // the full path will be unique. To fix any issues, an index is appended. size_t index = 1; - size_t offset = name.length(); + const size_t offset = name.length(); while (graph.Find(parentNode, name).IsValid()) { // Remove the previously tried extension. @@ -71,7 +72,8 @@ namespace AZ if (isNameUpdated) { AZ_TraceContext("New node name", name); - AZ_TracePrintf(Utilities::WarningWindow, "The name of the node was invalid or conflicting and was updated."); + AZ_TracePrintf(Utilities::WarningWindow, "The name of the node '%s' was invalid or conflicting and was updated to '%s'.", + originalNodeName.c_str(), name.c_str()); } return isNameUpdated; diff --git a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp index eccbe729c6..2cda1e68ae 100644 --- a/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp +++ b/Code/Tools/SceneAPI/SDKWrapper/AssImpSceneWrapper.cpp @@ -38,12 +38,14 @@ namespace AZ { AZ_TracePrintf(SceneAPI::Utilities::LogWindow, "AssImpSceneWrapper::LoadSceneFromFile %s", fileName); AZ_TraceContext("Filename", fileName); + // aiProcess_JoinIdenticalVertices is not enabled because O3DE has a mesh optimizer that also does this, + // this flag is disabled to keep AssImp output similar to FBX SDK to reduce downstream bugs for the initial AssImp release. + // There's currently a minimum of properties and flags set to maximize compatibility with the existing node graph. m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, false); m_sceneFileName = fileName; m_assImpScene = m_importer.ReadFile(fileName, aiProcess_Triangulate //Triangulates all faces of all meshes - | aiProcess_JoinIdenticalVertices //Identifies and joins identical vertex data sets for the imported meshes | aiProcess_LimitBoneWeights //Limits the number of bones that can affect a vertex to a maximum value //dropping the least important and re-normalizing | aiProcess_GenNormals); //Generate normals for meshes diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp index e99788d570..f3c01df2e3 100644 --- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp +++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.cpp @@ -19,6 +19,16 @@ namespace AZ::SceneAPI::Utilities m_output += AZStd::string::format("\t%s: %s\n", name, data); } + void DebugOutput::WriteArray(const char* name, const unsigned int* data, int size) + { + m_output += AZStd::string::format("\t%s: ", name); + for (int index = 0; index < size; ++index) + { + m_output += AZStd::string::format("%d, ", data[index]); + } + m_output += AZStd::string::format("\n"); + } + void DebugOutput::Write(const char* name, const AZStd::string& data) { Write(name, data.c_str()); diff --git a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h index a598c996c9..83dc9dd6ab 100644 --- a/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h +++ b/Code/Tools/SceneAPI/SceneCore/Utilities/DebugOutput.h @@ -29,6 +29,7 @@ namespace AZ::SceneAPI::Utilities void Write(const char* name, const AZStd::vector>& data); SCENE_CORE_API void Write(const char* name, const char* data); + SCENE_CORE_API void WriteArray(const char* name, const unsigned int* data, int size); SCENE_CORE_API void Write(const char* name, const AZStd::string& data); SCENE_CORE_API void Write(const char* name, double data); SCENE_CORE_API void Write(const char* name, uint64_t data); diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp index 902928d404..7d81166829 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp +++ b/Code/Tools/SceneAPI/SceneData/GraphData/BlendShapeData.cpp @@ -285,8 +285,26 @@ namespace AZ void BlendShapeData::GetDebugOutput(SceneAPI::Utilities::DebugOutput& output) const { output.Write("Positions", m_positions); + int index = 0; + for (const auto& position : m_positions) + { + output.Write(AZStd::string::format("\t%d", index).c_str(), position); + ++index; + } + index = 0; output.Write("Normals", m_normals); + for (const auto& normal : m_normals) + { + output.Write(AZStd::string::format("\t%d", index).c_str(), normal); + ++index; + } + index = 0; output.Write("Faces", m_faces); + for (const auto& face : m_faces) + { + output.WriteArray(AZStd::string::format("\t%d", index).c_str(), face.vertexIndex, 3); + ++index; + } } } // GraphData } // SceneData diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp index 8bf49b2898..8f464240c9 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.cpp @@ -45,7 +45,6 @@ namespace AZ behaviorContext->Class() ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Module, "scene") - ->Method("GetSdkMeshIndex", &MeshData::GetSdkMeshIndex) ->Method("GetControlPointIndex", &MeshData::GetControlPointIndex) ->Method("GetUsedControlPointCount", &MeshData::GetUsedControlPointCount) ->Method("GetUsedPointIndexForControlPoint", &MeshData::GetUsedPointIndexForControlPoint) @@ -77,10 +76,6 @@ namespace AZ void MeshData::CloneAttributesFrom(const IGraphObject* sourceObject) { IMeshData::CloneAttributesFrom(sourceObject); - if (const auto* typedSource = azrtti_cast(sourceObject)) - { - SetSdkMeshIndex(typedSource->GetSdkMeshIndex()); - } } void MeshData::AddPosition(const AZ::Vector3& position) @@ -111,15 +106,6 @@ namespace AZ m_faceMaterialIds.push_back(faceMaterialId); } - void MeshData::SetSdkMeshIndex(int sdkMeshIndex) - { - m_sdkMeshIndex = sdkMeshIndex; - } - int MeshData::GetSdkMeshIndex() const - { - return m_sdkMeshIndex; - } - void MeshData::SetVertexIndexToControlPointIndexMap(int vertexIndex, int controlPointIndex) { m_vertexIndexToControlPointIndexMap[vertexIndex] = controlPointIndex; @@ -206,8 +192,26 @@ namespace AZ void MeshData::GetDebugOutput(SceneAPI::Utilities::DebugOutput& output) const { output.Write("Positions", m_positions); + int index = 0; + for (const auto& position : m_positions) + { + output.Write(AZStd::string::format("\t%d", index).c_str(), position); + ++index; + } + index = 0; output.Write("Normals", m_normals); + for (const auto& normal : m_normals) + { + output.Write(AZStd::string::format("\t%d", index).c_str(), normal); + ++index; + } + index = 0; output.Write("FaceList", m_faceList); + for (const auto& face : m_faceList) + { + output.WriteArray(AZStd::string::format("\t%d", index).c_str(), face.vertexIndex, 3); + ++index; + } output.Write("FaceMaterialIds", m_faceMaterialIds); } } diff --git a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h index c5197d6cb4..4321096857 100644 --- a/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h +++ b/Code/Tools/SceneAPI/SceneData/GraphData/MeshData.h @@ -49,9 +49,6 @@ namespace AZ SCENE_DATA_API void AddFace(const AZ::SceneAPI::DataTypes::IMeshData::Face& face, unsigned int faceMaterialId = AZ::SceneAPI::DataTypes::IMeshData::s_invalidMaterialId); - SCENE_DATA_API void SetSdkMeshIndex(int sdkMeshIndex); - SCENE_DATA_API int GetSdkMeshIndex() const; - SCENE_DATA_API void SetVertexIndexToControlPointIndexMap(int vertexIndex, int controlPointIndex); SCENE_DATA_API size_t GetUsedControlPointCount() const override; SCENE_DATA_API int GetControlPointIndex(int vertexIndex) const override; @@ -80,8 +77,6 @@ namespace AZ AZStd::unordered_map m_vertexIndexToControlPointIndexMap; AZStd::unordered_map m_controlPointToUsedVertexIndexMap; - - int m_sdkMeshIndex = -1; }; } } diff --git a/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp b/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp index 79c6cfea7e..84018bbc9a 100644 --- a/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp +++ b/Code/Tools/SceneAPI/SceneData/Tests/GraphData/GraphDataBehaviorTests.cpp @@ -57,7 +57,6 @@ namespace AZ meshData->AddNormal(Vector3{0.1f, 0.2f, 0.3f}); meshData->AddNormal(Vector3{0.4f, 0.5f, 0.6f}); meshData->SetOriginalUnitSizeInMeters(10.0f); - meshData->SetSdkMeshIndex(1337); meshData->SetUnitSizeInMeters(0.5f); meshData->SetVertexIndexToControlPointIndexMap(0, 10); meshData->SetVertexIndexToControlPointIndexMap(1, 11); @@ -252,7 +251,6 @@ namespace AZ ExpectExecute("TestExpectFloatEquals(meshData:GetNormal(1).z, 0.6)"); ExpectExecute("TestExpectFloatEquals(meshData:GetOriginalUnitSizeInMeters(), 10.0)"); ExpectExecute("TestExpectFloatEquals(meshData:GetUnitSizeInMeters(), 0.5)"); - ExpectExecute("TestExpectIntegerEquals(meshData:GetSdkMeshIndex(), 1337)"); ExpectExecute("TestExpectIntegerEquals(meshData:GetUsedControlPointCount(), 4)"); ExpectExecute("TestExpectIntegerEquals(meshData:GetControlPointIndex(0), 10)"); ExpectExecute("TestExpectIntegerEquals(meshData:GetControlPointIndex(1), 11)"); diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl index 84095ac163..456d7cbabe 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl @@ -101,7 +101,7 @@ struct VSOutput float2 m_uv[UvSetCount] : UV1; float2 m_detailUv : UV3; - float4 m_blendMask : UV8; + float4 m_wrinkleBlendFactors : UV8; }; #include @@ -132,11 +132,11 @@ VSOutput SkinVS(VSInput IN) if(o_blendMask_isBound) { - OUT.m_blendMask = IN.m_optional_blendMask; + OUT.m_wrinkleBlendFactors = IN.m_optional_blendMask; } else { - OUT.m_blendMask = float4(0,1,0,0); + OUT.m_wrinkleBlendFactors = float4(0,0,0,0); } VertexHelper(IN, OUT, worldPosition, false); @@ -214,7 +214,22 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) float2 normalUv = IN.m_uv[MaterialSrg::m_normalMapUvIndex]; float detailLayerNormalFactor = MaterialSrg::m_detail_normal_factor * detailLayerBlendFactor; - + + // ------- Wrinkle Map Setup ------- + + // Combine the optional per-morph target wrinkle masks + float4 wrinkleBlendFactors = float4(0.0, 0.0, 0.0, 0.0); + for(uint wrinkleMaskIndex = 0; wrinkleMaskIndex < ObjectSrg::m_wrinkle_mask_count; ++wrinkleMaskIndex) + { + wrinkleBlendFactors += ObjectSrg::m_wrinkle_masks[wrinkleMaskIndex].Sample(MaterialSrg::m_sampler, normalUv) * ObjectSrg::GetWrinkleMaskWeight(wrinkleMaskIndex); + } + + // If texture based morph target driven masks are being used, use those values instead of the per-vertex colors + if(ObjectSrg::m_wrinkle_mask_count) + { + IN.m_wrinkleBlendFactors = saturate(wrinkleBlendFactors); + } + // Since the wrinkle normal maps should all be in the same tangent space as the main normal map, we should be able to blend the raw normal map // texture values before doing all the tangent space transforms, so we only have to do the transforms once, for better performance. @@ -223,12 +238,12 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) { normalMapSample = SampleNormalXY(MaterialSrg::m_normalMap, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY); } - if(o_wrinkleLayers_enabled && o_blendMask_isBound && o_wrinkleLayers_normal_enabled) + if(o_wrinkleLayers_enabled && o_wrinkleLayers_normal_enabled) { - normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture1, normalMapSample, MaterialSrg::m_wrinkle_normal_texture1, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_blendMask.r); - normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture2, normalMapSample, MaterialSrg::m_wrinkle_normal_texture2, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_blendMask.g); - normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture3, normalMapSample, MaterialSrg::m_wrinkle_normal_texture3, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_blendMask.b); - normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture4, normalMapSample, MaterialSrg::m_wrinkle_normal_texture4, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_blendMask.a); + normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture1, normalMapSample, MaterialSrg::m_wrinkle_normal_texture1, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_wrinkleBlendFactors.r); + normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture2, normalMapSample, MaterialSrg::m_wrinkle_normal_texture2, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_wrinkleBlendFactors.g); + normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture3, normalMapSample, MaterialSrg::m_wrinkle_normal_texture3, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_wrinkleBlendFactors.b); + normalMapSample = ApplyNormalWrinkleMap(o_wrinkleLayers_normal_useTexture4, normalMapSample, MaterialSrg::m_wrinkle_normal_texture4, MaterialSrg::m_sampler, normalUv, MaterialSrg::m_flipNormalX, MaterialSrg::m_flipNormalY, IN.m_wrinkleBlendFactors.a); } if(o_detail_normal_useTexture) @@ -255,7 +270,7 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) float3 baseColor = GetBaseColorInput(MaterialSrg::m_baseColorMap, MaterialSrg::m_sampler, baseColorUv, MaterialSrg::m_baseColor, o_baseColor_useTexture); bool useSampledBaseColor = o_baseColor_useTexture; - if(o_wrinkleLayers_enabled && o_blendMask_isBound && o_wrinkleLayers_baseColor_enabled) + if(o_wrinkleLayers_enabled && o_wrinkleLayers_baseColor_enabled) { // If any of the wrinkle maps are applied, we will use the Base Color blend settings to apply the MaterialSrg::m_baseColor tint to the wrinkle maps, // even if the main base color map is not used. @@ -272,10 +287,10 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) baseColor = float3(1,1,1); } - baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture1, baseColor, MaterialSrg::m_wrinkle_baseColor_texture1, MaterialSrg::m_sampler, baseColorUv, IN.m_blendMask.r); - baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture2, baseColor, MaterialSrg::m_wrinkle_baseColor_texture2, MaterialSrg::m_sampler, baseColorUv, IN.m_blendMask.g); - baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture3, baseColor, MaterialSrg::m_wrinkle_baseColor_texture3, MaterialSrg::m_sampler, baseColorUv, IN.m_blendMask.b); - baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture4, baseColor, MaterialSrg::m_wrinkle_baseColor_texture4, MaterialSrg::m_sampler, baseColorUv, IN.m_blendMask.a); + baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture1, baseColor, MaterialSrg::m_wrinkle_baseColor_texture1, MaterialSrg::m_sampler, baseColorUv, IN.m_wrinkleBlendFactors.r); + baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture2, baseColor, MaterialSrg::m_wrinkle_baseColor_texture2, MaterialSrg::m_sampler, baseColorUv, IN.m_wrinkleBlendFactors.g); + baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture3, baseColor, MaterialSrg::m_wrinkle_baseColor_texture3, MaterialSrg::m_sampler, baseColorUv, IN.m_wrinkleBlendFactors.b); + baseColor = ApplyBaseColorWrinkleMap(o_wrinkleLayers_baseColor_useTexture4, baseColor, MaterialSrg::m_wrinkle_baseColor_texture4, MaterialSrg::m_sampler, baseColorUv, IN.m_wrinkleBlendFactors.a); } @@ -283,13 +298,13 @@ PbrLightingOutput SkinPS_Common(VSOutput IN) baseColor = ApplyTextureOverlay(o_detail_baseColor_useTexture, baseColor, MaterialSrg::m_detail_baseColor_texture, MaterialSrg::m_sampler, IN.m_detailUv, detailLayerBaseColorFactor); - if(o_wrinkleLayers_enabled && o_wrinkleLayers_showBlendMaskValues && o_blendMask_isBound) + if(o_wrinkleLayers_enabled && o_wrinkleLayers_showBlendMaskValues) { // Overlay debug colors to highlight the different blend weights coming from the vertex color stream. - if(o_wrinkleLayers_count > 0) { baseColor = lerp(baseColor, float3(1,0,0), IN.m_blendMask.r); } - if(o_wrinkleLayers_count > 1) { baseColor = lerp(baseColor, float3(0,1,0), IN.m_blendMask.g); } - if(o_wrinkleLayers_count > 2) { baseColor = lerp(baseColor, float3(0,0,1), IN.m_blendMask.b); } - if(o_wrinkleLayers_count > 3) { baseColor = lerp(baseColor, float3(1,1,1), IN.m_blendMask.a); } + if(o_wrinkleLayers_count > 0) { baseColor = lerp(baseColor, float3(1,0,0), IN.m_wrinkleBlendFactors.r); } + if(o_wrinkleLayers_count > 1) { baseColor = lerp(baseColor, float3(0,1,0), IN.m_wrinkleBlendFactors.g); } + if(o_wrinkleLayers_count > 2) { baseColor = lerp(baseColor, float3(0,0,1), IN.m_wrinkleBlendFactors.b); } + if(o_wrinkleLayers_count > 3) { baseColor = lerp(baseColor, float3(1,1,1), IN.m_wrinkleBlendFactors.a); } } // ------- Specular ------- diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype index b8951d69c7..101a03b907 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype @@ -987,15 +987,6 @@ { "file": "Shaders/MotionVector/SkinnedMeshMotionVector.shader", "tag": "SkinnedMeshMotionVector" - }, - // Used by the light culling system to produce accurate depth bounds for this object when it uses blended transparency - { - "file": "Shaders/Depth/DepthPassTransparentMin.shader", - "tag": "DepthPassTransparentMin" - }, - { - "file": "Shaders/Depth/DepthPassTransparentMax.shader", - "tag": "DepthPassTransparentMax" } ], "functors": [ diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype index a74ceb1783..2b0d09bc5c 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR.materialtype @@ -1199,6 +1199,14 @@ "file": "./StandardPBR_ForwardPass_EDS.shader", "tag": "ForwardPass_EDS" }, + { + "file": "./StandardPBR_LowEndForward.shader", + "tag": "LowEndForward" + }, + { + "file": "./StandardPBR_LowEndForward_EDS.shader", + "tag": "LowEndForward_EDS" + }, { "file": "Shaders/Shadow/Shadowmap.shader", "tag": "Shadowmap" @@ -1289,10 +1297,6 @@ "textureProperty": "baseColor.textureMap", "useTextureProperty": "baseColor.useTexture", "dependentProperties": ["baseColor.textureMapUv", "baseColor.textureBlendMode"], - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], "shaderOption": "o_baseColor_useTexture" } }, @@ -1302,10 +1306,6 @@ "textureProperty": "metallic.textureMap", "useTextureProperty": "metallic.useTexture", "dependentProperties": ["metallic.textureMapUv"], - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], "shaderOption": "o_metallic_useTexture" } }, @@ -1315,10 +1315,6 @@ "textureProperty": "specularF0.textureMap", "useTextureProperty": "specularF0.useTexture", "dependentProperties": ["specularF0.textureMapUv"], - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], "shaderOption": "o_specularF0_useTexture" } }, @@ -1328,10 +1324,6 @@ "textureProperty": "normal.textureMap", "useTextureProperty": "normal.useTexture", "dependentProperties": ["normal.textureMapUv", "normal.factor", "normal.flipX", "normal.flipY"], - "shaderTags": [ - "ForwardPass", - "ForwardPass_EDS" - ], "shaderOption": "o_normal_useTexture" } }, diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl index f362349a7b..bf1ec96b79 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ForwardPass.azsl @@ -10,6 +10,8 @@ * */ +#include "Atom/Features/ShaderQualityOptions.azsli" + #include "StandardPBR_Common.azsli" // SRGs @@ -317,13 +319,18 @@ ForwardPassOutputWithDepth StandardPbr_ForwardPassPS(VSOutput IN, bool isFrontFa PbrLightingOutput lightingOutput = ForwardPassPS_Common(IN, isFrontFace, depth); +#ifdef UNIFIED_FORWARD_OUTPUT + OUT.m_color.rgb = lightingOutput.m_diffuseColor.rgb + lightingOutput.m_specularColor.rgb; + OUT.m_color.a = lightingOutput.m_diffuseColor.a; + OUT.m_depth = depth; +#else OUT.m_diffuseColor = lightingOutput.m_diffuseColor; OUT.m_specularColor = lightingOutput.m_specularColor; OUT.m_specularF0 = lightingOutput.m_specularF0; OUT.m_albedo = lightingOutput.m_albedo; OUT.m_normal = lightingOutput.m_normal; OUT.m_depth = depth; - +#endif return OUT; } @@ -335,12 +342,16 @@ ForwardPassOutput StandardPbr_ForwardPassPS_EDS(VSOutput IN, bool isFrontFace : PbrLightingOutput lightingOutput = ForwardPassPS_Common(IN, isFrontFace, depth); +#ifdef UNIFIED_FORWARD_OUTPUT + OUT.m_color.rgb = lightingOutput.m_diffuseColor.rgb + lightingOutput.m_specularColor.rgb; + OUT.m_color.a = lightingOutput.m_diffuseColor.a; +#else OUT.m_diffuseColor = lightingOutput.m_diffuseColor; OUT.m_specularColor = lightingOutput.m_specularColor; OUT.m_specularF0 = lightingOutput.m_specularF0; OUT.m_albedo = lightingOutput.m_albedo; OUT.m_normal = lightingOutput.m_normal; - +#endif return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward.azsl new file mode 100644 index 0000000000..c87faffcbe --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward.azsl @@ -0,0 +1,17 @@ +/* +* 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. +* +*/ + +// NOTE: This file is a temporary workaround until .shader files can #define macros for their .azsl files + +#define QUALITY_LOW_END 1 + +#include "StandardPBR_ForwardPass.azsl" diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward.shader new file mode 100644 index 0000000000..44139608ca --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward.shader @@ -0,0 +1,59 @@ +{ + // Note: "LowEnd" shaders are for supporting the low end pipeline + // These shaders can be safely added to materials without incurring additional runtime draw + // items as draw items for shaders are only created if the scene has a pass with a matching + // DrawListTag. If your pipeline doesn't have a "lowEndForward" DrawListTag, no draw items + // for this shader will be created. + + "Source" : "./StandardPBR_LowEndForward.azsl", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : true, + "CompareFunc" : "GreaterEqual" + }, + "Stencil" : + { + "Enable" : true, + "ReadMask" : "0x00", + "WriteMask" : "0xFF", + "FrontFace" : + { + "Func" : "Always", + "DepthFailOp" : "Keep", + "FailOp" : "Keep", + "PassOp" : "Replace" + }, + "BackFace" : + { + "Func" : "Always", + "DepthFailOp" : "Keep", + "FailOp" : "Keep", + "PassOp" : "Replace" + } + } + }, + + "CompilerHints" : { + "DisableOptimizations" : false + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "StandardPbr_ForwardPassVS", + "type": "Vertex" + }, + { + "name": "StandardPbr_ForwardPassPS", + "type": "Fragment" + } + ] + }, + + "DrawList" : "lowEndForward" +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward_EDS.shader b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward_EDS.shader new file mode 100644 index 0000000000..9faa1d3698 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_LowEndForward_EDS.shader @@ -0,0 +1,59 @@ +{ + // Note: "LowEnd" shaders are for supporting the low end pipeline + // These shaders can be safely added to materials without incurring additional runtime draw + // items as draw items for shaders are only created if the scene has a pass with a matching + // DrawListTag. If your pipeline doesn't have a "lowEndForward" DrawListTag, no draw items + // for this shader will be created. + + "Source" : "./StandardPBR_LowEndForward.azsl", + + "DepthStencilState" : + { + "Depth" : + { + "Enable" : true, + "CompareFunc" : "GreaterEqual" + }, + "Stencil" : + { + "Enable" : true, + "ReadMask" : "0x00", + "WriteMask" : "0xFF", + "FrontFace" : + { + "Func" : "Always", + "DepthFailOp" : "Keep", + "FailOp" : "Keep", + "PassOp" : "Replace" + }, + "BackFace" : + { + "Func" : "Always", + "DepthFailOp" : "Keep", + "FailOp" : "Keep", + "PassOp" : "Replace" + } + } + }, + + "CompilerHints" : { + "DisableOptimizations" : false + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "StandardPbr_ForwardPassVS", + "type": "Vertex" + }, + { + "name": "StandardPbr_ForwardPassPS_EDS", + "type": "Fragment" + } + ] + }, + + "DrawList" : "lowEndForward" +} diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua index 7c3d989c35..2733713122 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua +++ b/Gems/Atom/Feature/Common/Assets/Materials/Types/StandardPBR_ShaderEnable.lua @@ -29,26 +29,33 @@ function Process(context) local depthPass = context:GetShaderByTag("DepthPass") local shadowMap = context:GetShaderByTag("Shadowmap") local forwardPassEDS = context:GetShaderByTag("ForwardPass_EDS") + local lowEndForwardEDS = context:GetShaderByTag("LowEndForward_EDS") + local depthPassWithPS = context:GetShaderByTag("DepthPass_WithPS") local shadowMapWitPS = context:GetShaderByTag("Shadowmap_WithPS") local forwardPass = context:GetShaderByTag("ForwardPass") + local lowEndForward = context:GetShaderByTag("LowEndForward") if parallaxEnabled and parallaxPdoEnabled then depthPass:SetEnabled(false) shadowMap:SetEnabled(false) forwardPassEDS:SetEnabled(false) + lowEndForwardEDS:SetEnabled(false) depthPassWithPS:SetEnabled(true) shadowMapWitPS:SetEnabled(true) forwardPass:SetEnabled(true) + lowEndForward:SetEnabled(true) else depthPass:SetEnabled(opacityMode == OpacityMode_Opaque) shadowMap:SetEnabled(opacityMode == OpacityMode_Opaque) forwardPassEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) + lowEndForwardEDS:SetEnabled((opacityMode == OpacityMode_Opaque) or (opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) depthPassWithPS:SetEnabled(opacityMode == OpacityMode_Cutout) shadowMapWitPS:SetEnabled(opacityMode == OpacityMode_Cutout) forwardPass:SetEnabled(opacityMode == OpacityMode_Cutout) + lowEndForward:SetEnabled(opacityMode == OpacityMode_Cutout) end context:GetShaderByTag("DepthPassTransparentMin"):SetEnabled((opacityMode == OpacityMode_Blended) or (opacityMode == OpacityMode_TintedTransparent)) diff --git a/Gems/Atom/Feature/Common/Assets/Passes/Forward.pass b/Gems/Atom/Feature/Common/Assets/Passes/Forward.pass index 31a8ed1879..b66e3bb4e1 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/Forward.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/Forward.pass @@ -148,22 +148,6 @@ }, "LoadAction": "Clear" } - }, - { - "Name": "ScatterDistanceOutput", - "SlotType": "Output", - "ScopeAttachmentUsage": "RenderTarget", - "LoadStoreAction": { - "ClearValue": { - "Value": [ - 0.0, - 0.0, - 0.0, - 0.0 - ] - }, - "LoadAction": "Clear" - } } ], "ImageAttachments": [ @@ -238,19 +222,6 @@ "AssetRef": { "FilePath": "Textures/BRDFTexture.attimage" } - }, - { - "Name": "ScatterDistanceImage", - "SizeSource": { - "Source": { - "Pass": "Parent", - "Attachment": "SwapChainOutput" - } - }, - "ImageDescriptor": { - "Format": "R11G11B10_FLOAT", - "SharedQueueMask": "Graphics" - } } ], "Connections": [ @@ -295,13 +266,6 @@ "Pass": "This", "Attachment": "BRDFTexture" } - }, - { - "LocalSlot": "ScatterDistanceOutput", - "AttachmentRef": { - "Pass": "This", - "Attachment": "ScatterDistanceImage" - } } ] } diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass new file mode 100644 index 0000000000..3e804d23e2 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/LightAdaptationParent.pass @@ -0,0 +1,146 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "LightAdaptationParentTemplate", + "PassClass": "ParentPass", + "Slots": [ + // Inputs... + { + "Name": "LightingInput", + "SlotType": "Input" + }, + // SwapChain here is only used to reference the frame height and format + { + "Name": "SwapChainOutput", + "SlotType": "InputOutput" + }, + // Outputs... + { + "Name": "Output", + "SlotType": "Output" + }, + // Debug Outputs... + { + "Name": "LuminanceMipChainOutput", + "SlotType": "Output" + } + ], + "Connections": [ + { + "LocalSlot": "Output", + "AttachmentRef": { + "Pass": "DisplayMapperPass", + "Attachment": "Output" + } + }, + { + "LocalSlot": "LuminanceMipChainOutput", + "AttachmentRef": { + "Pass": "DownsampleLuminanceMipChain", + "Attachment": "MipChainInputOutput" + } + } + ], + "PassRequests": [ + { + "Name": "DownsampleLuminanceMinAvgMax", + "TemplateName": "DownsampleLuminanceMinAvgMaxCS", + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "LightingInput" + } + } + ] + }, + { + "Name": "DownsampleLuminanceMipChain", + "TemplateName": "DownsampleMipChainTemplate", + "Connections": [ + { + "LocalSlot": "MipChainInputOutput", + "AttachmentRef": { + "Pass": "DownsampleLuminanceMinAvgMax", + "Attachment": "Output" + } + } + ], + "PassData": { + "$type": "DownsampleMipChainPassData", + "ShaderAsset": { + "FilePath": "Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader" + } + } + }, + { + "Name": "EyeAdaptationPass", + "TemplateName": "EyeAdaptationTemplate", + "Enabled": false, + "Connections": [ + { + "LocalSlot": "SceneLuminanceInput", + "AttachmentRef": { + "Pass": "DownsampleLuminanceMipChain", + "Attachment": "MipChainInputOutput" + } + } + ] + }, + { + "Name": "LookModificationTransformPass", + "TemplateName": "LookModificationTransformTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "LightingInput" + } + }, + { + "LocalSlot": "EyeAdaptationDataInput", + "AttachmentRef": { + "Pass": "EyeAdaptationPass", + "Attachment": "EyeAdaptationDataInputOutput" + } + }, + { + "LocalSlot": "SwapChainOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + }, + { + "Name": "DisplayMapperPass", + "TemplateName": "DisplayMapperTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "LookModificationTransformPass", + "Attachment": "Output" + } + }, + { + "LocalSlot": "SwapChainOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + } + ] + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LowEndForward.pass b/Gems/Atom/Feature/Common/Assets/Passes/LowEndForward.pass new file mode 100644 index 0000000000..4b865fcb6d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/LowEndForward.pass @@ -0,0 +1,133 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "LowEndForwardPassTemplate", + "PassClass": "RasterPass", + "Slots": [ + // Inputs... + { + "Name": "BRDFTextureInput", + "ShaderInputName": "m_brdfMap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "DirectionalLightShadowmap", + "ShaderInputName": "m_directionalLightShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ExponentialShadowmapDirectional", + "ShaderInputName": "m_directionalLightExponentialShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ProjectedShadowmap", + "ShaderInputName": "m_projectedShadowmaps", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "ExponentialShadowmapProjected", + "ShaderInputName": "m_projectedExponentialShadowmap", + "SlotType": "Input", + "ScopeAttachmentUsage": "Shader", + "ImageViewDesc": { + "IsArray": 1 + } + }, + { + "Name": "TileLightData", + "SlotType": "Input", + "ShaderInputName": "m_tileLightData", + "ScopeAttachmentUsage": "Shader" + }, + { + "Name": "LightListRemapped", + "SlotType": "Input", + "ShaderInputName": "m_lightListRemapped", + "ScopeAttachmentUsage": "Shader" + }, + // Input/Outputs... + { + "Name": "DepthStencilInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" + }, + // Outputs... + { + "Name": "LightingOutput", + "SlotType": "Output", + "ScopeAttachmentUsage": "RenderTarget", + "LoadStoreAction": { + "ClearValue": { + "Value": [ + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + "LoadAction": "Clear" + } + } + ], + "ImageAttachments": [ + { + "Name": "LightingAttachment", + "SizeSource": { + "Source": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + }, + "MultisampleSource": { + "Pass": "This", + "Attachment": "DepthStencilInputOutput" + }, + "ImageDescriptor": { + "Format": "R16G16B16A16_FLOAT", + "SharedQueueMask": "Graphics" + } + }, + { + "Name": "BRDFTexture", + "Lifetime": "Imported", + "AssetRef": { + "FilePath": "Textures/BRDFTexture.attimage" + } + } + ], + "Connections": [ + { + "LocalSlot": "LightingOutput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "LightingAttachment" + } + }, + { + "LocalSlot": "BRDFTextureInput", + "AttachmentRef": { + "Pass": "This", + "Attachment": "BRDFTexture" + } + } + ] + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass new file mode 100644 index 0000000000..b19569fb9d --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/LowEndPipeline.pass @@ -0,0 +1,344 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "LowEndPipelineTemplate", + "PassClass": "ParentPass", + "Slots": [ + { + "Name": "SwapChainOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + } + ], + "PassRequests": [ + { + "Name": "MorphTargetPass", + "TemplateName": "MorphTargetPassTemplate" + }, + { + "Name": "SkinningPass", + "TemplateName": "SkinningPassTemplate", + "Connections": [ + { + "LocalSlot": "SkinnedMeshOutputStream", + "AttachmentRef": { + "Pass": "MorphTargetPass", + "Attachment": "MorphTargetDeltaOutput" + } + } + ] + }, + { + "Name": "DepthPrePass", + "TemplateName": "DepthMSAAParentTemplate", + "Connections": [ + { + "LocalSlot": "SkinnedMeshes", + "AttachmentRef": { + "Pass": "SkinningPass", + "Attachment": "SkinnedMeshOutputStream" + } + }, + { + "LocalSlot": "SwapChainOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + }, + { + "Name": "LightCullingPass", + "TemplateName": "LightCullingParentTemplate", + "Connections": [ + { + "LocalSlot": "SkinnedMeshes", + "AttachmentRef": { + "Pass": "SkinningPass", + "Attachment": "SkinnedMeshOutputStream" + } + }, + { + "LocalSlot": "DepthMSAA", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthMSAA" + } + }, + { + "LocalSlot": "SwapChainOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + }, + { + "Name": "ShadowPass", + "TemplateName": "ShadowParentTemplate", + "Connections": [ + { + "LocalSlot": "SkinnedMeshes", + "AttachmentRef": { + "Pass": "SkinningPass", + "Attachment": "SkinnedMeshOutputStream" + } + }, + { + "LocalSlot": "SwapChainOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + }, + { + "Name": "ForwardPass", + "TemplateName": "LowEndForwardPassTemplate", + "Connections": [ + // Inputs... + { + "LocalSlot": "DirectionalLightShadowmap", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "DirectionalShadowmap" + } + }, + { + "LocalSlot": "ExponentialShadowmapDirectional", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "DirectionalESM" + } + }, + { + "LocalSlot": "ProjectedShadowmap", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "ProjectedShadowmap" + } + }, + { + "LocalSlot": "ExponentialShadowmapProjected", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "ProjectedESM" + } + }, + { + "LocalSlot": "TileLightData", + "AttachmentRef": { + "Pass": "LightCullingPass", + "Attachment": "TileLightData" + } + }, + { + "LocalSlot": "LightListRemapped", + "AttachmentRef": { + "Pass": "LightCullingPass", + "Attachment": "LightListRemapped" + } + }, + // Input/Outputs... + { + "LocalSlot": "DepthStencilInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "DepthMSAA" + } + } + ], + "PassData": { + "$type": "RasterPassData", + "DrawListTag": "lowEndForward", + "PipelineViewTag": "MainCamera", + "PassSrgAsset": { + "FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg" + } + } + }, + { + "Name": "SkyBoxPass", + "TemplateName": "SkyBoxTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "SpecularInputOutput", + "AttachmentRef": { + "Pass": "ForwardPass", + "Attachment": "LightingOutput" + } + }, + { + "LocalSlot": "SkyBoxDepth", + "AttachmentRef": { + "Pass": "ForwardPass", + "Attachment": "DepthStencilInputOutput" + } + } + ] + }, + { + "Name": "MSAAResolvePass", + "TemplateName": "MSAAResolveColorTemplate", + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "SkyBoxPass", + "Attachment": "SpecularInputOutput" + } + } + ] + }, + { + "Name": "TransparentPass", + "TemplateName": "TransparentParentTemplate", + "Connections": [ + { + "LocalSlot": "DirectionalShadowmap", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "DirectionalShadowmap" + } + }, + { + "LocalSlot": "DirectionalESM", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "DirectionalESM" + } + }, + { + "LocalSlot": "ProjectedShadowmap", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "ProjectedShadowmap" + } + }, + { + "LocalSlot": "ProjectedESM", + "AttachmentRef": { + "Pass": "ShadowPass", + "Attachment": "ProjectedESM" + } + }, + { + "LocalSlot": "TileLightData", + "AttachmentRef": { + "Pass": "LightCullingPass", + "Attachment": "TileLightData" + } + }, + { + "LocalSlot": "LightListRemapped", + "AttachmentRef": { + "Pass": "LightCullingPass", + "Attachment": "LightListRemapped" + } + }, + { + "LocalSlot": "DepthStencil", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } + }, + { + "LocalSlot": "InputOutput", + "AttachmentRef": { + "Pass": "MSAAResolvePass", + "Attachment": "Output" + } + } + ] + }, + { + "Name": "LightAdaptation", + "TemplateName": "LightAdaptationParentTemplate", + "Connections": [ + { + "LocalSlot": "LightingInput", + "AttachmentRef": { + "Pass": "TransparentPass", + "Attachment": "InputOutput" + } + }, + { + "LocalSlot": "SwapChainOutput", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + }, + { + "Name": "AuxGeomPass", + "TemplateName": "AuxGeomPassTemplate", + "Enabled": true, + "Connections": [ + { + "LocalSlot": "ColorInputOutput", + "AttachmentRef": { + "Pass": "LightAdaptation", + "Attachment": "Output" + } + }, + { + "LocalSlot": "DepthInputOutput", + "AttachmentRef": { + "Pass": "DepthPrePass", + "Attachment": "Depth" + } + } + ], + "PassData": { + "$type": "RasterPassData", + "DrawListTag": "auxgeom", + "PipelineViewTag": "MainCamera" + } + }, + { + "Name": "UIPass", + "TemplateName": "UIParentTemplate", + "Connections": [ + { + "LocalSlot": "InputOutput", + "AttachmentRef": { + "Pass": "AuxGeomPass", + "Attachment": "ColorInputOutput" + } + } + ] + }, + { + "Name": "CopyToSwapChain", + "TemplateName": "FullscreenCopyTemplate", + "Connections": [ + { + "LocalSlot": "Input", + "AttachmentRef": { + "Pass": "UIPass", + "Attachment": "InputOutput" + } + }, + { + "LocalSlot": "Output", + "AttachmentRef": { + "Pass": "Parent", + "Attachment": "SwapChainOutput" + } + } + ] + } + ] + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass index dda120e164..a691fe2534 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/OpaqueParent.pass @@ -305,7 +305,7 @@ }, { "Name": "SkyBoxPass", - "TemplateName": "SkyBoxTemplate", + "TemplateName": "SkyBoxTwoOutputsTemplate", "Enabled": true, "Connections": [ { diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset index b83ab65ff2..c56e8932b1 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset +++ b/Gems/Atom/Feature/Common/Assets/Passes/PassTemplates.azasset @@ -92,6 +92,10 @@ "Name": "SkyBoxTemplate", "Path": "Passes/SkyBox.pass" }, + { + "Name": "SkyBoxTwoOutputsTemplate", + "Path": "Passes/SkyBox_TwoOutputs.pass" + }, { "Name": "UIPassTemplate", "Path": "Passes/UI.pass" @@ -483,6 +487,18 @@ { "Name": "UIParentTemplate", "Path": "Passes/UIParent.pass" + }, + { + "Name": "LightAdaptationParentTemplate", + "Path": "Passes/LightAdaptationParent.pass" + }, + { + "Name": "LowEndForwardPassTemplate", + "Path": "Passes/LowEndForward.pass" + }, + { + "Name": "LowEndPipelineTemplate", + "Path": "Passes/LowEndPipeline.pass" } ] } diff --git a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass index 37b1ee5c5a..36f7f1e985 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/PostProcessParent.pass @@ -40,7 +40,7 @@ { "LocalSlot": "Output", "AttachmentRef": { - "Pass": "DisplayMapperPass", + "Pass": "LightAdaptation", "Attachment": "Output" } }, @@ -54,8 +54,8 @@ { "LocalSlot": "LuminanceMipChainOutput", "AttachmentRef": { - "Pass": "DownsampleLuminanceMipChain", - "Attachment": "MipChainInputOutput" + "Pass": "LightAdaptation", + "Attachment": "LuminanceMipChainOutput" } } ], @@ -115,94 +115,16 @@ } ] }, - // Everything before this point deals in raw lighting values - // --------------------------------------------------------- - // Everything after starts to map to values we see on screen { - "Name": "DownsampleLuminanceMinAvgMax", - "TemplateName": "DownsampleLuminanceMinAvgMaxCS", + "Name": "LightAdaptation", + "TemplateName": "LightAdaptationParentTemplate", "Connections": [ { - "LocalSlot": "Input", + "LocalSlot": "LightingInput", "AttachmentRef": { "Pass": "BloomPass", "Attachment": "InputOutput" } - } - ] - }, - { - "Name": "DownsampleLuminanceMipChain", - "TemplateName": "DownsampleMipChainTemplate", - "Connections": [ - { - "LocalSlot": "MipChainInputOutput", - "AttachmentRef": { - "Pass": "DownsampleLuminanceMinAvgMax", - "Attachment": "Output" - } - } - ], - "PassData": { - "$type": "DownsampleMipChainPassData", - "ShaderAsset": { - "FilePath": "Shaders/PostProcessing/DownsampleMinAvgMaxCS.shader" - } - } - }, - { - "Name": "EyeAdaptationPass", - "TemplateName": "EyeAdaptationTemplate", - "Enabled": false, - "Connections": [ - { - "LocalSlot": "SceneLuminanceInput", - "AttachmentRef": { - "Pass": "DownsampleLuminanceMipChain", - "Attachment": "MipChainInputOutput" - } - } - ] - }, - { - "Name": "LookModificationTransformPass", - "TemplateName": "LookModificationTransformTemplate", - "Enabled": true, - "Connections": [ - { - "LocalSlot": "Input", - "AttachmentRef": { - "Pass": "BloomPass", - "Attachment": "InputOutput" - } - }, - { - "LocalSlot": "EyeAdaptationDataInput", - "AttachmentRef": { - "Pass": "EyeAdaptationPass", - "Attachment": "EyeAdaptationDataInputOutput" - } - }, - { - "LocalSlot": "SwapChainOutput", - "AttachmentRef": { - "Pass": "Parent", - "Attachment": "SwapChainOutput" - } - } - ] - }, - { - "Name": "DisplayMapperPass", - "TemplateName": "DisplayMapperTemplate", - "Enabled": true, - "Connections": [ - { - "LocalSlot": "Input", - "AttachmentRef": { - "Pass": "LookModificationTransformPass", - "Attachment": "Output" - } }, { "LocalSlot": "SwapChainOutput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SkyBox.pass b/Gems/Atom/Feature/Common/Assets/Passes/SkyBox.pass index 57f442e5de..fb16271ba7 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/SkyBox.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/SkyBox.pass @@ -12,11 +12,6 @@ "SlotType": "InputOutput", "ScopeAttachmentUsage": "RenderTarget" }, - { - "Name": "ReflectionInputOutput", - "SlotType": "InputOutput", - "ScopeAttachmentUsage": "RenderTarget" - }, { "Name": "SkyBoxDepth", "SlotType": "InputOutput", diff --git a/Gems/Atom/Feature/Common/Assets/Passes/SkyBox_TwoOutputs.pass b/Gems/Atom/Feature/Common/Assets/Passes/SkyBox_TwoOutputs.pass new file mode 100644 index 0000000000..0ed7b39288 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Passes/SkyBox_TwoOutputs.pass @@ -0,0 +1,43 @@ +{ + "Type": "JsonSerialization", + "Version": 1, + "ClassName": "PassAsset", + "ClassData": { + "PassTemplate": { + "Name": "SkyBoxTwoOutputsTemplate", + "PassClass": "FullScreenTriangle", + "Slots": [ + { + "Name": "SpecularInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { + "Name": "ReflectionInputOutput", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "RenderTarget" + }, + { + "Name": "SkyBoxDepth", + "SlotType": "InputOutput", + "ScopeAttachmentUsage": "DepthStencil" + } + ], + "PassData": { + "$type": "FullscreenTrianglePassData", + "ShaderAsset": { + "FilePath": "shaders/skybox/skybox_twooutputs.shader" + }, + "PipelineViewTag": "MainCamera", + "ShaderDataMappings": { + "FloatMappings": [ + { + "Name": "m_sunIntensityMultiplier", + "Value": 1.0 + } + ] + } + } + } + } +} diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli index 50896cdf25..abc4ec7fc4 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/DefaultObjectSrg.azsli @@ -31,6 +31,16 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject return SceneSrg::GetObjectToWorldInverseTransposeMatrix(m_objectId); } + //[GFX TODO][ATOM-15280] Move wrinkle mask data from the default object srg into something specific to the Skin shader + uint m_wrinkle_mask_count; + float4 m_wrinkle_mask_weights[4]; + Texture2D m_wrinkle_masks[16]; + + float GetWrinkleMaskWeight(uint index) + { + return m_wrinkle_mask_weights[index / 4][index % 4]; + } + //! Reflection Probe (smallest probe volume that overlaps the object position) struct ReflectionProbeData { diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassOutput.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassOutput.azsli index acc215f1c9..5821deb3b1 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassOutput.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassOutput.azsli @@ -10,6 +10,21 @@ * */ +#ifdef UNIFIED_FORWARD_OUTPUT + +struct ForwardPassOutput +{ + float4 m_color : SV_Target0; +}; + +struct ForwardPassOutputWithDepth +{ + float4 m_color : SV_Target0; + float m_depth : SV_Depth; +}; + +#else + struct ForwardPassOutput { float4 m_diffuseColor : SV_Target0; //!< RGB = Diffuse Lighting, A = Blend Alpha (for blended surfaces) OR A = special encoding of surfaceScatteringFactor, m_subsurfaceScatteringQuality, o_enableSubsurfaceScattering @@ -30,3 +45,5 @@ struct ForwardPassOutputWithDepth float4 m_normal : SV_Target4; float m_depth : SV_Depth; }; + +#endif diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli index 7400005508..3be9d5756a 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/Ibl.azsli @@ -12,38 +12,39 @@ #pragma once +// --- Static Options Available --- +// FORCE_IBL_IN_FORWARD_PASS - forces IBL lighting to be run in the forward pass, used in pipelines that don't have a reflection pass + #include #include #include #include -void ApplyIblDiffuse( +float3 GetIblDiffuse( float3 normal, float3 albedo, - float3 diffuseResponse, - out float3 outDiffuse) + float3 diffuseResponse) { float3 irradianceDir = MultiplyVectorQuaternion(normal, SceneSrg::m_iblOrientation); float3 diffuseSample = SceneSrg::m_diffuseEnvMap.Sample(SceneSrg::m_samplerEnv, GetCubemapCoords(irradianceDir)).rgb; - outDiffuse = diffuseResponse * albedo * diffuseSample; + return diffuseResponse * albedo * diffuseSample; } -void ApplyIblSpecular( +float3 GetIblSpecular( float3 position, float3 normal, float3 specularF0, float roughnessLinear, float3 dirToCamera, - float2 brdf, - out float3 outSpecular) + float2 brdf) { float3 reflectDir = reflect(-dirToCamera, normal); reflectDir = MultiplyVectorQuaternion(reflectDir, SceneSrg::m_iblOrientation); // global - outSpecular = SceneSrg::m_specularEnvMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(roughnessLinear)).rgb; + float3 outSpecular = SceneSrg::m_specularEnvMap.SampleLevel(SceneSrg::m_samplerEnv, GetCubemapCoords(reflectDir), GetRoughnessMip(roughnessLinear)).rgb; outSpecular *= (specularF0 * brdf.x + brdf.y); // reflection probe @@ -72,86 +73,55 @@ void ApplyIblSpecular( outSpecular = lerp(outSpecular, probeSpecular, blendAmount); } + return outSpecular; } void ApplyIBL(Surface surface, inout LightingData lightingData) { - if (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent) +#ifdef FORCE_IBL_IN_FORWARD_PASS + bool useDiffuseIbl = true; + bool useSpecularIbl = true; + bool useIbl = o_enableIBL; +#else + bool isTransparent = (o_opacity_mode == OpacityMode::Blended || o_opacity_mode == OpacityMode::TintedTransparent); + bool useDiffuseIbl = isTransparent; + bool useSpecularIbl = (isTransparent || o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular); + bool useIbl = o_enableIBL && (useDiffuseIbl || useSpecularIbl); +#endif + + if(useIbl) { - // transparencies currently require IBL in the forward pass - if (o_enableIBL) + float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure); + + if(useDiffuseIbl) { - float3 iblDiffuse = 0.0f; - ApplyIblDiffuse( - surface.normal, - surface.albedo, - lightingData.diffuseResponse, - iblDiffuse); - - float3 iblSpecular = 0.0f; - ApplyIblSpecular( - surface.position, - surface.normal, - surface.specularF0, - surface.roughnessLinear, - lightingData.dirToCamera, - lightingData.brdf, - iblSpecular); - - // Adjust IBL lighting by exposure. - float iblExposureFactor = pow(2.0, SceneSrg::m_iblExposure); + float3 iblDiffuse = GetIblDiffuse(surface.normal, surface.albedo, lightingData.diffuseResponse); lightingData.diffuseLighting += (iblDiffuse * iblExposureFactor * lightingData.diffuseAmbientOcclusion); - lightingData.specularLighting += (iblSpecular * iblExposureFactor); } - } - else if (o_meshUseForwardPassIBLSpecular || o_materialUseForwardPassIBLSpecular) - { - if (o_enableIBL) - { - float3 iblSpecular = 0.0f; - ApplyIblSpecular( - surface.position, - surface.normal, - surface.specularF0, - surface.roughnessLinear, - lightingData.dirToCamera, - lightingData.brdf, - iblSpecular); + if(useSpecularIbl) + { + float3 iblSpecular = GetIblSpecular(surface.position, surface.normal, surface.specularF0, surface.roughnessLinear, lightingData.dirToCamera, lightingData.brdf); iblSpecular *= lightingData.multiScatterCompensation; - if (o_clearCoat_feature_enabled) + if (o_clearCoat_feature_enabled && surface.clearCoat.factor > 0.0f) { - if (surface.clearCoat.factor > 0.0f) - { - float clearCoatNdotV = saturate(dot(surface.clearCoat.normal, lightingData.dirToCamera)); - clearCoatNdotV = max(clearCoatNdotV, 0.01f); // [GFX TODO][ATOM-4466] This is a current band-aid for specular noise at grazing angles. - float2 clearCoatBrdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, GetBRDFTexCoords(surface.clearCoat.roughness, clearCoatNdotV)).rg; + float clearCoatNdotV = saturate(dot(surface.clearCoat.normal, lightingData.dirToCamera)); + clearCoatNdotV = max(clearCoatNdotV, 0.01f); // [GFX TODO][ATOM-4466] This is a current band-aid for specular noise at grazing angles. + float2 clearCoatBrdf = PassSrg::m_brdfMap.Sample(PassSrg::LinearSampler, GetBRDFTexCoords(surface.clearCoat.roughness, clearCoatNdotV)).rg; - // clear coat uses fixed IOR = 1.5 represents polyurethane which is the most common material for gloss clear coat - // coat layer assumed to be dielectric thus don't need multiple scattering compensation - float3 clearCoatSpecularF0 = float3(0.04f, 0.04f, 0.04f); - float3 clearCoatIblSpecular = 0.0f; + // clear coat uses fixed IOR = 1.5 represents polyurethane which is the most common material for gloss clear coat + // coat layer assumed to be dielectric thus don't need multiple scattering compensation + float3 clearCoatSpecularF0 = float3(0.04f, 0.04f, 0.04f); + float3 clearCoatIblSpecular = GetIblSpecular(surface.position, surface.clearCoat.normal, clearCoatSpecularF0, surface.clearCoat.roughness, lightingData.dirToCamera, clearCoatBrdf); - ApplyIblSpecular( - surface.position, - surface.clearCoat.normal, - clearCoatSpecularF0, - surface.clearCoat.roughness, - lightingData.dirToCamera, - clearCoatBrdf, - clearCoatIblSpecular); - - clearCoatIblSpecular *= surface.clearCoat.factor; + clearCoatIblSpecular *= surface.clearCoat.factor; - // attenuate base layer energy - float3 clearCoatResponse = FresnelSchlickWithRoughness(clearCoatNdotV, clearCoatSpecularF0, surface.clearCoat.roughness) * surface.clearCoat.factor; - iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular; - } + // attenuate base layer energy + float3 clearCoatResponse = FresnelSchlickWithRoughness(clearCoatNdotV, clearCoatSpecularF0, surface.clearCoat.roughness) * surface.clearCoat.factor; + iblSpecular = iblSpecular * (1.0 - clearCoatResponse) * (1.0 - clearCoatResponse) + clearCoatIblSpecular; } - - float iblExposureFactor = pow(2.0f, SceneSrg::m_iblExposure); lightingData.specularLighting += (iblSpecular * iblExposureFactor); } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ShaderQualityOptions.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ShaderQualityOptions.azsli new file mode 100644 index 0000000000..cc4aa7cf42 --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/ShaderQualityOptions.azsli @@ -0,0 +1,26 @@ +/* +* 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 + +// This file translates quality option macros like QUALITY_LOW_END to their relevant settings + +#ifdef QUALITY_LOW_END + + // Unifies the forward output into a single lighting buffer instead of splitting it into a GBuffer + #define UNIFIED_FORWARD_OUTPUT 1 + + // Forces IBL lighting to be executed in the forward pass instead of subsequent refleciton passes + #define FORCE_IBL_IN_FORWARD_PASS 1 + +#endif + diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.azsl index 1ee30a4f98..1bebb2ec47 100644 --- a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.azsl +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox.azsl @@ -10,6 +10,9 @@ * */ +// --- Static Options Available --- +// SKYBOX_TWO_OUTPUTS - Skybox renders to two rendertargets instead of one (SkyBox_TwoOutputs.pass writes to specular and reflection targets) + #include #include #include @@ -102,7 +105,9 @@ float3 GetCubemapCoords(float3 original) struct PSOutput { float4 m_specular : SV_Target0; +#ifdef SKYBOX_TWO_OUTPUTS float4 m_reflection : SV_Target1; +#endif }; PSOutput MainPS(VSOutput input) @@ -163,6 +168,8 @@ PSOutput MainPS(VSOutput input) PSOutput OUT; OUT.m_specular = float4(color, 1.0); +#ifdef SKYBOX_TWO_OUTPUTS OUT.m_reflection = float4(color, 1.0); +#endif return OUT; } diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.azsl b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.azsl new file mode 100644 index 0000000000..feacd2f44f --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.azsl @@ -0,0 +1,17 @@ +/* +* 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. +* +*/ + +// NOTE: This file is a temporary workaround until .shader files can #define macros for their .azsl files + +#define SKYBOX_TWO_OUTPUTS + +#include "SkyBox.azsl" diff --git a/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader new file mode 100644 index 0000000000..ec80d4a20e --- /dev/null +++ b/Gems/Atom/Feature/Common/Assets/Shaders/SkyBox/SkyBox_TwoOutputs.shader @@ -0,0 +1,22 @@ +{ + "Source" : "SkyBox_TwoOutputs", + + "DepthStencilState" : { + "Depth" : { "Enable" : true, "CompareFunc" : "GreaterEqual" } + }, + + "ProgramSettings": + { + "EntryPoints": + [ + { + "name": "MainVS", + "type": "Vertex" + }, + { + "name": "MainPS", + "type": "Fragment" + } + ] + } +} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 84ef494216..f1d8fa81be 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -38,6 +38,7 @@ set(FILES Materials/Types/StandardMultilayerPBR_ForwardPass_EDS.shader Materials/Types/StandardMultilayerPBR_Parallax.lua Materials/Types/StandardMultilayerPBR_ParallaxPerLayer.lua + Materials/Types/StandardMultilayerPBR_ShaderEnable.lua Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.azsl Materials/Types/StandardMultilayerPBR_Shadowmap_WithPS.shader Materials/Types/StandardPBR.materialtype @@ -52,6 +53,9 @@ set(FILES Materials/Types/StandardPBR_ForwardPass_EDS.shader Materials/Types/StandardPBR_HandleOpacityDoubleSided.lua Materials/Types/StandardPBR_HandleOpacityMode.lua + Materials/Types/StandardPBR_LowEndForward.azsl + Materials/Types/StandardPBR_LowEndForward.shader + Materials/Types/StandardPBR_LowEndForward_EDS.shader Materials/Types/StandardPBR_ParallaxState.lua Materials/Types/StandardPBR_Roughness.lua Materials/Types/StandardPBR_ShaderEnable.lua @@ -116,6 +120,7 @@ set(FILES Passes/DiffuseProbeGridBlendDistance.pass Passes/DiffuseProbeGridBlendIrradiance.pass Passes/DiffuseProbeGridBorderUpdate.pass + Passes/DiffuseProbeGridClassification.pass Passes/DiffuseProbeGridDownsample.pass Passes/DiffuseProbeGridRayTracing.pass Passes/DiffuseProbeGridRelocation.pass @@ -144,6 +149,7 @@ set(FILES Passes/FullscreenCopy.pass Passes/FullscreenOutputOnly.pass Passes/ImGui.pass + Passes/LightAdaptationParent.pass Passes/LightCulling.pass Passes/LightCullingHeatmap.pass Passes/LightCullingParent.pass @@ -152,6 +158,8 @@ set(FILES Passes/LightCullingTilePrepareMSAA.pass Passes/LookModificationComposite.pass Passes/LookModificationTransform.pass + Passes/LowEndForward.pass + Passes/LowEndPipeline.pass Passes/LuminanceHeatmap.pass Passes/LuminanceHistogramGenerator.pass Passes/MainPipeline.pass @@ -179,13 +187,16 @@ set(FILES Passes/ReflectionScreenSpace.pass Passes/ReflectionScreenSpaceBlur.pass Passes/ReflectionScreenSpaceBlurHorizontal.pass + Passes/ReflectionScreenSpaceBlurMobile.pass Passes/ReflectionScreenSpaceBlurVertical.pass Passes/ReflectionScreenSpaceComposite.pass + Passes/ReflectionScreenSpaceMobile.pass Passes/ReflectionScreenSpaceTrace.pass Passes/Reflections_nomsaa.pass Passes/ShadowParent.pass Passes/Skinning.pass Passes/SkyBox.pass + Passes/SkyBox_TwoOutputs.pass Passes/SMAA1xApplyLinearHDRColor.pass Passes/SMAA1xApplyPerceptualColor.pass Passes/SMAABlendingWeightCalculation.pass @@ -205,6 +216,7 @@ set(FILES ShaderLib/Atom/Features/IndirectRendering.azsli ShaderLib/Atom/Features/MatrixUtility.azsli ShaderLib/Atom/Features/ParallaxMapping.azsli + ShaderLib/Atom/Features/ShaderQualityOptions.azsli ShaderLib/Atom/Features/SphericalHarmonicsUtility.azsli ShaderLib/Atom/Features/SrgSemantics.azsli ShaderLib/Atom/Features/ColorManagement/TransformColor.azsli @@ -272,6 +284,7 @@ set(FILES ShaderLib/Atom/Features/PostProcessing/GlyphData.azsli ShaderLib/Atom/Features/PostProcessing/GlyphRender.azsli ShaderLib/Atom/Features/PostProcessing/PostProcessUtil.azsli + ShaderLib/Atom/Features/RayTracing/RayTracingSceneSrg.azsli ShaderLib/Atom/Features/ScreenSpace/ScreenSpaceUtil.azsli ShaderLib/Atom/Features/Shadow/BicubicPcfFilters.azsli ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli @@ -471,4 +484,6 @@ set(FILES Shaders/SkinnedMesh/LinearSkinningPassSRG.azsli Shaders/SkyBox/SkyBox.azsl Shaders/SkyBox/SkyBox.shader + Shaders/SkyBox/SkyBox_TwoOutputs.azsl + Shaders/SkyBox/SkyBox_TwoOutputs.shader ) diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h index a89653c359..03fbb93923 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h @@ -60,6 +60,12 @@ namespace AZ : public DisplayMapperFeatureProcessorInterface { public: + enum OutputDeviceTransformFlags + { + AlterSurround = 0x1, // Apply gamma adjustment to compensate for dim surround + ApplyDesaturation = 0x2, // Apply desaturation to compensate for luminance difference + ApplyCATD60toD65 = 0x4, // Apply Color appearance transform (CAT) from ACES white point to assumed observer adapted white point + }; AZ_RTTI(AZ::Render::AcesDisplayMapperFeatureProcessor, "{995C2B93-8B08-4313-89B0-02394F90F1B8}", AZ::Render::DisplayMapperFeatureProcessorInterface); @@ -92,12 +98,6 @@ namespace AZ static void ApplyLdrOdtParameters(DisplayMapperParameters* pOutParameters); static void ApplyHdrOdtParameters(DisplayMapperParameters* pOutParameters, const OutputDeviceTransformType& odtType); - enum OutputDeviceTransformFlags { - AlterSurround = 0x1, // Apply gamma adjustment to compensate for dim surround - ApplyDesaturation = 0x2, // Apply desaturation to compensate for luminance difference - ApplyCATD60toD65 = 0x4, // Apply Color appearance transform (CAT) from ACES white point to assumed observer adapted white point - }; - enum OutputDeviceTransformMode { Srgb = 0, PerceptualQuantizer, diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h index 5fce71fc07..5a0ccdb32a 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/AcesOutputTransformPass.h @@ -46,6 +46,7 @@ namespace AZ static RPI::Ptr Create(const RPI::PassDescriptor& descriptor); void SetDisplayBufferFormat(RHI::Format format); + void SetAcesParameterOverrides(const AcesParameterOverrides& acesParameterOverrides); private: explicit AcesOutputTransformPass(const RPI::PassDescriptor& descriptor); @@ -65,6 +66,8 @@ namespace AZ AZ::Render::DisplayMapperParameters m_displayMapperParameters = {}; RHI::Format m_displayBufferFormat = RHI::Format::Unknown; + + AcesParameterOverrides m_acesParameterOverrides; }; } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h index b645df4f6f..4dc090b831 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/DisplayMapper/DisplayMapperConfigurationDescriptor.h @@ -24,6 +24,48 @@ namespace AZ namespace Render { + /** + * The ACES display mapper parameter overrides. + * These parameters override default ACES parameters when m_overrideDefaults is true. + */ + struct AcesParameterOverrides final + { + AZ_TYPE_INFO(AcesParameterOverrides, "{3EE8C0D4-3792-46C0-B91C-B89A81C36B91}"); + static void Reflect(ReflectContext* context); + + void LoadPreset(); + + // When enabled allows parameter overrides for ACES configuration + bool m_overrideDefaults = false; + + // Apply gamma adjustment to compensate for dim surround + bool m_alterSurround = true; + // Apply desaturation to compensate for luminance difference + bool m_applyDesaturation = true; + // Apply Color appearance transform (CAT) from ACES white point to assumed observer adapted white point + bool m_applyCATD60toD65 = true; + + // Reference white and black luminance values + float m_cinemaLimitsBlack = 0.02f; + float m_cinemaLimitsWhite = 48.0f; + + // luminance linear extension below this + float m_minPoint = 0.0028798957f; + // luminance mid grey + float m_midPoint = 4.8f; + // luminance linear extension above this + float m_maxPoint = 1005.71912f; + + // Gamma adjustment to be applied to compensate for the condition of the viewing environment. + // Note that ACES uses a value of 0.9811 for adjusting from dark to dim surrounding. + float m_surroundGamma = 0.9811f; + // Optional gamma value that is applied as basic gamma curve OETF + float m_gamma = 2.2f; + + // Allows specifying default preset for different ODT modes + OutputDeviceTransformType m_preset = OutputDeviceTransformType_48Nits; + }; + //! A descriptor used to configure the DisplayMapper struct DisplayMapperConfigurationDescriptor final { @@ -37,6 +79,8 @@ namespace AZ bool m_ldrGradingLutEnabled = false; Data::Asset m_ldrColorGradingLut; + + AcesParameterOverrides m_acesParameterOverrides; }; //! Custom pass data for DisplayMapperPass. diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h index 7875e38fc0..0d61ef82d1 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessor.h @@ -148,6 +148,8 @@ namespace AZ Data::Instance GetModel(const MeshHandle& meshHandle) const override; Data::Asset GetModelAsset(const MeshHandle& meshHandle) const override; + Data::Instance GetObjectSrg(const MeshHandle& meshHandle) const override; + void QueueObjectSrgForCompile(const MeshHandle& meshHandle) const override; void SetMaterialAssignmentMap(const MeshHandle& meshHandle, const Data::Instance& material) override; void SetMaterialAssignmentMap(const MeshHandle& meshHandle, const MaterialAssignmentMap& materials) override; const MaterialAssignmentMap& GetMaterialAssignmentMap(const MeshHandle& meshHandle) const override; diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h index c2360068de..fb5bff5584 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/Mesh/MeshFeatureProcessorInterface.h @@ -61,6 +61,14 @@ namespace AZ virtual Data::Instance GetModel(const MeshHandle& meshHandle) const = 0; //! Gets the underlying RPI::ModelAsset for a meshHandle. virtual Data::Asset GetModelAsset(const MeshHandle& meshHandle) const = 0; + //! Gets the ObjectSrg for a meshHandle. + //! Updating the ObjectSrg should be followed by a call to QueueObjectSrgForCompile, + //! instead of compiling the srg directly. This way, if the srg has already been queued for compile, + //! it will not be queued twice in the same frame. The ObjectSrg should not be updated during + //! Simulate, or it will create a race between updating the data and the call to Compile + virtual Data::Instance GetObjectSrg(const MeshHandle& meshHandle) const = 0; + //! Queues the object srg for compile. + virtual void QueueObjectSrgForCompile(const MeshHandle& meshHandle) const = 0; //! Sets the MaterialAssignmentMap for a meshHandle, using just a single material for the DefaultMaterialAssignmentId. //! Note if there is already a material assignment map, this will replace the entire map with just a single material. virtual void SetMaterialAssignmentMap(const MeshHandle& meshHandle, const Data::Instance& material) = 0; diff --git a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h index 39fd7b4380..418ee0cfb8 100644 --- a/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Mocks/MockMeshFeatureProcessor.h @@ -23,6 +23,8 @@ namespace UnitTest MOCK_METHOD1(CloneMesh, MeshHandle(const MeshHandle&)); MOCK_CONST_METHOD1(GetModel, AZStd::intrusive_ptr(const MeshHandle&)); MOCK_CONST_METHOD1(GetModelAsset, AZ::Data::Asset(const MeshHandle&)); + MOCK_CONST_METHOD1(GetObjectSrg, AZStd::intrusive_ptr(const MeshHandle&)); + MOCK_CONST_METHOD1(QueueObjectSrgForCompile, void(const MeshHandle&)); MOCK_CONST_METHOD1(GetMaterialAssignmentMap, const AZ::Render::MaterialAssignmentMap&(const MeshHandle&)); MOCK_METHOD2(ConnectModelChangeEventHandler, void(const MeshHandle&, ModelChangedEvent::Handler&)); MOCK_METHOD3(SetTransform, void(const MeshHandle&, const AZ::Transform&, const AZ::Vector3&)); diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp index b2103739ba..6fe38ff032 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/AcesOutputTransformPass.cpp @@ -102,6 +102,36 @@ namespace AZ AcesDisplayMapperFeatureProcessor::GetAcesDisplayMapperParameters(&m_displayMapperParameters, OutputDeviceTransformType_48Nits); } } + + if (m_acesParameterOverrides.m_overrideDefaults) + { + m_displayMapperParameters.m_OutputDisplayTransformFlags = 0; + if (m_acesParameterOverrides.m_alterSurround) + { + m_displayMapperParameters.m_OutputDisplayTransformFlags |= AcesDisplayMapperFeatureProcessor::AlterSurround; + } + if (m_acesParameterOverrides.m_applyDesaturation) + { + m_displayMapperParameters.m_OutputDisplayTransformFlags |= AcesDisplayMapperFeatureProcessor::ApplyDesaturation; + } + if (m_acesParameterOverrides.m_applyCATD60toD65) + { + m_displayMapperParameters.m_OutputDisplayTransformFlags |= AcesDisplayMapperFeatureProcessor::ApplyCATD60toD65; + } + + m_displayMapperParameters.m_cinemaLimits[0] = m_acesParameterOverrides.m_cinemaLimitsBlack; + m_displayMapperParameters.m_cinemaLimits[1] = m_acesParameterOverrides.m_cinemaLimitsWhite; + m_displayMapperParameters.m_acesSplineParams.minPoint[0] = m_acesParameterOverrides.m_minPoint; + m_displayMapperParameters.m_acesSplineParams.midPoint[0] = m_acesParameterOverrides.m_midPoint; + m_displayMapperParameters.m_acesSplineParams.maxPoint[0] = m_acesParameterOverrides.m_maxPoint; + m_displayMapperParameters.m_surroundGamma = m_acesParameterOverrides.m_surroundGamma; + m_displayMapperParameters.m_gamma = m_acesParameterOverrides.m_gamma; + } + } + + void AcesOutputTransformPass::SetAcesParameterOverrides(const AcesParameterOverrides& acesParameterOverrides) + { + m_acesParameterOverrides = acesParameterOverrides; } } // namespace Render } // namespace AZ diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp index d80a02d083..e91e125b40 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperConfigurationDescriptor.cpp @@ -9,14 +9,54 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ + #include #include +#include namespace AZ { namespace Render { + void AcesParameterOverrides::Reflect(ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(0) + ->Field("OverrideDefaults", &AcesParameterOverrides::m_overrideDefaults) + ->Field("AlterSurround", &AcesParameterOverrides::m_alterSurround) + ->Field("ApplyDesaturation", &AcesParameterOverrides::m_applyDesaturation) + ->Field("ApplyCATD60toD65", &AcesParameterOverrides::m_applyCATD60toD65) + ->Field("PresetODT", &AcesParameterOverrides::m_preset) + ->Field("CinemaLimitsBlack", &AcesParameterOverrides::m_cinemaLimitsBlack) + ->Field("CinemaLimitsWhite", &AcesParameterOverrides::m_cinemaLimitsWhite) + ->Field("MinPoint", &AcesParameterOverrides::m_minPoint) + ->Field("MidPoint", &AcesParameterOverrides::m_midPoint) + ->Field("MaxPoint", &AcesParameterOverrides::m_maxPoint) + ->Field("SurroundGamma", &AcesParameterOverrides::m_surroundGamma) + ->Field("Gamma", &AcesParameterOverrides::m_gamma); + } + } + + void AcesParameterOverrides::LoadPreset() + { + DisplayMapperParameters displayMapperParameters; + AcesDisplayMapperFeatureProcessor::GetAcesDisplayMapperParameters(&displayMapperParameters, m_preset); + + m_alterSurround = (displayMapperParameters.m_OutputDisplayTransformFlags & AcesDisplayMapperFeatureProcessor::AlterSurround) != 0; + m_applyDesaturation = (displayMapperParameters.m_OutputDisplayTransformFlags & AcesDisplayMapperFeatureProcessor::ApplyDesaturation) != 0; + m_applyCATD60toD65 = (displayMapperParameters.m_OutputDisplayTransformFlags & AcesDisplayMapperFeatureProcessor::ApplyCATD60toD65) != 0; + m_cinemaLimitsBlack = displayMapperParameters.m_cinemaLimits[0]; + m_cinemaLimitsWhite = displayMapperParameters.m_cinemaLimits[1]; + m_minPoint = displayMapperParameters.m_acesSplineParams.minPoint[0]; + m_midPoint = displayMapperParameters.m_acesSplineParams.midPoint[0]; + m_maxPoint = displayMapperParameters.m_acesSplineParams.maxPoint[0]; + m_surroundGamma = displayMapperParameters.m_surroundGamma; + m_gamma = displayMapperParameters.m_gamma; + } + void DisplayMapperConfigurationDescriptor::Reflect(AZ::ReflectContext* context) { if (auto* serializeContext = azrtti_cast(context)) diff --git a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp index 1de92c42f6..8ae790e12c 100644 --- a/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/DisplayMapper/DisplayMapperPass.cpp @@ -106,6 +106,7 @@ namespace AZ { if (m_acesOutputTransformPass) { + m_acesOutputTransformPass->SetAcesParameterOverrides(m_displayMapperConfigurationDescriptor.m_acesParameterOverrides); m_acesOutputTransformPass->SetDisplayBufferFormat(m_displayBufferFormat); } if (m_bakeAcesOutputTransformLutPass) @@ -509,7 +510,8 @@ namespace AZ if (desc.m_operationType != m_displayMapperConfigurationDescriptor.m_operationType || desc.m_ldrGradingLutEnabled != m_displayMapperConfigurationDescriptor.m_ldrGradingLutEnabled || - desc.m_ldrColorGradingLut != m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut) + desc.m_ldrColorGradingLut != m_displayMapperConfigurationDescriptor.m_ldrColorGradingLut || + desc.m_acesParameterOverrides.m_overrideDefaults != m_displayMapperConfigurationDescriptor.m_acesParameterOverrides.m_overrideDefaults) { m_needToRebuildChildren = true; } diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 29f0636e9e..4059d65cbb 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -231,6 +231,19 @@ namespace AZ return {}; } + Data::Instance MeshFeatureProcessor::GetObjectSrg(const MeshHandle& meshHandle) const + { + return meshHandle.IsValid() ? meshHandle->m_shaderResourceGroup : nullptr; + } + + void MeshFeatureProcessor::QueueObjectSrgForCompile(const MeshHandle& meshHandle) const + { + if (meshHandle.IsValid()) + { + meshHandle->m_objectSrgNeedsUpdate = true; + } + } + void MeshFeatureProcessor::SetMaterialAssignmentMap(const MeshHandle& meshHandle, const Data::Instance& material) { Render::MaterialAssignmentMap materials; diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp index 388f4112a0..cb2d6a69ef 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.cpp @@ -71,16 +71,6 @@ namespace AZ } - void SkinnedMeshFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet) - { - AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); - AZ_ATOM_PROFILE_FUNCTION("SkinnedMesh", "SkinnedMeshFeatureProcessor: Simulate"); - AZ_UNUSED(packet); - - SkinnedMeshFeatureProcessorNotificationBus::Broadcast(&SkinnedMeshFeatureProcessorNotificationBus::Events::OnUpdateSkinningMatrices); - - } - void SkinnedMeshFeatureProcessor::Render(const FeatureProcessor::RenderPacket& packet) { AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender); @@ -268,6 +258,8 @@ namespace AZ void SkinnedMeshFeatureProcessor::OnBeginPrepareRender() { m_renderProxiesChecker.soft_lock(); + + SkinnedMeshFeatureProcessorNotificationBus::Broadcast(&SkinnedMeshFeatureProcessorNotificationBus::Events::OnUpdateSkinningMatrices); } void SkinnedMeshFeatureProcessor::OnRenderEnd() diff --git a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h index bb3dd242a1..75d41742b2 100644 --- a/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h +++ b/Gems/Atom/Feature/Common/Code/Source/SkinnedMesh/SkinnedMeshFeatureProcessor.h @@ -49,7 +49,6 @@ namespace AZ // FeatureProcessor overrides ... void Activate() override; void Deactivate() override; - void Simulate(const FeatureProcessor::SimulatePacket& packet) override; void Render(const FeatureProcessor::RenderPacket& packet) override; void OnRenderEnd() override; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h index b0d6bd4117..1cba71ae7e 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Public/Pass/Pass.h @@ -381,6 +381,7 @@ namespace AZ uint64_t m_createdByPassRequest : 1; uint64_t m_initialized : 1; uint64_t m_enabled : 1; + uint64_t m_parentEnabled : 1; uint64_t m_alreadyCreated : 1; uint64_t m_alreadyReset : 1; uint64_t m_alreadyPrepared : 1; diff --git a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/MorphTargetMetaAsset.h b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/MorphTargetMetaAsset.h index 5b92047226..4aa3faa6c9 100644 --- a/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/MorphTargetMetaAsset.h +++ b/Gems/Atom/RPI/Code/Include/Atom/RPI.Reflect/Model/MorphTargetMetaAsset.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace AZ::RPI { @@ -56,6 +57,9 @@ namespace AZ::RPI float m_minPositionDelta; float m_maxPositionDelta; + //! Reference to the wrinkle mask, if it exists + AZ::Data::Asset m_wrinkleMask; + //! Boolean to indicate the presence or absence of color deltas bool m_hasColorDeltas = false; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp index 7aace50760..3d0cbca8e6 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.cpp @@ -18,6 +18,9 @@ #include #include +#include +#include + namespace AZ::RPI { using namespace AZ::SceneAPI; @@ -114,7 +117,7 @@ namespace AZ::RPI meshNodeName, sourceMesh.m_name.GetCStr()); const DataTypes::MatrixType globalTransform = Utilities::BuildWorldTransform(sceneGraph, sceneNodeIndex); - BuildMorphTargetMesh(vertexOffset, sourceMesh, productMesh, metaAssetCreator, blendShapeName, blendShapeData, globalTransform, coordSysConverter); + BuildMorphTargetMesh(vertexOffset, sourceMesh, productMesh, metaAssetCreator, blendShapeName, blendShapeData, globalTransform, coordSysConverter, scene.GetSourceFilename()); } } } @@ -157,7 +160,8 @@ namespace AZ::RPI const AZStd::string& blendShapeName, const AZStd::shared_ptr& blendShapeData, const DataTypes::MatrixType& globalTransform, - const AZ::SceneAPI::CoordinateSystemConverter& coordSysConverter) + const AZ::SceneAPI::CoordinateSystemConverter& coordSysConverter, + const AZStd::string& sourceSceneFilename) { const float tolerance = CalcPositionDeltaTolerance(sourceMesh); AZ::Aabb deltaPositionAabb = AZ::Aabb::CreateNull(); @@ -288,6 +292,8 @@ namespace AZ::RPI metaData.m_maxPositionDelta = maxValue; } + metaData.m_wrinkleMask = GetWrinkleMask(sourceSceneFilename, blendShapeName); + metaAssetCreator.AddMorphTarget(metaData); AZ_Assert(uncompressedPositionDeltas.size() == compressedDeltas.size(), "Number of uncompressed (%d) and compressed position delta components (%d) do not match.", @@ -312,4 +318,47 @@ namespace AZ::RPI AZ_Assert((packedCompressedMorphTargetVertexData.size() - metaData.m_startIndex) == numMorphedVertices, "Vertex index range (%d) in morph target meta data does not match number of morphed vertices (%d).", packedCompressedMorphTargetVertexData.size() - metaData.m_startIndex, numMorphedVertices); } + + Data::Asset MorphTargetExporter::GetWrinkleMask(const AZStd::string& sourceSceneFullFilePath, const AZStd::string& blendShapeName) const + { + AZ::Data::Asset imageAsset; + + // See if there is a wrinkle map mask for this mesh + AZStd::string sceneRelativeFilePath; + bool relativePathFound = true; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(relativePathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetRelativeProductPathFromFullSourceOrProductPath, sourceSceneFullFilePath, sceneRelativeFilePath); + + if (relativePathFound) + { + AZ::StringFunc::Path::StripFullName(sceneRelativeFilePath); + + // Get the folder the masks are supposed to be in + AZStd::string folderName; + AZ::StringFunc::Path::GetFileName(sourceSceneFullFilePath.c_str(), folderName); + folderName += "_wrinklemasks"; + + // Note: for now, we're assuming the mask is always authored as a .tif + AZStd::string blendMaskFileName = blendShapeName + "_wrinklemask.tif.streamingimage"; + + AZStd::string maskFolderAndFile; + AZ::StringFunc::Path::Join(folderName.c_str(), blendMaskFileName.c_str(), maskFolderAndFile); + + AZStd::string maskRelativePath; + AZ::StringFunc::Path::Join(sceneRelativeFilePath.c_str(), maskFolderAndFile.c_str(), maskRelativePath); + AZ::StringFunc::Path::Normalize(maskRelativePath); + + // Now see if the file exists + AZ::Data::AssetId maskAssetId; + Data::AssetCatalogRequestBus::BroadcastResult(maskAssetId, &Data::AssetCatalogRequests::GetAssetIdByPath, maskRelativePath.c_str(), AZ::Data::s_invalidAssetType, false); + + if (maskAssetId.IsValid()) + { + // Flush asset manager events to ensure no asset references are held by closures queued on Ebuses. + AZ::Data::AssetManager::Instance().DispatchEvents(); + + imageAsset.Create(maskAssetId, AZ::Data::AssetLoadBehavior::PreLoad, false); + } + } + return imageAsset; + } } // namespace AZ::RPI diff --git a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h index d968a803d6..4845d7d1da 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h +++ b/Gems/Atom/RPI/Code/Source/RPI.Builders/Model/MorphTargetExporter.h @@ -64,7 +64,11 @@ namespace AZ const AZStd::string& blendShapeName, const AZStd::shared_ptr& blendShapeData, const AZ::SceneAPI::DataTypes::MatrixType& globalTransform, - const AZ::SceneAPI::CoordinateSystemConverter& coordSysConverter); + const AZ::SceneAPI::CoordinateSystemConverter& coordSysConverter, + const AZStd::string& sourceSceneFilename); + + // Find a wrinkle mask for this morph target, if it exists + Data::Asset GetWrinkleMask(const AZStd::string& sourceSceneFullFilePath, const AZStd::string& blendShapeName) const; }; } // namespace RPI } // namespace AZ diff --git a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp index 2ab6ee92e9..109af70166 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Edit/Material/MaterialTypeSourceData.cpp @@ -422,7 +422,7 @@ namespace AZ const MaterialPropertyDescriptor* propertyDescriptor = materialTypeAssetCreator.GetMaterialPropertiesLayout()->GetPropertyDescriptor(propertyIndex); AZ::Name enumName = AZ::Name(property.m_value.GetValue()); - uint32_t enumValue = propertyDescriptor->GetEnumValue(enumName); + uint32_t enumValue = propertyDescriptor ? propertyDescriptor->GetEnumValue(enumName) : MaterialPropertyDescriptor::InvalidEnumValue; if (enumValue == MaterialPropertyDescriptor::InvalidEnumValue) { materialTypeAssetCreator.ReportError("Enum value '%s' couldn't be found in the 'enumValues' list", enumName.GetCStr()); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp index 9401d1a9e0..6ed8ac018c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/Pass.cpp @@ -93,11 +93,12 @@ namespace AZ void Pass::SetEnabled(bool enabled) { m_flags.m_enabled = enabled; + OnHierarchyChange(); } bool Pass::IsEnabled() const { - return m_flags.m_enabled; + return m_flags.m_enabled && (m_flags.m_parentEnabled || m_parent == nullptr); } // --- Error Logging --- @@ -140,6 +141,7 @@ namespace AZ } // Set new tree depth and path + m_flags.m_parentEnabled = m_parent->m_flags.m_enabled && (m_parent->m_flags.m_parentEnabled || m_parent->m_parent == nullptr); m_treeDepth = m_parent->m_treeDepth + 1; m_path = ConcatPassName(m_parent->m_path, m_name); m_flags.m_partOfHierarchy = m_parent->m_flags.m_partOfHierarchy; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp index 1fad385fa6..3f5c16678c 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Pass/RenderPass.cpp @@ -163,6 +163,11 @@ namespace AZ binding.m_shaderInputIndex = idx.IsValid() ? static_cast(idx.GetIndex()) : PassAttachmentBinding::ShaderInputNoBind; } } + else + { + AZ_Error("Pass System", false, "[Pass %s] Could not bind shader buffer index '%s' because it has no attachment.", GetName().GetCStr(), shaderName.GetCStr()); + binding.m_shaderInputIndex = PassAttachmentBinding::ShaderInputNoBind; + } } } diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp index 313e0bea31..3c0f832807 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Model/MorphTargetMetaAsset.cpp @@ -28,6 +28,7 @@ namespace AZ::RPI ->Field("numVertices", &MorphTargetMetaAsset::MorphTarget::m_numVertices) ->Field("minPositionDelta", &MorphTargetMetaAsset::MorphTarget::m_minPositionDelta) ->Field("maxPositionDelta", &MorphTargetMetaAsset::MorphTarget::m_maxPositionDelta) + ->Field("wrinkleMask", &MorphTargetMetaAsset::MorphTarget::m_wrinkleMask) ->Field("hasColorDeltas", &MorphTargetMetaAsset::MorphTarget::m_hasColorDeltas) ; } diff --git a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material index f43f6d0808..c359fea3b5 100644 --- a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material +++ b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/001_lucy_regression_test.material @@ -11,7 +11,7 @@ 0.29372090101242068, 1.0 ], - "textureMap": "Objects/Lucy/Lucy_brass_baseColor.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "useTexture": false }, "detailLayerGroup": { @@ -30,7 +30,7 @@ }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.tif" + "textureMap": "Objects/Lucy/Lucy_normal.png" }, "subsurfaceScattering": { "enableSubsurfaceScattering": true, diff --git a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material index c611b992b6..ce42f32b67 100644 --- a/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material +++ b/Gems/Atom/TestData/TestData/Materials/SkinTestCases/002_wrinkle_regression_test.material @@ -29,7 +29,7 @@ }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.tif" + "textureMap": "Objects/Lucy/Lucy_normal.png" }, "subsurfaceScattering": { "enableSubsurfaceScattering": true, diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material index 2c711a3bf3..7b1f0ba6a9 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/101_DetailMaps_LucyBaseNoDetailMaps.material @@ -5,20 +5,20 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_brass_baseColor.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_BaseColor.png", "textureMapUv": "Unwrapped" }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_brass_metalness.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.tif", + "textureMap": "Objects/Lucy/Lucy_normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_brass_roughness.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", "textureMapUv": "Unwrapped" } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material index 7a94386a18..55a01866b5 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/102_DetailMaps_All.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_brass_baseColor.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_baseColor.png", "textureMapUv": "Unwrapped" }, "detailLayerGroup": { @@ -22,16 +22,16 @@ "scale": 10.0 }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_brass_metalness.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.tif", + "textureMap": "Objects/Lucy/Lucy_normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_brass_roughness.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", "textureMapUv": "Unwrapped" } } diff --git a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material index ddeace43da..6193cf4eed 100644 --- a/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material +++ b/Gems/Atom/TestData/TestData/Materials/StandardPbrTestCases/105_DetailMaps_BlendMaskUsingDetailUVs.material @@ -5,7 +5,7 @@ "propertyLayoutVersion": 3, "properties": { "baseColor": { - "textureMap": "Objects/Lucy/Lucy_brass_baseColor.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_baseColor.png", "textureMapUv": "Unwrapped" }, "detailLayerGroup": { @@ -21,16 +21,16 @@ "scale": 10.0 }, "metallic": { - "textureMap": "Objects/Lucy/Lucy_brass_metalness.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_metallic.png", "textureMapUv": "Unwrapped" }, "normal": { "flipY": true, - "textureMap": "Objects/Lucy/Lucy_normal.tif", + "textureMap": "Objects/Lucy/Lucy_normal.png", "textureMapUv": "Unwrapped" }, "roughness": { - "textureMap": "Objects/Lucy/Lucy_brass_roughness.tif", + "textureMap": "Objects/Lucy/Lucy_bronze_roughness.png", "textureMapUv": "Unwrapped" } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConfig.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConfig.h index 81645b25a9..3c5bcbea00 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConfig.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/PostProcess/DisplayMapper/DisplayMapperComponentConfig.h @@ -20,7 +20,6 @@ namespace AZ { namespace Render { - class DisplayMapperComponentConfig final : public ComponentConfig { @@ -33,6 +32,7 @@ namespace AZ DisplayMapperOperationType m_displayMapperOperation = DisplayMapperOperationType::Aces; bool m_ldrColorGradingLutEnabled = false; Data::Asset m_ldrColorGradingLut = {}; + AcesParameterOverrides m_acesParameterOverrides; }; } } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp index e6afd4f21f..317fa96450 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentConfig.cpp @@ -20,16 +20,18 @@ namespace AZ { void DisplayMapperComponentConfig::Reflect(ReflectContext* context) { + AcesParameterOverrides::Reflect(context); + if (auto serializeContext = azrtti_cast(context)) { serializeContext->Class() - ->Version(0) + ->Version(1) ->Field("DisplayMapperOperationType", &DisplayMapperComponentConfig::m_displayMapperOperation) ->Field("LdrColorGradingLutEnabled", &DisplayMapperComponentConfig::m_ldrColorGradingLutEnabled) ->Field("LdrColorGradingLut", &DisplayMapperComponentConfig::m_ldrColorGradingLut) + ->Field("AcesParameterOverrides", &DisplayMapperComponentConfig::m_acesParameterOverrides) ; } } - } // namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp index d90e170322..0e283199e7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/DisplayMapperComponentController.cpp @@ -85,6 +85,7 @@ namespace AZ desc.m_operationType = m_configuration.m_displayMapperOperation; desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled; desc.m_ldrColorGradingLut = m_configuration.m_ldrColorGradingLut; + desc.m_acesParameterOverrides = m_configuration.m_acesParameterOverrides; fp->RegisterDisplayMapperConfiguration(desc); } diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp index 80aad79218..64cd450940 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/PostProcess/DisplayMapper/EditorDisplayMapperComponent.cpp @@ -10,6 +10,8 @@ * */ +#include "Atom/Feature/ACES/AcesDisplayMapperFeatureProcessor.h" + #include #include @@ -47,6 +49,76 @@ namespace AZ ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; + editContext->Class( + "AcesParameterOverrides", "") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_overrideDefaults, "Override Defaults", + "When enabled allows parameter overrides for ACES configuration") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_alterSurround, "Alter Surround", + "Apply gamma adjustment to compensate for dim surround") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_applyDesaturation, "Alter Desaturation", + "Apply desaturation to compensate for luminance difference") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement( + AZ::Edit::UIHandlers::CheckBox, &AcesParameterOverrides::m_applyCATD60toD65, "Alter CAT D60 to D65", + "Apply Color appearance transform (CAT) from ACES white point to assumed observer adapted white point") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + ->DataElement( + Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsBlack, + "Cinema Limit (black)", + "Reference black luminance value") + ->DataElement( + Edit::UIHandlers::Default, &AcesParameterOverrides::m_cinemaLimitsWhite, + "Cinema Limit (white)", + "Reference white luminance value") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + ->DataElement( + Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_minPoint, "Min Point (luminance)", + "Linear extension below this") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement( + Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_midPoint, "Mid Point (luminance)", + "Middle gray") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement( + Edit::UIHandlers::Vector2, &AcesParameterOverrides::m_maxPoint, "Max Point (luminance)", + "Linear extension above this") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + ->DataElement( + AZ::Edit::UIHandlers::Default, &AcesParameterOverrides::m_surroundGamma, "Surround Gamma", + "Gamma adjustment to be applied to compensate for the condition of the viewing environment") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + ->DataElement( + AZ::Edit::UIHandlers::Default, &AcesParameterOverrides::m_gamma, "Gamma", + "Optional gamma value that is applied as basic gamma curve OETF") + ->Attribute(Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly) + + // Load preset group + ->ClassElement(AZ::Edit::ClassElements::Group, "Load Preset") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement( + Edit::UIHandlers::ComboBox, &AcesParameterOverrides::m_preset, "Preset Selection", + "Allows specifying default preset for different ODT modes") + ->EnumAttribute(OutputDeviceTransformType::OutputDeviceTransformType_48Nits, "48 Nits") + ->EnumAttribute(OutputDeviceTransformType::OutputDeviceTransformType_1000Nits, "1000 Nits") + ->EnumAttribute(OutputDeviceTransformType::OutputDeviceTransformType_2000Nits, "2000 Nits") + ->EnumAttribute(OutputDeviceTransformType::OutputDeviceTransformType_4000Nits, "4000 Nits") + ->UIElement(AZ::Edit::UIHandlers::Button, "Load", "Load default preset") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AcesParameterOverrides::LoadPreset) + ->Attribute(AZ::Edit::Attributes::ButtonText, "Load") + ; + editContext->Class("ToneMapperComponentConfig", "") ->ClassElement(Edit::ClassElements::EditorData, "") ->DataElement(Edit::UIHandlers::ComboBox, @@ -64,7 +136,10 @@ namespace AZ &DisplayMapperComponentConfig::m_ldrColorGradingLutEnabled, "Enable LDR color grading LUT", "Enable LDR color grading LUT.") - ->DataElement(AZ::Edit::UIHandlers::Default, &DisplayMapperComponentConfig::m_ldrColorGradingLut, "LDR color Grading LUT", "LDR color grading LUT"); + ->DataElement(AZ::Edit::UIHandlers::Default, &DisplayMapperComponentConfig::m_ldrColorGradingLut, "LDR color Grading LUT", "LDR color grading LUT") + ->DataElement(AZ::Edit::UIHandlers::Default, &DisplayMapperComponentConfig::m_acesParameterOverrides, "ACES Parameters", "Parameter overrides for ACES.") + ; + } } diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp index d0116452b7..9079f639ba 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -39,6 +40,8 @@ namespace AZ { namespace Render { + static constexpr uint32_t s_maxActiveWrinkleMasks = 16; + AZ_CLASS_ALLOCATOR_IMPL(AtomActorInstance, EMotionFX::Integration::EMotionFXAllocator, 0) AtomActorInstance::AtomActorInstance(AZ::EntityId entityId, @@ -413,6 +416,10 @@ namespace AZ EMotionFX::MorphSetup* morphSetup = m_actorInstance->GetActor()->GetMorphSetup(lodIndex); if (morphSetup) { + // Track all the masks/weights that are currently active + m_wrinkleMasks.clear(); + m_wrinkleMaskWeights.clear(); + uint32_t morphTargetCount = morphSetup->GetNumMorphTargets(); m_morphTargetWeights.clear(); for (uint32_t morphTargetIndex = 0; morphTargetIndex < morphTargetCount; ++morphTargetIndex) @@ -437,11 +444,28 @@ namespace AZ const EMotionFX::MorphTargetStandard::DeformData* deformData = morphTargetStandard->GetDeformData(deformDataIndex); if (deformData->mNumVerts > 0) { - m_morphTargetWeights.push_back(morphTargetSetupInstance->GetWeight()); + float weight = morphTargetSetupInstance->GetWeight(); + m_morphTargetWeights.push_back(weight); + + // If the morph target is active and it has a wrinkle mask + auto wrinkleMaskIter = m_morphTargetWrinkleMaskMapsByLod[lodIndex].find(morphTargetStandard); + if (weight > 0 && wrinkleMaskIter != m_morphTargetWrinkleMaskMapsByLod[lodIndex].end()) + { + // Add the wrinkle mask and weight, to be set on the material + m_wrinkleMasks.push_back(wrinkleMaskIter->second); + m_wrinkleMaskWeights.push_back(weight); + } } } } m_skinnedMeshRenderProxy->SetMorphTargetWeights(lodIndex, m_morphTargetWeights); + + // Until EMotionFX and Atom lods are synchronized [ATOM-13564] we don't know which EMotionFX lod to pull the weights from + // Until that is fixed, just use lod 0 [ATOM-15251] + if (lodIndex == 0) + { + UpdateWrinkleMasks(); + } } } } @@ -453,6 +477,8 @@ namespace AZ MaterialComponentRequestBus::EventResult(materials, m_entityId, &MaterialComponentRequests::GetMaterialOverrides); CreateRenderProxy(materials); + InitWrinkleMasks(); + TransformNotificationBus::Handler::BusConnect(m_entityId); MaterialComponentNotificationBus::Handler::BusConnect(m_entityId); MeshComponentRequestBus::Handler::BusConnect(m_entityId); @@ -573,5 +599,77 @@ namespace AZ { CreateSkinnedMeshInstance(); } + + void AtomActorInstance::InitWrinkleMasks() + { + EMotionFX::Actor* actor = m_actorAsset->GetActor(); + m_morphTargetWrinkleMaskMapsByLod.resize(m_skinnedMeshInputBuffers->GetLodCount()); + m_wrinkleMasks.reserve(s_maxActiveWrinkleMasks); + m_wrinkleMaskWeights.reserve(s_maxActiveWrinkleMasks); + + for (size_t lodIndex = 0; lodIndex < m_skinnedMeshInputBuffers->GetLodCount(); ++lodIndex) + { + EMotionFX::MorphSetup* morphSetup = actor->GetMorphSetup(lodIndex); + if (morphSetup) + { + const AZStd::vector& metaDatas = actor->GetMorphTargetMetaAsset()->GetMorphTargets(); + // Loop over all the EMotionFX morph targets + uint32_t numMorphTargets = morphSetup->GetNumMorphTargets(); + for (uint32_t morphTargetIndex = 0; morphTargetIndex < numMorphTargets; ++morphTargetIndex) + { + EMotionFX::MorphTargetStandard* morphTarget = static_cast(morphSetup->GetMorphTarget(morphTargetIndex)); + for (const RPI::MorphTargetMetaAsset::MorphTarget& metaData : metaDatas) + { + // Find the metaData associated with this morph target + if (metaData.m_morphTargetName == morphTarget->GetNameString() && metaData.m_wrinkleMask && metaData.m_numVertices > 0) + { + // If the metaData has a wrinkle mask, add it to the map + Data::Instance streamingImage = RPI::StreamingImage::FindOrCreate(metaData.m_wrinkleMask); + if (streamingImage) + { + m_morphTargetWrinkleMaskMapsByLod[lodIndex][morphTarget] = streamingImage; + } + } + } + } + } + } + } + + void AtomActorInstance::UpdateWrinkleMasks() + { + if (m_meshHandle) + { + Data::Instance wrinkleMaskObjectSrg = m_meshFeatureProcessor->GetObjectSrg(*m_meshHandle); + if (wrinkleMaskObjectSrg) + { + RHI::ShaderInputImageIndex wrinkleMasksIndex = wrinkleMaskObjectSrg->FindShaderInputImageIndex(Name{ "m_wrinkle_masks" }); + RHI::ShaderInputConstantIndex wrinkleMaskWeightsIndex = wrinkleMaskObjectSrg->FindShaderInputConstantIndex(Name{ "m_wrinkle_mask_weights" }); + RHI::ShaderInputConstantIndex wrinkleMaskCountIndex = wrinkleMaskObjectSrg->FindShaderInputConstantIndex(Name{ "m_wrinkle_mask_count" }); + if (wrinkleMasksIndex.IsValid() || wrinkleMaskWeightsIndex.IsValid() || wrinkleMaskCountIndex.IsValid()) + { + AZ_Error("AtomActorInstance", wrinkleMasksIndex.IsValid(), "m_wrinkle_masks not found on the ObjectSrg, but m_wrinkle_mask_weights and/or m_wrinkle_mask_count are being used."); + AZ_Error("AtomActorInstance", wrinkleMaskWeightsIndex.IsValid(), "m_wrinkle_mask_weights not found on the ObjectSrg, but m_wrinkle_masks and/or m_wrinkle_mask_count are being used."); + AZ_Error("AtomActorInstance", wrinkleMaskCountIndex.IsValid(), "m_wrinkle_mask_count not found on the ObjectSrg, but m_wrinkle_mask_weights and/or m_wrinkle_masks are being used."); + + if (m_wrinkleMasks.size()) + { + wrinkleMaskObjectSrg->SetImageArray(wrinkleMasksIndex, AZStd::array_view>(m_wrinkleMasks.data(), m_wrinkleMasks.size())); + + // Set the weights for any active masks + for (size_t i = 0; i < m_wrinkleMaskWeights.size(); ++i) + { + wrinkleMaskObjectSrg->SetConstant(wrinkleMaskWeightsIndex, m_wrinkleMaskWeights[i], i); + } + AZ_Error("AtomActorInstance", m_wrinkleMaskWeights.size() <= s_maxActiveWrinkleMasks, "The skinning shader supports no more than %d active morph targets with wrinkle masks.", s_maxActiveWrinkleMasks); + } + + wrinkleMaskObjectSrg->SetConstant(wrinkleMaskCountIndex, aznumeric_cast(m_wrinkleMasks.size())); + m_meshFeatureProcessor->QueueObjectSrgForCompile(*m_meshHandle); + } + } + } + } + } //namespace Render } // namespace AZ diff --git a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h index 1002fcbde1..e05280e896 100644 --- a/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h +++ b/Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h @@ -17,6 +17,7 @@ #include #include +#include #include @@ -29,6 +30,8 @@ #include #include #include +#include + #include #include @@ -41,6 +44,7 @@ namespace AZ::RPI { class Model; class Buffer; + class StreamingImage; } namespace AZ @@ -168,6 +172,11 @@ namespace AZ // SkinnedMeshOutputStreamNotificationBus void OnSkinnedMeshOutputStreamMemoryAvailable() override; + // Check to see if the skin material is being used, + // and if there are blend shapes with wrinkle masks that should be applied to it + void InitWrinkleMasks(); + void UpdateWrinkleMasks(); + AZStd::intrusive_ptr m_skinnedMeshInputBuffers = nullptr; AZStd::intrusive_ptr m_skinnedMeshInstance; AZ::Data::Instance m_boneTransforms = nullptr; @@ -179,6 +188,12 @@ namespace AZ AZ::TransformInterface* m_transformInterface = nullptr; AZStd::set m_waitForMaterialLoadIds; AZStd::vector m_morphTargetWeights; + + typedef AZStd::unordered_map> MorphTargetWrinkleMaskMap; + AZStd::vector m_morphTargetWrinkleMaskMapsByLod; + + AZStd::vector> m_wrinkleMasks; + AZStd::vector m_wrinkleMaskWeights; }; } // namespace Render diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp index 473e9534cb..6fa69c7a24 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp @@ -73,7 +73,10 @@ namespace PhysX { } - CharacterControllerComponent::~CharacterControllerComponent() = default; + CharacterControllerComponent::~CharacterControllerComponent() + { + DisableController(); + } // AZ::Component void CharacterControllerComponent::Init() @@ -92,7 +95,7 @@ namespace PhysX void CharacterControllerComponent::Deactivate() { - DestroyController(); + DisableController(); Physics::CollisionFilteringRequestBus::Handler::BusDisconnect(); AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); @@ -198,7 +201,7 @@ namespace PhysX void CharacterControllerComponent::DisablePhysics() { - DestroyController(); + DisableController(); } bool CharacterControllerComponent::IsPhysicsEnabled() const @@ -421,17 +424,32 @@ namespace PhysX AZ::TransformBus::EventResult(entityTranslation, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); m_characterConfig->m_position = entityTranslation; - if (auto* sceneInterface = AZ::Interface::Get()) + auto* sceneInterface = AZ::Interface::Get(); + if (sceneInterface != nullptr) { - AzPhysics::SimulatedBodyHandle bodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, m_characterConfig.get()); - m_controller = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, bodyHandle)); + m_controllerBodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, m_characterConfig.get()); + m_controller = azdynamic_cast( + sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, m_controllerBodyHandle)); } if (m_controller == nullptr) { AZ_Error("PhysX Character Controller Component", false, "Failed to create character controller."); return; } - + + if (sceneInterface != nullptr) + { + // if the scene removes this controller body, we should also clean up our resources. + m_onSimulatedBodyRemovedHandler = AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler( + [this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle) { + if (bodyHandle == m_controllerBodyHandle) + { + DestroyController(); + } + }); + sceneInterface->RegisterSimulationBodyRemovedHandler(defaultSceneHandle, m_onSimulatedBodyRemovedHandler); + } + CharacterControllerRequestBus::Handler::BusConnect(GetEntityId()); m_preSimulateHandler = AzPhysics::SystemEvents::OnPresimulateEvent::Handler( @@ -447,7 +465,7 @@ namespace PhysX } } - void CharacterControllerComponent::DestroyController() + void CharacterControllerComponent::DisableController() { if (!IsPhysicsEnabled()) { @@ -460,10 +478,15 @@ namespace PhysX { sceneInterface->RemoveSimulatedBody(m_controller->m_sceneOwner, m_controller->m_bodyHandle); } + + DestroyController(); + } + + void CharacterControllerComponent::DestroyController() + { m_controller = nullptr; - m_preSimulateHandler.Disconnect(); - + m_onSimulatedBodyRemovedHandler.Disconnect(); CharacterControllerRequestBus::Handler::BusDisconnect(); } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h index a7a1a92ad2..7c25312b72 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h @@ -131,7 +131,12 @@ namespace PhysX void ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) override; private: + // Creates the physics character controller in the current default physics scene. + // This will do nothing if the controller is already created. void CreateController(); + // Removes the physics character controller from the scene and will call DestroyController for clean up. + void DisableController(); + // Cleans up all references and events used with the physics character controller. void DestroyController(); void OnPreSimulate(float deltaTime); @@ -139,6 +144,8 @@ namespace PhysX AZStd::unique_ptr m_characterConfig; AZStd::shared_ptr m_shapeConfig; PhysX::CharacterController* m_controller = nullptr; + AzPhysics::SimulatedBodyHandle m_controllerBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; AzPhysics::SystemEvents::OnPresimulateEvent::Handler m_preSimulateHandler; + AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler m_onSimulatedBodyRemovedHandler; }; } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 79aa767959..689ea47be7 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -489,6 +489,7 @@ namespace PhysX // Disable simulation on body (not signaling OnSimulationBodySimulationDisabled event) DisableSimulationOfBodyInternal(*simulatedBody.second); } + m_simulatedBodyRemovedEvent.Signal(m_sceneHandle, simulatedBody.second->m_bodyHandle); delete simulatedBody.second; } } diff --git a/Gems/SceneProcessing/Code/Source/Generation/Components/MeshOptimizer/MeshOptimizerComponent.cpp b/Gems/SceneProcessing/Code/Source/Generation/Components/MeshOptimizer/MeshOptimizerComponent.cpp index 592a8e2a75..45908eb2d5 100644 --- a/Gems/SceneProcessing/Code/Source/Generation/Components/MeshOptimizer/MeshOptimizerComponent.cpp +++ b/Gems/SceneProcessing/Code/Source/Generation/Components/MeshOptimizer/MeshOptimizerComponent.cpp @@ -135,7 +135,7 @@ namespace AZ::SceneGenerationComponents { for (size_t controlPointIndex = 0; controlPointIndex < skinData.get().GetVertexCount(); ++controlPointIndex) { - const int usedPointIndex = meshData->GetUsedPointIndexForControlPoint(aznumeric_caster(controlPointIndex)); + const int usedPointIndex = meshData->GetUsedPointIndexForControlPoint(meshData->GetControlPointIndex(aznumeric_caster(controlPointIndex))); const size_t linkCount = skinData.get().GetLinkCount(controlPointIndex); if (usedPointIndex < 0 || linkCount == 0) diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_1000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_1000.dds deleted file mode 100644 index 872d7b71d4..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_1000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f454e6505870d9159eaac1eb0c53751e45e803cf50bf94e5c5f51ba2232cebba -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_2000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_2000.dds deleted file mode 100644 index 97ed5efe4d..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_2000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c1047fad9be53568fc471bdb5633445a030efaed6bf9b5e9d47abb09efb4d01e -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_3000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_3000.dds deleted file mode 100644 index 337e63a40c..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_3000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:11b5326877643b06a5687cf1470e388752296df64ce3abb69aa06f1933e0f3b8 -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_4000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_4000.dds deleted file mode 100644 index 2e6b3a3eda..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_4000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:de54072f3eca1a1de6250b1585335a1aa6fa9e07e4e7ad00cd37ea5f809a303c -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_5000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_5000.dds deleted file mode 100644 index 4a80ce04e1..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_5000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ac0f98198af41590052eff6550d34f05d0e3ca374bc59e7ece314be2380c210f -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_6000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_6000.dds deleted file mode 100644 index d7e0e74fca..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_6000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:be47d7c0a2a64b17925e51b65abba72dd22735ef7f3913e9aa389f8c31124ede -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_7000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_7000.dds deleted file mode 100644 index 391bd3ec9f..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_7000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:372359625eec486abbbe7f9ab438b51627939bfbd39002d136b6d6b9c61bbe1b -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_8000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_8000.dds deleted file mode 100644 index 418a7ee3ed..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_8000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91ba2bd2504a10a199964b35014df14a4405cf8fd47955a6c4ef9ca4f300637d -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_9000.dds b/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_9000.dds deleted file mode 100644 index 9c11c2fa43..0000000000 --- a/Tests/Atom/GoldenImages/Windows/amd/Baseviewer/BistroBenchmark/screenshot_bistro_9000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7d8442967964bab77d02a572e6e7f8fcd158e62308b7e6340d4d56be13f7f455 -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_1000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_1000.dds deleted file mode 100644 index f9a46c53c8..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_1000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5bf373730d725a14b6833b7862c6268a93d8f9d847032a60cdc99ce14fea9dfe -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_2000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_2000.dds deleted file mode 100644 index 25cce25c63..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_2000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:181c31eb7fa068027d3e42c415006fd70d1fbeb0bfe373b3290aea6e3002d762 -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_3000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_3000.dds deleted file mode 100644 index a1c5d4e1b8..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_3000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:efc4fbc90bfd01a1ba09ebd925b8379e67263b7156f37473dd13a91f346ed1c4 -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_4000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_4000.dds deleted file mode 100644 index 4cafe4e613..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_4000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b48137886ef2a38312eedbddb111367b3f3924ab5425b49f81a92ae7d4ea898e -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_5000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_5000.dds deleted file mode 100644 index bb74f06212..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_5000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea47fd8d68dab6ba54e45bfc50d84d380ec2f5b7e6915713dc9e9aa41423d4ea -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_6000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_6000.dds deleted file mode 100644 index 8bc6e3de17..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_6000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9bd3fe0208cbee26cfcf00cba0a127c5938061b8bea2143d05e28fbc7f55d9ca -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_7000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_7000.dds deleted file mode 100644 index 4c286062d0..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_7000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:30f6aac28e74da435ad6c018b6a7e9b7cee1ad74ec5452778e56036a7d438ec9 -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_8000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_8000.dds deleted file mode 100644 index 31023d437e..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_8000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2628992a8c8774ff5e77152999009ba3cbb3b68ee79d53669dd16aebe978da27 -size 5946320 diff --git a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_9000.dds b/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_9000.dds deleted file mode 100644 index a5f85d6b22..0000000000 --- a/Tests/Atom/GoldenImages/Windows/nvidia/Baseviewer/BistroBenchmark/screenshot_bistro_9000.dds +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42faae0235f0c74bc0201dd8fc805a9c01f086b093026b654e207f83ea0f3f90 -size 5946320 diff --git a/Tests/Atom/__init__.py b/Tests/Atom/__init__.py deleted file mode 100755 index 36d43bea05..0000000000 --- a/Tests/Atom/__init__.py +++ /dev/null @@ -1,9 +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. diff --git a/Tests/Atom/image_comparison_utils.py b/Tests/Atom/image_comparison_utils.py deleted file mode 100755 index 698901710a..0000000000 --- a/Tests/Atom/image_comparison_utils.py +++ /dev/null @@ -1,105 +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. - -# Utility functions for image comparison tests. -# """ -# import test_tools.shared.images.qssim as qssim -# import PythonMagick -# import logging -# import os -# import shared.s3_utils -# import platform - - -#################################### -# Commented out due to need to shift to new LyTestTools, Python3 and new screenshot workflow -# Don't merge to Mainline -#################################### - - -# def create_image_path(screenshot, path, extension, golden): - # """ - # Create image path from name, path and extension - # From a specified path, create the path for the diff image - # :param screenshot: path to the screenshot which needs to be saved - # :param path: path to where screenshot should be saved - # :param extension: extension of the diff image (.dds, .jpg) - # :param golden: True or False whether screenshot is golden image or not - # :return diff_full_path: path of the iamge to be saved - # """ - # screenshot_name = os.path.basename(screenshot) - # if golden: - # diff_name = "{}_golden{}".format(screenshot_name.split('.')[:-1][0], extension) - # else: - # diff_name = "{}{}".format(screenshot_name.split('.')[:-1][0], extension) - # diff_full_path = os.path.join(path, diff_name) - # return diff_full_path - - -# def convert_dds_to_jpg(image, path, golden): - # """ - # Convert DDS to JPEG - # :param image: DDS image to convert - # :param path: path to where iamge will be saved - # :return screenshotJPG_path: path to the newly JPEG-converted DDS image - # """ - # # Convert image as JPEG for quick review - # screenshotJPG_path = create_image_path(image, path, '.jpg', golden) - # screenshot = PythonMagick.Image(image) - # screenshot.quality(100) - # screenshot.magick('JPEG') - # screenshot.write(screenshotJPG_path) - # return screenshotJPG_path - - -# def compare_screenshot_to_golden_image(screenshot, golden_image, path, threshold=0.985): - # """ - # Compare Screenshots to Golden Images - # Function to compare a newly taken screenshot with the golden image - # :param screenshot: path of the screenshot - # :param golden_image: path of the golden image (in Perforce) - # :param path: path to where the screenshot diff image will be saved - # :param threshold: threshold for the image comparison test to fail/pass (optional) - # :return failure_not_found: True or False whether screenshots are similar (due to threshold) or not - # """ - # failure_not_found = True - # logging.info("Comparing screenshot {}".format(screenshot)) - # # Calculating screenshots similarity - # quaternion_similarity = qssim.qssim(screenshot, golden_image, diff_path = path) - # # Converting original screenshots to jpg - # convert_dds_to_jpg(screenshot, path, False) - # convert_dds_to_jpg(golden_image, path, True) - # # Checking if similarity index is bypassing the threshold - # if (quaternion_similarity < threshold): - # failure_not_found = False - # logging.error("%s failed the image comparison with %s", screenshot, golden_image) - # else: - # logging.info("Comparison successful, screenshots are similar.") - # return failure_not_found - - -# def upload_screenshots_to_s3(folder_path, folder_name): - # """ - # Uploading screenshots to certain s3 bucket - # Will require certain credentials (from the IAM that has access to l-qa@amazon acc) on the machine to work - # :param folder_path: full path to folder that needs to be uploaded to s3 - # :param folder_name: name of the folder to be uploaded to s3 - # :return: None - # """ - # host_name = platform.uname()[1] - # s3_folder_name = '_'.join([folder_name, host_name]) - - # logging.info("Trying to create a folder on S3; bucket: ly.screenshot.automation.artifacts, folder: {}".format(s3_folder_name)) - # shared.s3_utils.create_folder_in_bucket('ly.screenshot.automation.artifacts', s3_folder_name) - - # for file in os.listdir(folder_path): - # key = '{}/{}'.format(s3_folder_name, file) - # shared.s3_utils.upload_to_bucket('ly.screenshot.automation.artifacts', '{}/{}'.format(folder_path, file), key) - diff --git a/Tests/Atom/windows/__init__.py b/Tests/Atom/windows/__init__.py deleted file mode 100755 index 36d43bea05..0000000000 --- a/Tests/Atom/windows/__init__.py +++ /dev/null @@ -1,9 +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. diff --git a/Tests/Atom/windows/atomsampleviewer_tests_stability.py b/Tests/Atom/windows/atomsampleviewer_tests_stability.py deleted file mode 100755 index b480a7bc1a..0000000000 --- a/Tests/Atom/windows/atomsampleviewer_tests_stability.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -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 is a file to test basic functionality of the Base Viewer executable -""" - -import os -import pytest -import subprocess -import time -import re -from ly_test_tools.environment.process_utils import kill_processes_named as kill_processes_named - -dev_dir = os.path.abspath(os.path.join(os.path.abspath(__file__), '..', '..', '..', '..')) -bin_dir = 'Bin64vc141' - - -def gather_sample_names(): - """ - Gathers the currently eligible samples from the output of a single run of baseviewer.exe (with no sample argument). - For use in the fixture parameters. - """ - viewer_dir = os.path.join(dev_dir, bin_dir) - os.chdir(viewer_dir) - process = subprocess.Popen(['BaseViewer.exe', '-timeout', '5'], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - out = process.communicate()[0] - log = out.splitlines() - samples = [] - for line in log: - line = str(line) - if "SampleComponentManager" in line and '-' not in line and 'Not Supported' not in line: - line_regexp = re.search('\[.*\]', line) - line = line_regexp.group(0) - sample = line.replace('[', '').replace(']', '') - samples.append(sample) - kill_processes_named('AssetProcessor', ignore_extensions=True) - if samples is not None: - return samples - - -@pytest.fixture -def kill_AP(request): - def teardown(): - kill_processes_named('AssetProcessor', ignore_extensions=True) - request.addfinalizer(teardown) - - -@pytest.mark.parametrize("samples", gather_sample_names()) -class TestBaseViewerExe(object): - - def test_OpenSampleLevel_CorrectFormat_ShouldPass(self, samples, kill_AP): - """ - Opens the specific BaseViewer samples individually and verifies they're stable for a few seconds and then exit - cleanly - """ - viewer_dir = os.path.join(dev_dir, bin_dir) - os.chdir(viewer_dir) - return_code = subprocess.check_call(['BaseViewer.exe', '-sample', samples, '-timeout', '20'], timeout=30) - assert return_code == 0, "Sample '{}' did not exit properly with code '{}'".format(samples, str(returncode)) - - def test_OpenSampleLevel_NoErrors_ShouldPass(self, samples, kill_AP): - """ - Opens the specific BaseViewer samples individually and verifies there are no errors in the output while running - """ - viewer_dir = os.path.join(dev_dir, bin_dir) - os.chdir(viewer_dir) - output = subprocess.check_output(['BaseViewer.exe', '-sample', samples, '-timeout', '20'], timeout=30) - log = output.splitlines() - errors = [] - assertions = [] - for i in range(len(log)): - line = str(log[i]) - surrounding_lines = str(log[i:i+3]) - if "Trace::Error" in line: - errors.append(surrounding_lines) - if "Trace::Assert" in line: - assertions.append(surrounding_lines) - - assert len(errors) == 0, "Sample '{}' had the following errors when run: {}".format(samples, "\n".join(errors)) - assert len(assertions) == 0, "Sample '{}' had the following assertions when run: {}".format(samples, "\n".join(assertions)) - diff --git a/Tests/Atom/windows/conftest.py b/Tests/Atom/windows/conftest.py deleted file mode 100755 index e0766a4015..0000000000 --- a/Tests/Atom/windows/conftest.py +++ /dev/null @@ -1,39 +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. - -# Conftest file for providing additional configuration for Screenshot Comparison tests -# """ - - - -#################################### -# Commented out due to need to shift to new LyTestTools, Python3 and new screenshot workflow -# Don't merge to Mainline -#################################### - - -# import pytest - -# def pytest_addoption(parser): - # parser.addoption( - # '--graphics_vendor', action='store', help='graphics vendor name: nvidia or amd', required=True - # ) - # parser.addoption( - # '--upload_results_to_s3', action='store_true', default=False, help='Specify if you need to upload screenshot artifacts to s3' - # ) - -# @pytest.fixture -# def graphics_vendor(request): - # return request.config.getoption('--graphics_vendor') - -# @pytest.fixture -# def upload_results_to_s3(request): - # return request.config.getoption('--upload_results_to_s3') - diff --git a/Tests/Atom/windows/screenshot_comparison_atomsampleviewer_windows.py b/Tests/Atom/windows/screenshot_comparison_atomsampleviewer_windows.py deleted file mode 100755 index fca50c9901..0000000000 --- a/Tests/Atom/windows/screenshot_comparison_atomsampleviewer_windows.py +++ /dev/null @@ -1,143 +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. - -# BaseViewer image comparison tests on windows -# """ - -# import Atom.image_comparison_utils as image_comparison_utils -# import test_tools.builtin.fixtures as fixtures -# import subprocess -# import pytest -# import os -# import logging -# import time -# import datetime -# from test_tools import WINDOWS -# from test_tools.shared.process_utils import kill_processes_named -# from test_tools.shared.waiter import wait_for - - - - -#################################### -# Commented out due to need to shift to new LyTestTools, Python3 and new screenshot workflow -# Don't merge to Mainline -#################################### - - - - -# workspace = fixtures.use_fixture(fixtures.builtin_workspace_fixture, scope='function') -# logger = logging.getLogger(__name__) - -# @pytest.fixture(scope='session', autouse=True) -# def closing_ap(request): - # """ - # Fixture to call once per all tests to teardown AP at the end - # :param request: pytest request - # """ - # def teardown(): - # kill_processes_named('AssetProcessor_tmp', ignore_extensions=True) - # kill_processes_named('AssetProcessor', ignore_extensions=True) - # kill_processes_named('AssetProcessorBatch', ignore_extensions=True) - # kill_processes_named('AssetBuilder', ignore_extensions=True) - # kill_processes_named('rc', ignore_extensions=True) - # request.addfinalizer(teardown) - -# @pytest.fixture() -# def screenshots_setup(request, workspace, sample, graphics_vendor, upload_results_to_s3): - # """ - # Fixture for setting up workspace needed for screenshot comparison test - # :param request: pytest request - # :param workspace: pythontesttools workspace object - # :param sample: name of BaseViwer sample - # :return final_path: path to folder where output screenshots will be stored - # """ - # # Creating output folder - # tests_path = os.path.dirname(os.path.realpath(__file__)) - # dir_name = "{}_screenshot_tests_{}_{}_{}_{}".format(datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S_%f"), sample.replace('/', ""), workspace.release.platform, workspace.release.configuration, - # graphics_vendor) - # dir_name = dir_name.replace(":", '_') - # final_path = os.path.join(tests_path, dir_name) - # os.mkdir(final_path) - - # # Teardown to clean up Cache from .dds screenshots that are produced by BaseViewer.exe - # def teardown(): - # cache = os.path.join(workspace.release.paths.dev(), "Cache", "BaseViewer", "pc", "baseviewer") - # files = os.listdir(cache) - # for file in files: - # name, file_extension = os.path.splitext(file) - # if 'screenshot' in name and file_extension == '.dds': - # screen_to_remove = os.path.join(cache, file) - # logger.info('Deleting temp screenshot file {}.'.format(screen_to_remove)) - # os.remove(screen_to_remove) - # kill_processes_named('BaseViewer', ignore_extensions=True) - # # uploading screenshots to s3 - # if upload_results_to_s3: - # image_comparison_utils.upload_screenshots_to_s3(final_path, dir_name) - # request.addfinalizer(teardown) - # return final_path - - -# # Commenting out debug due to ATOM-1677 -# @pytest.mark.parametrize("platform,configuration,project,spec,sample", [ - # pytest.param("win_x64_vs2017", "profile", "BaseViewer", "all", "RPI/BistroBenchmark", - # marks=pytest.mark.skipif(not WINDOWS, reason="Only supported on Windows hosts")), - # #pytest.param("win_x64_vs2017", "debug", "BaseViewer", "all", "RPI/BistroBenchmark", - # # marks=pytest.mark.skipif(not WINDOWS, reason="Only supported on Windows hosts")), - # ]) -# class TestBaseViewerScreenshots(object): - # def test_BistroBenchmarkSample_CompareScreenshots(self, request, workspace, sample, screenshots_setup, graphics_vendor): - # """ - # Launches BaseViewer.exe RPI/BistoBenchmark, taking screenshot and comparing on certain frames. - # """ - # base_path = workspace.release.paths.dev() - # # Generating frames list parameter - # frames_list = range(1000,10000,1000) - # frames_param = "" - # screenshot_names = [] - # for parameter in frames_list: - # frames_param += '{},'.format(parameter) - # screenshot_names.append('screenshot_bistro_{}.dds'.format(parameter)) - # frames_param = frames_param[:-1] - # # Loading BaseViewer - # self.load_baseviewer_directly(workspace, sample, frames_param, timeout=100) - - # logger.info('Comparing screenshots to golden images') - # taken_screens_path = os.path.join(base_path, "Cache", "BaseViewer", "pc", "baseviewer") - # golden_screens_path = os.path.join(base_path, "Tests", "Atom", "GoldenImages", "Windows", graphics_vendor, "Baseviewer", "BistroBenchmark") - # failed_screenshots = [] - # for screen in screenshot_names: - # taken_image = os.path.join(taken_screens_path, screen) - # golden_image = os.path.join(golden_screens_path, screen) - # if not image_comparison_utils.compare_screenshot_to_golden_image(taken_image, golden_image, screenshots_setup): - # failed_screenshots.append(screen) - # if len(failed_screenshots) > 0: - # assert False, "A failure has been found during image comparison for the following images: {}".format(failed_screenshots) - - - # def load_baseviewer_directly(self, workspace, sample, frames, timeout): - # """ - # Launch directly Baseviewer without using the launcher (since Atom is not yet integrated in Lumberyard) - # :param workspace: pythontesttools workspace object - # :param sample: name of the sample from BaseViewer - # :param frames: list of frames to take screenshots at - # :param timeout: time in seconds to wait BaseViewer to take screenshots - # """ - # base_path = workspace.release.paths.dev() - # bin_dir = workspace.release.paths.bin_dir() - # cmd_path = os.path.join(base_path, bin_dir) - # os.chdir(cmd_path) - # p = subprocess.Popen(['BaseViewer.exe', '-sample', sample, '-screenshot', frames]) - # # Wait for BaseViewer to run and take screenshot - # last_screenshot = frames.split(',')[-1] - # screenshot_file = os.path.join(base_path, "Cache", "BaseViewer", "pc", "baseviewer", "screenshot_bistro_{}.dds".format(last_screenshot)) - # wait_for(lambda: os.path.exists(screenshot_file), timeout) - diff --git a/Tests/BuildSystems/test_BuildBAT.py b/Tests/BuildSystems/test_BuildBAT.py deleted file mode 100755 index 1521508550..0000000000 --- a/Tests/BuildSystems/test_BuildBAT.py +++ /dev/null @@ -1,212 +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. - -BuildSystems BAT to automate building on packages for Windows -""" -import logging -import pytest -import os - -pytest.importorskip('ly_test_tools') - -import ly_test_tools.builtin.helpers as helpers -from .test_lib import build_helper - -logger = logging.getLogger(__name__) - - -@pytest.mark.BAT -@pytest.mark.parametrize('spec', ['all']) -@pytest.mark.parametrize('project', ['AutomatedTesting']) - -class TestWindowsBuildConfig(object): - """ - Automated tests for all the build configurations for Windows. - Test cases live in Repository/Build System/Lumberyard Builds/Configurations - """ - @pytest.mark.test_case_id('C15723869') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['profile']) - @pytest.mark.build - def test_build_win_x64_vs2017_profile(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15723870') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['profile_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2017_profile_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15723871') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['profile_test']) - @pytest.mark.build - def test_build_win_x64_vs2017_profile_test(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15723872') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['profile_test_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2017_profile_test_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15716369') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['debug']) - @pytest.mark.build - def test_build_win_x64_vs2017_debug(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15716370') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['debug_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2017_debug_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15716371') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['debug_test']) - @pytest.mark.build - def test_build_win_x64_vs2019_debug_test(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15716372') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['debug_test_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2017_debug_test_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15723889') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['release']) - @pytest.mark.build - def test_build_win_x64_vs2017_release(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15723890') - @pytest.mark.parametrize('platform', ['win_x64_vs2017']) - @pytest.mark.parametrize('configuration', ['release_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2017_release_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815180') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['profile']) - @pytest.mark.build - def test_build_win_x64_vs2019_profile(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815181') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['profile_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2019_profile_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815182') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['profile_test']) - @pytest.mark.build - def test_build_win_x64_vs2019_profile_test(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815183') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['profile_test_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2019_profile_test_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815174') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['debug']) - @pytest.mark.build - def test_build_win_x64_vs2019_debug(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815175') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['debug_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2019_debug_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815176') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['debug_test']) - @pytest.mark.build - def test_build_win_x64_vs2019_debug_test(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815177') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['debug_test_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2019_debug_test_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815190') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['release']) - @pytest.mark.build - def test_build_win_x64_vs2019_release(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) - - @pytest.mark.test_case_id('C15815191') - @pytest.mark.parametrize('platform', ['win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['release_dedicated']) - @pytest.mark.build - def test_build_win_x64_vs2019_release_dedicated(self, workspace, platform, configuration, project, spec): - workspace.build() - build_log = os.path.join(workspace.artifact_manager.dest_path, 'waf_build.log') - assert build_helper.verify_build_log(build_log, platform, configuration) \ No newline at end of file diff --git a/Tests/BuildSystems/test_lib/build_helper.py b/Tests/BuildSystems/test_lib/build_helper.py deleted file mode 100755 index c2213d139b..0000000000 --- a/Tests/BuildSystems/test_lib/build_helper.py +++ /dev/null @@ -1,38 +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. - -Helper functions for build systems -""" -import os -import logging - -logger = logging.getLogger(__name__) - - -def verify_build_log(build_log_path, platform, configuration): - """ - This will search the log file for an expected success message for a specific platform configuration. - :param build_log_path: the full path to the log file. e.g. \\dev\TestResults\timestamp_folder\pytest_results\... - :param platform: the compiler to use, e.g. "win_x64_vs2017" - :param configuration: the flavor of the build, e.g. "profile" - :return: True, if success message is found within the build log. False, if success message is not found and raise an assertion error if the build log cannot be found. - """ - success_message = "[WAF] 'build_{0}_{1}' finished successfully".format(platform, configuration) - if os.path.exists(build_log_path): - with open(build_log_path) as build_file: - for line in build_file: - if success_message in line: - logger.info('Success message was found for {0}_{1}'.format(platform, configuration)) - return True - logger.info('Success message not found for {0}_{1}'.format(platform, configuration)) - return False - else: - logger.info('We cannot find the build log and this is the path we are looking for {0}'.format(build_log_path)) - raise AssertionError diff --git a/Tests/README.txt b/Tests/README.txt deleted file mode 100644 index 71598c7890..0000000000 --- a/Tests/README.txt +++ /dev/null @@ -1 +0,0 @@ -This folder contains integration tests which do not ship with Lumberyard. Tests that ship with the product can be found in folders adjacent to the code that they test. \ No newline at end of file diff --git a/Tests/__init__.py b/Tests/__init__.py deleted file mode 100755 index 6ed3dc4bda..0000000000 --- a/Tests/__init__.py +++ /dev/null @@ -1,10 +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. -""" \ No newline at end of file diff --git a/Tests/ai/EditorScripts/LY_114727_NavigationComponent_MovementMethods.py b/Tests/ai/EditorScripts/LY_114727_NavigationComponent_MovementMethods.py deleted file mode 100755 index 75a13b8e17..0000000000 --- a/Tests/ai/EditorScripts/LY_114727_NavigationComponent_MovementMethods.py +++ /dev/null @@ -1,46 +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. -# -''' -This script tests movement methods Transform, Physics and Custom -''' -import sys, os -import time -import azlmbr.legacy.general as general - -from tests_common import TestHelper - -class TestMovementMethods(TestHelper): - def __init__(self): - TestHelper.__init__(self, log_prefix = 'LY-114727', args=['level']) - - def run_test(self): - # Start by assuming we'll crash and fail - self.test_success = False - # Open the level non-interactively - level_opened = self.open_level(self.get_arg('level')) - if not level_opened: - return - - # Enter game mode, so that physics in the test level starts running. - general.enter_game_mode() - # Wait for game mode to start. (Not sure if this is necessary, just being extra-cautious) - while (general.is_in_game_mode() != True): - general.idle_wait(1.0) - # Wait for game mode to finish. Entities should be navigating - while (general.is_in_game_mode() == True): - general.idle_wait(2.0) - - # We finished and haven't crashed, so assume success and exit the Editor. - self.test_success = True - -test = TestMovementMethods() -test.run() - diff --git a/Tests/ai/EditorScripts/tests_common.py b/Tests/ai/EditorScripts/tests_common.py deleted file mode 100755 index 76ad183c60..0000000000 --- a/Tests/ai/EditorScripts/tests_common.py +++ /dev/null @@ -1,163 +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. -# - -import sys, os -import azlmbr.legacy.general as general -import azlmbr.legacy.settings as settings - -class TestHelper: - def __init__(self, log_prefix, args = None): - self.log_prefix = log_prefix + ': ' - self.test_success = True - self.args = {} - if args: - # Get the level name and heightmap name from command-line args - if (len(sys.argv) == (len(args) + 1)): - for arg_index in range(len(args)): - self.args[args[arg_index]] = sys.argv[arg_index + 1] - else: - test_success = False - self.log('Expected command-line args: {}'.format(args)) - - - # Test Setup - # Set helpers - # Set viewport size - # Turn off display mode, antialiasing - # set log prefix, log test started - # TODO: Turn off user dialogs like Amazon login, surveys, etc... - def setup(self): - self.log('test started') - - def after_level_load(self): - # Enable the Editor to start running its idle loop. - # This is needed for Python scripts passed into the Editor startup. Since they're executed - # during the startup flow, they run before idle processing starts. Without this, the engine loop - # won't run during idle_wait, which will prevent our test level from working. - general.idle_enable(True) - - # Give everything a second to initialize - general.idle_wait(1.0) - - self.original_settings = settings.get_misc_editor_settings() - self.helpers_visible = general.is_helpers_shown() - self.viewport_size = general.get_viewport_size() - # Turn off the helper gizmos if visible - if (self.helpers_visible): - general.toggle_helpers() - general.idle_wait(1.0) - - # Set Editor viewport to a well-defined size - general.set_viewport_size(1280, 720) - general.idle_wait(1.0) - - # Turn off any display info like FPS, as that will mess up our image comparisons - # Turn off antialiasing as well - general.run_console("r_displayInfo=0") - general.run_console("r_antialiasingmode=0") - general.idle_wait(1.0) - - - - # Test Teardown - # Restore everything from above - # log test results, exit editor - def teardown(self): - # Restore the original Editor settings - settings.set_misc_editor_settings(self.original_settings) - # If the helper gizmos were on at the start, restore them - if (self.helpers_visible): - general.toggle_helpers() - # Set the viewport back to whatever size it was at the start - general.set_viewport_size(self.viewport_size.x, self.viewport_size.y) - general.idle_wait(1.0) - - self.log('test finished') - - if self.test_success == True: - self.log('result=SUCCESS') - general.set_result_to_success() - else: - self.log('result=FAILURE') - general.set_result_to_failure() - - general.exit_no_prompt() - - def run_test(self): - self.log('run') - - def run(self): - self.setup() - - # Only run the actual test if we didn't have setup issues - if self.test_success: - self.run_test() - - self.teardown() - - def get_arg(self, arg_name): - if arg_name in self.args: - return self.args[arg_name] - return '' - - - # general logger that adds prefix? - def log(self, log_line): - general.log(self.log_prefix + log_line) - - # isclose: Compares two floating-point values for "nearly-equal" - # From https://www.python.org/dev/peps/pep-0485/#proposed-implementation : - def isclose(self, a, b, rel_tol=1e-9, abs_tol=0.0): - return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol) - - - # Create a new empty level - def create_level(self, level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain): - self.log('Creating level {}'.format(level_name)) - result = general.create_level_no_prompt(level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain) - - # Result codes are ECreateLevelResult defined in CryEdit.h - if (result == 1): - self.log('Temp level already exists') - elif (result == 2): - self.log('Failed to create directory') - elif (result == 3): - self.log('Directory length is too long') - elif (result != 0): - self.log('Unknown error, failed to create level') - else: - self.log('Level created successfully') - self.after_level_load() - - return (result == 0) - - def open_level(self, level_name): - # Open the level non-interactively - self.log('Opening level {}'.format(level_name)) - result = general.open_level_no_prompt(level_name) - self.after_level_load() - if result: - self.log('Level opened successfully') - else: - self.log('Unknown error, level failed to open') - - return result - - # Take Screenshot - def take_screenshot(self, posX, posY, posZ, rotX, rotY, rotZ): - # Set our camera position / rotation and wait for the Editor to acknowledge it - general.set_current_view_position(posX, posY, posZ) - general.set_current_view_rotation(rotX, rotY, rotZ) - general.idle_wait(1.0) - # Request a screenshot and wait for the Editor to process it - general.run_console("r_GetScreenShot=2") - general.idle_wait(1.0) - diff --git a/Tests/ai/LY_114727_NavigationComponent_test.py b/Tests/ai/LY_114727_NavigationComponent_test.py deleted file mode 100755 index 3eff39c787..0000000000 --- a/Tests/ai/LY_114727_NavigationComponent_test.py +++ /dev/null @@ -1,70 +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. -""" - - -""" -ly102242: Runs the ly-102242 level in the Editor which reproduces the appropriate steps for -bug ly-102242 to crash when spawning an invalid touchbending asset. The touchbending asset -can be made invalid by including multiple meshes - one with skinning data and one without. -NOTE: In the bugfixed case, a crash will not occur, but touchbending will not occur and errors -will be printed to the console every time a new asset gets "touched" (spawned in the physics system). -""" -import pytest -pytest.importorskip('ly_test_tools') -import logging -import os - -from ..ly_shared import hydra_lytt_test_utils as hydra_utils - -logger = logging.getLogger(__name__) -test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts') -editor_timeout = 30 - -@pytest.mark.parametrize('platform', ['win_x64_vs2017']) -@pytest.mark.parametrize('configuration', ['profile']) -@pytest.mark.parametrize('project', ['AutomatedTesting']) -@pytest.mark.parametrize('spec', ['all']) -@pytest.mark.parametrize('level', ['AI/NavigationComponentTest']) -class TestNavigationComponent(object): - @pytest.fixture(autouse=True) - def setup_teardown(self, request): - def teardown(): - if hasattr(self, 'cfg_file_name'): - hydra_utils.cleanup_cfg_file(self.cfg_file_name) - - # Setup - add the teardown finalizer - request.addfinalizer(teardown) - - # entities with Transform, Physics and Custom movement methods navigate to the goal - def test_NavigationComponent(self, request, legacy_editor, level): - - cfg_args = [level] - - expected_lines = [ - "OnActivate NavigationAgentCustom", - "OnActivate NavigationAgentPhysics", - "OnActivate NavigationAgentTransform", - - "OnTraversalComplete NavigationAgentCustom", - "OnTraversalComplete NavigationAgentPhysics", - "OnTraversalComplete NavigationAgentTransform", - ] - - unexpected_lines = [ - "OnTraversalCanceled NavigationAgentCustom", - "OnTraversalCanceled NavigationAgentPhysics", - "OnTraversalCanceled NavigationAgentTransform", - ] - - hydra_utils.launch_and_validate_results(request, test_directory, legacy_editor, - 'LY_114727_NavigationComponent_MovementMethods.py', - expected_lines, unexpected_lines, timeout=editor_timeout, cfg_args=cfg_args) - diff --git a/Tests/ai/__init__.py b/Tests/ai/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/ai/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/demos/__init__.py b/Tests/demos/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/demos/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/demos/launcher_loading_tests.py b/Tests/demos/launcher_loading_tests.py deleted file mode 100755 index 6d23f56f1b..0000000000 --- a/Tests/demos/launcher_loading_tests.py +++ /dev/null @@ -1,183 +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. - -""" -import pytest -import ly_test_tools - -import os -import shutil -import subprocess -from ly_test_tools.builtin.helpers import * -import tempfile -import time -# The following imports are used to detect capabilities in the current system - -from ly_test_tools.environment.process_utils import * -import Tests.shared.asset_processor_utils as aputil - -# Built-in fixture: provides a ready to use workspace. -from Tests.shared import substring -from ly_test_tools.environment.waiter import wait_for - -LAUNCHER_TIMEOUT = 120 - - -@pytest.mark.system -class TestProjectLauncher: - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, launcher): - path_to_cache = workspace.paths.asset_cache() - # Remove previous artifacts from cache - if os.path.isdir(os.path.join(path_to_cache, "user")): - shutil.rmtree(os.path.join(path_to_cache, "user")) - - user_cfg_path = os.path.join(workspace.paths.dev(), "user.cfg") - cfg_staging_path = None - - # Backup user.cfg if one exists - if os.path.exists(user_cfg_path): - cfg_staging_path = os.path.join(tempfile.gettempdir(), "user.cfg") - shutil.move(user_cfg_path, cfg_staging_path) - - # Configure the headless client - with open(user_cfg_path, "w") as user_cfg: - user_cfg.write("r_driver=NULL\n") - user_cfg.write("sys_audio_disable=1\n") - user_cfg.write("sys_skip_input=1\n") - - def teardown(): - launcher.kill() - - aputil.kill_asset_processor() - - # Restore previous user.cfg if one existed and unconfigure the headless client - if cfg_staging_path: - shutil.move(os.path.join(tempfile.gettempdir(), "user.cfg"), user_cfg_path) - elif os.path.exists(user_cfg_path): - os.remove(user_cfg_path) - - # save logs,screenshots - if os.path.exists(workspace.paths.project_log()): - workspace.artifact_manager.save_artifact(workspace.paths.project_log()) - if os.path.isdir(workspace.paths.project_screenshots()): - workspace.artifact_manager.save_artifact(workspace.paths.project_screenshots()) - - request.addfinalizer(teardown) - - @pytest.mark.parametrize('level', ['Samples/Fur_Technical_Sample', 'Samples/Advanced_RinLocomotion', 'UI/UiFeatures','Samples/Simple_JackLocomotion', - 'Samples/ScriptedEntityTweenerSample/SampleFullscreenAnimation', 'UI/UiMainMenuLuaSample', 'Samples/Metastream_Sample', - 'Samples/ScriptCanvas_Sample/ScriptCanvas_Basic_Sample', 'UI/UiIn3DWorld', 'Samples/Audio_Sample']) - @pytest.mark.test_case_id('C1698289,C1698287,C1698294,C1698280,C1698295,C1698293,C1698289,C1698290,C1698292,C1698288') - @pytest.mark.parametrize('platform', ['win_x64_vs2017', 'win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['profile']) - @pytest.mark.parametrize('project', ['SamplesProject']) - @pytest.mark.parametrize('spec', ['all']) - def test_LaunchAndWait_LoadsLevelAndQuit_NoCrash(self, workspace, level, launcher): - """ - Launch the Project Launcher and sets the specified level. - Performs the console steps by passing in args. - Loads the specified level and quit's the launcher. - """ - # Fast fail if the level doesn't exist - assert os.path.exists(os.path.join(workspace.paths.project(), "Levels", level)) and os.path.isdir( - os.path.join(workspace.paths.project(), "Levels", level)), "Level Doesn't Exist" - - launcher.args = ["+map", level] - launcher.launch() - - pattern_string = "Loading level " + level - test = os.path.join(workspace.paths.project_log(), "Game.log") - wait_for(lambda: os.path.exists(os.path.join(workspace.paths.project_log(), "Game.log"))) - wait_for(lambda: substring.in_file( - os.path.join(workspace.paths.project_log(), "Game.log"), pattern_string), LAUNCHER_TIMEOUT) - assert not os.path.exists( - os.path.join(workspace.paths.project_log(), "error.log")), "Launcher Crashed Unexpectedly" - - # This is a convenient place to also verify LY-90255 for free here - # (as well as any other text that must appear in the log). - # writing a separate test to launch the launcher and examine the log would just waste time as the - # above test already launches the launcher, and waits for it to finish anyway. - - assert substring.in_file(os.path.join(workspace.paths.project_log(), "Game.log"), "Initializing CryFont done, MemUsage") - - - - @pytest.mark.parametrize('level',['UI/UiFeatures','Samples/Metastream_Sample']) - @pytest.mark.parametrize('platform', ['win_x64_vs2017', 'win_x64_vs2019']) - @pytest.mark.parametrize('configuration', ['profile']) - @pytest.mark.parametrize('project', ['SamplesProject']) - @pytest.mark.parametrize('spec', ['all']) - def test_LaunchAndWait_LoadsLevelFromPakAndQuit_NoCrash(self, workspace, level, launcher): - """ - This test ensure that the Launcher can load levels from inside paks - """ - - if workspace.platform == 'win_x64_vs2017': - binDirPath = os.path.join(workspace.paths._dev_path, 'Bin64vc141') - if workspace.platform == 'win_x64_vs2019': - binDirPath = os.path.join(workspace.paths._dev_path, 'Bin64vc142') - - # Launch APBatch so that it can process all assets and quit - subprocess.check_call( - [os.path.join(binDirPath, 'AssetProcessorBatch'), "/gamefolder=SamplesProject"]) - - lowercaseProjectName = workspace.project.lower() - cacheLevelDir = os.path.join(workspace.paths.platform_cache(), lowercaseProjectName, "levels") - cacheTempLevelDir = os.path.join(workspace.paths.platform_cache(), lowercaseProjectName, "templevels") - - # Rename levels dir so that runtime cannot load levels from it - os.rename(cacheLevelDir, cacheTempLevelDir) - # Make an empty levels folder - os.mkdir(cacheLevelDir) - - # make an archive of all the levels - outputLevelsArchive = os.path.join(cacheLevelDir, "templevels") - shutil.make_archive(outputLevelsArchive, 'zip', cacheTempLevelDir) - # make archive will make a zip file - outputLevelsArchive = outputLevelsArchive + ".zip" - - # change extension from zip to pak - baseFileName = os.path.splitext(outputLevelsArchive)[0] - os.rename(outputLevelsArchive, baseFileName + ".pak") - - # Launching AP again for SamplesProject gameproject - subprocess.Popen([os.path.join(binDirPath, 'AssetProcessor'), "/gamefolder=SamplesProject", "--zeroAnalysisMode"]) - - # Waiting to give AP time some time to start the listening thread otherwise the launcher will try to launch another instance of AP - time.sleep(1) - - # ensure that in the cache level does not exist on disk - assert not os.path.exists(os.path.join(cacheLevelDir, level)) - - # load the levels - launcher.args = ["+map", level] - launcher.launch() - - pattern_string = "Loading level " + level - wait_for(lambda: os.path.exists(os.path.join(workspace.paths.project_log(), "Game.log"))) - wait_for(lambda: substring.in_file( - os.path.join(workspace.paths.project_log(), "Game.log"), pattern_string), LAUNCHER_TIMEOUT) - assert not os.path.exists( - os.path.join(workspace.paths.project_log(), "error.log")), "Launcher Crashed Unexpectedly" - - loaded_level_pattern = "Level " + level + " loaded" - wait_for(lambda: substring.in_file( - os.path.join(workspace.paths.project_log(), "Game.log"), loaded_level_pattern), LAUNCHER_TIMEOUT) - - launcher.stop() - - aputil.kill_asset_processor() - - shutil.rmtree(cacheLevelDir) - os.rename(cacheTempLevelDir, cacheLevelDir) - - - diff --git a/Tests/demos/mac/__init__.py b/Tests/demos/mac/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/demos/mac/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/demos/mac/demos_mac.py b/Tests/demos/mac/demos_mac.py deleted file mode 100755 index a2a44eab20..0000000000 --- a/Tests/demos/mac/demos_mac.py +++ /dev/null @@ -1,142 +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. - -These tests will validate that StarterGame and SamplesProject can be setup and built, have no failing assets processed, -and will then have a screen shot taken to verify that it renders normally. All the logs and screenshots will be -transferred to the test results to be zipped up and added to the Flume result. These projects will be run in profile -and debug. -""" -import logging -import os -import pytest - -from demos.test_lib.demos_testlib import load_level, remote_console_take_screenshot, start_launcher, start_remote_console -import shared.shader_compile_server_utils as compile_server -import test_tools.shared.file_utils as file_utils -from test_tools.shared.launcher_testlib import configure_setup, assert_build_success, assert_process_assets - -import test_tools.builtin.fixtures as fixtures -from test_tools import MAC_LAUNCHER -import test_tools.launchers.phase -from test_tools.shared.remote_console_commands import RemoteConsole - -logger = logging.getLogger(__name__) - -workspace = fixtures.use_fixture(fixtures.builtin_workspace_fixture, scope='function') - - -@pytest.fixture -def launcher_instance(request, workspace, level): - """ - Creates a launcher fixture instance with an extra teardown for error log grabbing. - """ - def teardown(): - """ - Tries to grab any error logs before moving on to the next test. - """ - compile_server.stop_shader_compile_server() - - if os.path.exists(launcher.workspace.release.paths.project_log()): - for file_name in os.listdir(launcher.workspace.release.paths.project_log()): - file_utils.move_file(launcher.workspace.release.paths.project_log(), - launcher.workspace.artifact_manager.get_save_artifact_path(), - file_name) - - logs_exist = lambda: file_utils.gather_error_logs( - launcher.workspace.release.paths.dev(), - launcher.workspace.artifact_manager.get_save_artifact_path()) - try: - test_tools.shared.waiter.wait_for(logs_exist) - except AssertionError: - print("No error logs found. Completing test...") - - request.addfinalizer(teardown) - - launcher = fixtures.launcher(request, workspace, level) - return launcher - - -@pytest.fixture -def remote_console_instance(request): - """ - Creates a remote console instance to send console commands. - """ - console = RemoteConsole() - - def teardown(): - try: - console.stop() - except: - pass - - request.addfinalizer(teardown) - - return console - - -@pytest.mark.parametrize("platform,configuration,project,spec,level", [ - pytest.param("darwin_x64", "profile", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not MAC_LAUNCHER, reason="Only supported on Mac hosts")), - pytest.param("darwin_x64", "debug", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not MAC_LAUNCHER, reason="Only supported on Mac hosts")), - ]) -class TestSingleLevel(object): - def test_single_level(self, launcher_instance, configuration, level, remote_console_instance): - """ - Verifies projects with a given demo-level can compile and successfully launch. - """ - configure_setup(launcher_instance) - - assert_build_success(launcher_instance) - assert_process_assets(launcher_instance) - - compile_server.start_mac_shader_compile_server(os.path.join(launcher_instance.workspace.release.paths.dev(), - "Tools"), configuration) - start_launcher(launcher_instance) - start_remote_console(launcher_instance, remote_console_instance) - - load_level(launcher_instance, remote_console_instance, level) - remote_console_take_screenshot(launcher_instance, remote_console_instance, level) - - -@pytest.mark.parametrize("platform,configuration,project,spec,level,levels", [ - pytest.param("darwin_x64", "profile", "SamplesProject", "all", "Advanced_RinLocomotion", - ["Advanced_RinLocomotion", "Audio_Sample", "Fur_Technical_Sample", - "Gems_InAppPurchases_Sample", "Metastream_Sample", "ScriptCanvas_Basic_Sample", - "Simple_JackLocomotion", "SampleFullscreenAnimation", "UiFeatures", "UiIn3DWorld", - "UiMainMenuLuaSample", "UiMainMenuScriptCanvasSample"], - marks=pytest.mark.skipif(not MAC_LAUNCHER, reason="Only supported on Mac hosts")), - pytest.param("darwin_x64", "debug", "SamplesProject", "all", "Advanced_RinLocomotion", - ["Advanced_RinLocomotion", "Audio_Sample", "Fur_Technical_Sample", - "Gems_InAppPurchases_Sample", "Metastream_Sample", "ScriptCanvas_Basic_Sample", - "Simple_JackLocomotion", "SampleFullscreenAnimation", "UiFeatures", "UiIn3DWorld", - "UiMainMenuLuaSample", "UiMainMenuScriptCanvasSample"], - marks=pytest.mark.skipif(not MAC_LAUNCHER, reason="Only supported on Mac hosts")), - ]) -# Testing for projects with multiple demo levels -class TestMultipleLevels(object): - """ - Verifies projects with multiple demo-levels can compile and successfully launch. - """ - def test_multiple_levels(self, launcher_instance, configuration, levels, remote_console_instance): - configure_setup(launcher_instance) - - assert_build_success(launcher_instance) - assert_process_assets(launcher_instance) - - compile_server.start_mac_shader_compile_server(os.path.join(launcher_instance.workspace.release.paths.dev(), - "Tools"), configuration) - start_launcher(launcher_instance) - start_remote_console(launcher_instance, remote_console_instance) - - # Switch to each level, check if it loads and takes a screen shot, then move to test folder for Flume - for level in levels: - load_level(launcher_instance, remote_console_instance, level) - remote_console_take_screenshot(launcher_instance, remote_console_instance, level) diff --git a/Tests/demos/test_lib/__init__.py b/Tests/demos/test_lib/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/demos/test_lib/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/demos/test_lib/demos_testlib.py b/Tests/demos/test_lib/demos_testlib.py deleted file mode 100755 index 46ed627cab..0000000000 --- a/Tests/demos/test_lib/demos_testlib.py +++ /dev/null @@ -1,70 +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. - -This demos_testlib file is used for a collection of reusable functionality that QA will use in their scripts specific -to the setup of demo level tests. -""" -import sys - -import shared.network_utils as network_utils -from shared.screenshot_utils import move_screenshots, take_screenshot_with_retries - -from test_tools.shared.launcher_testlib import * - -import test_tools.shared.waiter - - -def start_launcher(launcher): - """ - For PC: Used to start launcher and give time to load. - """ - launcher.launch() - launcher.run(test_tools.launchers.phase.TimePhase(120, 120)) - - -def load_level(launcher, remote_console, level): - """ - Uses the remote console to use the map command to load a level and checks the console output for a successful load. - """ - command = 'map {}'.format(level) - load = remote_console.expect_log_line('LEVEL_LOAD_COMPLETE', 300) - retry_console_command(remote_console, command, "Executing console command '{}'".format(command)) - assert load(), "{} level failed to load.".format(level) - - # Allow one minute to let level fully render and to test for stability - launcher.run(test_tools.launchers.phase.TimePhase(60, 60)) - - -def start_remote_console(launcher, remote_console, on_devkit=False): - """ - Starts the remote console. Used in QA scripts that require the use of remote console. - """ - if on_devkit: - test_tools.shared.waiter.wait_for(lambda: network_utils.check_for_remote_listening_port(4600, launcher.ip), - timeout=600, exc=AssertionError('Port 4600 not listening.')) - else: - test_tools.shared.waiter.wait_for(lambda: network_utils.check_for_listening_port(4600), timeout=300, - exc=AssertionError('Port 4600 not listening.')) - - remote_console.start() - - # Allows remote console time to connect to launcher. - launcher.run(test_tools.launchers.phase.TimePhase(60, 60)) - - -def remote_console_take_screenshot(launcher, remote_console, level): - """ - Uses the remote console to run the r_GetScreenshot command to take a screenshot of the current launcher and move - the screenshot to the test results location. - """ - screenshot_path = os.path.join(launcher.workspace.release.paths.platform_cache(), "user", "screenshots") - take_screenshot_with_retries(remote_console, launcher, level) - if os.path.exists(screenshot_path): - move_screenshots(screenshot_path, '.jpg', launcher.workspace.artifact_manager.get_save_artifact_path()) diff --git a/Tests/demos/win/__init__.py b/Tests/demos/win/__init__.py deleted file mode 100755 index 4d5680a30d..0000000000 --- a/Tests/demos/win/__init__.py +++ /dev/null @@ -1,10 +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. -# diff --git a/Tests/demos/win/demos_pc.py b/Tests/demos/win/demos_pc.py deleted file mode 100755 index cb8b9a0e4b..0000000000 --- a/Tests/demos/win/demos_pc.py +++ /dev/null @@ -1,151 +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. - -These tests will validate that each project can be setup and built, have no failing assets processed, and will then -have a screenshot taken to verify that it renders normally. All the logs and screenshots will be transferred to the -test results to be zipped up and added to the Flume result. These projects will be run in profile and debug. -Currently SearchForEden and Bistro are failing and are temporarily commented out of these tests. -""" -import logging -import os -import pytest - -from demos.test_lib.demos_testlib import load_level, remote_console_take_screenshot, start_launcher, start_remote_console -from test_tools.shared.file_utils import gather_error_logs, move_file -from test_tools.shared.launcher_testlib import configure_setup, assert_build_success, assert_process_assets - -from test_tools import WINDOWS_LAUNCHER -import test_tools.builtin.fixtures as fixtures -import test_tools.launchers.phase -from test_tools.shared.remote_console_commands import RemoteConsole - -logger = logging.getLogger(__name__) - -# use_fixture registers the imported fixture in pytest at the specified scope. The test should provide all the -# parameters in the fixture's signature -workspace = fixtures.use_fixture(fixtures.builtin_workspace_fixture, scope='function') - - -@pytest.fixture -def launcher_instance(request, workspace, level): - """ - Creates a launcher fixture instance with an extra teardown for error log grabbing. - """ - def teardown(): - """ - Tries to grab any error logs before moving on to the next test. - """ - if os.path.exists(launcher.workspace.release.paths.project_log()): - for file_name in os.listdir(launcher.workspace.release.paths.project_log()): - move_file(launcher.workspace.release.paths.project_log(), - launcher.workspace.artifact_manager.get_save_artifact_path(), - file_name) - - logs_exist = lambda: gather_error_logs( - launcher.workspace.release.paths.dev(), - launcher.workspace.artifact_manager.get_save_artifact_path()) - try: - test_tools.shared.waiter.wait_for(logs_exist) - except AssertionError: - print("No error logs found. Completing test...") - - request.addfinalizer(teardown) - - launcher = fixtures.launcher(request, workspace, level) - return launcher - - -@pytest.fixture -def remote_console_instance(request): - """ - Creates a remote console instance to send console commands. - """ - console = RemoteConsole() - - def teardown(): - try: - console.stop() - except: - pass - - request.addfinalizer(teardown) - - return console - - -@pytest.mark.parametrize("platform,configuration,project,spec,level", [ - pytest.param("win_x64_vs2017", "profile", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2019", "profile", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2017", "debug", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2019", "debug", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - ]) -class TestSingleLevel(object): - def test_single_level(self, launcher_instance, level, remote_console_instance): - """ - Verifies projects with a given demo-level can compile and successfully launch. - """ - configure_setup(launcher_instance) - - assert_build_success(launcher_instance) - assert_process_assets(launcher_instance) - - start_launcher(launcher_instance) - start_remote_console(launcher_instance, remote_console_instance) - - load_level(launcher_instance, remote_console_instance, level) - remote_console_take_screenshot(launcher_instance, remote_console_instance, level) - - -@pytest.mark.parametrize("platform,configuration,project,spec,level,levels", [ - pytest.param("win_x64_vs2017", "profile", "SamplesProject", "all", "Advanced_RinLocomotion", - ["Advanced_RinLocomotion", "Audio_Sample", "Fur_Technical_Sample", "Gems_InAppPurchases_Sample", - "Metastream_Sample", "ScriptCanvas_Basic_Sample", "Simple_JackLocomotion", - "SampleFullscreenAnimation", "UiDrawCallsSample", "UiFeatures", "UiIn3DWorld", "UiMainMenuLuaSample", - "UiMainMenuScriptCanvasSample", "UiTextureAtlasSample"], - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2019", "profile", "SamplesProject", "all", "Advanced_RinLocomotion", - ["Advanced_RinLocomotion", "Audio_Sample", "Fur_Technical_Sample", "Gems_InAppPurchases_Sample", - "Metastream_Sample", "ScriptCanvas_Basic_Sample", "Simple_JackLocomotion", - "SampleFullscreenAnimation", "UiDrawCallsSample", "UiFeatures", "UiIn3DWorld", "UiMainMenuLuaSample", - "UiMainMenuScriptCanvasSample", "UiTextureAtlasSample"], - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2017", "debug", "SamplesProject", "all", "Advanced_RinLocomotion", - ["Advanced_RinLocomotion", "Audio_Sample", "Fur_Technical_Sample", "Gems_InAppPurchases_Sample", - "Metastream_Sample", "ScriptCanvas_Basic_Sample", "Simple_JackLocomotion", - "SampleFullscreenAnimation", "UiDrawCallsSample", "UiFeatures", "UiIn3DWorld", "UiMainMenuLuaSample", - "UiMainMenuScriptCanvasSample", "UiTextureAtlasSample"], - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2019", "debug", "SamplesProject", "all", "Advanced_RinLocomotion", - ["Advanced_RinLocomotion", "Audio_Sample", "Fur_Technical_Sample", "Gems_InAppPurchases_Sample", - "Metastream_Sample", "ScriptCanvas_Basic_Sample", "Simple_JackLocomotion", - "SampleFullscreenAnimation", "UiDrawCallsSample", "UiFeatures", "UiIn3DWorld", "UiMainMenuLuaSample", - "UiMainMenuScriptCanvasSample", "UiTextureAtlasSample"], - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")) - ]) -class TestMultipleLevels(object): - def test_multiple_levels(self, launcher_instance, levels, remote_console_instance): - """ - Verifies projects with multiple demo-levels can compile and successfully launch. - """ - configure_setup(launcher_instance) - - assert_build_success(launcher_instance) - assert_process_assets(launcher_instance) - - start_launcher(launcher_instance) - start_remote_console(launcher_instance, remote_console_instance) - - for level in levels: - load_level(launcher_instance, remote_console_instance, level) - remote_console_take_screenshot(launcher_instance, remote_console_instance, level) diff --git a/Tests/graphics/__init__.py b/Tests/graphics/__init__.py deleted file mode 100755 index 6ed3dc4bda..0000000000 --- a/Tests/graphics/__init__.py +++ /dev/null @@ -1,10 +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. -""" \ No newline at end of file diff --git a/Tests/graphics/ly107748_LightningArcProperties.cfg b/Tests/graphics/ly107748_LightningArcProperties.cfg deleted file mode 100644 index fe05f5f782..0000000000 --- a/Tests/graphics/ly107748_LightningArcProperties.cfg +++ /dev/null @@ -1,2 +0,0 @@ -# this file is copied to $/dev/editor_autoexec.cfg so the the Editor automation runs for this Hydra test -pyRunFile @devroot@/Tests/graphics/ly107748_LightningArcProperties_test_case.py \ No newline at end of file diff --git a/Tests/graphics/ly107748_LightningArcProperties_test.py b/Tests/graphics/ly107748_LightningArcProperties_test.py deleted file mode 100755 index 59cc3d8d1b..0000000000 --- a/Tests/graphics/ly107748_LightningArcProperties_test.py +++ /dev/null @@ -1,83 +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. -""" - -# -# This is a pytest module to test the in-Editor Python API from PythonEditorFuncs -# -import pytest -pytest.importorskip('test_tools') -import time -import logging -import os -import shutil - -from test_tools import WINDOWS_LAUNCHER -import test_tools.shared.log_monitor -import test_tools.launchers.phase -import test_tools.builtin.fixtures as fixtures - -# Use the built-in workspace and editor fixtures. -# These will configure the requested project and run the editor. -workspace = fixtures.use_fixture(fixtures.builtin_workspace_fixture, scope='function') -editor = fixtures.use_fixture(fixtures.editor, scope='function') - -logger = logging.getLogger(__name__) - - -@pytest.mark.parametrize("platform,configuration,project,spec", [ - pytest.param("win_x64_vs2017", "profile", "AutomatedTesting", "all", marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), -]) -class TestLightningArcPropertyRanges(object): - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, workspace, editor): - def teardown(): - editor.ensure_stopped() - - file_utils.delete_level(editor, "LightningArcTestLevel") - - request.addfinalizer(teardown) - - def test_change_properties(self, request, editor, project): - logger.debug("Running automated test") - - request.addfinalizer(editor.ensure_stopped) - - editor.deploy() - editor.launch(["--exec", "@engroot@/Tests/graphics/ly107748_LightningArcProperties.cfg"]) - - editorlog_file = os.path.join(editor.workspace.release.paths.project_log(), 'Editor.log') - - # LY-107861 LY-108088 - # expected failure cases are commented out pending implementation of property validation by hydra - expected_lines = [ - "Created new entity.", - "Lightning Arc component added to entity.", - #"ChangeProperty m_config|Arc Parameters|Segment Count to 0 failed.", - "ChangeProperty m_config|Arc Parameters|Segment Count to 1 succeeded.", - "ChangeProperty m_config|Arc Parameters|Segment Count to 25 succeeded.", - "ChangeProperty m_config|Arc Parameters|Segment Count to 50 succeeded.", - "ChangeProperty m_config|Arc Parameters|Segment Count to 70 succeeded.", - #"ChangeProperty m_config|Arc Parameters|Segment Count to 75 failed.", - #"ChangeProperty m_config|Arc Parameters|Segment Count to 100 failed.", - #"ChangeProperty m_config|Arc Parameters|Point Count to 0 failed.", - "ChangeProperty m_config|Arc Parameters|Point Count to 1 succeeded.", - "ChangeProperty m_config|Arc Parameters|Point Count to 25 succeeded.", - "ChangeProperty m_config|Arc Parameters|Point Count to 50 succeeded.", - "ChangeProperty m_config|Arc Parameters|Point Count to 70 succeeded.", - #"ChangeProperty m_config|Arc Parameters|Point Count to 75 failed.", - #"ChangeProperty m_config|Arc Parameters|Point Count to 100 failed.", - ] - - test_tools.shared.log_monitor.monitor_for_expected_lines(editor, editorlog_file, expected_lines) - - # Rely on the test script to quit after running - editor.run(test_tools.launchers.phase.WaitForLauncherToQuit(editor, 10)) diff --git a/Tests/graphics/ly107748_LightningArcProperties_test_case.py b/Tests/graphics/ly107748_LightningArcProperties_test_case.py deleted file mode 100755 index dd0c72e893..0000000000 --- a/Tests/graphics/ly107748_LightningArcProperties_test_case.py +++ /dev/null @@ -1,80 +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. -""" - -# Tests a portion of the Component Property Get/Set Python API while the Editor is running - -import azlmbr.legacy.general as general -import azlmbr.bus as bus -import azlmbr.entity as entity -import azlmbr.editor as editor -import azlmbr.math as math - - -# Create a test level -general.create_level_no_prompt("LightningArcTestLevel", 1024, 1, 1024, True) - -def ChangeProperty(component, path, value): - getPropertyOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentProperty', component, path) - if not(getPropertyOutcome.IsSuccess()): - print("GetComponentProperty " + path + " failed.") - else: - oldValue = getPropertyOutcome.GetValue() - - setPropertyOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'SetComponentProperty', component, path, value) - if not(setPropertyOutcome.IsSuccess()): - print("SetComponentProperty " + path + " to " + str(value) + " failed.") - - getPropertyOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentProperty', component, path) - if not(getPropertyOutcome.IsSuccess()): - print("GetComponentProperty " + path + " failed.") - else: - newValue = getPropertyOutcome.GetValue() - - if not(newValue == oldValue): - print("ChangeProperty " + path + " to " + str(value) + " succeeded.") - else: - print("ChangeProperty " + path + " to " + str(value) + " failed.") - -# Create new Entity -entityId = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId()) - -if (entityId.IsValid()): - print("Created new entity.") - -# Get Component Type for Lightning Arc -typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Lightning Arc"], entity.EntityType().Game) - -componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', entityId, typeIdsList) - -if (componentOutcome.IsSuccess()): - print("Lightning Arc component added to entity.") - -components = componentOutcome.GetValue() -component = components[0] - -# Tests for GetComponentProperty/SetComponentProperty -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 0) -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 1) -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 25) -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 50) -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 70) -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 75) -ChangeProperty(component, "m_config|Arc Parameters|Segment Count", 100) - -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 0) -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 1) -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 25) -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 50) -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 70) -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 75) -ChangeProperty(component, "m_config|Arc Parameters|Point Count", 100) - -general.exit_no_prompt() diff --git a/Tests/hydra/ctests/open_level_tweak_and_exit.py b/Tests/hydra/ctests/open_level_tweak_and_exit.py deleted file mode 100755 index de81327852..0000000000 --- a/Tests/hydra/ctests/open_level_tweak_and_exit.py +++ /dev/null @@ -1,46 +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. -""" - -# --runpythontest @devroot@\tests\hydra\ctests\open_level_tweak_and_exit.py -# An example of how a create a level, make an entity, and terminate successfully - -import time -import azlmbr.editor -import azlmbr.entity -import azlmbr.framework -import azlmbr.legacy.general as general -from azlmbr.bus import Broadcast - -handler = None - -def open_level(level): - print('opening level {}'.format(level)) - azlmbr.editor.EditorToolsApplicationRequestBus(Broadcast, 'OpenLevelNoPrompt', level) - general.idle_wait(1.0) - -def on_entity_registered(args): - print('on_entity_registered') - azlmbr.framework.Terminate(0) - -def main(): - print ('open_level_tweak_and_exit - starting') - open_level('auto_test') - - azlmbr.editor.ToolsApplicationRequestBus(Broadcast, 'CreateNewEntity', azlmbr.entity.EntityId()) - general.idle_wait(1.0) - - handler = azlmbr.editor.ToolsApplicationNotificationBusHandler() - handler.connect() - handler.add_callback('EntityRegistered', on_entity_registered) - azlmbr.editor.ToolsApplicationRequestBus(Broadcast, 'CreateNewEntity', azlmbr.entity.EntityId()) - -if __name__ == "__main__": - main() diff --git a/Tests/hydra/ctests/start_stop.py b/Tests/hydra/ctests/start_stop.py deleted file mode 100755 index c8fd670a97..0000000000 --- a/Tests/hydra/ctests/start_stop.py +++ /dev/null @@ -1,34 +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. -""" - -# --runpythontest @devroot@\tests\hydra\ctests\start_stop.py -# an example of a test script that loads a level, listens for the first entity, and terminates the Editor with a 0 - -import azlmbr.framework -import azlmbr.editor -import azlmbr.bus - -handler = None - -def on_entity_registered(args): - print('on_entity_registered') - azlmbr.framework.Terminate(0) - -def main(): - print ('hello, start_stop') - handler = azlmbr.editor.ToolsApplicationNotificationBusHandler() - handler.connect() - handler.add_callback('EntityRegistered', on_entity_registered) - azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'OpenLevelNoPrompt', 'auto_test') - print ('start_stop started') - -if __name__ == "__main__": - main() diff --git a/Tests/hydra/ctests/start_with_args.py b/Tests/hydra/ctests/start_with_args.py deleted file mode 100755 index e8b99b54ac..0000000000 --- a/Tests/hydra/ctests/start_with_args.py +++ /dev/null @@ -1,28 +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. -""" - -# --runpythontest @devroot@\tests\hydra\ctests\start_with_args.py --runpythonargs foo bar baz -# An example of how to use runpythontest with a main() + args - -import azlmbr.framework - -def main(): - print("hello, start_with_args") - - # print command line arguments - for arg in sys.argv: - print (arg) - - azlmbr.framework.Terminate(0) - -if __name__ == "__main__": - main() - diff --git a/Tests/hydra/ctests/stop_with_error_one.py b/Tests/hydra/ctests/stop_with_error_one.py deleted file mode 100755 index b17b5247aa..0000000000 --- a/Tests/hydra/ctests/stop_with_error_one.py +++ /dev/null @@ -1,17 +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. -""" - -# --runpythontest @devroot@\tests\hydra\ctests\stop_with_error_one.py -# an example terminating with a non-zero return code from Editor.exe - -import azlmbr.framework -print ('hello, stop_with_error_one') -azlmbr.framework.Terminate(1) diff --git a/Tests/hydra/ctests/stop_with_zero.py b/Tests/hydra/ctests/stop_with_zero.py deleted file mode 100755 index d1d3f4f58d..0000000000 --- a/Tests/hydra/ctests/stop_with_zero.py +++ /dev/null @@ -1,17 +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. -""" - -# --runpythontest @devroot@\tests\hydra\ctests\stop_with_zero.py -# An example of how a test script stops the Editor.exe with a succuessful zero return code - -import azlmbr.framework -print ('hello, stop_with_zero') -azlmbr.framework.Terminate(0) diff --git a/Tests/hydra/ctests/throws_exception.py b/Tests/hydra/ctests/throws_exception.py deleted file mode 100755 index e8b4507fb1..0000000000 --- a/Tests/hydra/ctests/throws_exception.py +++ /dev/null @@ -1,19 +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. -""" - -# --runpythontest @devroot@\tests\hydra\ctests\throws_exception.py -# An example of how a test script to fatal from Editor.exe when a Python exception happens - -print ('hello, throws_exception') -foo = 1.0 -bar = 0.0 -baz = foo / bar - diff --git a/Tests/ly_shared/PlatformSetting.py b/Tests/ly_shared/PlatformSetting.py deleted file mode 100755 index b215275c16..0000000000 --- a/Tests/ly_shared/PlatformSetting.py +++ /dev/null @@ -1,69 +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. - -Class for querying and setting a system setting/preference. - -""" - -import pytest -import logging -from typing import Optional, Any - -import ly_test_tools.o3de.pipeline_utils as utils - -logger = logging.getLogger(__name__) - - -class PlatformSetting: - """ - Interface for managing different platforms' system variables. - """ - - class DATA_TYPE: - """Platform-agnostic data type enums""" - - INT = 1 - STR = 2 - STR_LIST = 3 - - def __init__(self, workspace: pytest.fixture, subkey: str, key: str) -> None: - self._workspace = workspace - self._key = key - self._subkey = subkey - - def get_value(self, get_type: bool = False) -> object: - """Gets the current setting's value (and optionally type as tuple) from the system. Returns None if entry DNE""" - raise NotImplementedError("Virtual PlatformSetting not implemented. Instantiate a specific platform") - - def set_value(self, value: any) -> bool: - """Sets the current setting's value. Creates the entry if it DNE. Returns True for success.""" - raise NotImplementedError("Virtual PlatformSetting not implemented. Instantiate a specific platform") - - def delete_entry(self) -> bool: - """Deletes the settings entry. Returns boolean for success.""" - raise NotImplementedError("Virtual PlatformSetting not implemented. Instantiate a specific platform") - - def entry_exists(self) -> bool: - """Checks if the settings entry exists.""" - raise NotImplementedError("Virtual PlatformSetting not implemented. Instantiate a specific platform") - - @staticmethod - def get_system_setting(workspace: pytest.fixture, subkey: str, key: str, hive: Optional[str] = None) -> Any: - """Factory method creates a platform-specific system setting accessor""" - if workspace.asset_processor_platform is 'windows': - # import WindowsSetting and return an instance - from Tests.ly_shared.WindowsRegistrySetting import WindowsRegistrySetting - - return WindowsRegistrySetting(workspace, subkey, key, hive) - # ######################################################## - # Insert Mac (and Linux?) Setting implementations - # ######################################################## - else: - raise NotImplementedError(f"Platform: {workspace.platform} not supported yet") diff --git a/Tests/ly_shared/PlatformSettingTest.py b/Tests/ly_shared/PlatformSettingTest.py deleted file mode 100755 index f0ef8b6d1a..0000000000 --- a/Tests/ly_shared/PlatformSettingTest.py +++ /dev/null @@ -1,92 +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. - -Tests the functionality of the PlatformSetting class -""" - -import pytest - -import ly_test_tools.builtin.helpers as helpers -from Tests.ly_shared.PlatformSetting import PlatformSetting - -all_platforms = helpers.all_host_platforms_params() -automatic_platform_skipping = helpers.automatic_platform_skipping -targetProjects = ["Helios"] - - -@pytest.mark.usefixtures("automatic_platform_skipping") -@pytest.mark.parametrize("platform", all_platforms) -@pytest.mark.parametrize("configuration", ["profile"]) -@pytest.mark.parametrize("spec", ["all"]) -@pytest.mark.parametrize("project", targetProjects) -class TestsPlatformSetting(object): - """ - Tests for the PlatformSetting class - """ - - def test_PlatformSetting(self, workspace): - - key = "Software" - subkey = "TemporarySystemSetting" - - # Create setting reference - setting = PlatformSetting.get_system_setting(workspace, subkey, key) - - # Test storing integer - value = 74 - setting.set_value(value) - - # Test creation of subkey - assert setting.entry_exists(), f"Failed creating key:subkey, {key}:{subkey}" - - # Test data retrieval (without type) - retrieved = setting.get_value() - # fmt:off - assert retrieved == value, \ - f"Unexpected value retrieved from system settings. Expected: {value}, Actual: {retrieved}" - # fmt:on - - # Test data retrieval (with type) - retrieved = setting.get_value(get_type=True) - assert type(retrieved) == tuple, "Getting value with type DID NOT return a tuple" - assert len(retrieved) == 2, f"Getting value with type returned a tuple of size {len(retrieved)}: expected 2" - assert retrieved[1] == PlatformSetting.DATA_TYPE.INT, "Value stored was int, but type retrieved was NOT int" - assert type(retrieved[0]) == int, "Value stored was int, but value retrieved was NOT int" - - # fmt:off - assert retrieved[0] == value, \ - f"Unexpected value retrieved from system settings. Expected: {value}, Actual: {retrieved[0]}" - # fmt:on - - # Test storing string - value = "Some Text" - setting.set_value(value) - retrieved = setting.get_value(get_type=True) - assert ( - retrieved[1] == PlatformSetting.DATA_TYPE.STR - ), "Value stored was string, but type retrieved was NOT string" - assert type(retrieved[0]) == str, "Value stored was string, but value retrieved was NOT string" - assert value == retrieved[0], f"Value retrieved not expected. Expected: {value}, Actual: {retrieved[0]}" - - # Test storing list of strings - value = ["Some", "List", "Of", "Text"] - setting.set_value(value) - retrieved = setting.get_value(get_type=True) - assert ( - retrieved[1] == PlatformSetting.DATA_TYPE.STR_LIST - ), "Value stored was string list, but type retrieved was NOT string list" - assert type(retrieved[0]) == list, "Value stored was string, but value retrieved was NOT string" - # fmt:off - assert sorted(value) == sorted(retrieved[0]), f"Value retrieved not expected. " \ - f"Expected: {value}, Actual: {retrieved[0]}" - # fmt:on - - setting.delete_entry() - assert not setting.entry_exists(), f"Failed to delete key:subkey, {key}:{subkey}" diff --git a/Tests/ly_shared/WindowsRegistrySetting.py b/Tests/ly_shared/WindowsRegistrySetting.py deleted file mode 100755 index c1aa4e4a98..0000000000 --- a/Tests/ly_shared/WindowsRegistrySetting.py +++ /dev/null @@ -1,165 +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. - -Class for querying and setting a windows registry setting. - -""" -import pytest -import logging -from typing import List, Optional, Tuple, Any - -from winreg import ( - CreateKey, - OpenKey, - QueryValueEx, - DeleteValue, - SetValueEx, - KEY_ALL_ACCESS, - KEY_WRITE, - REG_SZ, - REG_MULTI_SZ, - REG_DWORD, - HKEY_CURRENT_USER, -) - - -from Tests.ly_shared.PlatformSetting import PlatformSetting - -logger = logging.getLogger(__name__) - - -class WindowsRegistrySetting(PlatformSetting): - def __init__(self, workspace: pytest.fixture, subkey: str, key: str, hive: Optional[str] = None) -> None: - super().__init__(workspace, subkey, key) - self._hive = None - try: - if hive is not None: - self._hive = self._str_to_hive(hive) - except ValueError: - logger.warning(f"Windows Registry Hive {hive} not recognized, using default: HKEY_CURRENT_USER") - finally: - if self._hive is None: - self._hive = HKEY_CURRENT_USER - - def get_value(self, get_type: Optional[bool] = False) -> Any: - """Retrieves the fast scan value in Windows registry (and optionally the type). If entry DNE, returns None.""" - if self.entry_exists(): - registryKey = OpenKey(self._hive, self._key) - value = QueryValueEx(registryKey, self._subkey) - registryKey.Close() - # Convert windows data type to universal data type flag: PlatformSettings.DATA_TYPE - # And handles unicode conversion for strings - value = self._convert_value(value) - return value if get_type else value[0] - - else: - logger.warning(f"Could not retrieve Registry entry; key: {self._key}, subkey: {self._subkey}.") - return None - - def set_value(self, value: Any) -> bool: - """Sets the Windows registry value.""" - value, win_type = self._format_data(value) - registryKey = None - result = False - try: - CreateKey(self._hive, self._subkey) - registryKey = OpenKey(self._hive, self._key, 0, KEY_WRITE) - SetValueEx(registryKey, self._subkey, 0, win_type, value) - result = True - except WindowsError as e: - logger.warning(f"Windows error caught while setting fast scan registry: {e}") - finally: - if registryKey is not None: - # Close key if it's been opened successfully - registryKey.Close() - return result - - def delete_entry(self) -> bool: - """Deletes the Windows registry entry for fast scan enabled""" - try: - if self.entry_exists(): - registryKey = OpenKey(self._hive, self._key, 0, KEY_ALL_ACCESS) - DeleteValue(registryKey, self._subkey) - registryKey.Close() - return True - except WindowsError: - logger.error(f"Could not delete registry entry; key: {self._key}, subkey: {self._subkey}") - finally: - return False - - def entry_exists(self) -> bool: - """Checks for existence of the setting in Windows registry.""" - try: - # Attempt to open and query key. If fails then the entry DNE - registryKey = OpenKey(self._hive, self._key) - QueryValueEx(registryKey, self._subkey) - registryKey.Close() - return True - - except WindowsError: - return False - - @staticmethod - def _format_data(value: bool or int or str or List[str]) -> Tuple[int or str or List[str], int]: - """Formats the type of the value provided. Returns the formatted value and the windows registry type (int).""" - if type(value) == str: - return value, REG_SZ - elif type(value) == bool: - value = "true" if value else "false" - return value, REG_SZ - elif type(value) == int or type(value) == float: - if type(value) == float: - logger.warning(f"Windows registry does not support floats. Truncating {value} to integer") - value = int(value) - return value, REG_DWORD - elif type(value) == list: - for single_value in value: - if type(single_value) != str: - # fmt:off - raise ValueError( - f"Windows Registry lists only support strings, got a {type(single_value)} in the list") - # fmt:on - return value, REG_MULTI_SZ - else: - raise ValueError(f"Windows registry expected types: int, str and [str], found {type(value)}") - - @staticmethod - def _convert_value(value_tuple: Tuple[Any, int]) -> Tuple[Any, PlatformSetting.DATA_TYPE]: - """Converts the Windows registry data and type (tuple) to a (standardized) data and PlatformSetting.DATA_TYPE""" - value, windows_type = value_tuple - if windows_type == REG_SZ: - # Convert from unicode to string - return value, PlatformSetting.DATA_TYPE.STR - elif windows_type == REG_MULTI_SZ: - # Convert from unicode to string - return [string for string in value], PlatformSetting.DATA_TYPE.STR_LIST - elif windows_type == REG_DWORD: - return value, PlatformSetting.DATA_TYPE.INT - else: - raise ValueError(f"Type flag not recognized: {windows_type}") - - @staticmethod - def _str_to_hive(hive_str: str) -> int: - """Converts a string to a Windows Registry Hive enum (int)""" - from winreg import HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_LOCAL_MACHINE, HKEY_USERS - - lower = hive_str.lower() - if lower == "hkey_current_user" or lower == "current_user": - return HKEY_CURRENT_USER - elif lower == "hkey_classes_root" or lower == "classes_root": - return HKEY_CLASSES_ROOT - elif lower == "hkey_current_config" or lower == "current_config": - return HKEY_CURRENT_CONFIG - elif lower == "hkey_local_machine" or lower == "local_machine": - return HKEY_LOCAL_MACHINE - elif lower == "hkey_users" or lower == "users": - return HKEY_USERS - else: - raise ValueError(f"Hive: {hive_str} not recognized") diff --git a/Tests/ly_shared/__init__.py b/Tests/ly_shared/__init__.py deleted file mode 100755 index 6ed3dc4bda..0000000000 --- a/Tests/ly_shared/__init__.py +++ /dev/null @@ -1,10 +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. -""" \ No newline at end of file diff --git a/Tests/ly_shared/asset_database_utils.py b/Tests/ly_shared/asset_database_utils.py deleted file mode 100755 index da8ad3651e..0000000000 --- a/Tests/ly_shared/asset_database_utils.py +++ /dev/null @@ -1,83 +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. -""" - -import sqlite3 -import os -from typing import List - -# Index for ProductID in Products table in DB -PRODUCT_ID_INDEX = 0 - - -def do_select(asset_db_path, cmd): - try: - connection = sqlite3.connect(asset_db_path) - # Get ProductID from database - db_rows = connection.execute(cmd) - return_result = db_rows.fetchall() - connection.close() - return return_result - except sqlite3.Error as sqlite_error: - print(f'select on db {asset_db_path} failed with exception {sqlite_error}') - return [] - - -def get_active_platforms_from_db(asset_db_path) -> List[str]: - """Returns a list of platforms that are active in the database, based on what jobs were run""" - platform_rows = do_select(asset_db_path, f"select distinct Platform from Jobs") - # Condense this into a single list of platforms. - platforms = [platform[0] for platform in platform_rows] - return platforms - - -# Convert a source product path into a db product path -# cache_platform/projectname/product_path -def get_db_product_path(workspace, source_path, cache_platform): - product_path = os.path.join(cache_platform, workspace.project, source_path) - product_path = product_path.replace('\\', '/') - return product_path - - -def get_product_id(asset_db_path, product_name) -> str: - # Get ProductID from database - product_id = list(do_select(asset_db_path, f"SELECT ProductID FROM Products where ProductName='{product_name}'")) - if len(product_id) == 0: - return product_id # return empty list - return product_id[0][PRODUCT_ID_INDEX] # Get product id from 'first' row - - -# Retrieve a product_id given a source_path assuming the source is copied into the cache with the same -# name or a product name without cache_platform or projectname prepended -def get_product_id_from_relative(workspace, source_path, asset_platform): - return get_product_id(workspace.paths.asset_db(), get_db_product_path(workspace, source_path, asset_platform)) - - -def get_missing_dependencies(asset_db_path, product_id) -> List[str]: - return list(do_select(asset_db_path, f"SELECT * FROM MissingProductDependencies where ProductPK={product_id}")) - - -def do_single_transaction(asset_db_path, cmd): - try: - connection = sqlite3.connect(asset_db_path) - cursor = connection.cursor() # SQL cursor used for issuing commands - cursor.execute(cmd) - connection.commit() # Save changes - connection.close() - except sqlite3.Error as sqlite_error: - print(f'transaction on db {asset_db_path} cmd {cmd} failed with exception {sqlite_error}') - - -def clear_missing_dependencies(asset_db_path, product_id) -> None: - do_single_transaction(asset_db_path, f"DELETE FROM MissingProductDependencies where ProductPK={product_id}") - - -def clear_all_missing_dependencies(asset_db_path) -> None: - do_single_transaction(asset_db_path, "DELETE FROM MissingProductDependencies;") diff --git a/Tests/ly_shared/asset_processor_utils.py b/Tests/ly_shared/asset_processor_utils.py deleted file mode 100755 index 13dc50ea83..0000000000 --- a/Tests/ly_shared/asset_processor_utils.py +++ /dev/null @@ -1,51 +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. -""" - - -import logging -import os -import subprocess - -from ly_test_tools.environment.process_utils import kill_processes_named as kill_processes_named -logger = logging.getLogger(__name__) - - -def start_asset_processor(bin_dir): - """ - Starts the AssetProcessor from the given bin directory. Raises a RuntimeError if the process fails. - :param bin_dir: The bin directory from which to launch the AssetProcessor executable. - :return: A subprocess.Popen object for the AssetProcessor process. - """ - os.chdir(bin_dir) - asset_processor = subprocess.Popen(['AssetProcessor.exe']) - return_code = asset_processor.poll() - - if return_code is not None and return_code != 0: - logger.error("Failed to start AssetProcessor") - raise RuntimeError("AssetProcessor exited with code {}".format(return_code)) - else: - logger.info("AssetProcessor is running") - return asset_processor - - -def kill_asset_processor(): - """ - Kill the AssetProcessor and all its related processes . - """ - - kill_processes_named('AssetProcessor_tmp', ignore_extensions=True) - kill_processes_named('AssetProcessor', ignore_extensions=True) - kill_processes_named('AssetProcessorBatch', ignore_extensions=True) - kill_processes_named('AssetBuilder', ignore_extensions=True) - kill_processes_named('rc', ignore_extensions=True) - - - diff --git a/Tests/ly_shared/file_utils.py b/Tests/ly_shared/file_utils.py deleted file mode 100755 index 2fc93d7538..0000000000 --- a/Tests/ly_shared/file_utils.py +++ /dev/null @@ -1,169 +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. -""" - -import os -import shutil -import logging -import stat - -import ly_test_tools.environment.file_system as file_system -import ly_test_tools.environment.waiter as waiter - -logger = logging.getLogger(__name__) - - -def clear_out_file(file_path): - """ - Clears out the specified config file to be empty. - :param file_path: The full path to the file. - """ - if os.path.exists(file_path): - file_system.unlock_file(file_path) - with open(file_path, 'w') as file_to_write: - file_to_write.write('') - else: - logger.debug(f'{file_path} not found while attempting to clear out file.') - - -def add_commands_to_config_file(config_file_dir, config_file_name, command_list): - """ - From the command list, appends each command to the specified config file. - :param config_file_dir: The directory the config file is contained in. - :param config_file_name: The config file name. - :param command_list: The commands to add to the file. - :return: - """ - config_file_path = os.path.join(config_file_dir, config_file_name) - os.chmod(config_file_path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC) - with open(config_file_path, 'w') as launch_config_file: - for command in command_list: - launch_config_file.write("{}\n".format(command)) - - -def gather_error_logs(workspace): - """ - Grabs all error logs (if there are any) and puts them into the specified logs path. - :param workspace: The AbstractWorkspaceManager object that contains all of the paths - """ - error_log_path = os.path.join(workspace.paths.project_log(), 'error.log') - error_dump_path = os.path.join(workspace.paths.project_log(), 'error.dmp') - if os.path.exists(error_dump_path): - workspace.artifact_manager.save_artifact(error_dump_path) - if os.path.exists(error_log_path): - workspace.artifact_manager.save_artifact(error_log_path) - - -def delete_screenshot_folder(workspace): - """ - Deletes screenshot folder from platform path - :param workspace: The AbstractWorkspaceManager object that contains all of the paths - """ - shutil.rmtree(workspace.paths.project_screenshots(), ignore_errors=True) - - -def move_file(src_dir, dest_dir, file_name, timeout=120): - """ - Attempts to move a file from the source directory to the destination directory. Raises an IOError if - the file is in use. - :param src_dir: Directory of the file to be moved. - :param dest_dir: Directory where the file will be moved to. - :param file_name: Name of the file to be moved. - :param timeout: Number of seconds to wait for the file to be released. - """ - file_path = os.path.join(src_dir, file_name) - if os.path.exists(file_path): - waiter.wait_for(lambda: move_file_check(src_dir, dest_dir, file_name), timeout=timeout, - exc=IOError('Cannot move file {} while in use'.format(file_path))) - - -def move_file_check(src_dir, dest_dir, file_name): - """ - Moves file and checks if the file has been moved from the source to the destination directory. - :param src_dir: Source directory of the file to be moved - :param dest_dir: Destination directory where the file should move to - :param file_name: The name of the file to be moved - :return: - """ - try: - shutil.move(os.path.join(src_dir, file_name), os.path.join(dest_dir, file_name)) - except OSError as e: - logger.info(e) - return False - - return True - - -def rename_file(file_path, dest_path, timeout=10): - # type: (str, str, int) -> None - """ - Renames a file by moving it. Waits for file to become available and raises and exception if timeout occurs. - :param file_path: absolute path to the source file - :param dest_path: absolute path to the new file - :param timeout: timeout to wait for function to complete - :return: None - """ - def _rename_file_check(): - try: - shutil.move(file_path, dest_path) - except OSError as e: - logger.debug(f'Attempted to rename file: {file_path} but an error occurred, retrying.' - f'\nError: {e}', - stackinfo=True) - return False - return True - - if os.path.exists(file_path): - waiter.wait_for(lambda: _rename_file_check(), timeout=timeout, - exc=OSError('Cannot rename file {} while in use'.format(file_path))) - - -def delete_level(workspace, level_dir, timeout=120): - """ - Attempts to delete an entire level folder from the project. - :param workspace: The workspace instance to delete the level from. - :param level_dir: The level folder to delete - """ - - if not level_dir: - logger.warning("level_dir is empty, nothing to delete.") - return - - full_level_dir = os.path.join(workspace.paths.project(), 'Levels', level_dir) - if not os.path.isdir(full_level_dir): - if os.path.exists(full_level_dir): - logger.error("level '{}' isn't a directory, it won't be deleted.".format(full_level_dir)) - else: - logger.info("level '{}' doesn't exist, nothing to delete.".format(full_level_dir)) - return - - waiter.wait_for(lambda: delete_check(full_level_dir), - timeout=timeout, - exc=IOError('Cannot delete directory {} while in use'.format(full_level_dir))) - -def delete_check(src_dir): - """ - Deletes directory and verifies that it's been deleted. - :param src_dir: The directory to delete - """ - try: - def handle_delete_error(action, path, exception_info): - logger.info("Error deleting '{}' ({}), changing permissions to writeable.".format(path, exception_info)) - os.chmod(path, stat.S_IWRITE) - # Try the passed-in action (delete) again - action(path) - - shutil.rmtree(src_dir, onerror=handle_delete_error) - except OSError as e: - logger.debug("Delete for '{}' failed: {}".format(src_dir, e)) - return False - - return not os.path.exists(src_dir) - diff --git a/Tests/ly_shared/hydra_editor_utils.py b/Tests/ly_shared/hydra_editor_utils.py deleted file mode 100755 index b75e680cf8..0000000000 --- a/Tests/ly_shared/hydra_editor_utils.py +++ /dev/null @@ -1,340 +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. -""" - -import azlmbr.bus as bus -import azlmbr.editor as editor -import azlmbr.entity as entity -import azlmbr.object - -from typing import List -from math import isclose -import collections.abc - - -def find_entity_by_name(entity_name): - """ - Gets an entity ID from the entity with the given entity_name - :param entity_name: String of entity name to search for - :return entity ID - """ - search_filter = entity.SearchFilter() - search_filter.names = [entity_name] - matching_entity_list = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter) - if matching_entity_list: - matching_entity = matching_entity_list[0] - if matching_entity.IsValid(): - print(f'{entity_name} entity found with ID {matching_entity.ToString()}') - return matching_entity - else: - return matching_entity_list - - -def get_component_type_id(component_name): - """ - Gets the component_type_id from a given component name - :param component_name: String of component name to search for - :return component type ID - """ - type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component_name], entity.EntityType().Game) - component_type_id = type_ids_list[0] - return component_type_id - - -def add_component(componentName, entityId): - """ - Given a component name, finds component TypeId, adds to given entity, and verifies successful add/active state. - :param componentName: String of component name to add. - :param entityId: Entity to add component to. - :return: Component object. - """ - typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [componentName], entity.EntityType().Game) - typeNamesList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeNames', typeIdsList) - componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', entityId, typeIdsList) - isActive = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', componentOutcome.GetValue()[0]) - hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entityId, typeIdsList[0]) - if componentOutcome.IsSuccess() and isActive: - print('{} component was added to entity'.format(typeNamesList[0])) - elif componentOutcome.IsSuccess() and not isActive: - print('{} component was added to entity, but the component is disabled'.format(typeNamesList[0])) - elif not componentOutcome.IsSuccess(): - print('Failed to add {} component to entity'.format(typeNamesList[0])) - if hasComponent: - print('Entity has a {} component'.format(typeNamesList[0])) - return componentOutcome.GetValue()[0] - - -def get_component_property_value(component, component_propertyPath): - """ - Given a component name and component property path, outputs the property's value. - :param component: Component object to act on. - :param componentPropertyPath: String of component property. (e.g. 'Settings|Visible') - :return: Value set in given componentPropertyPath - """ - componentPropertyObj = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentProperty', component, - component_propertyPath) - if componentPropertyObj.IsSuccess(): - componentProperty = componentPropertyObj.GetValue() - print(f'{component_propertyPath} set to {componentProperty}') - return componentProperty - else: - print(f'FAILURE: Could not get value from {component_propertyPath}') - return None - - -def get_property_tree(component): - """ - Given a configured component object, prints the property tree info from that component - :param component: Component object to act on. - """ - pteObj = editor.EditorComponentAPIBus(bus.Broadcast, 'BuildComponentPropertyTreeEditor', component) - pte = pteObj.GetValue() - print(pte.build_paths_list()) - return pte - - -def compare_values(first_object: object, second_object: object, name: str) -> bool: - # Quick case - can we just directly compare the two objects successfully? - if (first_object == second_object): - result = True - # No, so get a lot more specific - elif isinstance(first_object, collections.abc.Container): - # If they aren't both containers, they're different - if not isinstance(second_object, collections.abc.Container): - result = False - # If they have different lengths, they're different - elif len(first_object) != len (second_object): - result = False - # If they're different strings, they're containers but they failed the == check so - # we know they're different - elif isinstance(first_object, str): - result = False - else: - # It's a collection of values, so iterate through them all... - collection_idx = 0 - result = True - for val1, val2 in zip(first_object, second_object): - result = result and compare_values(val1, val2, f"{name} (index [{collection_idx}])") - collection_idx = collection_idx + 1 - - else: - # Do approximate comparisons for floats - if isinstance(first_object, float) and isclose(first_object, second_object, rel_tol=0.001): - result = True - # We currently don't have a generic way to compare PythonProxyObject contents, so return a - # false positive result for now. - elif isinstance(first_object, azlmbr.object.PythonProxyObject): - print(f"{name}: validation inconclusive, the two objects cannot be directly compared.") - result = True - else: - result = False - - if not result: - print(f"compare_values failed: {first_object} ({type(first_object)}) vs {second_object} ({type(second_object)})") - - print(f"{name}: {'SUCCESS' if result else 'FAILURE'}") - return result - - -class Entity: - """ - Entity class used to create entity objects - :param name: String for the name of the Entity - :param id: The ID of the entity - """ - - def __init__(self, name: str, id: object = entity.EntityId()): - self.name: str = name - self.id: object = id - self.components: List[object] = None - self.parent_id = None - self.parent_name = None - - def create_entity(self, entity_position, components, parent_id=entity.EntityId()): - self.id = editor.ToolsApplicationRequestBus( - bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId() - ) - if self.id.IsValid(): - print(f"{self.name} Entity successfully created") - editor.EditorEntityAPIBus(bus.Event, 'SetName', self.id, self.name) - self.components = [] - for component in components: - new_component = add_component(component, self.id) - self.components.append(new_component) - - def get_parent_info(self): - """ - Sets the value for parent_id and parent_name on the entity (self) - Prints the string for papertrail - :return: None - """ - self.parent_id = editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", self.id) - self.parent_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", self.parent_id) - print(f"The parent entity of {self.name} is {self.parent_name}") - - def set_test_parent_entity(self, parent_entity_obj): - editor.EditorEntityAPIBus(bus.Event, "SetParent", self.id, parent_entity_obj.id) - self.get_parent_info() - - def get_set_test(self, component_index: int, path: str, value: object, expected_result: object = None) -> bool: - """ - Used to set and validate changes in component values - :param component_index: Index location in the self.components list - :param path: asset path in the component - :param value: new value for the variable being changed in the component - :param expected_result: (optional) check the result against a specific expected value - """ - - if expected_result is None: - expected_result = value - - # Test Get/Set (get old value, set new value, check that new value was set correctly) - print(f"Entity {self.name} Path {path} Component Index {component_index} ") - - component = self.components[component_index] - old_value = get_component_property_value(component, path) - - if old_value is not None: - print(f"SUCCESS: Retrieved property Value for {self.name}") - else: - print(f"FAILURE: Failed to find value in {self.name} {path}") - return False - - if old_value == expected_result: - print((f"WARNING: get_set_test on {self.name} is setting the same value that already exists ({old_value})." - "The set results will be inconclusive.")) - - editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", component, path, value) - - new_value = get_component_property_value(self.components[component_index], path) - - if new_value is not None: - print(f"SUCCESS: Retrieved new property Value for {self.name}") - else: - print(f"FAILURE: Failed to find new value in {self.name}") - return False - - return compare_values(new_value, expected_result, f"{self.name} {path}") - - -def get_set_test(entity: object, component_index: int, path: str, value: object) -> bool: - """ - Used to set and validate changes in component values - :param component_index: Index location in the entity.components list - :param path: asset path in the component - :param value: new value for the variable being changed in the component - """ - return entity.get_set_test(component_index, path, value) - - -def get_set_property_test(ly_object: object, attribute_name: str, value: object, expected_result: object = None) -> bool: - """ - Used to set and validate BehaviorContext property changes in Lumberyard objects - :param ly_object: The lumberyard object to test - :param attribute_name: property (attribute) name in the BehaviorContext - :param value: new value for the variable being changed in the component - :param expected_result: (optional) check the result against a specific expected value other than the one set - """ - - if expected_result is None: - expected_result = value - - # Test Get/Set (get old value, set new value, check that new value was set correctly) - print(f"Attempting to set {ly_object.typename}.{attribute_name} = {value} (expected result is {expected_result})") - - if hasattr(ly_object, attribute_name): - print(f"SUCCESS: Located attribute {attribute_name} for {ly_object.typename}") - else: - print(f"FAILURE: Failed to find attribute {attribute_name} in {ly_object.typename}") - return False - - old_value = getattr(ly_object, attribute_name) - - if old_value is not None: - print(f"SUCCESS: Retrieved existing value {old_value} for {attribute_name} in {ly_object.typename}") - else: - print(f"FAILURE: Failed to retrieve value for {attribute_name} in {ly_object.typename}") - return False - - if old_value == expected_result: - print((f"WARNING: get_set_test on {attribute_name} is setting the same value that already exists ({old_value})." - "The 'set' result for the test will be inconclusive.")) - - setattr(ly_object, attribute_name, expected_result) - - new_value = getattr(ly_object, attribute_name) - - if new_value is not None: - print(f"SUCCESS: Retrieved new value {new_value} for {attribute_name} in {ly_object.typename}") - else: - print(f"FAILURE: Failed to retrieve value for {attribute_name} in {ly_object.typename}") - return False - - return compare_values(new_value, expected_result, f"{ly_object.typename}.{attribute_name}") -def has_components(entity_id: object, component_list: list) -> bool: - """ - Used to verify if a given entity has all the components of components_list. Returns True if all the - components are present, else False - :param entity_id: entity id of the entity - :param component_list: list of component names to be verified - """ - typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', component_list , entity.EntityType().Game) - for type_id in typeIdsList: - if not editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id, type_id): - return False - return True - -class PathNotFoundError(Exception): - def __init__(self, path): - self.path = path - - def __str__(self): - return f"Path \"{self.path}\" not found in Editor Settings" - -def get_editor_settings_path_list(): - """ - Get the list of Editor Settings paths - """ - paths = editor.EditorSettingsAPIBus(bus.Broadcast, 'BuildSettingsList') - return paths - -def get_editor_settings_by_path(path): - """ - Get the value of Editor Settings based on the path. - :param path: path to the Editor Settings to get the value - """ - if path not in get_editor_settings_path_list(): - raise PathNotFoundError(path) - outcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', path) - if outcome.isSuccess(): - return outcome.GetValue() - raise RuntimeError(f"GetValue for path '{path}' failed") - -def set_editor_settings_by_path(path, value, is_bool = False): - """ - Set the value of Editor Settings based on the path. - # NOTE: Some Editor Settings may need an Editor restart to apply. - # Ex: Enabling or disabling New Viewport Interaction Model - :param path: path to the Editor Settings to get the value - :param value: value to be set - :param is_bool: True for Boolean settings (enable/disable), False for other settings - """ - if path not in get_editor_settings_path_list(): - raise PathNotFoundError(path) - if is_bool and not isinstance(value, bool): - def ParseBoolValue(value): - if(value == "0"): - return False - return True - value = ParseBoolValue(value) - outcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', path, value) - if not outcome.isSuccess(): - raise RuntimeError(f"SetValue for path '{path}' failed") - print(f"Value for path '{path}' is set to {value}") diff --git a/Tests/ly_shared/hydra_lytt_test_utils.py b/Tests/ly_shared/hydra_lytt_test_utils.py deleted file mode 100755 index bbdcbc6cb6..0000000000 --- a/Tests/ly_shared/hydra_lytt_test_utils.py +++ /dev/null @@ -1,77 +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. -""" - -import logging -import os -import tempfile -import ly_test_tools.log.log_monitor -import ly_test_tools.environment.process_utils as process_utils -import ly_test_tools.environment.waiter as waiter - -logger = logging.getLogger(__name__) - - -def teardown_editor(editor): - """ - :param editor: Configured editor object - :return: - """ - process_utils.kill_processes_named('AssetProcessor.exe') - logger.debug('Ensuring Editor is stopped') - editor.ensure_stopped() - - -def launch_and_validate_results(request, test_directory, editor, editor_script, expected_lines, unexpected_lines=[], - halt_on_unexpected=False, auto_test_mode=True, run_python="--runpythontest", cfg_args=[], timeout=60, log_creation_max_wait=60): - """ - Creates a temporary config file for Hydra execution, runs the Editor with the specified script, and monitors for - expected log lines. - :param request: Special fixture providing information of the requesting test function. - :param test_directory: Path to test directory that editor_script lives in. - :param editor: Configured editor object to run test against. - :param editor_script: Name of script that will execute in the Editor. - :param expected_lines: Expected lines to search log for. - :param unexpected_lines: Unexpected lines to search log for. Defaults to none. - :param halt_on_unexpected: Halts test if unexpected lines are found. Defaults to False. - :param auto_test_mode: Defaults to True. Runs the test in auto_test_mode. - :param run_python: Defaults to "--runpythontest", other option is "--runpython". - :param cfg_args: Additional arguments for CFG, such as LevelName. - :param timeout: Length of time for test to run. Default is 60. - :param log_creation_max_wait: Length of time for waiting to find the log file. Default is 60. - """ - test_case = os.path.join(test_directory, editor_script) - request.addfinalizer(lambda: teardown_editor(editor)) - logger.debug("Running automated test: {}".format(editor_script)) - - editor.args.extend(["--skipWelcomeScreenDialog"]) - if auto_test_mode: editor.args.extend(["--autotest_mode"]) - editor.args.extend([run_python, test_case, "--runpythonargs", cfg_args]) - - with editor.start(): - - editorlog_file = os.path.join(editor.workspace.paths.project_log(), 'Editor.log') - log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=editor, log_file_path=editorlog_file, log_creation_max_wait_time=log_creation_max_wait) - log_monitor.monitor_log_for_lines(expected_lines=expected_lines, unexpected_lines=unexpected_lines, - halt_on_unexpected=halt_on_unexpected, timeout=timeout) - - -def remove_files(artifact_path, suffix): - """ - Removes files with the specified suffix from the specified path - :param artifact_path: Path to search for files - :param suffix: File extension to remove - """ - if not os.path.isdir(artifact_path): - return - - for file_name in os.listdir(artifact_path): - if file_name.endswith(suffix): - os.remove(os.path.join(artifact_path, file_name)) diff --git a/Tests/ly_shared/network_utils.py b/Tests/ly_shared/network_utils.py deleted file mode 100755 index 8b772303f1..0000000000 --- a/Tests/ly_shared/network_utils.py +++ /dev/null @@ -1,66 +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. -""" - -import logging -import psutil -import socket - - -logger = logging.getLogger(__name__) - - -def check_for_listening_port(port): - """ - Checks to see if the connection to the designated port was established. - :param port: Port to listen to. - :return: True if port is listening. - """ - port_listening = False - for conn in psutil.net_connections(): - if 'port={}'.format(port) in str(conn): - port_listening = True - return port_listening - - -def check_for_remote_listening_port(port, ip_addr='127.0.0.1'): - """ - Tries to connect to a port to see if port is listening. - :param port: Port being tested. - :param ip_addr: IP address of the host being connected to. - :return: True if connection to the port is established. - """ - port_listening = True - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect((ip_addr, port)) - except socket.error as err: - # Socket error: Connection refused, error code 10061 - if err.errno == 10061: - port_listening = False - finally: - sock.close() - return port_listening - - -def get_local_ip_address(): - """ - Finds the IP address for the primary ethernet adapter by opening a connection and grabbing its IP address. - :return: The IP address for the adapter used to make the connection. - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - try: - # Connecting to Google's public DNS so there is an open connection - # and then getting the address used for that connection - sock.connect(('8.8.8.8', 80)) - host_ip = sock.getsockname()[0] - finally: - sock.close() - return host_ip diff --git a/Tests/ly_shared/phase.py b/Tests/ly_shared/phase.py deleted file mode 100755 index 4bb827b3ad..0000000000 --- a/Tests/ly_shared/phase.py +++ /dev/null @@ -1,198 +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. - -This represents one "phase" of a test. The test runner communicates with the launcher by running phases and waiting -for results, however they appear. -""" - -import logging -import os -import time -import xml.etree.ElementTree - -import ly_test_tools.launchers.exceptions as exceptions -from ly_test_tools.launchers.platforms.base import Launcher - -_POLL_INTERVAL_SEC = 1 -_CRASH_TIMEOUT = 5 - -logger = logging.getLogger(__name__) - - -class Phase(object): - """ - A generic test phase for running the launcher. With the launcher running elsewhere (at a minimum in a different - process, possibly on a different device altogether) the following kind of phases might occur: - - - Wait for a specific file to show up / complete processing. - - Send commands to the launcher over the network and wait for response. - - Wait for multiple launchers to coordinate networking testing. - - Wait for a specific amount of time. - - Each phase can then compile the artifacts for the next phase. - """ - def __init__(self, timeout): - """ - :param timeout: Maximum time allocated for phase. - """ - self.timeout = timeout - - def _start(self, previous_phase=None): - """ - Start the phase. - - :return: None - """ - logger.debug("start: {}".format(self.__class__.__name__)) - - def _is_complete(self): - """ - Check if the phase is complete. This is the only required function. - - :return: None - """ - raise NotImplementedError - - def _compile_artifacts(self): - """ - Compile artifacts after a completed phase. - """ - logger.debug("compile_artifacts: {}".format(self.__class__.__name__)) - - def _update(self, elapsed_time): - """ - Update the test phase if necessary. - - :param elapsed_time: Time since the last update. - :return: None - """ - logger.debug("update: {}".format(self.__class__.__name__)) - - def _wait(self, launcher): - """ - Wait for the phase to complete. - - :return: None. - :raises: TimeoutError, CrashError - """ - dead_time = -1 - logger.debug("wait begin: {}".format(self.__class__.__name__)) - start = time.time() - - while not self._is_complete(): - - if time.time() - start > self.timeout: - message = "Timeout exceeded {}s in {}".format(self.timeout, self.__class__.__name__) - logger.error(message) - raise exceptions.TimeoutError(message) - elif not launcher.is_alive(): - # The final result may arrive after the app closes. - if dead_time == -1: - dead_time = time.time() - if time.time() - dead_time > _CRASH_TIMEOUT: - message = "Unexpected termination in {} after {:0.2f}s".format( - self.__class__.__name__, time.time() - start) - logger.error(message) - raise exceptions.CrashError(message) - - sleep_start = time.time() - time.sleep(_POLL_INTERVAL_SEC) - - self._update(time.time() - sleep_start) - - logger.debug("wait end: {}, duration: {:.2f}".format(self.__class__.__name__, time.time() - start)) - - -class FileExistsPhase(Phase): - """ - Test phase that completes when a specific file is created. - """ - def __init__(self, path, timeout=60, non_empty=False): - super(FileExistsPhase, self).__init__(timeout) - self.path = path - self.non_empty = non_empty - - def _is_complete(self): - if self.path is not None and os.path.exists(self.path): - if self.non_empty: - return os.path.getsize(self.path) > 0 - else: - return True - - return False - - -class XMLValidPhase(FileExistsPhase): - """ - Test phase that completes when a valid XML file is found. - """ - def __init__(self, path, timeout=60): - super(XMLValidPhase, self).__init__(path, timeout, non_empty=True) - self.path = path - self.xml = None - - def _is_complete(self): - if not super(XMLValidPhase, self)._is_complete(): - return False - - try: - self.xml = xml.etree.ElementTree.parse(self.path) - except xml.etree.ElementTree.ParseError: - return False - - return True - - -class TimePhase(Phase): - """ - Simple class to complete in a specified time. Can be used to test timeout. - """ - def __init__(self, timeout, complete_time): - super(TimePhase, self).__init__(timeout) - self.complete_time = complete_time - - def _start(self, previous_phase=None): - super(TimePhase, self)._start(previous_phase) - self.start_time = time.time() - - def _is_complete(self): - return time.time() - self.start_time > self.complete_time - - -class ElapsedTimePhase(Phase): - """ - Simple class to complete in a specified time using elapsed time. Can be used to test timeout and elapsed time. - """ - def __init__(self, timeout, complete_time): - super(ElapsedTimePhase, self).__init__(timeout) - self.complete_time = complete_time - self.total_time = None - - def _start(self, previous_phase=None): - super(ElapsedTimePhase, self)._start(previous_phase) - self.total_time = 0 - - def _update(self, elapsed_time): - super(ElapsedTimePhase, self)._update(elapsed_time) - self.total_time += elapsed_time - - def _is_complete(self): - return self.total_time > self.complete_time - - -class WaitForLauncherToQuit(Phase): - def __init__(self, launcher, timeout=60): - # type: (Launcher, int) -> None - super(WaitForLauncherToQuit, self).__init__(timeout) - self.launcher = launcher - - def _is_complete(self): - # type: () -> bool - return not self.launcher.is_alive() diff --git a/Tests/ly_shared/pyside_utils.py b/Tests/ly_shared/pyside_utils.py deleted file mode 100755 index 1dfdcdb5c0..0000000000 --- a/Tests/ly_shared/pyside_utils.py +++ /dev/null @@ -1,939 +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. -""" - -import azlmbr.qt -import azlmbr.qt_helpers -import asyncio -import re -from shiboken2 import wrapInstance, getCppPointer -from PySide2 import QtCore, QtWidgets, QtGui, QtTest -from PySide2.QtWidgets import QAction, QWidget -from PySide2.QtCore import Qt -from PySide2.QtTest import QTest -import azlmbr.legacy.general as general -import traceback -import threading -import types - - -qApp = QtWidgets.QApplication.instance() -# Monkey patch static method calls -QtWidgets.QApplication.activeModalWidget = qApp.activeModalWidget - - -class LmbrQtEventLoop(asyncio.AbstractEventLoop): - def __init__(self): - self.running = False - self.shutdown = threading.Event() - self.blocked_events = set() - self.finished_events = set() - self.queue = [] - self._wait_future = None - self._event_loop_nesting = 0 - - def get_debug(self): - return False - - def time(self): - return azlmbr.qt_helpers.time() - - def wait_for_condition(self, condition, action, on_timeout=None, timeout=1.0): - timeout = self.time() + timeout if timeout is not None else None - def callback(time): - # Run our action and remove us from the queue if our condition is satisfied - if condition(): - action() - return True - # Give up if timeout has elapsed - if time > timeout: - if on_timeout is not None: - on_timeout() - return True - return False - self.queue.append((callback)) - - def event_loop(self): - time = self.time() - def run_event(event): - if event in self.blocked_events or event in self.finished_events: - return False - self.blocked_events.add(event) - try: - if event(time): - self.finished_events.add(event) - except Exception: - traceback.print_exc() - self.finished_events.add(event) - finally: - self.blocked_events.remove(event) - - self._event_loop_nesting += 1 - try: - for event in self.queue: - run_event(event) - finally: - self._event_loop_nesting -= 1 - - # Clear out any finished events if the queue is safe to mutate - if self._event_loop_nesting == 0: - self.queue = [event for event in self.queue if event not in self.finished_events] - self.finished_events = set() - - if not self.running or self._wait_future is not None and self._wait_future.done(): - self.close() - - def run_until_shutdown(self): - # Run our event loop callback (via azlmbr.qt_helpers) by pumping the Qt event loop - # azlmbr.qt_helpers will attempt to ensure our event loop is always run, even when a - # new event loop is started and run from the main event loop - self.running = True - self.shutdown.clear() - azlmbr.qt_helpers.set_loop_callback(self.event_loop) - while not self.shutdown.is_set(): - qApp.processEvents(QtCore.QEventLoop.AllEvents, 0) - - def run_forever(self): - self._wait_future = None - self.run_until_shutdown() - - def run_until_complete(self, future): - # Wrap coroutines into Tasks (future-like analogs) - if isinstance(future, types.CoroutineType): - future = self.create_task(future) - self._wait_future = future - self.run_until_shutdown() - - def _timer_handle_cancelled(self, handle): - pass - - def is_running(self): - return self.running - - def is_closed(self): - return not azlmbr.qt_helpers.loop_is_running() - - def stop(self): - self.running = False - - def close(self): - self.running = False - self.shutdown.set() - azlmbr.qt_helpers.clear_loop_callback() - - def shutdown_asyncgens(self): - pass - - def call_exception_handler(self, context): - try: - raise context.get('exception', None) - except: - traceback.print_exc() - - def call_soon(self, callback, *args, **kw): - h = asyncio.Handle(callback, args, self) - def callback_wrapper(time): - if not h.cancelled(): - h._run() - return True - self.queue.append(callback_wrapper) - return h - - def call_later(self, delay, callback, *args, **kw): - if delay < 0: - raise Exception("Can't schedule in the past") - return self.call_at(self.time() + delay, callback, *args) - - def call_at(self, when, callback, *args, **kw): - h = asyncio.TimerHandle(when, callback, args, self) - h._scheduled = True - def callback_wrapper(time): - if time > when: - if not h.cancelled(): - h._run() - return True - return False - self.queue.append(callback_wrapper) - return h - - def create_task(self, coro): - return asyncio.Task(coro, loop=self) - - def create_future(self): - return asyncio.Future(loop=self) - - -class EventLoopTimeoutException(Exception): - pass - - -event_loop = LmbrQtEventLoop() -def wait_for_condition(condition, timeout=1.0): - """ - Asynchronously waits for `condition` to evaluate to True. - condition: A function with the signature def condition() -> bool - This condition will be evaluated until it evaluates to True or the timeout elapses - timeout: The time in seconds to wait - if 0, this will wait forever - Throws pyside_utils.EventLoopTimeoutException on timeout. - """ - future = event_loop.create_future() - def on_complete(): - future.set_result(True) - def on_timeout(): - future.set_exception(EventLoopTimeoutException()) - event_loop.wait_for_condition(condition, on_complete, on_timeout=on_timeout, timeout=timeout) - return future - - -async def wait_for(expression, timeout=1.0): - """ - Asynchronously waits for "expression" to evaluate to a non-None value, - then returns that value. - - expression: A function with the signature def expression() -> Generic[Any,None] - The result of expression will be returned as soon as it returns a non-None value. - timeout: The time in seconds to wait - if 0, this will wait forever - Throws pyside_utils.EventLoopTimeoutException on timeout. - """ - result = None - def condition(): - nonlocal result - result = expression() - return result is not None - await wait_for_condition(condition, timeout) - return result - - -def run_soon(fn): - """ - Runs a function on the event loop to enable asynchronous execution. - - fn: The function to run, should be a function that takes no arguments - Returns a future that will be popualted with the result of fn or the exception it threw. - """ - future = event_loop.create_future() - def coroutine(): - try: - fn() - future.set_result(True) - except Exception as e: - future.set_exception(e) - event_loop.call_soon(coroutine) - return future - - -def run_async(awaitable): - """ - Synchronously runs a coroutine or a future on the event loop. - This can be used in lieu of "await" in non-async functions. - - awaitable: The coroutine or future to await. - Returns the result of operation specified. - """ - if isinstance(awaitable, types.CoroutineType): - awaitable = event_loop.create_task(awaitable) - event_loop.run_until_complete(awaitable) - return awaitable.result() - - -def wrap_async(fn): - """ - This decorator enables an async function's execution from a synchronous one. - - For example: - @pyside_utils.wrap_async - async def foo(): - result = await long_operation() - return result - - def non_async_fn(): - x = foo() # this will return the correct result by executing the event loop - - fn: The function to wrap - Returns the decorated function. - """ - def wrapper(*args, **kw): - result = fn(*args, **kw) - return run_async(result) - return wrapper - - -def get_editor_main_window(): - """ - Fetches the main Editor instance of QMainWindow for use with PySide tests - :return Instance of QMainWindow for the Editor - """ - params = azlmbr.qt.QtForPythonRequestBus(azlmbr.bus.Broadcast, "GetQtBootstrapParameters") - editor_id = QtWidgets.QWidget.find(params.mainWindowId) - main_window = wrapInstance(int(getCppPointer(editor_id)[0]), QtWidgets.QMainWindow) - return main_window - - -def get_action_for_menu_path(editor_window: QtWidgets.QMainWindow, main_menu_item: str, *menu_item_path: str): - """ - main_menu_item: Main menu item among the MenuBar actions. Ex: "File" - menu_item_path: Path to any nested menu item. Ex: "Viewport", "Goto Coordinates" - returns: QAction object for the corresponding path. - """ - # Check if path is valid - menu_bar = editor_window.menuBar() - menu_bar_actions = [index.iconText() for index in menu_bar.actions()] - - # Verify if the given Menu exists in the Menubar - if main_menu_item not in menu_bar_actions: - print(f"QAction not found for main menu item '{main_menu_item}'") - return None - curr_action = menu_bar.actions()[menu_bar_actions.index(main_menu_item)] - curr_menu = curr_action.menu() - for index, element in enumerate(menu_item_path): - curr_menu_actions = [index.iconText() for index in curr_menu.actions()] - if element not in curr_menu_actions: - print(f"QAction not found for menu item '{element}'") - return None - if index == len(menu_item_path) - 1: - return curr_menu.actions()[curr_menu_actions.index(element)] - curr_action = curr_menu.actions()[curr_menu_actions.index(element)] - curr_menu = curr_action.menu() - return None - - -def _pattern_to_dict(pattern, **kw): - """ - Helper function, turns a pattern match parameter into a normalized dictionary - """ - - def is_string_or_regex(x): - return isinstance(x, str) or isinstance(x, re.Pattern) - - # If it's None, just make an empty dict - if pattern is None: - pattern = {} - # If our pattern is a string or regex, turn it into a text match - elif is_string_or_regex(pattern): - pattern = dict(text=pattern) - # If our pattern is an (int, int) tuple, turn it into a row/column match - elif isinstance(pattern, tuple) and isinstance(pattern[0], int) and isinstance(pattern[1], int): - pattern = dict(row=pattern[0], column=pattern[1]) - # If our pattern is a QObject type, turn it into a type match - elif isinstance(pattern, type(QtCore.QObject)): - pattern = dict(type=pattern) - # Otherwise assume it's a dict and make a copy - else: - pattern = dict(pattern) - - # Merge with any kw arguments - for key, value in kw.items(): - pattern[key] = value - return pattern - - -def _match_pattern(obj, pattern): - """ - Helper function, determines whether obj matches the pattern specified by pattern. - - It is required that pattern is normalized into a dict before calling this. - """ - - def compare(value1, value2): - # Do a regex search if it's a regex, otherwise do a normal compare - if isinstance(value2, re.Pattern): - return re.search(value2, value1) - return value1 == value2 - - item_roles = Qt.ItemDataRole.values.values() - for key, value in pattern.items(): - if key == "type": # Class type - if not isinstance(obj, value): - return False - elif key == "text": # Default 'text' path, depends on type - text_values = [] - - def get_from_attrs(*args): - for attr in args: - try: - text_values.append(getattr(obj, attr)()) - except Exception: - pass - - # Use any of the following fields for default matching, if they're defined - get_from_attrs("text", "objectName", "windowTitle") - # Additionally, use the DisplayRole for QModelIndexes - if isinstance(obj, QtCore.QModelIndex): - text_values.append(obj.data(Qt.DisplayRole)) - - if not any(compare(text, value) for text in text_values): - return False - elif key in item_roles: # QAbstractItemModel display role - if not isinstance(obj, QtCore.QModelIndex): - raise RuntimeError(f"Attempted to match data role on unsupported object {obj}") - if not compare(obj.data(key), value): - return False - elif hasattr(obj, key): - # Look up our key on the object itself - objectValue = getattr(obj, key) - # Invoke it if it's a getter - if callable(objectValue): - objectValue = objectValue() - if not compare(objectValue, value): - return False - else: - return False - - return True - - -def get_child_indexes(model, parent_index=QtCore.QModelIndex()): - indexes = [parent_index] - while len(indexes) > 0: - parent_index = indexes.pop(0) - for row in range(model.rowCount(parent_index)): - # FIXME - # PySide appears to have a bug where-in it thinks columnCount is private - # Bail gracefully for now, we can add a C++ wrapper to work around if needed - try: - column_count = model.columnCount(parent_index) - except Exception: - column_count = 1 - for col in range(column_count): - cur_index = model.index(row, col, parent_index) - yield cur_index - - -def _get_children(obj): - """ - Helper function. Get the direct descendants from a given PySide object. - This includes all: QObject children, QActions owned by the object, and QModelIndexes if applicable - """ - if isinstance(obj, QtCore.QObject): - yield from obj.children() - if isinstance(obj, QtWidgets.QWidget): - yield from obj.actions() - if isinstance(obj, (QtWidgets.QAbstractItemView, QtCore.QModelIndex)): - model = obj.model() - if model is None: - return - - # For a QAbstractItemView (e.g. QTreeView, QListView), the parent index - # will be an invalid QModelIndex(), which will use find all indexes on the root. - # For a QModelIndex, we use the actual QModelIndex as the parent_index so that - # it will find any child indexes under it - parent_index = QtCore.QModelIndex() - if isinstance(obj, QtCore.QModelIndex): - parent_index = obj - - yield from get_child_indexes(model, parent_index) - - -def _get_parents_to_search(obj_entry_or_list): - """ - Helper function, turns obj_entry_or_list into a list of parents to search - - If obj_entry_or_list is None, returns all visible top level widgets - If obj_entry_or_list is iterable, return it as a list - Otherwise, return a list containing obj_entry_or_list - """ - if obj_entry_or_list is None: - return [widget for widget in QtWidgets.QApplication.topLevelWidgets() if widget.isVisible()] - try: - return list(obj_entry_or_list) - except TypeError: - return [obj_entry_or_list] - - -def find_children_by_pattern(obj=None, pattern=None, recursive=True, **kw): - """ - Finds the children of an object that match a given pattern. - See find_child_by_pattern for more information on usage. - """ - pattern = _pattern_to_dict(pattern, **kw) - parents_to_search = _get_parents_to_search(obj) - - while len(parents_to_search) > 0: - parent = parents_to_search.pop(0) - for child in _get_children(parent): - if _match_pattern(child, pattern): - yield child - if recursive: - parents_to_search.append(child) - - -def find_child_by_pattern(obj=None, pattern=None, recursive=True, **kw): - """ - Finds the child of an object that matches a given pattern. - A "child" in this context is not necessarily a QObject child. - QActions are also considered children, as are the QModelIndex children of QAbstractItemViews. - obj: The object to search - should be either a QObject or a QModelIndex, or a list of them - If None this will search all top level windows. - pattern: The pattern to match, the first child that matches all of the criteria specified will - be returned. This is a dictionary with any combination of the following: - - - "text": generic text to match, will search object names for QObjects, display role text - for QModelIndexes, or action text() for QActions - - "type": a class type, e.g. QtWidgets.QMenu, a child will only match if it's of this type - - "row" / "column": integer row and column indices of a QModelIndex - - "type": type class (e.g. PySide.QtWidgets.QComboBox) that the object must inherit from - - A Qt.ItemDataRole: matches for QModelIndexes with data of a given value - - Any other fields will fall back on being looked up on the object itself by name, e.g. - {"windowTitle": "Foo"} would match a windowTitle named "Foo" - - Any instances where a field is specified as text can also be specified as a regular expression: - find_child_by_pattern(obj, {text: re.compile("Foo_.*")}) would find a child with text starting - with "Foo_" - - For convenience, these parameter types may also be specified as keyword arguments: - find_child_by_pattern(obj, text="foo", type=QtWidgets.QAction) - is equivalent to - find_child_by_pattern(obj, {"text": "foo", "type": QtWidgets.QAction}) - - If pattern is specified as a string, it will turn into a pattern matching "text": - find_child_by_pattern(obj, "foo") - is equivalent to - find_child_by_pattern(obj, {"text": "foo"}) - - If a pattern is specified as an (int, int) tuple, it will turn into a row/column match: - find_child_by_pattern(obj, (0, 2)) - is equivalent to - find_child_by_pattern(obj, {"row": 0, "column": 2}) - - If a pattern is specified as a type, like PySide.QtWidgets.QLabel, it will turn into a type match: - find_child_by_pattern(obj, PySide.QtWidgets.QLabel) - is equivalent to - find_child_by_pattern(obj, {"type": PySide.QtWidgets.QLabel}) - """ - # Return the first match result, if found - for match in find_children_by_pattern(obj, pattern=pattern, recursive=recursive, **kw): - return match - return None - - -def find_child_by_hierarchy(parent, *patterns): - """ - Searches for a hierarchy of children descending from parent. - parent: The Qt object (or list of Qt obejcts) to search within - If none, this will search all top level windows. - patterns: A list of patterns to match to find a hierarchy of descendants. - These patterns will be tested in order. - - For example, to look for the QComboBox in a hierarchy like the following: - QWidget (window) - -QTabWidget - -QWidget named "m_exampleTab" - -QComboBox - One might invoke: - find_child_by_hierarchy(window, QtWidgets.QTabWidget, "m_exampleTab", QtWidgets.QComboBox) - - Alternatively, "..." may be specified in place of a parent, where the hierarchy will match any - ancestors along the path, so the above might be shortened to: - find_child_by_hierarchy(window, ..., "m_exampleTab", QtWidgets.QComboBox) - """ - search_recursively = False - current_objects = _get_parents_to_search(parent) - for pattern in patterns: - # If it's an ellipsis, do the next search recursively as we're looking for any number of intermediate ancestors - if pattern is ...: - search_recursively = True - continue - - candidates = [] - for parent_candidate in current_objects: - candidates += find_children_by_pattern(parent_candidate, pattern=pattern, recursive=search_recursively) - if len(candidates) == 0: - return None - current_objects = candidates - - search_recursively = False - return current_objects[0] - -async def wait_for_child_by_hierarchy(parent, *patterns, timeout=1.0): - """ - Searches for a hierarchy of children descending from parent until timeout occurs. - Returns a future that will result in either the found child or an EventLoopTimeoutException. - - See find_child_by_hierarchy for usage information. - """ - match = None - def condition(): - nonlocal match - match = find_child_by_hierarchy(parent, *patterns) - return match is not None - await wait_for_condition(condition, timeout) - return match - - -async def wait_for_child_by_pattern(obj=None, pattern=None, recursive=True, timeout=1.0, **kw): - """ - Finds the child of an object that matches a given pattern. - Returns a future that will result in either the found child or an EventLoopTimeoutException. - - See find_child_by_hierarchy for usage information. - """ - match = None - def condition(): - nonlocal match - match = find_child_by_pattern(obj, pattern, recursive, **kw) - return match is not None - await wait_for_condition(condition, timeout) - return match - - -def find_child_by_property(obj, obj_type, property_name, property_value, reg_exp_search=False): - """ - Finds the child of an object which has the property name matching the property value - of type obj_type - obj: The property value is searched through obj children - obj_type: Type of object to be matched - property_name: Property of the child which should be verified for the required value. - property_value: Property value that needs to be matched - reg_exp_search: If True searches for the property_value based on re search. Defaults to False. - """ - for child in obj.children(): - if reg_exp_search and re.search(property_value, getattr(child, property_name)()): - return child - if not reg_exp_search and isinstance(child, obj_type) and getattr(child, property_name)() == property_value: - return child - return None - -def get_item_view_index(item_view, row, column=0, parent=QtCore.QModelIndex()): - """ - Retrieve the index for a specified row/column, with optional parent - This is necessary when needing to reference into nested hierarchies in a QTreeView - item_view: The QAbstractItemView instance - row: The requested row index - column: The requested column index (defaults to 0 in case of single column) - parent: Parent index (defaults to invalid) - """ - item_model = item_view.model() - model_index = item_model.index(row, column, parent) - return model_index - - -def get_item_view_index_rect(item_view, index): - """ - Gets the QRect for a given index in a QAbstractItemView (e.g. QTreeView, QTableView, QListView). - This is helpful because for sending mouse events to a QAbstractItemView, you have to send them to - the viewport() widget of the QAbstractItemView. - item_view: The QAbstractItemView instance - index: A QModelIndex for the item index - """ - return item_view.visualRect(index) - - -def item_view_index_mouse_click(item_view, index, button=QtCore.Qt.LeftButton, modifier=QtCore.Qt.NoModifier): - """ - Helper method version of QTest.mouseClick for injecting mouse clicks on a QAbstractItemView - item_view: The QAbstractItemView instance - index: A QModelIndex for the item index to be clicked - """ - item_index_rect = get_item_view_index_rect(item_view, index) - item_index_center = item_index_rect.center() - - # For QAbstractItemView widgets, the events need to be forwarded to the actual viewport() widget - QTest.mouseClick(item_view.viewport(), button, modifier, item_index_center) - - -def item_view_mouse_click(item_view, row, column=0, button=QtCore.Qt.LeftButton, modifier=QtCore.Qt.NoModifier): - """ - Helper method version of 'item_view_index_mouse_click' using a row, column instead of a QModelIndex - item_view: The QAbstractItemView instance - row: The requested row index - column: The requested column index (defaults to 0 in case of single column) - """ - index = get_item_view_index(item_view, row, column) - item_view_index_mouse_click(item_view, index, button, modifier) - - -async def wait_for_action_in_menu(menu, pattern, timeout=1.0): - """ - Finds a QAction inside a menu, based on the specified pattern. - - menu: The QMenu to search - pattern: The action text or pattern to match (see find_child_by_pattern) - If pattern specifies a QWidget, this will search for the associated QWidgetAction - """ - action = await wait_for_child_by_pattern(menu, pattern, timeout=timeout) - if action is None: - raise TimeoutError(f"Failed to find context menu action for {pattern}") - - # If we've found a valid QAction, we're good to go - if hasattr(action, 'trigger'): - return action - - # If pattern matches a widget and not a QAction, look for an associated QWidgetAction - widget_actions = find_children_by_pattern(menu, type=QtWidgets.QWidgetAction) - underlying_widget_action = None - for widget_action in widget_actions: - widgets_to_check = [widget_action.defaultWidget()] + widget_action.createdWidgets() - for check_widget in widgets_to_check: - if action in _get_children(check_widget): - underlying_widget_action = widget_action - break - if underlying_widget_action is not None: - action = underlying_widget_action - break - - if not hasattr(action, 'trigger'): - raise RuntimeError(f"Failed to find action associated with widget {action}") - return action - - -def queue_hide_event(widget): - """ - Explicitly post a hide event for the next frame, this can be used to ensure modal dialogs exit correctly. - - widget: The widget to hide - """ - qApp.postEvent(widget, QtGui.QHideEvent()) - - -async def wait_for_destroyed(obj, timeout=1.0): - """ - Waits for a QObject (including a widget) to be fully destroyed - - This can be used to wait for a modal dialog to shut down properly - - obj: The object to wait on destruction - timeout: The time, in seconds to wait. 0 for an indefinite wait. - """ - was_destroyed = False - def on_destroyed(): - nonlocal was_destroyed - was_destroyed = True - obj.destroyed.connect(on_destroyed) - return await wait_for_condition(lambda: was_destroyed, timeout=timeout) - - -async def close_modal(modal_widget, timeout=1.0): - """ - Closes a modal dialog and waits for it to be cleaned up. - - This attempts to ensure the modal event loop gets properly exited. - - modal_widget: The widget to close - timeout: The time, in seconds, to wait. 0 for an indefinite wait. - """ - queue_hide_event(modal_widget) - return await wait_for_destroyed(modal_widget, timeout=timeout) - - -def trigger_context_menu_entry(widget, pattern, pos=None, index=None): - """ - Trigger a context menu event on a widget and activate an entry - widget: The widget to trigger the event on - pattern: The action text or pattern to match (see find_child_by_pattern) - pos: Optional, the QPoint to set as the event origin - index: Optional, the QModelIndex to click in widget - widget must be a QAbstractItemView - """ - async def async_wrapper(): - menu = await open_context_menu(widget, pos=pos, index=index) - action = await wait_for_action_in_menu(menu, pattern) - action.trigger() - queue_hide_event(menu) - - result = async_wrapper() - # If we have an event loop, go ahead and just return the coroutine - # Otherwise, do a synchronous wait - if event_loop.is_running(): - return result - else: - return run_async(result) - - -async def open_context_menu(widget, pos=None, index=None, timeout=1.0): - """ - Trigger a context menu event on a widget - widget: The widget to trigger the event on - pos: Optional, the QPoint to set as the event origin - index: Optional, the QModelIndex to click in widget - widget must be a QAbstractItemView - - Returns the menu that was created. - """ - if index is not None: - if pos is not None: - raise RuntimeError("Error: 'index' and 'pos' are mutually exclusive") - pos = widget.visualRect(index).center() - parent = widget - widget = widget.viewport() - pos = widget.mapFrom(parent, pos) - if pos is None: - pos = widget.rect().center() - - # Post both a mouse event and a context menu to let the widget handle whichever is appropriate - qApp.postEvent(widget, QtGui.QContextMenuEvent(QtGui.QContextMenuEvent.Mouse, pos)) - QtTest.QTest.mouseClick(widget, Qt.RightButton, Qt.NoModifier, pos) - - menu = None - # Wait for a menu popup - def menu_has_focus(): - nonlocal menu - for fw in [qApp.activePopupWidget(), qApp.activeModalWidget(), qApp.focusWidget(), qApp.activeWindow()]: - if fw and isinstance(fw, QtWidgets.QMenu) and fw.isVisible(): - menu = fw - return True - return False - await wait_for_condition(menu_has_focus, timeout) - return menu - - -def move_mouse(widget, position): - """ - Helper method to move the mouse to a specified position on a widget - widget: The widget to trigger the event on - position: The QPoint (local to widget) to move the mouse to - """ - # For some reason, Qt wouldn't register the mouse movement correctly unless both of these ways are invoked. - # The QTest.mouseMove seems to update the global cursor position, but doesn't always result in the MouseMove event being - # triggered, which prevents drag/drop being able to be simulated. - # Similarly, if only the MouseMove event is sent by itself to the core application, the global cursor position wasn't - # updated properly, so drag/drop logic that depends on grabbing the globalPos didn't work. - QtTest.QTest.mouseMove(widget, position) - event = QtGui.QMouseEvent(QtCore.QEvent.MouseMove, position, widget.mapToGlobal(position), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier) - QtCore.QCoreApplication.sendEvent(widget, event) - - -def drag_and_drop(source, target, source_point = QtCore.QPoint(), target_point = QtCore.QPoint()): - """ - Simulate a drag/drop event from a source object to a specified target - This has special case handling if the source is a QDockWidget (for docking) vs normal drag/drop - source: The source object to initiate the drag from - This is either a QWidget, or a tuple of (QAbstractItemView, QModelIndex) for dragging an item view item - target: The target object to drop on after dragging - This is either a QWidget, or a tuple of (QAbstractItemView, QModelIndex) for dropping on an item view item - source_point: Optional, The QPoint to initiate the drag from. If none is specified, the center of the source will be used. - target_point: Optional, The QPoint to drop on. If none is specified, the center of the target will be used. - """ - # Flag if this drag/drop is for docking, which has some special cases - docking = False - - # If the source is a tuple of (QAbstractItemView, QModelIndex), we need to use the - # viewport() as the source, and find the location of the index - if isinstance(source, tuple) and len(source) == 2: - source_item_view = source[0] - source_widget = source_item_view.viewport() - source_model_index = source[1] - source_rect = source_item_view.visualRect(source_model_index) - else: - # There are some special case actions if we are doing this drag for docking, - # so figure this out by checking if the source is a QDockWidget - if isinstance(source, QtWidgets.QDockWidget): - docking = True - - source_widget = source - source_rect = source.rect() - - # If the target is a tuple of (QAbstractItemView, QModelIndex), we need to use the - # viewport() as the target, and find the location of the index - if isinstance(target, tuple) and len(target) == 2: - target_item_view = target[0] - target_widget = target_item_view.viewport() - target_model_index = target[1] - target_rect = target_item_view.visualRect(target_model_index) - else: - # If we are doing a drag for docking, we actually want all the mouse events - # to still be directed through the source widget - if docking: - target_widget = source_widget - else: - target_widget = target - target_rect = target.rect() - - # If no source_point is specified, we need to find the center point of - # the source widget - if source_point.isNull(): - # If we are dragging for docking, initiate the drag from the center of the - # dock widget title bar - if docking: - title_bar_widget = source.titleBarWidget() - if title_bar_widget: - source_point = title_bar_widget.geometry().center() - else: - raise RuntimeError("No titleBarWidget found for QDockWidget") - # Otherwise, can just find the center of the rect - else: - source_point = source_rect.center() - - # If no target_point was specified, we need to find the center point of the target widget - if target_point.isNull(): - target_point = target_rect.center() - - # If we are dragging for docking and we aren't dragging within the same source/target, - # the mouse movements need to be directed to the source_widget, so we need to use the - # difference in global positions of our source and target widgets to adjust the target_point - # to be relative to the source - if docking and source != target: - source_top_left = source.mapToGlobal(QtCore.QPoint(0, 0)) - target_top_left = target.mapToGlobal(QtCore.QPoint(0, 0)) - offset = target_top_left - source_top_left - target_point += offset - - # Move the mouse to the source spot where we will start the drag - move_mouse(source_widget, source_point) - - # Press the left-mouse button to begin the drag - QtTest.QTest.mousePress(source_widget, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier, source_point) - - # If we are dragging for docking, we first need to drag the mouse past the minimum distance to - # trigger the docking system properly - if docking: - drag_distance = QtWidgets.QApplication.startDragDistance() + 1 - docking_trigger_point = source_point + QtCore.QPoint(drag_distance, drag_distance) - move_mouse(source_widget, docking_trigger_point) - - # Drag the mouse to the target widget over the desired point - move_mouse(target_widget, target_point) - - # Release the left-mouse button to complete the drop. - # If we are docking, we need to delay the actual mouse button release because the docking system has - # a delay before the drop zone becomes active after it has been hovered, which can be found here: - # FancyDockingDropZoneConstants::dockingTargetDelayMS = 110 ms - # So we need to delay greater than dockingTargetDelayMS after the final mouse move - # over the intended target. - delay = -1 - if docking: - delay = 200 - QtTest.QTest.mouseRelease(target_widget, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier, target_point, delay) - - # Some drag/drop events have extra processing on the following event tick, so let those processEvents - # first before we complete the drag/drop operation - QtWidgets.QApplication.processEvents() - - -def trigger_action_async(action): - """ - Convenience function. Triggers an action asynchronously. - This can be used if calling action.trigger might block (e.g. if it opens a modal dialog) - - action: The action to trigger - """ - return run_soon(lambda: action.trigger()) - - -def click_button_async(button): - """ - Convenience function. Clicks a button asynchronously. - This can be used if calling button.click might block (e.g. if it opens a modal dialog) - - button: The button to click - """ - return run_soon(lambda: button.click()) - - -async def wait_for_modal_widget(timeout=1.0): - """ - Waits for an active modal widget and returns it. - """ - return await wait_for(lambda: qApp.activeModalWidget(), timeout=timeout) - -async def wait_for_popup_widget(timeout=1.0): - """ - Waits for an active popup widget and returns it. - """ - return await wait_for(lambda: qApp.activePopupWidget(), timeout=timeout) \ No newline at end of file diff --git a/Tests/ly_shared/s3_utils.py b/Tests/ly_shared/s3_utils.py deleted file mode 100755 index 5c516094f3..0000000000 --- a/Tests/ly_shared/s3_utils.py +++ /dev/null @@ -1,131 +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. -""" -import pytest -pytest.importorskip("boto3") -import boto3 -import botocore.exceptions -import logging -import os - -import ly_test_tools.environment.file_system as file_system - -logger = logging.getLogger(__name__) - - -class KeyExistsError(Exception): - pass - - -class KeyDoesNotExistError(Exception): - pass - - -class S3Utils(object): - """ - Stores a boto3 S3 client to use for AWS S3 functionalities. - """ - DEFAULT_REGION = 'us-west-2' - - def __init__(self, boto3_session=None): - # type: (boto3.Session) -> None - """ - The boto3 session can be set during init, or a default one will be created. - :param boto3_session: A boto3 session - """ - if boto3_session: - self._session = boto3_session - else: - logger.info("No session provided, using default profile for s3 resource") - self._session = boto3.session.Session() - self._s3_resource = self._session.resource('s3') - - def upload_to_bucket(self, bucket_name, file_path, overwrite=False): - """ - Uploads a given file to the given S3 bucket. - :param bucket_name: Name of the S3 bucket where the file should be uploaded. - :param file_path: Path to the target file. - :param overwrite: Overwrite the key if it exists. - """ - if not self.bucket_exists_in_s3(bucket_name): - self._s3_resource.create_bucket(Bucket=bucket_name) - - s3_bucket = self._s3_resource.Bucket(bucket_name) - - file_key = os.path.basename(file_path) - if not overwrite and self.key_exists_in_bucket(bucket_name, file_key): - raise KeyExistsError("Key '{}' already exists in S3 bucket {}".format(file_key, bucket_name)) - - s3_bucket.upload_file(file_path, file_key) - logger.info("Uploading {} to S3 bucket {}".format(file_key, bucket_name)) - - def download_from_bucket(self, bucket_name, file_key, destination_dir, file_name=None): - """ - Download the given key from the given S3 bucket to the given destination. Logs an error if there is not enough \ - space available for the download. - :param bucket_name: Name of the S3 bucket containing the desired file. - :param file_key: Name of the file stored in S3. - :param destination_dir: Directory where the file should be downloaded to. - :param file_name: The name of the file you want to save it as. Defaults to the file_key. - """ - self.bucket_exists_in_s3(bucket_name) - - if not self.key_exists_in_bucket(bucket_name, file_key): - raise KeyDoesNotExistError("Key '{}' does not exist in S3 bucket {}".format(file_key, bucket_name)) - - obj_summary = self._s3_resource.ObjectSummary(bucket_name, file_key) - required_space = obj_summary.size - - file_system.check_free_space(destination_dir, required_space, "Insufficient space available for download:") - - if not os.path.exists(destination_dir): - os.makedirs(destination_dir) - - if file_name is None: - file_name = file_key - destination_path = os.path.join(destination_dir, file_name) - self._s3_resource.Object(bucket_name, file_key).download_file(destination_path) - logger.info("Downloading {} to {}".format(file_key, destination_path)) - - def bucket_exists_in_s3(self, bucket_name): - """ - Verifies that the S3 bucket exists. - :param bucket_name: Name of the S3 bucket that may or may not exist. - :return: True if the bucket exists. False otherwise. - """ - bucket_exists = True - - try: - self._s3_resource.meta.client.head_bucket(Bucket=bucket_name) - except botocore.exceptions.ClientError as err: - if err.response['Error']['Code'] == '404': - bucket_exists = False - - return bucket_exists - - def key_exists_in_bucket(self, bucket_name, file_key): - """ - Verifies that the given key does not already exist in the given S3 bucket. - :param bucket_name: Name of the S3 bucket that may or may not contain the file key. - :param file_key: Name of the file key in question. - :return: True if the key exists. False otherwise. - """ - key_exists = True - obj_summary = self._s3_resource.ObjectSummary(bucket_name, file_key) - - # Attempting to access any member of ObjectSummary for a nonexistent key will throw an exception - # There is no built-in way to check key existence otherwise - try: - obj_summary.size - except botocore.exceptions.ClientError as err: - if err.response['Error']['Code'] == '404': - key_exists = False - - return key_exists diff --git a/Tests/ly_shared/screenshot_utils.py b/Tests/ly_shared/screenshot_utils.py deleted file mode 100755 index c000cd8258..0000000000 --- a/Tests/ly_shared/screenshot_utils.py +++ /dev/null @@ -1,195 +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. -""" - -import os -import string - -from .file_utils import move_file -from . import phase as phase -from ly_test_tools.environment.waiter import wait_for -from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots - -from ly_remote_console.remote_console_commands import capture_screenshot_command as capture_screenshot_command -from ly_remote_console.remote_console_commands import send_command_and_expect_response as send_command_and_expect_response - - -def get_next_screenshot_at_path(screenshot_path, prefix='screenshot', num_digits=4): - """ - :param screenshot_path: Root folder where the screenshots are being generated by the Launcher pr Editor. - :param prefix: Generated screenshot files are named sequentially using the prefix. - e.g: screenshot0000.jpg, screenshot0001.jpg and so on. - :param num_digits: How many digits are used for file name formation. - :return: A string with the file name (relative to screenshot_path). - """ - max_counter = 10**num_digits - counter = 0 - while counter < max_counter: - numberstr = "{}".format(counter) - formattednumber = numberstr.zfill(num_digits) - filename = "{}{}.jpg".format(prefix, formattednumber) - filepath = os.path.join(screenshot_path, filename) - if not os.path.exists(filepath): - #This filename is available. - return filename - raise AssertionError("All possible screenshot names at directory {} are taken".format(screenshot_path)) - - -def take_screenshot(remote_console_instance, workspace, screenshot_name): - """ - Takes an in game screenshot using the remote console instance passed in, validates that the screenshot exists - and then renames that screenshot to something defined by the user of this function. - :param remote_console_instance: Remote console instance that is attached to a specific launcher instance - :param workspace: workspace instance so we can get the platform cache folder. - :param screenshot_name: Name of the screenshot - :return: None - """ - screenshot_path = os.path.join(workspace.paths.platform_cache(), 'user', 'screenshots') - expected_screenshot_name = get_next_screenshot_at_path(screenshot_path) - capture_screenshot_command(remote_console_instance) - wait_for(lambda: os.path.exists(os.path.join(screenshot_path, expected_screenshot_name)), - timeout=10, - exc=AssertionError('Screenshot at path:{} and with name:{} not found.'.format(screenshot_path, expected_screenshot_name)) ) - wait_for(lambda: rename_screenshot(screenshot_path, screenshot_name), - timeout=10, - exc=AssertionError('Screenshot at path:{} and with name:{} is still in use.'.format(screenshot_path, screenshot_name))) - - -def rename_screenshot(screenshot_path, screenshot_name): - """ - Tries to rename the screenshot when the file is done being written to - :param screenshot_path: Path to the Screenshot folder - :param screenshot_name: Name we wish to change the screenshot to - :return: True when operation is completed, False if the file is still in use - """ - try: - src_img = os.path.join(screenshot_path, 'screenshot0000.jpg') - dst_img = os.path.join(screenshot_path, '{}.jpg'.format(screenshot_name)) - print('Trying to rename {} to {}'.format(src_img, dst_img)) - os.rename(src_img, dst_img) - return True - except Exception as e: - print('Found error {0} when trying to rename screenshot.'.format(str(e))) - return False - - -def move_screenshots(screenshot_path, file_type, logs_path): - """ - Moves screenshots of a specific file type to the flume location so we can gather all of the screenshots we took. - :param screenshot_path: Path to the screenshot folder - :param file_type: Types of Files to look for. IE .jpg, .tif, etc - :param logs_path: Path where flume gathers logs to be upload - """ - for file_name in os.listdir(screenshot_path): - if file_name.endswith(file_type): - move_file(screenshot_path, logs_path, file_name) - -def move_screenshots_to_artifacts(screenshot_path, file_type, artifact_manager): - """ - Saves screenshots of a specific file type to the artifact manager then removes the original files - :param screenshot_path: Path to the screenshot folder - :param file_type: Types of Files to look for. IE .jpg, .tif, etc - :param artifact_manager: The artifact manager to save the artifacts to - """ - for file_name in os.listdir(screenshot_path): - if file_name.endswith(file_type): - full_path_name = os.path.join(screenshot_path, file_name) - artifact_manager.save_artifact(full_path_name) - os.remove(full_path_name) - - - -def compare_golden_image(similarity_threshold, screenshot, screenshot_path, golden_image_name, - golden_image_path=None): - """ - This function assumes that your golden image filename contains the same base screenshot name and the word "golden" - ex. pc_gamelobby_golden - - :param similarity_threshold: A float from 0.0 - 1.0 that determines how similar images must be or an asserts - :param screenshot: A string that is the full name of the screenshot (ex. 'gamelobby_host.jpg') - :param screenshot_path: A string that contains the path to the screenshots - :param golden_image_path: A string that contains the path to the golden images, defaults to the screenshot_path - :return: - """ - if golden_image_path is None: - golden_image_path = screenshot_path - - mean_similarity = compare_screenshots((os.path.join(screenshot_path, screenshot)), - (os.path.join(golden_image_path, golden_image_name))) - assert mean_similarity > similarity_threshold, \ - '{} screenshot comparison failed! Mean similarity value is: {}'\ - .format(screenshot, mean_similarity) - -def download_qa_golden_images(project_name, destination_dir, platform): - """ - Downloads the golden images for a specified project from s3. The project_name, platform, and filetype are used to - filter which images will be downloaded as the golden images. - - https://s3.console.aws.amazon.com/s3/buckets/ly-qae-jenkins-configs/golden-images/?region=us-west-1&tab=overview - - :param project_name: a string of the project name of the folder in s3. ex: 'MultiplayerSample' - :param destination_dir: a string of where the images will be downloaded to - :param platform: a string for the platform type ('pc', 'android', 'ios', 'darwin') - :param filetype: a string for the file type. ex: '.jpg', '.png' - :return: - """ - - # Currently we import s3_utils here instead of at the top because this is the only method that needs it, - # and s3_utils has an unmet dependency on boto3 that hasn't been resolved. Once s3_utils is functional again, - # this can move back to the top of the file. - try: - from . import s3_utils as s3_utils - except ImportError: - raise Exception("Failed to import s3_utils") - # end s3_utils import - - bucket_name = 'ly-qae-jenkins-configs' - path = 'golden-images/{}/{}/'.format(project_name, platform) - - if not s3_utils.key_exists_in_bucket(bucket_name, path): - raise s3_utils.KeyDoesNotExistError("Key '{}' does not exist in S3 bucket {}".format(path, bucket_name)) - for image in s3_utils.s3.Bucket(bucket_name).objects.filter(Prefix=path): - file_name = string.replace(image.key, path, '') - if file_name != '': - s3_utils.download_from_bucket(bucket_name, image.key, destination_dir, file_name) - - -def _retry_command(remote_console_instance, command, output, tries=10, timeout=10): - """ - Retries specified console command multiple times and asserts if it still can not send. - :param remote_console: the remote console connected to the launcher. - :param command: the command to send to the console. - :param output: The expected output to check if the command was sent successfully. - :param tries: The amount of times to try before asserting. - :param timeout: The amount of time in seconds to wait for each retry send. - :return: True if succeeded, will assert otherwise. - """ - while tries > 0: - tries -= 1 - try: - send_command_and_expect_response(remote_console_instance, command, output) - return True - except: - pass #Do nothing. Let the number of tries get to 0 if necessary. - assert False, "Command \"{}\" failed to run in remote console.".format(command) - - -def prepare_for_screenshot_compare(remote_console_instance): - """ - Prepares launcher for screenshot comparison. Removes any debug text and antialiasing that may result in interference - with the comparison. - - :param remote_console_instance: Remote console instance that is attached to a specific launcher instance - :return: - """ - wait_for(lambda: _retry_command(remote_console_instance, 'r_displayinfo 0', - '$3r_DisplayInfo = $60 $5[DUMPTODISK, RESTRICTEDMODE]$4')) - wait_for(lambda: _retry_command(remote_console_instance, 'r_antialiasingmode 0', - '$3r_AntialiasingMode = $60 $5[]$4')) diff --git a/Tests/performance/Scripts/apbatch_perf_summary.py b/Tests/performance/Scripts/apbatch_perf_summary.py deleted file mode 100755 index ed34cbf490..0000000000 --- a/Tests/performance/Scripts/apbatch_perf_summary.py +++ /dev/null @@ -1,211 +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. -""" - -""" -Script is designed to help with asset processor performance testing. - -This script is capable of running AssetProcessorBatch.exe and -calculating how much time did it take to execute it. Also it is -processing ap batch output and grabbing actual asset processing time. - -Apart from that script is capable of logging folder size -(files, folders, actual size in bytes). - -Usage: -python apbatch_perf_summary.py [-h] {folder_size,run_apbatch} - -python apbatch_perf_summary.py folder_size path project -cache - -Will show folder size: files, folders and actual size in bytes. - -build_path: Full path to the build dev directory, e.g. F:\builds\lumberyard-0.0-639162-pc-1985\dev -project: Full project name (e.g. StarterGame or SamplesProject). --cache: specify if you need to check cache folder instead of source assets. - -python apbatch_perf_summary.py run_apbatch build_path platform num_launches -delete_cache - -Will launch AssetProcessorBatch.exe num_launches times. Will return average -running time and average asset processing time. - -build_path: Full path to the build dev directory, e.g. F:\builds\lumberyard-0.0-639162-pc-1985\dev -platform: Which platform to launch - one of following: vc141, vc142, mac -num_launches: How many times do you want to launch ap batch. --delete_cache: specify if you want to delete Cache before each run -""" - - -import subprocess -import time -import os -import argparse -import test_tools.shared.file_system as fs -import errno - - -def run_ap_batch(build_path, platform): - """ - Given a path to build will run ap batch and return total running and processing times. - :param build_path: Full path to build dev, e.g. F:\builds\lumberyard-0.0-639162-pc-1985\dev - :param platform: Specify platform where to run apbatch: vc141, vc142 or mac. - :return: (processing_time, total_running_time) tuple. - """ - assert os.path.exists(build_path) - - now = time.time() - process = subprocess.Popen(['AssetProcessorBatch'], cwd=os.path.join(build_path, platform), - shell=True, stdout=subprocess.PIPE) - for line in iter(process.stdout.readline, ''): - if 'Total Assets Processing Time' in line: - processing_time = line.split(':')[2] - process.wait() - end = time.time() - - print 'Processing time: {}'.format(processing_time.strip()) - print 'Total time: {}s'.format(end - now) - - return float(processing_time.split('s')[0]), float(end - now) - - -def run_several_times(build_path, platform, num_launches, erase_cache): - """ - Given path to build, project name and boolean parameter (whether there is need to delete a cache) - will run AssetProcessorBatch.exe num_launches and will return average running and processing times. - :param build_path: Full path to build dev, e.g. F:\builds\lumberyard-0.0-639162-pc-1985\dev - :param platform: Specify platform where to run apbatch: vc141, vc142 or mac. - :param num_launches: How many times user needs to launch ap batch. - :param erase_cache: yes/no or True/False in case user needs Cache folder to be deleted prior to apbatch launch. - :return: (avg_processing_time, avg_running_time) tuple. - """ - if not os.path.exists(build_path): - raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), build_path) - - average_processing_time = 0 - average_total_time = 0 - - # running apbatch num_launches times and getting times - for i in range(num_launches): - if erase_cache and os.path.exists(os.path.join(build_path, 'Cache')): - fs.delete([os.path.join(build_path, 'Cache')], False, True) - print 'Iteration # {}'.format(i) - processing_time, total_time = run_ap_batch(build_path, platform) - average_processing_time += processing_time - average_total_time += total_time - - # calculating average times - average_processing_time /= num_launches - average_total_time /= num_launches - - return average_processing_time, average_total_time - - -def folder_size(path): - """ - Given path to a build will calculate folder size. - :param path: Full path to the folder for which you need folder size info. - :return: (total_files_count, total_folders_count, total_size_in_bytes). - """ - total_size = 0 - total_files_count = 0 - total_folder_count = 0 - - if not os.path.exists(path): - raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), path) - - # walking over the folder and calculating files, folder; total files size - for dirpath, dirnames, filenames in os.walk(path): - total_folder_count += len(dirnames) - total_files_count += len(filenames) - for f in filenames: - fp = os.path.join(dirpath, f) - total_size += os.path.getsize(fp) - - return total_files_count, total_folder_count, total_size - - -def run_apbatch(args): - """ - Function for argparse command run_apbatch: - running run_several_times function and printing its results. - :param args: args.build_path: see run_several_times build_path. - args.num_launches: see run_several_times num_launches. - args.platform: see run_several_times platform. - args.delete: see run_several_times erase_cache. - :return: None - """ - platform_bin = { - 'vc141': 'Bin64vc141', - 'vc142': 'Bin64vc142', - 'mac': 'BinMac64' - } - running_times = run_several_times(args.build_path, platform_bin[args.platform], args.num_launches, args.delete_cache) - print '\nAssets processing time: {}s'.format(running_times[0]) - print 'Total running time: {}s'.format(running_times[1]) - - -def print_folder_size(args): - """ - Function for argparse command folder_size: - running folder_size function and printing its results. - :param args: args.build_path: see folder_size path. - args.project: specified project which folder will be analyzed. - args.cache: yes/no or True/False - whether user need to check Cache folder or not. - :return: None - """ - print '{} (cache: {}) folder size:'.format(args.project, args.cache) - if args.cache: - path = os.path.join(args.build_path, 'Cache', args.project) - else: - path = os.path.join(args.build_path, args.project) - folder_size_data = folder_size(path) - print 'Files: {}'.format(folder_size_data[0]) - print 'Folders: {}'.format(folder_size_data[1]) - print 'Size: {}'.format(folder_size_data[2]) - - -def main(): - """Main function with set-up and commands execution""" - # creating command line arguments parser - parser = argparse.ArgumentParser(prog = 'apbatch_perf_summary') - subparsers = parser.add_subparsers(help = 'sub-command help', dest='command') - - parser_folder_size = subparsers.add_parser('folder_size', - help='Will show folder size: files, folders and actual size in bytes. ') - parser_run_apbatch = subparsers.add_parser('run_apbatch', help='run_apbatch help') - - parser_run_apbatch.add_argument('build_path', - help='Full path to the build dev directory, e.g. ' - 'F:\\builds\\lumberyard-0.0-639162-pc-1985\\dev') - parser_run_apbatch.add_argument('platform', choices=['vc141', 'vc142', 'mac'], help='vc141, vc142 or mac') - parser_run_apbatch.add_argument('num_launches', type=int, help='How many times do you want to launch ap batch.') - parser_run_apbatch.add_argument('-delete_cache', default=False, action='store_true', - help='Specify if you want to delete Cache before and between runs') - - parser_run_apbatch.set_defaults(func=run_apbatch) - - parser_folder_size.add_argument('build_path', - help='Full path to the build dev directory, e.g. ' - 'F:\\builds\\lumberyard-0.0-639162-pc-1985\dev') - parser_folder_size.add_argument('project', help='Full project name (e.g. StarterGame or SamplesProject).') - parser_folder_size.add_argument('-cache', default=False, action='store_true', - help='Specify if you want to check cache folder', required=False) - parser_folder_size.set_defaults(func=print_folder_size) - - args = parser.parse_args() - - # executing passed commands - args.func(args) - - -# calling main function if script is launched as standalone module -if __name__ == '__main__': - main() - diff --git a/Tests/pipeline/__init__.py b/Tests/pipeline/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/pipeline/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/pipeline/product_dependency_tests/AssetDependencyTests.py b/Tests/pipeline/product_dependency_tests/AssetDependencyTests.py deleted file mode 100755 index 25e94b150c..0000000000 --- a/Tests/pipeline/product_dependency_tests/AssetDependencyTests.py +++ /dev/null @@ -1,88 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -from TestFixtures import HeliosProjectFixture - -import os -import pytest -import sqlite3 -import time -import codecs - -def AssertProductHasDependencies(engineRoot, projectName, buildInfo, dbCheckWaitTime, product, pathDependencies, assetIdDependencies): - productNameInDB = os.path.join(buildInfo.cacheSubfolder, projectName, product) - productNameInDB = productNameInDB.replace("\\", "/") - - sqlDatabasePath = os.path.join(engineRoot, "Cache", projectName, "assetdb.sqlite") - print (" * Connecting to database {}".format(sqlDatabasePath)) - sqlConnection = sqlite3.connect(sqlDatabasePath) - productRowsList = list() - productDbWait = dbCheckWaitTime - while len(productRowsList) == 0 and productDbWait > 0: - productRows = sqlConnection.execute( - "SELECT ProductID FROM Products where ProductName='{}'".format(productNameInDB)) - productRowsList = list(productRows.fetchall()) - time.sleep(1) - productDbWait = productDbWait - 1 - assert len(productRowsList) == 1, str.format("productRowsList= {}", productRowsList) - productId = int(productRowsList[0][0]) - - foundDependencies = list() - dependencyDbTimeout = dbCheckWaitTime - while len(foundDependencies) == 0 and dependencyDbTimeout > 0: - dependencyRows = sqlConnection.execute( - "SELECT * FROM ProductDependencies where ProductPK={}".format(productId)) - foundDependencies = list(dependencyRows.fetchall()) - time.sleep(1) - dependencyDbTimeout = dependencyDbTimeout - 1 - - foundAssetIds = list() - foundUnresolvedPaths = list() - - uuidIndex = 2 - subIdIndex = 3 - unresolvedPathIndex = 6 - for foundDependency in foundDependencies: - if foundDependency[unresolvedPathIndex] != "": - # If there's a path, there won't be an asset ID - foundUnresolvedPaths.append(foundDependency[unresolvedPathIndex]) - else: - dependencyUUIDAsHex = codecs.encode(foundDependency[uuidIndex], 'hex_codec') - subId = str(foundDependency[subIdIndex]) - assetId = "{}:{}".format(dependencyUUIDAsHex.decode('utf8'), subId) - foundAssetIds.append(assetId) - - assert sorted(pathDependencies) == sorted(foundUnresolvedPaths) - assert sorted(assetIdDependencies) == sorted(foundAssetIds) - - -def test_VegdescriptorlistValidDependencies_DependenciesInDb(HeliosProjectFixture): - engineRoot, projectName, buildInfo, dbCheckWaitTime = HeliosProjectFixture - pathDependencies = {} - assetIdDependencies = { - # MeshAsset reference to "objects/default/primative_wedge_30.cgf" - "e8b39f901f905e3998aa6f8ec4e91507:0", - # MaterialAsset reference to "materials/am_grass1.mtl" - "1151f14d38a65579888abe3139882e68:0" - } - AssertProductHasDependencies(engineRoot, projectName, buildInfo, dbCheckWaitTime, "heliosvegetation.vegdescriptorlist", pathDependencies, assetIdDependencies) - -def test_CloudLibrarytValidDependencies_DependenciesInDb(HeliosProjectFixture): - engineRoot, projectName, buildInfo, dbCheckWaitTime = HeliosProjectFixture - pathDependencies = {} - assetIdDependencies = { - # MaterialAsset reference to "materials/clouds/baseclouds.mtl" - "f249f13854055cfba3b6d95bdc1a3db0:0" - } - AssertProductHasDependencies(engineRoot, projectName, buildInfo, dbCheckWaitTime, "libs/clouds/default.xml", pathDependencies, assetIdDependencies) diff --git a/Tests/pipeline/product_dependency_tests/LvlDepTestDynamicSlice.py b/Tests/pipeline/product_dependency_tests/LvlDepTestDynamicSlice.py deleted file mode 100755 index de7609c92c..0000000000 --- a/Tests/pipeline/product_dependency_tests/LvlDepTestDynamicSlice.py +++ /dev/null @@ -1,228 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -from TestFixtures import EmptyProjectFixture - -import fileinput -import os -import pytest -import shutil -import sqlite3 -import time - -import SubprocessUtils - -def MakeEditorPythonFile(testLevelName, assetGuid, tempFolder, templatePythonFile): - outputFileName = templatePythonFile.replace(".template", ".py") - outputFilePath = os.path.join(tempFolder, outputFileName) - shutil.copy(os.path.join(os.path.dirname(os.path.realpath( - __file__)), templatePythonFile), outputFilePath) - for line in fileinput.FileInput(outputFilePath, inplace=1): - line = line.replace("${LevelName}", str.format('"{}"', testLevelName)) - line = line.replace("${MeshGuid}", str.format('"{}"', assetGuid)) - print (line) - return outputFilePath - -@pytest.mark.skip(reason="This test takes too long on Jenkins, and bundler tests catch everything we want from here") -def test_productDependencies_EntityInLevelWithAssetReference_ReferencedAssetIsLevelProductDependency( - EmptyProjectFixture, tmpdir): - print ("RunLvlDynamicSliceTest") - engineRoot, projectName, buildInfo, dbCheckWaitTime = EmptyProjectFixture - - - tempFolder = tmpdir.mkdir("EditorPyScripts") - print (" * Launch editor with dynamic slice test creation script") - - testLevelName = "SimpleLevel" - expectedDependencyGuid = "81C4A6AF-C57D-5734-81B7-822074358C4D" - exportLevelScriptPath = MakeEditorPythonFile(testLevelName, expectedDependencyGuid, str(tempFolder), "export_test_level.template") - - lyCommand = [buildInfo.editorExe, '/BatchMode', '/runpython', exportLevelScriptPath] - SubprocessUtils.SubprocessWithTimeout(lyCommand, engineRoot, 60) - - # Logic that the export_test_level.py script will run in the editor: - # * Create new level - # * Place an entity in the level - # * Add the mesh component to the entity - # * Assign a test asset to that component [dev\Engine\Objects\default\primitive_sphere.cgf] - # * Export the level - - print (" * Wait for the Asset Processor to copy the asset to the cache and update the asset database") - - projectCacheRoot = os.path.join(engineRoot, "Cache", projectName) - levelRelativeSubFolder = os.path.join("Levels", testLevelName) - cachePath = os.path.join(projectCacheRoot, buildInfo.cacheSubfolder, projectName, levelRelativeSubFolder, "level.pak") - # On an i7 running Lumberyard on an SSD, it normally takes about 1-2 minutes to complete this step. - # Add a few minutes onto that because the Jenkins machines may not be as fast. - pakTimeoutSeconds = 10 * 60 - pakTimeoutWaitTimeSeconds = 1 - # Wait for the level.pak file to exist in the cache - while not os.path.exists(cachePath) and pakTimeoutSeconds > 0: - time.sleep(pakTimeoutWaitTimeSeconds) - pakTimeoutSeconds -= pakTimeoutWaitTimeSeconds - - assert(os.path.exists(cachePath)) - - print (" * Open the asset database, check that the correct product dependency is set for the exported level.pak") - - # A newly created level will have all of these dependencies by default. - # These are tracked by the relative source path instead of the UUID because it's more readable. - expectedDependencyPaths = { - "materials/material_terrain_default.mtl", # from leveldata.xml - "EngineAssets/Materials/sky/sky.mtl", # from mission_mission0.xml - "EngineAssets/Materials/Water/ocean_default.mtl", # from mission_mission0.xml - "textures/skys/night/half_moon.tif" - } - - # Nothing is currently expected to unresolved, but this is left here in case that changes. - expectedUnresolvedPaths = { - # Hardcoded levelbuilder relative path output - os.path.join(levelRelativeSubFolder, "auto_resourcelist.txt"), - os.path.join(levelRelativeSubFolder, "level.cfg"), - os.path.join(levelRelativeSubFolder, "levelparticles.xml"), - os.path.join(levelRelativeSubFolder, "occluder.ocm"), - os.path.join(levelRelativeSubFolder, "preloadlibs.txt"), - os.path.join(levelRelativeSubFolder, "terrain", "cover.ctc"), - os.path.join(levelRelativeSubFolder, "terrain", "merged_meshes_sectors", "mmrm_used_meshes.lst"), - os.path.join(levelRelativeSubFolder, str.format("{}.xml",testLevelName)) - } - - # All expected GUIDs should match the format in the database: Lowercase, with no separators. - expectedDependencyGuids = { - expectedDependencyGuid.lower().replace('-','') - } - - CheckDatabaseForDependency(projectCacheRoot, projectName, testLevelName, expectedDependencyGuids, - expectedDependencyPaths, expectedUnresolvedPaths, buildInfo, dbCheckWaitTime) - - print ("/RunLvlDynamicSliceTest") - - -def CheckDatabaseForDependency(projectCacheRoot, projectName, testLevelName, expectedDependencyGuids, expectedDependencyPaths, expectedUnresolvedPaths, buildInfo, dbCheckWaitTime): - print ("CheckDatabaseForDependency") - - print (str.format(" * Checking expected dependencies for level.pak for level {}", testLevelName)) - print (str.format(" * Searching for these GUIDs as dependencies: {}", str(expectedDependencyGuids))) - print (str.format(" * Searching for these paths as dependencies: {}", str(expectedDependencyPaths))) - print (str.format(" * Searching for these paths as unresolved paths: {}", str(expectedUnresolvedPaths))) - - sqlDatabasePath = os.path.join(projectCacheRoot, "assetdb.sqlite") - print (" * Connecting to database " + sqlDatabasePath) - sqlConnection = sqlite3.connect(sqlDatabasePath) - try: - # Not using os.path.join because this is an expected string in a database - levelPakProduct = str.format('{}/{}/levels/{}/level.pak', buildInfo.cacheSubfolder, projectName.lower(), testLevelName.lower()) - print (" * Looking in product table for " + levelPakProduct) - productRows = sqlConnection.execute( - str.format("SELECT ProductID FROM Products where ProductName='{}'", levelPakProduct)) - - productRowsList = list(productRows.fetchall()) - - productDbWait = dbCheckWaitTime - while len(productRowsList) == 0 and productDbWait > 0: - time.sleep(1) - productDbWait = productDbWait - 1 - productRows = sqlConnection.execute( - str.format("SELECT ProductID FROM Products where ProductName='{}'", levelPakProduct)) - productRowsList = list(productRows.fetchall()) - - - assert len(productRowsList) == 1, "productRowsList= {}".format(productRowsList) - - print (" * Searching product results for product ID") - productId = int(productRowsList[0][0]) - - assert productId - - print (str.format(" * Searching for dependencies for product ID {}", str(productId))) - dependencyDbSuccess = False - dependencyDbTimeout = dbCheckWaitTime - # Make copies of the list in case multiple runs are required - expectedUnresolvedPathsCopy = [] - expectedDependencyGuidsCopy = [] - remainingDependencies = [] - - while (not dependencyDbSuccess) and dependencyDbTimeout > 0: - expectedUnresolvedPathsCopy = expectedUnresolvedPaths.copy() - expectedDependencyGuidsCopy = expectedDependencyGuids.copy() - productDependencyRows = sqlConnection.execute("SELECT * FROM ProductDependencies where ProductPK={}".format(productId)) - - dependencyRowIndex_SourceId = 2 - dependencyRowIndex_SubId = 3 - dependencyRowIndex_UnresolvedPath = 6 - - productDependencyRowList = list(productDependencyRows.fetchall()) - - expectedDependencyCount = len(expectedDependencyGuidsCopy) + len( - expectedDependencyPaths) + len(expectedUnresolvedPathsCopy) - - dependencyDbSuccess = len( - productDependencyRowList) == expectedDependencyCount - - expectedSubId = 0 - - # This will contain SQL data buffers, which are not hashable. - remainingDependencies = [] - - for dependencyRow in productDependencyRowList: - dependencySourceId = dependencyRow[dependencyRowIndex_SourceId] - dependencySubId = int(dependencyRow[dependencyRowIndex_SubId]) - dependencyDbSuccess = dependencyDbSuccess and dependencySubId == expectedSubId - - dependencySourceAsHex = str(dependencySourceId).encode('hex') - wasExpectedDependency = False - # If this dependency's UUID is in our expected UUID list, then count it as found. - if dependencySourceAsHex in expectedDependencyGuidsCopy: - wasExpectedDependency = True - expectedDependencyGuidsCopy.remove(dependencySourceAsHex) - - # If this dependency has an unresolved path that we expect, then count it as found. - unresolvedPath = dependencyRow[dependencyRowIndex_UnresolvedPath] - if unresolvedPath in expectedUnresolvedPathsCopy: - wasExpectedDependency = True - expectedUnresolvedPathsCopy.remove(unresolvedPath) - - if not wasExpectedDependency: - remainingDependencies.append(dependencySourceId) - - dependencyDbSuccess = dependencyDbSuccess and (len(expectedDependencyGuidsCopy) == 0 and - len(expectedUnresolvedPathsCopy) == 0 and - len(remainingDependencies) == len(expectedDependencyPaths)) - if not dependencyDbSuccess: - time.sleep(1) - dependencyDbTimeout = dependencyDbTimeout - 1 - - # do all the checks in asserts, instead of just assert dependencyDbSuccess so that error messages are more specific - assert len(productDependencyRowList) == expectedDependencyCount, str.format("Expected {} dependencies, found {}", expectedDependencyCount, len(productDependencyRowList)) - assert len(expectedDependencyGuidsCopy) == 0, str.format( - "Expected dependencies were not found in the asset database: {}", str(expectedDependencyGuids)) - assert len(expectedUnresolvedPathsCopy) == 0, str.format( - "Expected unresolved paths were not found in the asset database: {}", str(expectedUnresolvedPathsCopy)) - assert len(remainingDependencies) == len(expectedDependencyPaths), str.format("Expected dependency sizes do not match for {} and {}", str(remainingDependencies), str(expectedDependencyPaths)) - - for remainingDependency in remainingDependencies: - sourceRows = sqlConnection.execute("SELECT SourceName FROM Sources where SourceGuid=?", (sqlite3.Binary(remainingDependency),) ) - sourceRowsList = list(sourceRows.fetchall()) - assert len(sourceRowsList) == 1, str.format("Expected to find 1 entry for {}, found {} instead.", str(remainingDependency).encode('hex'), len(sourceRowsList)) - sourcePath = sourceRowsList[0][0] - assert sourcePath in expectedDependencyPaths, str.format("Could not find {} for UUID {} in the list of expected dependencies.", str(sourcePath), str(remainingDependency).encode('hex')) - expectedDependencyPaths.remove(sourcePath) - assert len(expectedDependencyPaths) == 0, str.format("Missing expected dependencies {}", str(expectedDependencyPaths)) - - print (" * Found all expected dependencies") - finally: - print (" * Closing database connection") - sqlConnection.close() - print ("/CheckDatabaseForDependency") - diff --git a/Tests/pipeline/product_dependency_tests/SubprocessUtils.py b/Tests/pipeline/product_dependency_tests/SubprocessUtils.py deleted file mode 100755 index d5637527f2..0000000000 --- a/Tests/pipeline/product_dependency_tests/SubprocessUtils.py +++ /dev/null @@ -1,52 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -import subprocess -import threading -import time - -class ThreadedSubprocess(): - def __init__(self, command, workingDirectory, timeOutMinutes): - self.command = command - self.workingDirectory = workingDirectory - self.timeOutSeconds=timeOutMinutes*60 - self.process = None - self.logOutput = [] - # Pytest doesn't handle asserts on other threads, capture them and report on the main thread. - self.assertError = None - - def RunCommand(self): - def RunThread(): - print (str.format("Subprocess thread starting for command: {}", self.command)) - self.process = subprocess.Popen(self.command, cwd=self.workingDirectory, shell=True, stdout=subprocess.PIPE, universal_newlines=True) - for stdoutLine in iter(self.process.stdout.readline, ""): - self.logOutput.append(stdoutLine) - self.process.communicate() - if self.process.returncode is None: - self.assertError = str.format("Subprocess call '{}' had no return code", self.command) - elif self.process.returncode != 0: - self.assertError = str.format("Subprocess call '{}' returned code {}", self.command, self.process.returncode) - print (str.format("Finished command, result {}: {}", self.process.returncode, self.command)) - - commandThread = threading.Thread(target=RunThread) - commandThread.start() - commandThread.join(self.timeOutSeconds) - assert not commandThread.is_alive(), str.format("Subprocess call '{}' timed out", self.command) - assert self.assertError is None, self.assertError - - -def SubprocessWithTimeout(command, workingDirectory, timeOutMinutes): - threadedSubprocess = ThreadedSubprocess(command, workingDirectory, timeOutMinutes) - threadedSubprocess.RunCommand() - return threadedSubprocess.logOutput diff --git a/Tests/pipeline/product_dependency_tests/TestAssets/updated_xml_schema_test.xmlschema b/Tests/pipeline/product_dependency_tests/TestAssets/updated_xml_schema_test.xmlschema deleted file mode 100644 index a979a1b245..0000000000 --- a/Tests/pipeline/product_dependency_tests/TestAssets/updated_xml_schema_test.xmlschema +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/pipeline/product_dependency_tests/TestAssets/xml_schema_test.xml b/Tests/pipeline/product_dependency_tests/TestAssets/xml_schema_test.xml deleted file mode 100644 index 95b20dc6c8..0000000000 --- a/Tests/pipeline/product_dependency_tests/TestAssets/xml_schema_test.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/Tests/pipeline/product_dependency_tests/TestAssets/xml_schema_test.xmlschema b/Tests/pipeline/product_dependency_tests/TestAssets/xml_schema_test.xmlschema deleted file mode 100644 index 69be99578d..0000000000 --- a/Tests/pipeline/product_dependency_tests/TestAssets/xml_schema_test.xmlschema +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/pipeline/product_dependency_tests/TestCleanup.py b/Tests/pipeline/product_dependency_tests/TestCleanup.py deleted file mode 100755 index d57119db26..0000000000 --- a/Tests/pipeline/product_dependency_tests/TestCleanup.py +++ /dev/null @@ -1,76 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -import os, subprocess, shutil, time - -import SubprocessUtils - -def KillProcess_Windows(processName): - # This only runs on Windows - processList = subprocess.check_output(str.format('tasklist /NH /FO CSV /FI "IMAGENAME eq {}"', processName)) - if processList is None or processList.startswith("INFO: No tasks are running which match the specified criteria."): - # Asset processor isn't running, no need to kill it. - return - os.system(str.format('taskkill /F /IM {}', processName)) - -def KillLumberyardTools(): - if os.name == 'nt': - KillProcess_Windows("Editor.exe") - KillProcess_Windows("AssetProcessor.exe") - else: - # Other operating systems are not yet supported, so have the test fail - assert False - - -def RemoveFolder(folderPath): - # The Asset Processor may take a bit to shut down, retry a few times if it's still holding a lock on a file. - retryCount = 5 - for retry in range(retryCount): - if os.path.exists(folderPath) == False: - return - - try: - shutil.rmtree(folderPath) - except: - if retry < retryCount-1: - # Wait a few seconds for whatever has the file handle open to close it. - time.sleep(5) - continue - else: - raise - - -def cleanUpArtifacts(engineRoot, projectName, buildInfo): - print ("cleanUpArtifacts") - - print (" * Shutting down Asset Processor") - KillLumberyardTools() - - # Setting the active project to one that is in Perforce will make sure other commands work correctly. Once the - # project created here is destroyed, lmbr_waf commands won't work. - if os.path.exists(os.path.join(buildInfo.buildFolder,buildInfo.lmbrCommand)): - print (" * Setting project to Helios") - SubprocessUtils.SubprocessWithTimeout(str.format("{} projects set-active Helios", buildInfo.lmbrCommand), buildInfo.buildFolder, 60) - else: - print (" * Cannot set project to FeatureTests, lmbr executable is not available.") - # Clearing the cache to guarantee that no data persists between tests. - print (" * Clearing the asset cache") - cachePath = os.path.join(engineRoot, "Cache", projectName) - RemoveFolder(cachePath) - - print (" * Clearing generated data") - projectPath = os.path.join(engineRoot, projectName) - RemoveFolder(projectPath) - - print ("/cleanUpArtifacts") diff --git a/Tests/pipeline/product_dependency_tests/TestFixtures.py b/Tests/pipeline/product_dependency_tests/TestFixtures.py deleted file mode 100755 index 91151748a5..0000000000 --- a/Tests/pipeline/product_dependency_tests/TestFixtures.py +++ /dev/null @@ -1,63 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -import os -import pytest - -import LmbrBuildInfo -import SubprocessUtils -import TestCleanup -import TestSetup - -@pytest.fixture(scope="session") -def EmptyProjectFixture(request): - print ("EmptyProjectFixture") - engineRoot = TestSetup.FindEngineRoot() - assert engineRoot - projectName = "LevelDepTestProj" - buildInfo = LmbrBuildInfo.GetBuildInfo(request.config.option.buildFlavor, engineRoot) - TestCleanup.cleanUpArtifacts(engineRoot, projectName, buildInfo) - - TestSetup.CreateLvlDepTestProject(engineRoot, projectName, buildInfo, request.config.option.thirdPartyPath) - # cleanUpArtifacts deleted the temp dir, so create it. - print ("Finished EmptyProjectFixture setup") - yield engineRoot, projectName, buildInfo, int(request.config.option.dbWaitTimes) - - print ("Tearing down EmptyProjectFixture") - TestCleanup.cleanUpArtifacts(engineRoot, projectName, buildInfo) - print ("Finished EmptyProjectFixture tear down") - - -# These tests require the Helios project to be built in profile and active before they are run. -# This external requirement allows faster iteration on these tests on Jenkins and locally. -@pytest.fixture(scope="session") -def HeliosProjectFixture(request): - print ("HeliosProjectFixture") - engineRoot = TestSetup.FindEngineRoot() - assert engineRoot - projectName = "Helios" - buildInfo = LmbrBuildInfo.GetBuildInfo(request.config.option.buildFlavor, engineRoot) - - # These tests are run on Jenkins after other tests, to minimize time spent on Jenkins jobs. - # Verify that the correct project has been set before this test starts. - - # Temporarily disabling while LY-103017 is not in Helios branch - # Creating a task to revert this change later: LY-103334 - - # Run asset processor once to process all assets, so the tests themselves can run at consistent speeds. - SubprocessUtils.SubprocessWithTimeout([buildInfo.assetProcessorBatch], engineRoot, 120) - - print ("Finished HeliosProjectFixture setup") - yield engineRoot, projectName, buildInfo, int(request.config.option.dbWaitTimes) - print ("Tearing down EmptyProjectFixture") diff --git a/Tests/pipeline/product_dependency_tests/XmlSchemaSystemTests.py b/Tests/pipeline/product_dependency_tests/XmlSchemaSystemTests.py deleted file mode 100755 index 51a349c185..0000000000 --- a/Tests/pipeline/product_dependency_tests/XmlSchemaSystemTests.py +++ /dev/null @@ -1,212 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -from TestFixtures import HeliosProjectFixture - -import pytest -import shutil -import subprocess -import os -import sqlite3 -import time - -TEST_ASSETS_FOLDER_NAME = 'TestAssetS' -TEST_XML_NAME = 'xml_schema_test.xml' -TEST_SCHEMA_NAME = 'xml_schema_test.xmlschema' -UPDATED_TEST_SCHEMA_NAME = 'updated_xml_schema_test.xmlschema' -SCHEMA_FOLDER_NAME = 'Schema' - -def test_XmlSchemaSystem_AddNewSchema_ReprocessXml(HeliosProjectFixture): - engineRoot, projectName, buildInfo, dbCheckWaitTime = HeliosProjectFixture - projectFolder = os.path.join(engineRoot, projectName) - - testAssetsFolder = os.path.dirname(os.path.realpath(__file__)) - testAssetsFolder = os.path.join(testAssetsFolder, TEST_ASSETS_FOLDER_NAME) - testXmlPath = os.path.join(testAssetsFolder, TEST_XML_NAME) - testSchemaPath = os.path.join(testAssetsFolder, TEST_SCHEMA_NAME) - updatedTestSchemaPath = os.path.join(testAssetsFolder, UPDATED_TEST_SCHEMA_NAME) - - projectXmlPath = os.path.join(projectFolder, TEST_XML_NAME) - schemaFolderInProject = os.path.join(projectFolder, SCHEMA_FOLDER_NAME) - projectSchemaPath = os.path.join(schemaFolderInProject, TEST_SCHEMA_NAME) - - # Clean up the existing test xml and schema assets - if CleanUpTestAsset(projectXmlPath) or CleanUpTestAsset(projectSchemaPath): - # Remove the test assets and its dependencies from the database - subprocess.call( - [os.path.join(buildInfo.buildFolder, 'AssetProcessorBatch'), "/gamefolder=Helios"]) - - # Copy test XML asset to the project - # No product dependency should be output for the XML asset without a corresponding schema - print ("Add new XML asset") - expectedUnresolvedPaths = {} - UpdateProjectAsset(testXmlPath, projectXmlPath, engineRoot, projectName, - expectedUnresolvedPaths, buildInfo, dbCheckWaitTime, False) - - # Copy the XML schema asset to the project - # XML assets are expected to be reprocessed and output product dependencies - print ("Add new XML schema") - if not os.path.exists(schemaFolderInProject): - os.makedirs(schemaFolderInProject) - expectedUnresolvedPaths = { 'dependency1' } - UpdateProjectAsset(testSchemaPath, projectSchemaPath, engineRoot, projectName, - expectedUnresolvedPaths, buildInfo, dbCheckWaitTime, False) - - # Update the XML schema asset inside the project - # XML assets are expected to be reprocessed and output product dependencies - print ("Update XML schema") - expectedUnresolvedPaths = { 'dependency1', 'dependency2'} - UpdateProjectAsset(updatedTestSchemaPath, projectSchemaPath, engineRoot, projectName, - expectedUnresolvedPaths, buildInfo, dbCheckWaitTime, False) - - # Delete the schema asset in the project folder - # No product dependency should be output for the XML asset since the schema has been deleted - print ("Delete XML schema") - expectedUnresolvedPaths = {} - UpdateProjectAsset(updatedTestSchemaPath, projectSchemaPath, engineRoot, projectName, - expectedUnresolvedPaths, buildInfo, dbCheckWaitTime, True) - - # Clean up the test assets - CleanUpTestAsset(projectXmlPath) - if not os.listdir(schemaFolderInProject): - os.rmdir(schemaFolderInProject) - -def test_XmlSchemaSystem_MoveSchemaToGem_ReprocessXml(HeliosProjectFixture): - engineRoot, projectName, buildInfo, dbCheckWaitTime = HeliosProjectFixture - - projectFolder = os.path.join(engineRoot, projectName) - testAssetsFolder = os.path.dirname(os.path.realpath(__file__)) - testAssetsFolder = os.path.join(testAssetsFolder, TEST_ASSETS_FOLDER_NAME) - testXmlPath = os.path.join(testAssetsFolder, TEST_XML_NAME) - testSchemaPath = os.path.join(testAssetsFolder, TEST_SCHEMA_NAME) - projectXmlPath = os.path.join(projectFolder, TEST_XML_NAME) - projectSchemaPath = os.path.join(projectFolder, SCHEMA_FOLDER_NAME, TEST_SCHEMA_NAME) - # The CertificateManager Gem was arbitrarily chosen. Schema can be added to any enabled gem - gemSchemaFolder = os.path.join(engineRoot, 'Gems', 'CertificateManager', 'Assets', SCHEMA_FOLDER_NAME) - gemSchemaPath = os.path.join(gemSchemaFolder, TEST_SCHEMA_NAME) - - # Clean up all the potential test xml and schema assets - if CleanUpTestAsset(projectXmlPath) or CleanUpTestAsset(projectSchemaPath) or CleanUpTestAsset(gemSchemaPath): - # Remove the test assets and its dependencies from the database - subprocess.call( - [os.path.join(buildInfo.buildFolder, 'AssetProcessorBatch'), "/gamefolder=Helios"]) - - print ('Add XML schema to CertificateManager gem') - if not os.path.exists(gemSchemaFolder): - os.makedirs(gemSchemaFolder) - shutil.copyfile(testSchemaPath, gemSchemaPath) - - print ('Reprocess test assets') - expectedUnresolvedPaths = { 'dependency1' } - UpdateProjectAsset(testXmlPath, projectXmlPath, engineRoot, projectName, expectedUnresolvedPaths, buildInfo, dbCheckWaitTime, False) - - # Clean up the test assets - CleanUpTestAsset(projectXmlPath) - CleanUpTestAsset(gemSchemaPath) - if not os.listdir(gemSchemaFolder): - os.rmdir(gemSchemaFolder) - -def CleanUpTestAsset(assetName): - assetExists = False; - if os.path.exists(assetName): - assetExists = True; - os.remove(assetName) - - assert not os.path.exists(assetName) - - return assetExists - - -def UpdateProjectAsset(testAssetPath, projectAssetPath, engineRoot, projectName, expectedUnresolvedPaths, buildInfo, dbCheckWaitTime, deleteAsset): - if deleteAsset: - os.remove(projectAssetPath) - else: - shutil.copyfile(testAssetPath, projectAssetPath) - - # Let AP reprocess the new/updated asset - subprocess.call( - [os.path.join(buildInfo.buildFolder, 'AssetProcessorBatch'), "/gamefolder=Helios"]) - - projectCacheRoot = os.path.join(engineRoot, 'Cache', projectName) - CheckDatabaseForDependency(projectCacheRoot, projectName, - expectedUnresolvedPaths, buildInfo, dbCheckWaitTime) - - -def CheckDatabaseForDependency(projectCacheRoot, projectName, expectedUnresolvedPaths, buildInfo, dbCheckWaitTime): - print ("CheckDatabaseForDependency") - - print (" * Searching for these paths as unresolved paths: {}".format(str(expectedUnresolvedPaths))) - - sqlDatabasePath = os.path.join(projectCacheRoot, "assetdb.sqlite") - print (" * Connecting to database " + sqlDatabasePath) - sqlConnection = sqlite3.connect(sqlDatabasePath) - try: - # Not using os.path.join because this is an expected string in a database - xmlProduct = '{}/{}/xml_schema_test.xml'.format(buildInfo.cacheSubfolder, projectName.lower()) - print (" * Looking in product table for " + xmlProduct) - productRows = sqlConnection.execute( - "SELECT ProductID FROM Products where ProductName='{}'".format(xmlProduct)) - productRowsList = list(productRows.fetchall()) - - productDbWait = dbCheckWaitTime - while len(productRowsList) == 0 and productDbWait > 0: - time.sleep(1) - productDbWait = productDbWait - 1 - productRows = sqlConnection.execute( - "SELECT ProductID FROM Products where ProductName='{}'".format(xmlProduct)) - productRowsList = list(productRows.fetchall()) - - assert len(productRowsList) == 1, "productRowsList= {}".format(productRowsList) - - print (" * Searching product results for product ID") - productId = int(productRowsList[0][0]) - - assert productId - - print (" * Searching for dependencies for product ID {}".format(productId)) - dependencyDbSuccess = False - dependencyDbTimeout = dbCheckWaitTime - # Make copies of the list in case multiple runs are required - expectedUnresolvedPathsCopy = [] - dependencyRowIndex_UnresolvedPath = 6 - while (not dependencyDbSuccess) and dependencyDbTimeout > 0: - expectedUnresolvedPathsCopy = expectedUnresolvedPaths.copy() - productDependencyRows = sqlConnection.execute("SELECT * FROM ProductDependencies where ProductPK={}".format(productId)) - - productDependencyRowList = list(productDependencyRows.fetchall()) - expectedDependencyCount = len(expectedUnresolvedPathsCopy) - dependencyDbSuccess = len( - productDependencyRowList) == expectedDependencyCount - - for dependencyRow in productDependencyRowList: - # If this dependency has an unresolved path that we expect, then count it as found. - unresolvedPath = dependencyRow[dependencyRowIndex_UnresolvedPath] - if unresolvedPath in expectedUnresolvedPathsCopy: - expectedUnresolvedPathsCopy.remove(unresolvedPath) - - dependencyDbSuccess = dependencyDbSuccess and len(expectedUnresolvedPathsCopy) == 0 - - if not dependencyDbSuccess: - time.sleep(1) - dependencyDbTimeout = dependencyDbTimeout - 1 - - # do all the checks in asserts, instead of just assert dependencyDbSuccess so that error messages are more specific - assert len(expectedUnresolvedPathsCopy) == 0, str.format( - "Expected unresolved paths were not found in the asset database: {}", str(expectedUnresolvedPathsCopy)) - - print (" * Found all expected dependencies") - finally: - print (" * Closing database connection") - sqlConnection.close() - print ("/CheckDatabaseForDependency") \ No newline at end of file diff --git a/Tests/pipeline/product_dependency_tests/conftest.py b/Tests/pipeline/product_dependency_tests/conftest.py deleted file mode 100755 index cdac03401a..0000000000 --- a/Tests/pipeline/product_dependency_tests/conftest.py +++ /dev/null @@ -1,36 +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. - -Automated scripts for tests calling AssetProcessorBatch validating basic features. - -""" - -def pytest_addoption(parser): - parser.addoption( - "--buildFlavor", action="store", default="WindowsVS2017", help="Sets the build type (see lmbrBuildInfo.py for what's available)" - ) - - parser.addoption( - "--dbWaitTimes", action="store", default=60, help="How long (in seconds) to wait for a condition to be met in the database before timing out" - ) - - parser.addoption( - "--thirdPartyPath", action="store", help="Path to the 3rd party folder" - ) - -def pytest_generate_tests(metafunc): - if "buildFlavor" in metafunc.fixturenames: - metafunc.parametrize("buildFlavor", metafunc.config.getoption("buildFlavor"), scope="session") - - if "dbWaitTimes" in metafunc.fixturenames: - metafunc.parametrize("dbWaitTimes", metafunc.config.getoption("dbWaitTimes"), scope="session") - - if "thirdPartyPath" in metafunc.fixturenames: - metafunc.parametrize("thirdPartyPath", metafunc.config.getoption("thirdPartyPath"), scope="session") \ No newline at end of file diff --git a/Tests/pipeline/product_dependency_tests/export_test_level.template b/Tests/pipeline/product_dependency_tests/export_test_level.template deleted file mode 100644 index 2a6567904e..0000000000 --- a/Tests/pipeline/product_dependency_tests/export_test_level.template +++ /dev/null @@ -1,38 +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. -# - -import os - -game_folder = general.get_game_folder() -levelName = ${LevelName} -meshGuid = ${MeshGuid} - -# Don't use terrain because creating a level with terrain causes a popup message that requires user input. -general.create_level(levelName, 128, 1, False) -levelPath = os.path.join(game_folder, "Levels", levelName, str.format("{}.ly", levelName)) - -if not isinstance(input, str): - # general.open_level_no_prompt expects the file path in utf8 format - levelPath = levelName.encode("utf-8") - -general.open_level_no_prompt(levelPath) - -newEntityIdStr = str(general.create_entity("TestEntityName")) - -componentResult = general.add_mesh_component_with_mesh(newEntityIdStr, meshGuid) - -if not componentResult: - raise Exception('Export Test Level', str.format('Failed to add mesh ID {} to entity ID {}', str(meshGuid), str(newEntityIdStr))) - -general.save_level() -general.export_to_engine() - -general.exit() diff --git a/Tests/samples/__init__.py b/Tests/samples/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/samples/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/samples/sample_tests.py b/Tests/samples/sample_tests.py deleted file mode 100755 index aef6dc0f97..0000000000 --- a/Tests/samples/sample_tests.py +++ /dev/null @@ -1,163 +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. - -Sample tests to demonstrate typical functionality of PythonTestTools and how to integrate into the BAT. -""" -# Workaround for tests which depend on old tools, before they are updated to ly_test_tools and Python 3 -import pytest -pytest.importorskip('test_tools') - -# System level imports -import os -import subprocess - -# Basic PythonTestTools imports, in most cases you should always import these -import test_tools.builtin.fixtures as fixtures -from test_tools import HOST_PLATFORM, WINDOWS_LAUNCHER - -# test_tools and shared are modules with a lot of useful utility functions already written, use them! -# Pick and choose the ones below that you need, don't just blindly copy/paste -import test_tools.launchers.phase -import test_tools.shared.process_utils as process_utils -from test_tools.shared.file_utils import gather_error_logs, clear_out_config_file, delete_screenshot_folder, move_file -from shared.network_utils import check_for_listening_port -from test_tools.shared.remote_console_commands import RemoteConsole -from test_tools.shared.waiter import wait_for - - -# This is where you should access lumberyard - building, asset processing, finding paths/logs, etc. Do NOT change or -# remove this unless you know what you're doing. -# See the documentation for PythonTestTools for more information. -workspace = fixtures.use_fixture(fixtures.builtin_empty_workspace_fixture, scope='function') - -# What are fixtures? -# Fixtures set up a testing process (or test) by running all necessary code to satisfy its preconditions. -# More reading here: https://docs.pytest.org/en/latest/fixture.html - -# This is a shared instance of the remote console that be used across multiple tests. It must have the @pytest.fixture! -# You should remove this if you aren't going to use the remote console. -@pytest.fixture -def remote_console_instance(request): - """ - Creates a remote console instance to send console commands. - """ - console = RemoteConsole() - - def teardown(): - try: - console.stop() - except: - pass - - request.addfinalizer(teardown) - - return console - - -# This is a shared instance of the launcher that can be used across multiple tests within this file and any that it -# includes. It must have the @pytest.fixture for pytest to automatically pass it around! -# You should remove this if you aren't going to use the level-specific launchers. -@pytest.fixture -def launcher_instance(request, workspace, level): - """ - Creates a launcher fixture instance with an extra teardown for error log grabbing. - """ - def teardown_launcher_copy_logs(): - """ - Tries to grab any error logs before moving on to the next test. - """ - - for file_name in os.listdir(launcher.workspace.release.paths.project_log()): - move_file(launcher.workspace.release.paths.project_log(), - launcher.workspace.artifact_manager.get_save_artifact_path(), - file_name) - - logs_exist = lambda: gather_error_logs( - launcher.workspace.release.paths.dev(), - launcher.workspace.artifact_manager.get_save_artifact_path()) - try: - test_tools.shared.waiter.wait_for(logs_exist) - except AssertionError: - print("No error logs found. Completing test...") - - request.addfinalizer(teardown_launcher_copy_logs) - - launcher = fixtures.launcher(request, workspace, level) - return launcher - -# For the rest of the file, these are sample tests that you should remove entirely, or cannibalize them to help your -# own test-writing process. -class TestSamplesAPBatch: - - # This is a shared instance of test teardown that will be used across all tests in this class. - # It must have the @pytest.fixture(autouse=True)! - @pytest.fixture(autouse=True) - def setup_teardown(self, request): - - # This is the teardown function that will be run after *each* test finishes - def teardown(): - pass - - # This is the setup section that will be run before *each* test starts - request.addfinalizer(teardown) - - # mark.BAT adds this test to the BAT. - # mark.test_case is used to link to your testrail id. - # mark.parameterize allows you to run the same test multiple times but with different parameters, such as platform, - # configuration, project, level, and more. See more on parameters here: https://docs.pytest.org/en/latest/parametrize.html - @pytest.mark.BAT - @pytest.mark.test_case(testrail_id='Foo') - @pytest.mark.parametrize('platform,configuration,project,spec', ( - pytest.param('win_x64_vs2017', 'profile', 'StarterGame', 'all', - marks=pytest.mark.skipif(HOST_PLATFORM != 'win_x64', reason='Only supported on Windows hosts')), - pytest.param('darwin_x64', 'profile', 'StarterGame', 'all', - marks=pytest.mark.skipif(HOST_PLATFORM != 'darwin_x64', reason='Only supported on Mac hosts')), - )) - def test_RunAPBatch_WorkspacePreconfigured_NoLeftoverProcessesExist(self, workspace): - """ - Tests that the Asset Processor Batch and run and doesn't leave leftover processes. - """ - # Your function docstrings (the above text) will be part of the test catalog! - - subprocess.check_call([os.path.join(workspace.release.paths.bin(), 'AssetProcessorBatch')]) - - # This is how you should do timeouts - wait_for(lambda: not process_utils.process_exists('AssetProcessorBatch', True), timeout=10) - - # Make sure to include an informative assert message to make debugging easier - assert not process_utils.process_exists('rc', True), 'rc process still exists' - assert not process_utils.process_exists('AssetBuilder', True), 'AssetBuilder process still exists' - - -# Notice that you can put the marks both on the class and on the individual methods (seen above). -@pytest.mark.BAT -@pytest.mark.parametrize("platform,configuration,project,spec,level", [ - pytest.param("win_x64_vs2017", "profile", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")), - pytest.param("win_x64_vs2019", "profile", "StarterGame", "all", "StarterGame", - marks=pytest.mark.skipif(not WINDOWS_LAUNCHER, reason="Only supported on Windows hosts")) -]) -class TestSamplesRemoteConsole(object): - - # Notice here that both the launcher_instance and remote_console_instance fixtures are being reused from above - def test_LaunchRemoteConsoleAndLauncher_CanLaunch(self, launcher_instance, platform, configuration, project, spec, - level, remote_console_instance): - """ - Verifies launcher & remote console can successfully launch. Notice that there are no asserts here, and that is - because the called functions will raise exceptions if there is unexpected behavior. - """ - launcher_instance.launch() - - launcher_instance.run(test_tools.launchers.phase.TimePhase(120, 120)) - - test_tools.shared.waiter.wait_for(lambda: check_for_listening_port(4600), timeout=300, - exc=AssertionError('Port 4600 not listening.')) - - remote_console_instance.start() diff --git a/Tests/samples/sanity_test.py b/Tests/samples/sanity_test.py deleted file mode 100755 index 062bfeea63..0000000000 --- a/Tests/samples/sanity_test.py +++ /dev/null @@ -1,58 +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. - -A simple sanity test. -""" -import os -import pytest - -import ly_test_tools.builtin.helpers - - -class TestSanity: - - @pytest.fixture(autouse=True) - def setup_teardown(self, request, legacy_workspace): - def teardown(): - # Per-test cleanup goes here - pass - request.addfinalizer(teardown) # adds teardown to pytest important to hook before setup in case setup fails - - # Per-test setup goes here - - # builtin_empty_workspace_fixture doesn't configure lumberyard to run, it leaves all the configuration to - # the test. - # To run lumberayrd you need to at least setup 3rdParty and run setup assistant using the following lines. - # Alternatively, you can use 'builtin_workspace_fixture'. - # This sanity test only checks if the framework is sane, so there is no need to configure LY. - # workspace.run_waf_configure() - # workspace.setup_assistant.enable_default_capabilities() - - @pytest.mark.bvt - # Example of dynamically parametrized test, these parameters are consumed by the workspace fixture: - # TODO LY-109331 @pytest.mark.parametrize("platform", ["win_x64_vs2017", "win_x64_vs2019", "darwin_x64"]) - @pytest.mark.parametrize("platform", ["win_x64_vs2017", "win_x64_vs2019"]) - @pytest.mark.parametrize("configuration", ["profile"]) - @pytest.mark.parametrize("project", ["AutomatedTesting"]) - @pytest.mark.parametrize("spec", ["all"]) - def test_Paths_DevPathExists_PathItsADirectory(self, legacy_workspace, platform, configuration, project, spec): - # type: (WorkspaceManager, str, str, str, str) -> None - - # Test code goes here - # os.makedirs(workspace.paths.dev()) - - # These asserts verify that the parameters were correctly received by the workspace fixture - assert legacy_workspace.platform == platform, "Platform does not match parameters" - assert legacy_workspace.configuration == configuration, "Configuration does not match parameters" - assert legacy_workspace.project == project, "Project does not match parameters" - assert legacy_workspace.spec == spec, "Spec does not match parameters" - - # Verify that the dev folder exists - assert os.path.isdir(legacy_workspace.paths.dev()) diff --git a/Tests/shared/__init__.py b/Tests/shared/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/shared/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/shared/file_utils.py b/Tests/shared/file_utils.py deleted file mode 100755 index 98bf08c29a..0000000000 --- a/Tests/shared/file_utils.py +++ /dev/null @@ -1,182 +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. -""" - -import os -import shutil -import subprocess -import logging - -import test_tools.shared.file_system as file_system -from test_tools.shared.waiter import wait_for - -logger = logging.getLogger(__name__) - - -def clear_out_config_file(project_path, script_name): - """ - Clears out the specified config file to be empty. - :param project_path: The directory where the file is. - :param script_name: The file name. - """ - path = os.path.join(project_path, '{}.cfg'.format(script_name)) - - if os.path.exists(path): - # Clears read only flag - file_system.unlock_file(path) - - with open(path, 'w') as initialmap_script: - initialmap_script.write('') - - -def add_commands_to_config_file(config_file_dir, config_file_name, command_list): - """ - From the command list, appends each command to the specified config file. - :param config_file_dir: The directory the config file is contained in. - :param config_file_name: The config file name. - :param command_list: The commands to add to the file. - :return: - """ - config_file_path = os.path.join(config_file_dir, config_file_name) - os.chmod(config_file_path, 0755) - with open(config_file_path, 'w') as launch_config_file: - for command in command_list: - launch_config_file.write("{}\n".format(command)) - - -def gather_error_logs(dev_path, logs_path): - """ - Grabs all error logs (if there are any) and puts them into the specified logs path. - :param dev_path: Path to the dev directory. - :param logs_path: Path to the destination directory for the logs (the test results path). - """ - error_logs_sent = False - error_log_path = os.path.join(dev_path, 'error.log') - artifact_log_path = os.path.join(logs_path, 'error.log') - error_dump_path = os.path.join(dev_path, 'error.dmp') - artifact_dump_path = os.path.join(logs_path, 'error.dmp') - if os.path.exists(error_dump_path) and os.path.exists(error_log_path): - shutil.move(error_dump_path, artifact_dump_path) - shutil.move(error_log_path, artifact_log_path) - error_logs_sent = True - return error_logs_sent - - -def delete_screenshot_folder(platform_path): - """ - Deletes screenshot folder from platform path - :param platform_path: Platform Path - :return: None - """ - platform_path = r'{}\user\screenshots'.format(platform_path) - shutil.rmtree(platform_path, ignore_errors=True) - - -def move_file(src_dir, dest_dir, file_name, timeout=120): - """ - Attempts to move a file from the source directory to the destination directory. Raises an IOError if - the file is in use. - :param src_dir: Directory of the file to be moved. - :param dest_dir: Directory where the file will be moved to. - :param file_name: Name of the file to be moved. - :param timeout: Number of seconds to wait for the file to be released. - """ - file_path = os.path.join(src_dir, file_name) - if os.path.exists(file_path): - wait_for(lambda: move_file_check(src_dir, dest_dir, file_name), - timeout=timeout, - exc=IOError('Cannot move file {} while in use'.format(file_path))) - - -def move_file_check(src_dir, dest_dir, file_name): - """ - Moves file and checks if the file has been moved from the source to the destination directory. - """ - try: - shutil.move(os.path.join(src_dir, file_name), os.path.join(dest_dir, file_name)) - except OSError as e: - print e - return False - - return True - - -def revert_config_files(launcher): - """ - Reverts modified config files from test. Runs the perforce revert command. - :param launcher: The launcher instance to revert files from. - """ - files_revert = [os.path.join(launcher.workspace.release.paths.dev(), 'bootstrap.cfg'), - os.path.join(launcher.workspace.release.paths.dev(), 'AssetProcessorPlatformConfig.ini'), - launcher.workspace.release.paths.platform_config_file()] - - for file_path in files_revert: - subprocess.check_call(['p4', 'revert', file_path]) - subprocess.check_call(['p4', 'sync', '-f', file_path]) - - -def create_file(path, file_name): - """ - Creates a file with specified file name - :param path: The path of where the file needs to be created - :param file_name: The file name - :return: - """ - file_path = os.path.join(path, file_name) - - if not os.path.exists(file_path): - new_file = open(file_path, "w") - new_file.close() - else: - print "{} already exists".format(file_name) - -def delete_level(launcher, level_dir, timeout=120): - """ - Attempts to delete an entire level folder from the project. - :param launcher: The launcher instance to delete the level from. - :param level_dir: The level folder to delete - """ - - if (not level_dir): - logger.warning("level_dir is empty, nothing to delete.") - return - - full_level_dir = os.path.join(launcher.workspace.release.paths.project(), 'Levels', level_dir) - if (not os.path.isdir(full_level_dir)): - if (os.path.exists(full_level_dir)): - logger.error("level '{}' isn't a directory, it won't be deleted.".format(full_level_dir)) - else: - logger.info("level '{}' doesn't exist, nothing to delete.".format(full_level_dir)) - return - - wait_for(lambda: delete_check(full_level_dir), - timeout=timeout, - exc=IOError('Cannot delete directory {} while in use'.format(full_level_dir))) - -def delete_check(src_dir): - """ - Deletes directory and verifies that it's been deleted. - :param src_dir: The directory to delete - """ - try: - shutil.rmtree(src_dir) - except OSError as e: - logger.debug("Delete for '{}' failed: {}".format(src_dir, e)) - return False - - return (not os.path.exists(src_dir)) - -def get_log_file_path(launcher, project_name): - """ - Creates a log file path. - :param launcher: The launcher instance to get a path from. - :param project_name: The name of the project. - """ - return os.path.join(launcher.workspace.release.paths.dev(), 'Cache', project_name, 'pc', 'user', 'log', 'Editor.log') diff --git a/Tests/shared/hydra_test_utils.py b/Tests/shared/hydra_test_utils.py deleted file mode 100755 index cce54a0f45..0000000000 --- a/Tests/shared/hydra_test_utils.py +++ /dev/null @@ -1,78 +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. -""" - -import logging -import os -import tempfile -import test_tools.shared.log_monitor -import test_tools.launchers.phase - -logger = logging.getLogger(__name__) - - -def prepare_cfg_file(editor_python_script_name, args=[]): - """ - Create a temporary .cfg file containing the python script and args to pass to the Editor. - Ex: If you pass in 'create_level.py', ['LevelName'], you'll get a cfg file with this: - pyRunFile create_level.py LevelName - :param editor_python_script_name: Name of script that will execute in the Editor. - :param args: Additional arguments for CFG, such as LevelName. - :return Config file for Hydra execution - """ - cfg_contents = '-- Auto-generated cfg file\n' - cfg_contents += 'pyRunFile ' + editor_python_script_name - for arg in args: - cfg_contents += ' ' + arg - cfg_contents += '\n' - - logger.debug("Preparing a cfg file with the following contents:\n{}".format(cfg_contents)) - f = tempfile.NamedTemporaryFile(mode='w+', suffix='.cfg', delete=False) - cfg_filename = f.name - f.write(cfg_contents) - f.close() - logger.debug("Cfg file name: {}".format(cfg_filename)) - return cfg_filename - - -def cleanup_cfg_file(cfg_filename): - """ - Removes the temporary cfg file created in prepare_cfg_file. - :param cfg_filename: Config file for Hydra execution to delete - """ - logger.debug('Cleaning up the generated cfg file') - if os.path.exists(cfg_filename): - os.remove(cfg_filename) - - -def launch_and_validate_results(test_directory, editor, editor_script, editor_timeout, expected_lines, cfg_args=[]): - """ - Creates a temporary config file for Hydra execution, runs the Editor with the specified script, and monitors for - expected log lines. - :param test_directory: Path to test directory that editor_script lives in. - :param editor: Configured editor object to run test against. - :param editor_script: Name of script that will execute in the Editor. - :param editor_timeout: Timeout for editor run. - :param expected_lines: Expected lines to search log for. - :param cfg_args: Additional arguments for CFG, such as LevelName. - """ - cfg_file_name = prepare_cfg_file(os.path.join(test_directory, editor_script), cfg_args) - - logger.debug("Running automated test: {}".format(editor_script)) - - editor.deploy() - editor.launch(["--skipWelcomeScreenDialog", "--autotest_mode", "--exec", cfg_file_name]) - - editorlog_file = os.path.join(editor.workspace.release.paths.project_log(), 'Editor.log') - - test_tools.shared.log_monitor.monitor_for_expected_lines(editor, editorlog_file, expected_lines) - - # Rely on the test script to quit after running - editor.run(test_tools.launchers.phase.WaitForLauncherToQuit(editor, editor_timeout)) diff --git a/Tests/shared/jenkins-3rdparty-symlink/symlink_utils.py b/Tests/shared/jenkins-3rdparty-symlink/symlink_utils.py deleted file mode 100755 index c8fa8b7023..0000000000 --- a/Tests/shared/jenkins-3rdparty-symlink/symlink_utils.py +++ /dev/null @@ -1,71 +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. -""" - -import argparse -import logging -import os -import subprocess - -logger = logging.getLogger(__name__) - - -def create_symlink(path, name, reference): - """ - Checks if the defined symlink exists and returns True if it does. If it does not, it will create a - new symlink and return True. Unsuccessful commands will return False. - :param path: Path to where symlink should be created. - :param name: Name of the symlink. - :param reference: Source path of directory. - """ - sym_path = os.path.join(path, name) - - if not os.path.exists(sym_path): - if os.path.isfile(reference): - proc = subprocess.Popen('cmd /c mklink "{}" "{}"'.format(name, reference), cwd=path, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - elif os.path.isdir(reference): - proc = subprocess.Popen('cmd /c mklink /J "{}" "{}"'.format(name, reference), cwd=path, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - exit_code = proc.wait() - - if exit_code == 0: - logger.info("Successfully created symlink from {} to {}".format(reference, sym_path)) - return True - elif "You do not have sufficient privilege to perform this operation" in proc.stderr.read(): - raise AssertionError("Permissions denied. You should be running this script with Admin rights.") - - else: - raise AssertionError("The command could not run successfully.") - - else: - logger.info("Directory or file already exists: {}".format(os.path.join(path, name))) - return False - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Creates 3rdParty symlink.') - parser.add_argument('-symlink_path', metavar='symlink_path', type=str, required=True, - help='The path to create the 3rdParty symlink. If folder exists, symlinks will be' - 'created inside the 3rdParty folder.') - parser.add_argument('-name', metavar='symlink_name', type=str, default='3rdParty', - help='Name of symlink created. Defaults to 3rdParty.') - parser.add_argument('-source_path', metavar='source_path', type=str, required=True, - help='The path where the source 3rdParty is located.') - args = parser.parse_args() - - sym_path = os.path.join(args.symlink_path, args.name) - - # If there is an existing directory, copy all source contents as symlinks. - if not create_symlink(args.symlink_path, args.name, args.source_path): - for file_dir in os.listdir(args.source_path): - create_symlink(sym_path, file_dir, os.path.join(args.source_path, file_dir)) diff --git a/Tests/shared/logging_utils.py b/Tests/shared/logging_utils.py deleted file mode 100755 index 0d822c077d..0000000000 --- a/Tests/shared/logging_utils.py +++ /dev/null @@ -1,36 +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. -""" - -def extract_log_lines(start_line_marker, end_line_marker, log_file, platform, level): - """ - Parses log for a start and end marker, and extracts all lines between the markers. - :param start_line_marker: String that marks beginning of loglines to extract. - :param end_line_marker: String that marks end of loglines to extract. - :param log_file: Path to log to parse. - :param platform: Platform under test passed from test parameterization. - :param level: Level under test passed from test parameterization. - """ - extracted_log = '{}_{}_log.txt'.format(platform, level) - - with open(log_file) as log: - match = False - new_log = None - - for line in log: - if start_line_marker in line: - match = True - new_log = open(extracted_log, 'w') - elif end_line_marker in line: - break - elif match: - new_log.write(line) - if new_log: - new_log.close() diff --git a/Tests/shared/network_utils.py b/Tests/shared/network_utils.py deleted file mode 100755 index b51e12e2c5..0000000000 --- a/Tests/shared/network_utils.py +++ /dev/null @@ -1,65 +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. -""" - -import logging -import psutil -import socket - - -logger = logging.getLogger(__name__) - - -def check_for_listening_port(port): - """ - Checks to see if the connection to the designated port was established. - :param port: Port to listen to. - :return: True if port is listening. - """ - port_listening = False - for conn in psutil.net_connections(): - if 'port={}'.format(port) in str(conn): - port_listening = True - return port_listening - - -def check_for_remote_listening_port(port, ip_addr='127.0.0.1'): - """ - Tries to connect to a port to see if port is listening. - :param port: Port being tested. - :param ip_addr: IP address of the host being connected to. - :return: True if connection to the port is established. - """ - port_listening = True - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect((ip_addr, port)) - except socket.error as err: - if err.errno == 10061: - port_listening = False - finally: - sock.close() - return port_listening - - -def get_local_ip_address(): - """ - Finds the IP address for the primary ethernet adapter by opening a connection and grabbing its IP address. - :return: The IP address for the adapter used to make the connection. - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - try: - # Connecting to Google's public DNS so there is an open connection - # and then getting the address used for that connection - sock.connect(('8.8.8.8', 80)) - host_ip = sock.getsockname()[0] - finally: - sock.close() - return host_ip diff --git a/Tests/shared/pipeline_utils.py b/Tests/shared/pipeline_utils.py deleted file mode 100755 index a909c7a685..0000000000 --- a/Tests/shared/pipeline_utils.py +++ /dev/null @@ -1,170 +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. - -Small library of functions to support autotests for asset processor - -""" - -import os -import shutil -import re -import ly_test_tools.environment.file_system as fs -import hashlib -import shutil -import logging - -logger = logging.getLogger(__name__) - -def compare_assets_with_cache(assets, assets_cache_path): - """ - Given a list of assets names, will try to find them (disrespecting file extensions) from project's Cache folder with test assets - :param assets: A list of assets to be compared with Cache - :param assets_cache_path: A path to cache test assets folder - :return: A tuple with two lists - first is missing in cache assets, second is existing in cache assets - """ - missing_assets = [] - existing_assets = [] - if os.path.exists(assets_cache_path): - files_in_cache = list(map(fs.remove_path_and_extension, os.listdir(assets_cache_path))) - for asset in assets: - file_without_ext = fs.remove_path_and_extension(asset).lower() - if file_without_ext in files_in_cache: - existing_assets.append(file_without_ext) - files_in_cache.remove(file_without_ext) - else: - missing_assets.append(file_without_ext) - else: - missing_assets = assets - return missing_assets, existing_assets - - -def copy_assets_to_project(assets, source_directory, target_asset_dir): - """ - Given a list of asset names and a directory, copy those assets into the target project directory - :param assets: A list of asset names to be copied - :param source_directory: A path string where assets are located - :param target_asset_dir: A path to project tests assets directory where assets will be copied over to - :return: None - """ - for asset in assets: - full_name = os.path.join(source_directory, asset) - shutil.copy(full_name, target_asset_dir) - - -def prepare_test_assets(assets_path, function_name, project_test_assets_dir): - """ - Given function name and assets cache path, will clear cache and copy test assets assigned to function name to project's folder - :param assets_path: Path to tests assets folder - :param function_name: Name of a function that corresponds to folder with assets - :param project_test_assets_dir: A path to project directory with test assets - :return: Returning path to copied assets folder - """ - test_assets_folder = os.path.join(assets_path, 'assets', function_name) - copy_assets_to_project(os.listdir(test_assets_folder), test_assets_folder, project_test_assets_dir) - return test_assets_folder - - -def find_joblog_file(joblogs_path, regexp): - """ - Given path to joblogs files and asset name in form of regexp, will try to find joblog file for provided asset; if multiple - will return first occurrence - :param joblogs_path: Path to a folder with joblogs files to look for needed file - :param regexp: Python Regexp containing name of the asset that was processed, for which we're looking joblog file for - :return: Full path to joblog file, empty string if not found - """ - for file_name in os.listdir(joblogs_path): - if re.match(regexp, file_name): - return os.path.join(joblogs_path, file_name) - return '' - - -def find_missing_lines_in_joblog(joblog_location, strings_to_verify): - """ - Given joblog file full path and list of strings to verify, will find all missing strings in the file - :param joblog_location: Full path to joblog file - :param strings_to_verify: List of string to look for in joblog file - :return: Subset of original strings list, that were not found in the file - """ - lines_not_found = [] - with open(joblog_location, 'r') as f: - read_data = f.read() - for line in strings_to_verify: - if line not in read_data: - lines_not_found.append(line) - return lines_not_found - - -def clear_project_test_assets_dir(test_assets_dir): - """ - On call - deletes test assets dir if it exists and creates new empty one - :param test_assets_dir: A path to tests assets dir - :return: None - """ - if os.path.exists(test_assets_dir): - fs.delete([test_assets_dir], True, True) - os.mkdir(test_assets_dir) - - -def get_files_hashsum(path_to_files_dir): - """ - On call - calculates md5 hashsums for filecontents. - :param path_to_files_dir: A path to files directory - :return: Returns a dict with initial filenames from path_to_files_dir as keys and their contents hashsums as values - """ - checksum_dict = {} - try: - for fname in os.listdir(path_to_files_dir): - with open(os.path.join(path_to_files_dir, fname), 'rb') as fopen: - checksum_dict[fname] = hashlib.sha256(fopen.read()).digest() - except IOError: - logger.error('An error occured trying to read file') - return checksum_dict - - -def append_to_filename(file_name, path_to_file, append_text, ignore_extension): - """ - Function for appending text to file and folder names - :param file_name: Name of a file or folder - :param path_to_file: Path to file or folder - :param append_text: Text to append - :param ignore_extension: True or False for ignoring extensions - :return: None - """ - new_name = '' - if not ignore_extension: - (name, extension) = file_name.split('.') - new_name = name + append_text + '.' + extension - else: - new_name = file_name + append_text - os.rename(os.path.join(path_to_file, file_name), os.path.join(path_to_file, new_name)) - - -def create_asset_processor_backup_directories(backup_root_directory, test_backup_directory): - """ - Function for creating the asset processor logs backup directory structure - :param backup_root_directory: The location where logs should be stored - :param test_backup_directory: The directory for the specific test being ran - :return: None - """ - if not os.path.exists(os.path.join(backup_root_directory, test_backup_directory)): - os.makedirs(os.path.join(backup_root_directory, test_backup_directory)) - - -def backup_asset_processor_logs(bin_directory, backup_directory): - """ - Function for backing up the logs created by asset processor to designated backup directory - :param bin_directory: The bin directory created by the lumberyard build process - :param backup_directory: The location where asset processor logs should be backed up to - :return: None - """ - ap_logs = os.path.join(bin_directory, 'logs') - - if os.path.exists(ap_logs): - destination = os.path.join(backup_directory, 'logs') - shutil.copytree(ap_logs, destination) diff --git a/Tests/shared/process_utils.py b/Tests/shared/process_utils.py deleted file mode 100755 index 33ab175600..0000000000 --- a/Tests/shared/process_utils.py +++ /dev/null @@ -1,43 +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. -""" - -from contextlib import contextmanager -import psutil -import subprocess - - -@contextmanager -def managed_popen(args_list, cwd=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT): - """ - Context manager for subprocess.Popen objects which allows Popen to be used in a with/as statement. - This should not be used with processes that are to continue running outside of the with block. - :param args_list: Sequence of arguments as they would be passed to a terminal. - :param cwd: The current working directory to use when issuing the command. - :param stdout: File handle or pipe to use for stdout. - :param stderr: File handle or pipe to use for stderr. - """ - process = subprocess.Popen(args_list, cwd=cwd, stdout=stdout, stderr=stderr) - try: - yield process - finally: - if not process.poll(): - process.terminate() - - -def get_psutil_process(process_name): - """ - Gets a reference to a psutil.Process object with the given process name. - :return: A reference to the first process encountered with the given name or None if no process is found. - """ - for process in psutil.process_iter(): - if process_name == process.name(): - return process - return None diff --git a/Tests/shared/s3_utils.py b/Tests/shared/s3_utils.py deleted file mode 100755 index 403cf75642..0000000000 --- a/Tests/shared/s3_utils.py +++ /dev/null @@ -1,145 +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. -""" - -import boto3 -import botocore.exceptions -import logging -import os - -import test_tools.shared.file_system as file_system - -logger = logging.getLogger(__name__) -s3 = boto3.resource('s3') - - -class BucketNotExists(Exception): - pass - - -class KeyExistsError(Exception): - pass - - -class KeyDoesNotExistError(Exception): - pass - - -def create_folder_in_bucket(bucket_name, folder_key): - """ - Given bucket name and folder key will create specified folder if it doesn't exist - :param bucket_name: name of the bucket where folder will be created - :param folder_key: key in s3 where folder will be created (i.e. specifying full path to folder in s3) - :return: True if folder was successfully created, False otherwise, will raise BucketNotExists exception if - bucket doesn't exist - """ - if not bucket_exists_in_s3(bucket_name): - raise BucketNotExists("Bucket {} does not exist.".format(bucket_name)) - - if key_exists_in_bucket(bucket_name, '{}/'.format(folder_key)): - logger.error("Key {} already exists in bucket {}".format(folder_key, bucket_name)) - return False - - s3_bucket = s3.Bucket(bucket_name) - logger.info("Creating {} folder in a {} bucket".format(folder_key, bucket_name)) - s3_bucket.put_object(Bucket=bucket_name, Key=(folder_key+'/')) - return True - - -def upload_to_bucket(bucket_name, file_path, file_key=None, overwrite=False): - """ - Uploads a given file to the given S3 bucket. - :param bucket_name: Name of the S3 bucket where the file should be uploaded. - :param file_path: Full Path to the target file on hard drive. - :param file_key: Needed path to file on s3 (including file name). - :param overwrite: Overwrite the key if it exists. - """ - if not bucket_exists_in_s3(bucket_name): - s3.create_bucket(Bucket=bucket_name) - - s3_bucket = s3.Bucket(bucket_name) - - if file_key is None: - file_key = os.path.basename(file_path) - - if not overwrite and key_exists_in_bucket(bucket_name, file_key): - raise KeyExistsError("Key '{}' already exists in S3 bucket {}".format(file_key, bucket_name)) - - s3_bucket.upload_file(file_path, file_key) - logger.info("Uploading {} to S3 bucket {}".format(file_key, bucket_name)) - - -def download_from_bucket(bucket_name, file_key, destination_dir, file_name=None): - """ - Download the given key from the given S3 bucket to the given destination. Logs an error if there is not enough \ - space available for the download. - :param bucket_name: Name of the S3 bucket containing the desired file. - :param file_key: Name of the file stored in S3. - :param destination_dir: Directory where the file should be downloaded to. - :param file_name: The name of the file you want to save it as. Defaults to the file_key. - """ - bucket_exists_in_s3(bucket_name) - - if not key_exists_in_bucket(bucket_name, file_key): - raise KeyDoesNotExistError("Key '{}' does not exist in S3 bucket {}".format(file_key, bucket_name)) - - obj_summary = s3.ObjectSummary(bucket_name, file_key) - required_space = obj_summary.size - disk_name = os.path.splitdrive(destination_dir)[0] - - file_system.check_free_space(disk_name, required_space, "Insufficient space available for download:") - - if not os.path.exists(destination_dir): - os.makedirs(destination_dir) - - if file_name is None: - file_name = file_key - destination_path = os.path.join(destination_dir, file_name) - s3.Object(bucket_name, file_key).download_file(destination_path) - logger.info("Downloading {} to {}".format(file_key, destination_path)) - - -def bucket_exists_in_s3(bucket_name): - """ - Verifies that the S3 bucket exists. - :param bucket_name: Name of the S3 bucket that may or may not exist. - :return: True if the bucket exists. False otherwise. - """ - bucket_exists = True - - try: - s3.meta.client.head_bucket(Bucket=bucket_name) - except botocore.exceptions.ClientError as err: - if err.response['Error']['Code'] == '404': - bucket_exists = False - - return bucket_exists - - -def key_exists_in_bucket(bucket_name, file_key): - """ - Verifies that the given key does not already exist in the given S3 bucket. - :param bucket_name: Name of the S3 bucket that may or may not contain the file key. - :param file_key: Name of the file key in question. - :return: True if the key exists. False otherwise. - """ - key_exists = True - obj_summary = s3.ObjectSummary(bucket_name, file_key) - - # Attempting to access any member of ObjectSummary for a nonexistent key will throw an exception - # There is no built-in way to check key existence otherwise - try: - obj_summary.size - except botocore.exceptions.ClientError as err: - if err.response['Error']['Code'] == '404': - key_exists = False - - return key_exists - diff --git a/Tests/shared/screenshot_utils.py b/Tests/shared/screenshot_utils.py deleted file mode 100755 index 0c64bb848a..0000000000 --- a/Tests/shared/screenshot_utils.py +++ /dev/null @@ -1,203 +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. -""" - -import os -import botocore.exceptions -import string -import boto3 - -from test_tools.shared.file_utils import move_file - -import test_tools.launchers.phase as phase -import shared.s3_utils as s3_utils -from test_tools.shared.remote_console_commands import get_screenshot_command -from test_tools.shared.waiter import wait_for -from test_tools import HOST_PLATFORM -from test_tools.shared.images.qssim import qssim as compare_screenshots -from test_tools.shared.launcher_testlib import retry_console_command - - -def take_screenshot(remote_console_instance, launcher, screenshot_name): - """ - Takes an in game screenshot using the remote console instance passed in, validates that the screenshot exists - and then renames that screenshot to something defined by the user of this function. - :param remote_console_instance: Remote console instance that is attached to a specific launcher instance - :param launcher: Launcher instance so we can use the file exists functionality provided by test_tools - :param screenshot_name: Name of the screenshot - :return: None - """ - get_screenshot_command(remote_console_instance) - screenshot_path = os.path.join(launcher.workspace.release.paths.platform_cache(), 'user', 'screenshots') - launcher.run(phase.FileExistsPhase(os.path.join(screenshot_path, 'screenshot0000.jpg'))) - wait_for(lambda: rename_screenshot(screenshot_path, screenshot_name), - timeout=120, - exc=AssertionError('Screenshot at path:{} and with name:{} is still in use.'.format(screenshot_path, screenshot_name))) - - -def rename_screenshot(screenshot_path, screenshot_name): - """ - Tries to rename the screenshot when the file is done being written to - :param screenshot_path: Path to the Screenshot folder - :param screenshot_name: Name we wish to change the screenshot to - :return: True when operation is completed, False if the file is still in use - """ - try: - print 'Trying to rename {} to {}.'.format(os.path.join(screenshot_path, 'screenshot0000.jpg'), os.path.join(screenshot_path, '{}.jpg'.format(screenshot_name))) - os.rename(os.path.join(screenshot_path, 'screenshot0000.jpg'), - os.path.join(screenshot_path, '{}.jpg'.format(screenshot_name))) - return True - except OSError as e: - print ('Found error {0} when trying to rename screenshot. {1}'.format(str(e), str(e.message))) - return False - - -def move_screenshots(screenshot_path, file_type, logs_path): - """ - Moves screenshots of a specific file type to the flume location so we can gather all of the screenshots we took. - :param screenshot_path: Path to the screenshot folder - :param file_type: Types of Files to look for. IE .jpg, .tif, etc - :param logs_path: Path where flume gathers logs to be upload - """ - for file_name in os.listdir(screenshot_path): - if file_name.endswith(file_type): - move_file(screenshot_path, logs_path, file_name) - - -def screenshot_command(remote_console_instance, command_to_run, expected_log_line): - """ - This is just a helper function to help send and validate against screenshot console commands. - :param remote_console_instance: Remote console instance - :param command_to_run: The Screenshot command that you wish to run - :param expected_log_line: The console log line to expect in order to set the event to true - :return: - """ - return retry_console_command(remote_console_instance, command_to_run, expected_log_line) - - -def get_screenshot_command_with_retries(remote_console_instance): - """ - Used for an in-game screenshot - :param remote_console_instance: Remote console instance - :return: None - """ - if (HOST_PLATFORM == 'win_x64'): - command = 'Screenshot: @user@\screenshots/' - else: - command = 'Screenshot: @user@/screenshots/' - - wait_for(lambda: screenshot_command(remote_console_instance, 'r_GetScreenShot 1', command), timeout=240, - exc=AssertionError('Screenshot command failed')) - - -def take_screenshot_with_retries(remote_console_instance, launcher, screenshot_name): - """ - Takes an in game screenshot using the remote console instance passed in, validates that the screenshot exists - and then renames that screenshot to something defined by the user of this function. - :param remote_console_instance: Remote console instance that is attached to a specific launcher instance - :param launcher: Launcher instance so we can use the file exists functionality provided by test_tools - :param screenshot_name: Name of the screenshot - :return: None - """ - get_screenshot_command_with_retries(remote_console_instance) - screenshot_path = os.path.join(launcher.workspace.release.paths.platform_cache(), 'user', 'screenshots') - launcher.run(phase.FileExistsPhase(os.path.join(screenshot_path, 'screenshot0000.jpg'))) - wait_for(lambda: rename_screenshot(screenshot_path, screenshot_name), - timeout=120, - exc=AssertionError('Screenshot taken is still in use')) - - -def compare_golden_image(similarity_threshold, screenshot, screenshot_path, golden_image_name, - golden_image_path=None): - """ - This function assumes that your golden image filename contains the same base screenshot name and the word "golden" - ex. pc_gamelobby_golden - - :param similarity_threshold: A float from 0.0 - 1.0 that determines how similar images must be or an asserts - :param screenshot: A string that is the full name of the screenshot (ex. 'gamelobby_host.jpg') - :param screenshot_path: A string that contains the path to the screenshots - :param golden_image_path: A string that contains the path to the golden images, defaults to the screenshot_path - :return: - """ - if golden_image_path is None: - golden_image_path = screenshot_path - - mean_similarity = compare_screenshots('{}\{}'.format(screenshot_path, screenshot), - '{}\{}'.format(golden_image_path, golden_image_name)) - assert mean_similarity > similarity_threshold, \ - '{} screenshot comparison failed! Mean similarity value is: {}'\ - .format(screenshot, mean_similarity) - - -def take_screenshot_and_compare(remote_console, launcher, screenshot, similarity_threshold, - golden_image_name, screenshot_path=None, file_type='.jpg'): - """ - Takes a screenshot and compares it with its golden image. This utilizes the take_screenshot_with_retries function - which is only used for PC. There are some assumptions with the golden image naming convention that is explained in - the compare_golden_image function. This also assumes it is a jpg file. - - This function enforces a naming convention such that the golden image and screenshot share part of the same name. - Also, the screenshot will be appended with the screenshot_key as shown below. - - The screenshot name will be screenshot_base + screenshot_key + filetype - (ex. 'gamelobby_host.jpg', 'MultiplayerSampleClient_1.png') - - :param screenshot_base: A string that is the base name of the screenshot (ex. 'gamelobby') - :param screenshot_key: A string that acts as a key identifier for the screenshot. - :param similarity_threshold: A float from 0.0 - 1.0 that determines how similar images must be or it asserts - :param screenshot_path: A string for the screenshot path. Defaults to user/screenshots - :param file_type: A string for the screenshot filetype. Defaults to '.jpg' - :return: - """ - if screenshot_path is None: - screenshot_path = r'{}\user\screenshots'.format(launcher.workspace.release.paths.platform_cache()) - - take_screenshot_with_retries(remote_console, launcher, screenshot) - compare_golden_image(similarity_threshold, '{}{}'.format(screenshot, file_type), - screenshot_path, golden_image_name) - - -def download_qa_golden_images(project_name, destination_dir, platform): - """ - Downloads the golden images for a specified project from s3. The project_name, platform, and filetype are used to - filter which images will be downloaded as the golden images. - - https://s3.console.aws.amazon.com/s3/buckets/ly-qae-jenkins-configs/golden-images/?region=us-west-1&tab=overview - - :param project_name: a string of the project name of the folder in s3. ex: 'MultiplayerSample' - :param destination_dir: a string of where the images will be downloaded to - :param platform: a string for the platform type ('pc', 'android', 'ios', 'darwin') - :param filetype: a string for the file type. ex: '.jpg', '.png' - :return: - """ - bucket_name = 'ly-qae-jenkins-configs' - path = 'golden-images/{}/{}/'.format(project_name, platform) - - if not s3_utils.key_exists_in_bucket(bucket_name, path): - raise s3_utils.KeyDoesNotExistError("Key '{}' does not exist in S3 bucket {}".format(path, bucket_name)) - for image in s3_utils.s3.Bucket(bucket_name).objects.filter(Prefix=path): - file_name = string.replace(image.key, path, '') - if file_name != '': - s3_utils.download_from_bucket(bucket_name, image.key, destination_dir, file_name) - - -def prepare_for_screenshot_compare(remote_console_instance): - """ - Prepares launcher for screenshot comparison. Removes any debug text and antialiasing that may result in interference - with the comparison. - - :param remote_console_instance: Remote console instance that is attached to a specific launcher instance - :return: - """ - wait_for(lambda: retry_console_command(remote_console_instance, 'r_displayinfo 0', - '$3r_DisplayInfo = $60 $5[DUMPTODISK, RESTRICTEDMODE]$4'), timeout=120) - wait_for(lambda: retry_console_command(remote_console_instance, 'r_antialiasingmode 0', - '$3r_AntialiasingMode = $60 $5[]$4'), timeout=120) - diff --git a/Tests/shared/shader_compile_server_utils.py b/Tests/shared/shader_compile_server_utils.py deleted file mode 100755 index 4c93970561..0000000000 --- a/Tests/shared/shader_compile_server_utils.py +++ /dev/null @@ -1,120 +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. -""" - -import logging -import os -import psutil -import subprocess - -import test_tools.shared.file_system as file_system -from test_tools.shared.process_utils import kill_processes_named -from test_tools import HOST_PLATFORM - -logger = logging.getLogger(__name__) - - -def start_shader_compile_server(tools_dir, build_config, win_x64_compiler, clean=True): - """ - Starts the shader compile server for the given compiler version. Failure is currently not checked due to issues - with detecting if the process is alive immediately after requesting it to be spun up. - :param tools_dir: Tools directory inside of the dev root. - :param build_config: Build configuration name (profile, debug, etc.). - :param win_x64_compiler: Windows compiler version used to build the project. - :param clean: Removes Cache, Error, Shaders, and Temp directories if set to True. - """ - shader_compile_server_dir = os.path.join(tools_dir, 'CrySCompileServer', 'x64', build_config) - os.chdir(shader_compile_server_dir) - - shader_compile_server_exe = build_shader_compile_server_file_name(win_x64_compiler) - - # Only one shader compile server can run at a time, so kill the currently running process if any - kill_processes_named(shader_compile_server_exe) - - if clean: - clean_shader_compile_server_files(tools_dir, build_config) - - logger.info("Attempting to start shader compile server") - # Running with basic user permissions since the shader compile server warns against running as admin - subprocess.Popen(['RunAs', '/trustlevel:0x20000', shader_compile_server_exe]) - - -def start_mac_shader_compile_server(tools_dir, build_config, clean=True): - """ - Mac version of starting shader compiler given build configuration. Failure is currently not checked due to issues - with detecting if the process is alive immediately after requesting it to be spun up. - :param tools_dir: Tools directory inside of the dev root. - :param build_config: Build configuration name (profile, debug, etc.). - :param clean: Removes Cache, Error, Shaders, and Temp directories if set to True. - :return: Returns the actual process. - """ - shader_compile_server_dir = os.path.join(tools_dir, 'CrySCompileServer', 'osx', build_config) - os.chdir(shader_compile_server_dir) - - kill_processes_named('CrySCompileServer') - - if clean: - clean_shader_compile_server_files(tools_dir, build_config) - - logger.info("Attempting to start shader compile server") - subprocess.Popen('./CrySCompileServer', shell=True) - - -def build_shader_compile_server_file_name(win_x64_compiler): - """ - Puts together the shader compile server file name based on the specified VC compiler version. - :param win_x64_compiler: The VC compiler version specified in vsyyyy format, where yyyy is a year (ex: vs2017) - :return: The shader compile server file name complete with extension. - """ - shader_compile_server_exe = 'CrySCompileServer' - - return '{}.exe'.format(shader_compile_server_exe) - - -def clean_shader_compile_server_files(tools_dir, build_config): - """ - Removes the shader compile server generated Cache, Error, Shaders, and Temp directories. - :param tools_dir: Tools directory inside of the dev root. - :param build_config: Build configuration name (profile, debug, etc.). - """ - if HOST_PLATFORM == 'win_x64': - shader_compile_server_dir = os.path.join(tools_dir, 'CrySCompileServer', 'x64', build_config) - else: - shader_compile_server_dir = os.path.join(tools_dir, 'CrySCompileServer', 'osx', build_config) - os.chdir(shader_compile_server_dir) - - shader_compile_server_dirs = [os.path.join(shader_compile_server_dir, 'Cache'), - os.path.join(shader_compile_server_dir, 'Error'), - os.path.join(shader_compile_server_dir, 'Shaders'), - os.path.join(shader_compile_server_dir, 'Temp')] - if not file_system.delete(shader_compile_server_dirs, True, True): - directories_still_present = [] - for directory in shader_compile_server_dirs: - if os.path.exists(directory): - directories_still_present.append(directory) - raise RuntimeError("Failed to clean folders {} from directory {}".format(directories_still_present, - shader_compile_server_dir)) - - -def stop_shader_compile_server(): - """ - Finds any process with CrySCompileServer in its name and kills it. - """ - # This is necessary because the shader compile server is spawned from another process which - # immediately terminates after spawning its child - for process in psutil.process_iter(): - try: - if 'CrySCompileServer' in process.name(): - success_code = process.kill() - if success_code == 0: - logger.error("Failed to terminate CrySCompileServer process") - except psutil.NoSuchProcess: - # Process was already killed but caught as a zombie process, so pass as normal. - pass diff --git a/Tests/shared/substring.py b/Tests/shared/substring.py deleted file mode 100755 index 45c5f6d3af..0000000000 --- a/Tests/shared/substring.py +++ /dev/null @@ -1,73 +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. - -String and search related functions. -""" -import os -import re - - -def in_file(file_path, pattern_string): - """ - This method checks if pattern_string exists in any line in the file specified in file_path. - It cannot match multi-line patterns. - :param file_path: files path for the file to search in - :param pattern_string: string to search for. - :return: True if the String is found, False otherwise. - """ - if not os.path.exists(file_path): - raise RuntimeError("File does not exist at {}".format(file_path)) - if not pattern_string: - raise RuntimeError("Must provide string to search for") - - with open(file_path, "r") as game_log: - for line in game_log.readlines(): - if pattern_string in line: - return True - return False - - -def regex_in_file(file_path, pattern_string): - """ - This method uses regex to check if pattern_string exists in the file specified in file_path. - It can match multi-line patterns but is a lot heavier as it parses whole file as string. - :param file_path: files path for the file to search in - :param pattern_string: regex-pattern to search for. - :return: True if the regex-pattern is found found in file, False otherwise. - """ - if not os.path.exists(file_path): - raise RuntimeError("File does not exist at {}".format(file_path)) - if not pattern_string: - raise RuntimeError("Must provide string to search for") - - re.compile(pattern_string) - with open(file_path, "r") as game_log: - return re.search(pattern_string, game_log.read()) - - -def regex_in_lines_in_file(file_path, pattern_string): - """ - This method uses regex to check if pattern_string exists in any line in the file specified in file_path. - It cannot match multi-line patterns, but will often more performant than regex_in_file. - :param file_path: files path for the file to search in - :param pattern_string: regex-pattern to search for. - :return: True if the regex-pattern is found found in file, False otherwise. - """ - if not os.path.exists(file_path): - raise RuntimeError("File does not exist at {}".format(file_path)) - if not pattern_string: - raise RuntimeError("Must provide string to search for") - - re.compile(pattern_string) - with open(file_path, "r") as game_log: - for line in game_log.readlines(): - if re.search(pattern_string, line): - return True - return False diff --git a/Tests/shared/windows_registry_utils.py b/Tests/shared/windows_registry_utils.py deleted file mode 100755 index 9f10317ea9..0000000000 --- a/Tests/shared/windows_registry_utils.py +++ /dev/null @@ -1,96 +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. - -Small library of functions to support autotests for utilizing Windows Utilities - -""" - -import logging -import winreg -logger = logging.getLogger(__name__) - - -def registry_key_exists(registry_hive, registry_key, registry_subkey): - """ - Searches the Windows registry for the existance of a registry key - :param registry_hive: The hive in which to find keys & subkeys. EG: HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER - :param registry_key: The bin directory from which to launch the AssetProcessor executable. - :param registry_subkey: The subkey that can contain a value assignment - :return: A boolean value of the existance of the key - """ - - try: - registryKey = winreg.OpenKey(registry_hive, registry_key) - logger.debug("Registry Key: {0} found.".format(registry_key)) - - winreg.QueryValueEx(registryKey, registry_subkey) - logger.debug("Registry Subkey: {0} found.".format(registry_subkey)) - - registryKey.Close() - - return True - except WindowsError: - # Do not raise an assert since tests could revolve around a non-existant key - logger.debug("Registry SubKey: {0} was not found.".format(registry_key)) - return False - - -def get_registry_key_value(registry_hive, registry_key, registry_subkey): - """ - If a registry key exists, it will return the value else return None - :param registry_hive: The hive in which to find keys & subkeys. EG: HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER - :param registry_key: The bin directory from which to launch the AssetProcessor executable. - :param registry_subkey: The subkey that can contain a value assignment - :return: The value of the registry key value - """ - - if registry_key_exists(registry_hive, registry_key, registry_subkey): - - registryKey = winreg.OpenKey(registry_hive, registry_key) - logger.debug("Registry Key: {0} found.".format(registry_key)) - - subkeyValue = winreg.QueryValueEx(registryKey, registry_subkey) - logger.debug("Registry Subkey: {0} value found is is set to {1}".format(registry_subkey, subkeyValue)) - - registryKey.Close() - logger.debug("Registry Key hander closed") - - return str(subkeyValue[0]) # Index 0 contains the value, Index 1 contains the registry value type - else: - assert None, "Could not retrieve Registry Key Value since Registry Key '{0}' was not found.".format(registry_key) - - -def check_registry_key_value(registry_hive, registry_key, registry_subkey, expected='', case_sensitive=True): - """ - If registry key exists, then case insensitively checks that the registry key value is as expected - :param registry_hive: The hive in which to find keys & subkeys. EG: HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER - :param registry_key: The bin directory from which to launch the AssetProcessor executable. - :param registry_subkey: The subkey that can contain a value assignment - :param expected: The expected value to be found in the registry - :param case_sensitive: Whether or not to perform a case sensitive or insentive validation - :return: A boolean value if the registry key value matches expected - """ - - if registry_key_exists(registry_hive, registry_key, registry_subkey): - - subkeyValue = get_registry_key_value(registry_hive, registry_key, registry_subkey) - - if case_sensitive: - logger.debug("Case sensitive comparison of Subkey '{0}' to Expected Value '{1}'" - .format(subkeyValue, expected)) - return subkeyValue == expected - else: - logger.debug("Case insensitive comparison of Subkey '{0}' to Expected Value '{1}'" - .format(subkeyValue, expected)) - return subkeyValue.lower() == expected.lower() - else: - logger.debug("Could not compare Subkey Value to expected value, Subkey '{0}' was not found at Key {1}." - .format(registry_subkey, registry_key)) - return False diff --git a/Tests/shared/windows_utils.py b/Tests/shared/windows_utils.py deleted file mode 100755 index a6bcff8d91..0000000000 --- a/Tests/shared/windows_utils.py +++ /dev/null @@ -1,130 +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. - -Small library of functions to support autotests for utilizing Windows Utilities - -""" - -import logging -import psutil -import os -import subprocess -import pyscreenshot as winScreenshot - -from _winreg import * - -logger = logging.getLogger(__name__) - -def kill_app_by_name(project): - """ - Kills the app on windows for the specified app name - :param name: name of app running on the devkit to kill - """ - process_name = project + 'Launcher.exe' - for proc in psutil.process_iter(): - # check whether the process to kill name matches - if proc.name() == process_name: - proc.kill() - -def take_screenshot(result_path, image_filename, project=None): - """ - Takes a windows screenshot - :param result_path: path for the output file - :param image_filename: filename for the new image - :param project: project name for the running process, if provided only the window for this process will be captured - """ - if not os.path.exists(result_path): - os.mkdir(result_path) - - filename = "{}.png".format(image_filename) - filename = os.path.join(result_path, filename) - - image = winScreenshot.grab() # bbox=(10, 10, 510, 510)) # X1,Y1,X2,Y2 - image.save(filename) - - if not os.path.exists(filename): - # Capture failed - return False - - return True - -def launch(bin_path, project, parameters = None): - command_line = [os.path.join(bin_path, project + 'Launcher.exe')] - if parameters != None: - command_line = command_line + parameters - process = subprocess.Popen(command_line, stdout=subprocess.PIPE) - process.poll() - -def check_registry_key_exits(registry_key): - """ - Searches the Windows registry for the existance of a registry key - :param registry_key: The bin directory from which to launch the AssetProcessor executable. - :return: A boolean value of the existance of the key - """ - try: - registryHandle = ConnectRegistry(None, HKEY_LOCAL_MACHINE) - registryKey = OpenKey(registryHandle, registry_key) - - logger.info("Registry Key: {0} found.".format(registry_key)) - registryKey.Close() - registryHandle.Close() - - return True - except: - logger.error("Registry Key: {0} was not found.".format(registry_key)) - return False - -def get_registry_key_value(registry_key): - """ - If a registry key exists, it will return the value else return None - :param registry_key: The bin directory from which to launch the AssetProcessor executable. - :return: The value of the registry key value - """ - if check_registry_key_exits(registry_key): - logger.log("Retrieving the value of Registry Key '{0}'" - .format(registry_key)) - - registryHandle = ConnectRegistry(None, HKEY_LOCAL_MACHINE) - registryKey = OpenKey(registryHandle, registry_key) - - keyValue = registryKey.Value() - - registryKey.Close() - registryHandle.Close() - - return keyValue - else: - logger.error("Could not retrieve Registry Key Value since Registry Key '{0}' was not found." - .format(registry_key)) - return None - - -def check_registry_key_value(registry_key, expected): - """ - If registry key exists, then checks that the registry key value is as expected - :param registry_key: The bin directory from which to launch the AssetProcessor executable. - :return: A boolean value if the registry key value matches expected - """ - if check_registry_key_exits(registry_key): - - keyValue = get_registry_key_value(registry_key) - - if str.lower(keyValue) == str.lower(expected): - logger.info("The value of Registry Key '{0}' matched the expected '{1}'" - .format(registry_key, expected)) - return True - else: - logger.error("The value of Registry Key '{0}' die not match the expected '{1}'" - .format(registry_key, expected)) - return False - else: - logger.error("Could not compare Registry Key Value to expected Registry Key '{0}' was not found." - .format(registry_key)) - return False diff --git a/Tests/test_lib/launcher_testlib.py b/Tests/test_lib/launcher_testlib.py deleted file mode 100755 index 00ff8bc598..0000000000 --- a/Tests/test_lib/launcher_testlib.py +++ /dev/null @@ -1,194 +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. - -This launcher_testlib file is used for a collection of reusable functionality that QA will use in their scripts. -""" - -import os - -import test_tools.shared.asset_processor_utils as asset_processor_utils -import test_tools.shared.file_utils as file_utils -import shared.shader_compile_server_utils as compile_server -import test_tools.shared.file_system as file_system - - -def setup_win_launcher_test(launcher): - """ - Assert that the launcher was able to build and that assets were processed successfully. - - :param launcher: The test-tools Launcher to be built. - """ - assert_build_success(launcher) - assert_process_assets(launcher) - - -def setup_mac_launcher_test(launcher, configuration): - """ - Assert that the launcher was able to build and that assets were processed successfully. Also starts the Mac - shader compile server. - - :param launcher: The test-tools Launcher to be built. - :param configuration: The shader compile server configuration to be launched. - """ - assert_build_success(launcher) - assert_process_assets(launcher) - start_mac_shader_compile_server(launcher, configuration, '127.0.0.1') - - -def setup_android_launcher_test(launcher, configuration, win_x64_compiler_version): - """ - Builds the Windows asset processor, asserts that the launcher was able to build and that assets were processed - successfully, and starts the Windows shader compile server. - - :param launcher: The test-tools Launcher to be built. - :param configuration: The shader compile server configuration to be launched. - :param win_x64_compiler_version: The msvc compiler version used to build the shader compile server executable. - """ - assert_asset_processor_build_success(launcher.workspace.release, win_x64_compiler_version) - assert_build_success(launcher) - assert_process_assets(launcher) - - start_shader_compile_server(launcher, configuration, '127.0.0.1', win_x64_compiler_version) - - -def assert_asset_processor_build_success(release, win_x64_compiler_version): - """ - Builds the win_x64 AssetProcessor and asserts on build failure. - - For use with platforms that require Windows tools but not the Windows launcher. - - :param release: The test-tools Release which holds path info for the test environment in use. - :param win_x64_compiler_version: The msvc compiler version used to build the AssetProcessor. - """ - asset_proc_build_success = asset_processor_utils.build_win_x64(release.paths.dev(), release.configuration, - win_x64_compiler_version) - assert asset_proc_build_success, "AssetProcessor did not build properly" - - -def assert_process_assets(launcher): - """ - Runs the Asset Processor Batch and asserts if any assets fail to process. - - :param launcher: The test-tools Launcher to be built. - """ - process_assets_success = launcher.workspace.release.process_assets() - assert process_assets_success, 'Assets did not process correctly' - - -def assert_build_success(launcher): - """ - Runs the build command for specified launcher configuration and asserts if the build fails. - - :param launcher: The test-tools Launcher to be built. - """ - build_success = launcher.workspace.release.build() - assert build_success, "{} - Build Failed! - {} {} {}".format(launcher.workspace.release.project, - launcher.workspace.release.platform, - launcher.workspace.release.configuration, - launcher.workspace.release.spec) - -def start_shader_compile_server(launcher, configuration, ip_addr, win_x64_compiler_version): - """ - Deals with the extra setup to start the Windows shader compiler. - - :param launcher: The test-tools Launcher to be built. - :param configuration: The shader compile server configuration to be launched. - :param ip_addr: The IP address of the remote shader compile server and AssetProcessor. - :param win_x64_compiler_version: The msvc compiler version used to build the shader compile server executable. - """ - launcher.workspace.release.modify_bootstrap_setting('remote_ip', ip_addr) - launcher.workspace.release.modify_platform_setting('r_ShaderCompilerServer', ip_addr) - launcher.workspace.release.modify_platform_setting('log_RemoteConsoleAllowedAddresses', ip_addr) - compile_server.start_shader_compile_server(launcher.workspace.release.paths.tools(), configuration, - win_x64_compiler_version) - - -def start_mac_shader_compile_server(launcher, configuration, ip_addr='127.0.0.1'): - """ - Deals with the extra setup to start the Mac shader compiler. - - :param launcher: The test-tools Launcher to be built. - :param configuration: The shader compile server configuration to be launched. - :param ip_addr: The IP address of the remote shader compile server and AssetProcessor. - """ - launcher.workspace.release.modify_bootstrap_setting('remote_ip', ip_addr) - launcher.workspace.release.modify_platform_setting('r_ShaderCompilerServer', ip_addr) - launcher.workspace.release.modify_platform_setting('log_RemoteConsoleAllowedAddresses', ip_addr) - compile_server.start_mac_shader_compile_server(launcher.workspace.release.paths.tools(), configuration) - - -def set_launcher_startup_config_file(launcher, project_name, config_name, commands): - """ - Clears out the specified config_name file and adds the commands specified to the cfg file. - - :param launcher: The test-tools Launcher to be built. - :param project_name: Name of the game project in test. - :param config_name: Name of the config file holding the launcher's startup commands. - :param commands: List of commands to append to the specified config file. - """ - file_utils.clear_out_config_file(launcher.workspace.release.paths.project(), config_name) - config_path = os.path.join(launcher.workspace.release.paths.platform_cache(), project_name) - file_utils.add_commands_to_config_file(config_path, '{}.cfg'.format(config_name), commands) - - -def configure_setup(launcher, delete_logs=True, delete_shaders=True): - """ - Deletes old artifact folders and clears out any config files. - - :param launcher: - :param delete_logs: Delete the game project's logs directory if True. - :param delete_shaders: Delete the project's cache directory if True. - """ - if delete_logs: - delete_project_logs(launcher) - - if delete_shaders: - delete_shader_cache(launcher) - - file_utils.delete_screenshot_folder(launcher.workspace.release.paths.platform_cache()) - - file_utils.clear_out_config_file(launcher.workspace.release.paths.project(), 'initialmap') - file_utils.clear_out_config_file(launcher.workspace.release.paths.project(), 'autoexec') - - -def delete_project_logs(launcher): - """ - Deletes project logs in the launcher's project folder. - """ - if os.path.exists(launcher.workspace.release.paths.project_log()): - file_system.delete([launcher.workspace.release.paths.project_log()], True, True) - - -def delete_shader_cache(launcher, asset_type="pc"): - """ - Deletes shader cache in the launcher's project folder. - """ - user_folder = os.path.join(launcher.workspace.release.paths.project_cache(), asset_type, "user", "cache") - if os.path.exists(user_folder): - file_system.delete([user_folder], True, True) - - -def retry_console_command(remote_console, command, output, tries=10, timeout=10): - """ - Retries specified console command multiple times and asserts if it still can not send. - :param remote_console: the remote console connected to the launcher. - :param command: the command to send to the console. - :param output: The expected output to check if the command was sent successfully. - :param tries: The amount of times to try before asserting. - :param timeout: The amount of time in seconds to wait for each retry send. - :return: True if succeeded, will assert otherwise. - """ - while tries > 0: - check_command = remote_console.expect_log_line(output, timeout) - remote_console.send_command(command) - if check_command(): - return True - tries -= 1 - assert False, "Command \"{}\" failed to run in remote console.".format(command) diff --git a/Tests/workflow/__init__.py b/Tests/workflow/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/workflow/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/workflow/android/__init__.py b/Tests/workflow/android/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/workflow/android/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/workflow/android/workflow_android.py b/Tests/workflow/android/workflow_android.py deleted file mode 100755 index 4eb0f578b1..0000000000 --- a/Tests/workflow/android/workflow_android.py +++ /dev/null @@ -1,15 +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. - -""" -import logging -import pytest - -import workflow.shared.workflow_shared as workflow_shared \ No newline at end of file diff --git a/Tests/workflow/ios/__init__.py b/Tests/workflow/ios/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/workflow/ios/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/workflow/ios/workflow_ios.py b/Tests/workflow/ios/workflow_ios.py deleted file mode 100755 index 4eb0f578b1..0000000000 --- a/Tests/workflow/ios/workflow_ios.py +++ /dev/null @@ -1,15 +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. - -""" -import logging -import pytest - -import workflow.shared.workflow_shared as workflow_shared \ No newline at end of file diff --git a/Tests/workflow/mac/__init__.py b/Tests/workflow/mac/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/workflow/mac/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/workflow/mac/workflow_mac.py b/Tests/workflow/mac/workflow_mac.py deleted file mode 100755 index 4eb0f578b1..0000000000 --- a/Tests/workflow/mac/workflow_mac.py +++ /dev/null @@ -1,15 +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. - -""" -import logging -import pytest - -import workflow.shared.workflow_shared as workflow_shared \ No newline at end of file diff --git a/Tests/workflow/shared/__init__.py b/Tests/workflow/shared/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/workflow/shared/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/workflow/shared/workflow_shared.py b/Tests/workflow/shared/workflow_shared.py deleted file mode 100755 index 3581f9349e..0000000000 --- a/Tests/workflow/shared/workflow_shared.py +++ /dev/null @@ -1,95 +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. - -This demos_testlib file is used for a collection of reusable functionality that QA will use in their scripts specific -to the setup of demo level tests. -""" -import sys - -import shared.network_utils as network_utils -from shared.screenshot_utils import move_screenshots, take_screenshot_with_retries - -from test_tools.shared.launcher_testlib import * - -import test_tools.shared.waiter -import test_tools.launchers.phase - - -def start_launcher(launcher): - """ - For PC: Used to start launcher and give time to load. - """ - launcher.launch() - launcher.run(test_tools.launchers.phase.TimePhase(120, 120)) - - -def remote_console_load_level(launcher, remote_console, level): - """ - Uses the remote console to use the map command to load a level and checks the console output for a successful load. - """ - command = 'map {}'.format(level) - load = remote_console.expect_log_line('LEVEL_LOAD_COMPLETE', 300) - retry_console_command(remote_console, command, "Executing console command '{}'".format(command)) - assert load(), "{} level failed to load.".format(level) - - # Allow one minute to let level fully render and to test for stability - launcher.run(test_tools.launchers.phase.TimePhase(60, 60)) - - -def start_remote_console(launcher, remote_console, on_devkit=False): - """ - Starts the remote console. Used in QA scripts that require the use of remote console. - """ - if on_devkit: - test_tools.shared.waiter.wait_for(lambda: network_utils.check_for_remote_listening_port(4600, launcher.ip), - timeout=600, exc=AssertionError('Port 4600 not listening.')) - else: - test_tools.shared.waiter.wait_for(lambda: network_utils.check_for_listening_port(4600), timeout=300, - exc=AssertionError('Port 4600 not listening.')) - - remote_console.start() - - # Allows remote console time to connect to launcher. - launcher.run(test_tools.launchers.phase.TimePhase(60, 60)) - - -def remote_console_take_screenshot(launcher, remote_console, level): - """ - Uses the remote console to run the r_GetScreenshot command to take a screenshot of the current launcher and move - the screenshot to the test results location. - """ - screenshot_path = os.path.join(launcher.workspace.release.paths.platform_cache(), "user", "screenshots") - take_screenshot_with_retries(remote_console, launcher, level) - if os.path.exists(screenshot_path): - move_screenshots(screenshot_path, '.jpg', launcher.workspace.artifact_manager.get_save_artifact_path()) - - -def build_setup(launcher, project_dir): - game_cfg = os.path.join(project_dir, 'game.cfg') - test_tools.shared.settings.edit_text_settings_file(game_cfg, 'sys_primaryUserSelectionEnabled', 0) - test_tools.shared.settings.edit_text_settings_file(game_cfg, 'sys_localUserLobbyEnabled', 0) - - -def enable_full_mode(launcher, console_remote_filesystem, console_paks): - print("Enabling FULL Mode") - launcher.workspace.release.modify_bootstrap_setting(console_remote_filesystem, 0) - launcher.workspace.release.modify_user_setting(console_paks, "False") - - -def enable_pak_mode(launcher, console_remote_filesystem, console_paks): - print("Enabling PAK Mode") - launcher.workspace.release.modify_bootstrap_setting(console_remote_filesystem, 0) - launcher.workspace.release.modify_user_setting(console_paks, "True") - - -def enable_vfs_mode(launcher, console_remote_filesystem, console_paks): - print("Enabling VFS Mode") - launcher.workspace.release.modify_bootstrap_setting(console_remote_filesystem, 1) - launcher.workspace.release.modify_user_setting(console_paks, "False") diff --git a/Tests/workflow/win/__init__.py b/Tests/workflow/win/__init__.py deleted file mode 100755 index e912252f4e..0000000000 --- a/Tests/workflow/win/__init__.py +++ /dev/null @@ -1,12 +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. -""" - diff --git a/Tests/workflow/win/workflow_win.py b/Tests/workflow/win/workflow_win.py deleted file mode 100755 index 4eb0f578b1..0000000000 --- a/Tests/workflow/win/workflow_win.py +++ /dev/null @@ -1,15 +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. - -""" -import logging -import pytest - -import workflow.shared.workflow_shared as workflow_shared \ No newline at end of file diff --git a/scripts/build/Jenkins/Jenkinsfile b/scripts/build/Jenkins/Jenkinsfile index ae2779eba8..eb0778791d 100644 --- a/scripts/build/Jenkins/Jenkinsfile +++ b/scripts/build/Jenkins/Jenkinsfile @@ -190,12 +190,14 @@ def CheckoutBootstrapScripts(String branchName) { doGenerateSubmoduleConfigurations: false, extensions: [ [$class: 'PruneStaleBranch'], + [$class: 'AuthorInChangelog'], [$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [ [ $class: 'SparseCheckoutPath', path: 'scripts/build/Jenkins/' ], [ $class: 'SparseCheckoutPath', path: 'scripts/build/bootstrap/' ], [ $class: 'SparseCheckoutPath', path: 'scripts/build/Platform' ] ]], - [$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true] + // Shallow checkouts break changelog computation. Do not enable. + [$class: 'CloneOption', noTags: false, reference: '', shallow: false] ], submoduleCfg: [], userRemoteConfigs: scm.userRemoteConfigs @@ -234,6 +236,7 @@ def CheckoutRepo(boolean disableSubmodules = false) { branches: scm.branches, extensions: [ [$class: 'PruneStaleBranch'], + [$class: 'AuthorInChangelog'], [$class: 'SubmoduleOption', disableSubmodules: disableSubmodules, recursiveSubmodules: true], [$class: 'CheckoutOption', timeout: 60] ], @@ -339,7 +342,10 @@ def TestMetrics(Map pipelineConfig, String workspace, String branchName, String checkout scm: [ $class: 'GitSCM', branches: [[name: '*/main']], - extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'mars']], + extensions: [ + [$class: 'AuthorInChangelog'], + [$class: 'RelativeTargetDirectory', relativeTargetDir: 'mars'] + ], userRemoteConfigs: [[url: "${env.MARS_REPO}", name: 'mars', credentialsId: "${env.GITHUB_USER}"]] ] withCredentials([usernamePassword(credentialsId: "${env.SERVICE_USER}", passwordVariable: 'apitoken', usernameVariable: 'username')]) { diff --git a/scripts/build/Platform/Linux/build_config.json b/scripts/build/Platform/Linux/build_config.json index bbfc3e4269..5d96ae7846 100644 --- a/scripts/build/Platform/Linux/build_config.json +++ b/scripts/build/Platform/Linux/build_config.json @@ -83,7 +83,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=TRUE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest --repeat until-pass:5" } }, "test_profile_nounity": { @@ -95,7 +95,7 @@ "CMAKE_OPTIONS": "-G 'Ninja Multi-Config' -DCMAKE_C_COMPILER=clang-6.0 -DCMAKE_CXX_COMPILER=clang++-6.0 -DLY_UNITY_BUILD=FALSE -DLY_PARALLEL_LINK_JOBS=4 -DO3DE_HOME_PATH=\"${WORKSPACE}/home\" -DO3DE_REGISTER_ENGINE_PATH=\"${WORKSPACE}/o3de\" -DO3DE_REGISTER_THIS_ENGINE=TRUE", "CMAKE_LY_PROJECTS": "AutomatedTesting", "CMAKE_TARGET": "all", - "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest" + "CTEST_OPTIONS": "-E (Gem::EMotionFX.Editor.Tests|Gem::AWSClientAuth.Tests|Gem::AWSCore.Editor.Tests) -L FRAMEWORK_googletest --repeat until-pass:5" } }, "asset_profile": { diff --git a/scripts/build/Platform/Windows/pipeline.json b/scripts/build/Platform/Windows/pipeline.json index 5f10ccc7ae..622fa9d5ae 100644 --- a/scripts/build/Platform/Windows/pipeline.json +++ b/scripts/build/Platform/Windows/pipeline.json @@ -1,6 +1,6 @@ { "ENV": { - "NODE_LABEL": "windows-047e5cdf", + "NODE_LABEL": "windows-b3c8994f1", "LY_3RDPARTY_PATH": "C:/ly/3rdParty", "TIMEOUT": 30, "WORKSPACE": "D:/workspace",