فهرست منبع

Merge branch 'stabilization/2110' into Prism/AddEditGemsButton

nggieber 3 سال پیش
والد
کامیت
95da89d0cc
100فایلهای تغییر یافته به همراه1398 افزوده شده و 1284 حذف شده
  1. 36 0
      AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/aws_client_auth_automation_test.py
  2. 46 34
      AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py
  3. 25 0
      Code/Framework/AzToolsFramework/AzToolsFramework/API/PythonLoader.h
  4. 1 0
      Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
  5. 20 0
      Code/Framework/AzToolsFramework/Platform/Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
  6. 34 0
      Code/Framework/AzToolsFramework/Platform/Linux/AzToolsFramework/API/PythonLoader_Linux.cpp
  7. 1 0
      Code/Framework/AzToolsFramework/Platform/Linux/platform_linux_files.cmake
  8. 1 0
      Code/Framework/AzToolsFramework/Platform/Mac/platform_mac_files.cmake
  9. 1 0
      Code/Framework/AzToolsFramework/Platform/Windows/platform_windows_files.cmake
  10. 68 0
      Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp
  11. 13 0
      Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h
  12. 1 0
      Code/Tools/AssetProcessor/AssetBuilderSDK/CMakeLists.txt
  13. 4 60
      Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp
  14. 9 2
      Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp
  15. 28 1
      Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp
  16. 54 0
      Code/Tools/ProjectManager/Source/ProjectManagerSettings.cpp
  17. 21 0
      Code/Tools/ProjectManager/Source/ProjectManagerSettings.h
  18. 24 3
      Code/Tools/ProjectManager/Source/ProjectsScreen.cpp
  19. 18 0
      Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp
  20. 2 0
      Code/Tools/ProjectManager/project_manager_files.cmake
  21. 44 0
      Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h
  22. 3 2
      Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSCognitoAuthorizationController.h
  23. 122 0
      Gems/AWSClientAuth/Code/Source/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.cpp
  24. 11 4
      Gems/AWSClientAuth/Code/Source/Authorization/AWSCognitoAuthorizationController.cpp
  25. 10 10
      Gems/AWSClientAuth/Code/Tests/Authorization/AWSCognitoAuthorizationControllerTest.cpp
  26. 2 0
      Gems/AWSClientAuth/Code/awsclientauth_files.cmake
  27. 16 0
      Gems/AWSCore/cdk/README.md
  28. 9 1
      Gems/AWSCore/cdk/core/core_stack.py
  29. 11 0
      Gems/AWSCore/cdk/example/example_resources_stack.py
  30. 0 50
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Albedo.preset
  31. 0 45
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AlbedoWithCoverage.preset
  32. 1 51
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AlbedoWithGenericAlpha.preset
  33. 0 30
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AmbientOcclusion.preset
  34. 0 20
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ConvolvedCubemap.preset
  35. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Decal_AlbedoWithOpacity.preset
  36. 0 60
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Displacement.preset
  37. 0 35
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Emissive.preset
  38. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Gradient.preset
  39. 5 20
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Greyscale.preset
  40. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLDiffuse.preset
  41. 0 5
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLGlobal.preset
  42. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSkybox.preset
  43. 0 20
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecular.preset
  44. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularHigh.preset
  45. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularLow.preset
  46. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularVeryHigh.preset
  47. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularVeryLow.preset
  48. 150 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ImageBuilder.settings
  49. 0 1
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_R32F.preset
  50. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RG16.preset
  51. 0 1
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RG32F.preset
  52. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RG8.preset
  53. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA16.preset
  54. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA16F.preset
  55. 0 1
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA32F.preset
  56. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA8.preset
  57. 0 20
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LayerMask.preset
  58. 0 55
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Normals.preset
  59. 0 35
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/NormalsWithSmoothness.preset
  60. 5 60
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Opacity.preset
  61. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage.preset
  62. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage_HDRLinear.preset
  63. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage_HDRLinearUncompressed.preset
  64. 0 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage_Linear.preset
  65. 66 0
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Reflectance.preset
  66. 0 15
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Skybox.preset
  67. 1 2
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/UserInterface_Compressed.preset
  68. 1 2
      Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/UserInterface_Lossless.preset
  69. 272 125
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp
  70. 52 12
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.h
  71. 1 1
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp
  72. 66 1
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp
  73. 45 13
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp
  74. 1 1
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h
  75. 1 2
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp
  76. 7 0
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.cpp
  77. 2 0
      Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.h
  78. 1 1
      Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp
  79. 0 67
      Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings
  80. 0 157
      Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset
  81. 8 1
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli
  82. 2 0
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h
  83. 2 0
      Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h
  84. 8 29
      Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp
  85. 0 8
      Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h
  86. 5 0
      Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp
  87. 1 0
      Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h
  88. 5 0
      Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp
  89. 1 0
      Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h
  90. 3 2
      Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp
  91. 1 2
      Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h
  92. 1 0
      Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/__init__.py
  93. 19 11
      Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/capture_displaymapper.py
  94. 7 12
      Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/initialize.py
  95. 3 3
      Gems/Atom/Feature/Common/Tools/ColorGrading/cmdline/CMD_ColorGradingTools.bat
  96. 10 36
      Gems/Atom/Feature/Common/Tools/ColorGrading/cmdline/Env_Python.bat
  97. 0 5
      Gems/Atom/Feature/Common/Tools/ColorGrading/cmdline/User_Env.bat.template
  98. 2 0
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.h
  99. 7 0
      Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h
  100. 1 0
      Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h

+ 36 - 0
AutomatedTesting/Gem/PythonTests/AWS/Windows/client_auth/aws_client_auth_automation_test.py

@@ -12,6 +12,7 @@ import pytest
 import ly_test_tools.log.log_monitor
 
 from AWS.common import constants
+from AWS.common.resource_mappings import AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY
 
 # fixture imports
 from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
@@ -70,6 +71,41 @@ class TestAWSClientAuthWindows(object):
                 halt_on_unexpected=True,
             )
             assert result, 'Anonymous credentials fetched successfully.'
+    
+    @pytest.mark.parametrize('level', ['AWS/ClientAuth'])
+    def test_anonymous_credentials_no_global_accountid(self,
+                                                       level: str,
+                                                       launcher: pytest.fixture,
+                                                       resource_mappings: pytest.fixture,
+                                                       workspace: pytest.fixture,
+                                                       asset_processor: pytest.fixture
+                                                       ):
+        """
+        Test to verify AWS Cognito Identity pool anonymous authorization.
+
+        Setup: Updates resource mapping file using existing CloudFormation stacks.
+        Tests: Getting credentials when no credentials are configured
+        Verification: Log monitor looks for success credentials log.
+        """
+        # Remove top-level account ID from resource mappings
+        resource_mappings.clear_select_keys([AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY])
+        
+        asset_processor.start()
+        asset_processor.wait_for_idle()
+
+        file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
+        log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
+
+        launcher.args = ['+LoadLevel', level]
+        launcher.args.extend(['-rhi=null'])
+
+        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.'
 
     def test_password_signin_credentials(self,
                                          launcher: pytest.fixture,

+ 46 - 34
AutomatedTesting/Gem/PythonTests/PythonAssetBuilder/AssetBuilder_test_case.py

@@ -8,42 +8,54 @@ import azlmbr.bus
 import azlmbr.asset
 import azlmbr.editor
 import azlmbr.math
-import azlmbr.legacy.general
 
-def raise_and_stop(msg):
-    print (msg)
-    azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
-
-# These tests are meant to check that the test_asset.mock source asset turned into
-# a test_asset.mock_asset product asset via the Python asset builder system
-mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
-mockAssetPath = 'gem/pythontests/pythonassetbuilder/test_asset.mock_asset'
-assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', mockAssetPath, mockAssetType, False)
-if (assetId.is_valid() is False):
-    raise_and_stop(f'Mock AssetId is not valid! Got {assetId.to_string()} instead')
-
-assetIdString = assetId.to_string()
-if (assetIdString.endswith(':528cca58') is False):
-    raise_and_stop(f'Mock AssetId {assetIdString} has unexpected sub-id for {mockAssetPath}!')
-
-print ('Mock asset exists')
+print('Starting mock asset tests')
+handler = azlmbr.editor.EditorEventBusHandler()
+
+def on_notify_editor_initialized(args):
+    # These tests are meant to check that the test_asset.mock source asset turned into
+    # a test_asset.mock_asset product asset via the Python asset builder system
+    mockAssetType = azlmbr.math.Uuid_CreateString('{9274AD17-3212-4651-9F3B-7DCCB080E467}', 0)
+    mockAssetPath = 'gem/pythontests/pythonassetbuilder/test_asset.mock_asset'
+    assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', mockAssetPath, mockAssetType, False)
+    if (assetId.is_valid() is False):
+        print(f'Mock AssetId is not valid! Got {assetId.to_string()} instead')
+    else:
+        print(f'Mock AssetId is valid!')
 
-# These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
-def test_azmodel_product(generatedModelAssetPath):
-    azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
-    assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
     assetIdString = assetId.to_string()
-    if (assetId.is_valid()):
-        print(f'AssetId found for asset ({generatedModelAssetPath}) found')
+    if (assetIdString.endswith(':528cca58') is False):
+        print(f'Mock AssetId {assetIdString} has unexpected sub-id for {mockAssetPath}!')
     else:
-        raise_and_stop(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString})!')
-
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel')
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel')
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel')
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel')
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel')
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel')
-test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel')
+        print(f'Mock AssetId has expected sub-id for {mockAssetPath}!')
+
+    print ('Mock asset exists')
+
+    # These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
+    def test_azmodel_product(generatedModelAssetPath):
+        azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
+        assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
+        assetIdString = assetId.to_string()
+        if (assetId.is_valid()):
+            print(f'AssetId found for asset ({generatedModelAssetPath}) found')
+        else:
+            print(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString})!')
+
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel')
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel')
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel')
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel')
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel')
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel')
+    test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel')
+
+    # clear up notification handler
+    global handler
+    handler.disconnect()
+    handler = None
+
+    print('Finished mock asset tests')
+    azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
 
-azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
+handler.connect()
+handler.add_callback('NotifyEditorInitialized', on_notify_editor_initialized)

+ 25 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/API/PythonLoader.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+namespace AzToolsFramework::EmbeddedPython
+{
+    // When using embedded Python, some platforms need to explicitly load the python library.
+    // For any modules that depend on 3rdParty::Python package, the AZ::Module should inherit this class.
+    class PythonLoader
+    {
+    public:
+        PythonLoader();
+        ~PythonLoader();
+
+    private:
+        void* m_embeddedLibPythonHandle{ nullptr };
+    };
+
+} // namespace AzToolsFramework::EmbeddedPython

+ 1 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake

@@ -47,6 +47,7 @@ set(FILES
     API/EntityCompositionRequestBus.h
     API/EntityCompositionNotificationBus.h
     API/EditorViewportIconDisplayInterface.h
+    API/PythonLoader.h
     API/ViewPaneOptions.h
     API/ViewportEditorModeTrackerInterface.h
     Application/Ticker.h

+ 20 - 0
Code/Framework/AzToolsFramework/Platform/Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp

@@ -0,0 +1,20 @@
+/*
+* Copyright (c) Contributors to the Open 3D Engine Project.
+* For complete copyright and license terms please see the LICENSE at the root of this distribution.
+*
+* SPDX-License-Identifier: Apache-2.0 OR MIT
+*
+*/
+
+#include <AzToolsFramework/API/PythonLoader.h>
+
+namespace AzToolsFramework::EmbeddedPython
+{
+    PythonLoader::PythonLoader()
+    {
+    }
+
+    PythonLoader::~PythonLoader()
+    {
+    }
+}

+ 34 - 0
Code/Framework/AzToolsFramework/Platform/Linux/AzToolsFramework/API/PythonLoader_Linux.cpp

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzToolsFramework/API/PythonLoader.h>
+#include <AzCore/Debug/Trace.h>
+#include <dlfcn.h>
+
+namespace AzToolsFramework::EmbeddedPython
+{
+    PythonLoader::PythonLoader()
+    {
+        constexpr char libPythonName[] = "libpython3.7m.so.1.0";
+        if (m_embeddedLibPythonHandle = dlopen(libPythonName, RTLD_NOW | RTLD_GLOBAL);
+            m_embeddedLibPythonHandle == nullptr)
+        {
+            char* err = dlerror();
+            AZ_Error("PythonLoader", false, "Failed to load %s with error: %s\n", libPythonName, err ? err : "Unknown Error");
+        }
+    }
+
+    PythonLoader::~PythonLoader()
+    {
+        if (m_embeddedLibPythonHandle)
+        {
+            dlclose(m_embeddedLibPythonHandle);
+        }
+    }
+    
+} // namespace AzToolsFramework::EmbeddedPython

+ 1 - 0
Code/Framework/AzToolsFramework/Platform/Linux/platform_linux_files.cmake

@@ -7,4 +7,5 @@
 #
 
 set(FILES
+    AzToolsFramework/API/PythonLoader_Linux.cpp
 )

+ 1 - 0
Code/Framework/AzToolsFramework/Platform/Mac/platform_mac_files.cmake

@@ -7,4 +7,5 @@
 #
 
 set(FILES
+    ../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
 )

+ 1 - 0
Code/Framework/AzToolsFramework/Platform/Windows/platform_windows_files.cmake

@@ -7,4 +7,5 @@
 #
 
 set(FILES
+    ../Common/Default/AzToolsFramework/API/PythonLoader_Default.cpp
 )

+ 68 - 0
Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.cpp

@@ -23,6 +23,8 @@
 #include <AzCore/RTTI/BehaviorContext.h>
 //////////////////////////////////////////////////////////////////////////
 
+#include <xxhash/xxhash.h>
+
 namespace AssetBuilderSDK
 {
     const char* const ErrorWindow = "Error"; //Use this window name to log error messages.
@@ -1599,4 +1601,70 @@ namespace AssetBuilderSDK
     {
         return m_errorsOccurred;
     }
+
+    AZ::u64 GetHashFromIOStream(AZ::IO::GenericStream& readStream, AZ::IO::SizeType* bytesReadOut, int hashMsDelay)
+    {        
+        constexpr AZ::u64 HashBufferSize = 1024 * 64;
+        char buffer[HashBufferSize];
+
+        if(readStream.IsOpen() && readStream.CanRead())
+        {
+            AZ::IO::SizeType bytesRead;
+
+            auto* state = XXH64_createState();
+
+            if(state == nullptr)
+            {
+                AZ_Assert(false, "Failed to create hash state");
+                return 0;
+            }
+
+            if (XXH64_reset(state, 0) == XXH_ERROR)
+            {
+                AZ_Assert(false, "Failed to reset hash state");
+                return 0;
+            }
+
+            do
+            {
+                // In edge cases where another process is writing to this file while this hashing is occuring and that file wasn't locked,
+                // the following read check can fail because it performs an end of file check, and asserts and shuts down if the read size
+                // was smaller than the buffer and the read is not at the end of the file. The logic used to check end of file internal to read
+                // will be out of date in the edge cases where another process is actively writing to this file while this hash is running.
+                // The stream's length ends up more accurate in this case, preventing this assert and shut down.
+                // One area this occurs is the navigation mesh file (mnmnavmission0.bai) that's temporarily created when exporting a level,
+                // the navigation system can still be writing to this file when hashing begins, causing the EoF marker to change.
+                AZ::IO::SizeType remainingToRead = AZStd::min(readStream.GetLength() - readStream.GetCurPos(), aznumeric_cast<AZ::IO::SizeType>(AZ_ARRAY_SIZE(buffer)));
+                bytesRead = readStream.Read(remainingToRead, buffer);
+
+                if(bytesReadOut)
+                {
+                    *bytesReadOut += bytesRead;
+                }
+
+                XXH64_update(state, buffer, bytesRead);
+
+                // Used by unit tests to force the race condition mentioned above, to verify the crash fix.
+                if(hashMsDelay > 0)
+                {
+                    AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(hashMsDelay));
+                }
+
+            } while (bytesRead > 0);
+
+            auto hash = XXH64_digest(state);
+
+            XXH64_freeState(state);
+
+            return hash;
+        }
+        return 0;
+    }
+
+    AZ::u64 GetFileHash(const char* filePath, AZ::IO::SizeType* bytesReadOut, int hashMsDelay)
+    {
+        constexpr bool ErrorOnReadFailure = true;
+        AZ::IO::FileIOStream readStream(filePath, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, ErrorOnReadFailure);
+        return GetHashFromIOStream(readStream, bytesReadOut, hashMsDelay);
+    }
 }

+ 13 - 0
Code/Tools/AssetProcessor/AssetBuilderSDK/AssetBuilderSDK/AssetBuilderSDK.h

@@ -910,6 +910,19 @@ namespace AssetBuilderSDK
         //! There can be multiple builders running at once, so we need to filter out ones coming from other builders
         AZStd::thread_id m_jobThreadId;
     };
+
+    //! Get hash for a whole file
+    //! @filePath the path for the file
+    //! @bytesReadOut output the read file size in bytes
+    //! @hashMsDelay [Do not use except for unit test] add a delay in ms for between each block reading.
+    AZ::u64 GetFileHash(const char* filePath, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
+
+    //! Get hash for a generic IO stream
+    //! @readStream the input readable stream
+    //! @bytesReadOut output the read size in bytes
+    //! @hashMsDelay [Do not use except for unit test] add a delay in ms for between each block reading.
+    AZ::u64 GetHashFromIOStream(AZ::IO::GenericStream& readStream, AZ::IO::SizeType* bytesReadOut = nullptr, int hashMsDelay = 0);
+
 } // namespace AssetBuilderSDK
 
 namespace AZ

+ 1 - 0
Code/Tools/AssetProcessor/AssetBuilderSDK/CMakeLists.txt

@@ -32,6 +32,7 @@ ly_add_target(
         PUBLIC
             AZ::AzFramework
             AZ::AzToolsFramework
+            3rdParty::xxhash
 )
 ly_add_source_properties(
     SOURCES AssetBuilderSDK/AssetBuilderSDK.cpp

+ 4 - 60
Code/Tools/AssetProcessor/native/utilities/assetUtils.cpp

@@ -1161,7 +1161,7 @@ namespace AssetUtilities
     {
 #ifndef AZ_TESTS_ENABLED
         // Only used for unit tests, speed is critical for GetFileHash.
-        AZ_UNUSED(hashMsDelay);
+        hashMsDelay = 0;
 #endif
         bool useFileHashing = ShouldUseFileHashing();
 
@@ -1170,10 +1170,10 @@ namespace AssetUtilities
             return 0;
         }
 
+        AZ::u64 hash = 0;
         if(!force)
         {
             auto* fileStateInterface = AZ::Interface<AssetProcessor::IFileStateRequests>::Get();
-            AZ::u64 hash = 0;
 
             if (fileStateInterface && fileStateInterface->GetHash(filePath, &hash))
             {
@@ -1181,64 +1181,8 @@ namespace AssetUtilities
             }
         }
 
-        char buffer[FileHashBufferSize];
-
-        constexpr bool ErrorOnReadFailure = true;
-        AZ::IO::FileIOStream readStream(filePath, AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeBinary, ErrorOnReadFailure);
-
-        if(readStream.IsOpen() && readStream.CanRead())
-        {
-            AZ::IO::SizeType bytesRead;
-
-            auto* state = XXH64_createState();
-
-            if(state == nullptr)
-            {
-                AZ_Assert(false, "Failed to create hash state");
-                return 0;
-            }
-
-            if (XXH64_reset(state, 0) == XXH_ERROR)
-            {
-                AZ_Assert(false, "Failed to reset hash state");
-                return 0;
-            }
-
-            do
-            {
-                // In edge cases where another process is writing to this file while this hashing is occuring and that file wasn't locked,
-                // the following read check can fail because it performs an end of file check, and asserts and shuts down if the read size
-                // was smaller than the buffer and the read is not at the end of the file. The logic used to check end of file internal to read
-                // will be out of date in the edge cases where another process is actively writing to this file while this hash is running.
-                // The stream's length ends up more accurate in this case, preventing this assert and shut down.
-                // One area this occurs is the navigation mesh file (mnmnavmission0.bai) that's temporarily created when exporting a level,
-                // the navigation system can still be writing to this file when hashing begins, causing the EoF marker to change.
-                AZ::IO::SizeType remainingToRead = AZStd::min(readStream.GetLength() - readStream.GetCurPos(), aznumeric_cast<AZ::IO::SizeType>(AZ_ARRAY_SIZE(buffer)));
-                bytesRead = readStream.Read(remainingToRead, buffer);
-
-                if(bytesReadOut)
-                {
-                    *bytesReadOut += bytesRead;
-                }
-
-                XXH64_update(state, buffer, bytesRead);
-#ifdef AZ_TESTS_ENABLED
-                // Used by unit tests to force the race condition mentioned above, to verify the crash fix.
-                if(hashMsDelay > 0)
-                {
-                    AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(hashMsDelay));
-                }
-#endif
-
-            } while (bytesRead > 0);
-
-            auto hash = XXH64_digest(state);
-
-            XXH64_freeState(state);
-
-            return hash;
-        }
-        return 0;
+        hash = AssetBuilderSDK::GetFileHash(filePath, bytesReadOut, hashMsDelay);
+        return hash;
     }
 
     AZ::u64 AdjustTimestamp(QDateTime timestamp)

+ 9 - 2
Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp

@@ -363,7 +363,10 @@ namespace O3DE::ProjectManager
         {
             const QString selectedGemPath = m_gemModel->GetPath(modelIndex);
 
-            // Remove gem from gems to be added
+            const bool wasAdded = GemModel::WasPreviouslyAdded(modelIndex);
+            const bool wasAddedDependency = GemModel::WasPreviouslyAddedDependency(modelIndex);
+
+            // Remove gem from gems to be added to update any dependencies
             GemModel::SetIsAdded(*m_gemModel, modelIndex, false);
 
             // Unregister the gem
@@ -391,6 +394,8 @@ namespace O3DE::ProjectManager
 
                 // Select remote gem
                 QModelIndex remoteGemIndex = m_gemModel->FindIndexByNameString(selectedGemName);
+                GemModel::SetWasPreviouslyAdded(*m_gemModel, remoteGemIndex, wasAdded);
+                GemModel::SetWasPreviouslyAddedDependency(*m_gemModel, remoteGemIndex, wasAddedDependency);
                 QModelIndex proxyIndex = m_proxyModel->mapFromSource(remoteGemIndex);
                 m_proxyModel->GetSelectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect);
             }
@@ -523,7 +528,9 @@ namespace O3DE::ProjectManager
             const QString& gemPath = GemModel::GetPath(modelIndex);
 
             // make sure any remote gems we added were downloaded successfully 
-            if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote && GemModel::GetDownloadStatus(modelIndex) != GemInfo::Downloaded)
+            const GemInfo::DownloadStatus status = GemModel::GetDownloadStatus(modelIndex);
+            if (GemModel::GetGemOrigin(modelIndex) == GemInfo::Remote &&
+                !(status == GemInfo::Downloaded || status == GemInfo::DownloadSuccessful))
             {
                 QMessageBox::critical(
                     nullptr, "Cannot add gem that isn't downloaded",

+ 28 - 1
Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp

@@ -9,12 +9,14 @@
 #include <ProjectBuilderController.h>
 #include <ProjectBuilderWorker.h>
 #include <ProjectButtonWidget.h>
+#include <ProjectManagerSettings.h>
+
+#include <AzCore/Settings/SettingsRegistry.h>
 
 #include <QMessageBox>
 #include <QDesktopServices>
 #include <QUrl>
 
-
 namespace O3DE::ProjectManager
 {
     ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent)
@@ -27,6 +29,15 @@ namespace O3DE::ProjectManager
         m_worker = new ProjectBuilderWorker(m_projectInfo);
         m_worker->moveToThread(&m_workerThread);
 
+        auto settingsRegistry = AZ::SettingsRegistry::Get();
+        if (settingsRegistry)
+        {
+            // Remove key here in case Project Manager crashing while building that causes HandleResults to not be called
+            QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
+            settingsRegistry->Remove(settingsKey.toStdString().c_str());
+            SaveProjectManagerSettings();
+        }
+
         connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater);
         connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject);
         connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults);
@@ -80,6 +91,8 @@ namespace O3DE::ProjectManager
 
     void ProjectBuilderController::HandleResults(const QString& result)
     {
+        QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
+
         if (!result.isEmpty())
         {
             if (result.contains(tr("log")))
@@ -109,12 +122,26 @@ namespace O3DE::ProjectManager
                 emit NotifyBuildProject(m_projectInfo);
             }
 
+            auto settingsRegistry = AZ::SettingsRegistry::Get();
+            if (settingsRegistry)
+            {
+                settingsRegistry->Remove(settingsKey.toStdString().c_str());
+                SaveProjectManagerSettings();
+            }
+
             emit Done(false);
             return;
         }
         else
         {
             m_projectInfo.m_buildFailed = false;
+
+            auto settingsRegistry = AZ::SettingsRegistry::Get();
+            if (settingsRegistry)
+            {
+                settingsRegistry->Set(settingsKey.toStdString().c_str(), true);
+                SaveProjectManagerSettings();
+            }
         }
 
         emit Done(true);

+ 54 - 0
Code/Tools/ProjectManager/Source/ProjectManagerSettings.cpp

@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "ProjectManagerSettings.h"
+
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <AzCore/IO/ByteContainerStream.h>
+#include <AzCore/Utils/Utils.h>
+
+namespace O3DE::ProjectManager
+{
+    void SaveProjectManagerSettings()
+    {
+        auto settingsRegistry = AZ::SettingsRegistry::Get();
+        AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
+        dumperSettings.m_prettifyOutput = true;
+        dumperSettings.m_jsonPointerPrefix = ProjectManagerKeyPrefix;
+
+        AZStd::string stringBuffer;
+        AZ::IO::ByteContainerStream stringStream(&stringBuffer);
+        if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(
+                *settingsRegistry, ProjectManagerKeyPrefix, stringStream, dumperSettings))
+        {
+            AZ_Warning("ProjectManager", false, "Could not save Project Manager settings to stream");
+            return;
+        }
+
+        AZ::IO::FixedMaxPath o3deUserPath = AZ::Utils::GetO3deManifestDirectory();
+        o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder;
+        o3deUserPath /= "ProjectManager.setreg";
+
+        bool saved = false;
+        constexpr auto configurationMode =
+            AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
+
+        AZ::IO::SystemFile outputFile;
+        if (outputFile.Open(o3deUserPath.c_str(), configurationMode))
+        {
+            saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
+        }
+
+        AZ_Warning("ProjectManager", saved, "Unable to save Project Manager registry file to path: %s", o3deUserPath.c_str());
+    }
+
+    QString GetProjectBuiltSuccessfullyKey(const QString& projectName)
+    {
+        return QString("%1/Projects/%2/BuiltSuccessfully").arg(ProjectManagerKeyPrefix).arg(projectName);
+    }
+}

+ 21 - 0
Code/Tools/ProjectManager/Source/ProjectManagerSettings.h

@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#if !defined(Q_MOC_RUN)
+#include <QString>
+#endif
+
+namespace O3DE::ProjectManager
+{
+    static constexpr char ProjectManagerKeyPrefix[] = "/O3DE/ProjectManager";
+
+    void SaveProjectManagerSettings();
+    QString GetProjectBuiltSuccessfullyKey(const QString& projectName);
+}

+ 24 - 3
Code/Tools/ProjectManager/Source/ProjectsScreen.cpp

@@ -14,6 +14,7 @@
 #include <ProjectUtils.h>
 #include <ProjectBuilderController.h>
 #include <ScreensCtrl.h>
+#include <ProjectManagerSettings.h>
 
 #include <AzQtComponents/Components/FlowLayout.h>
 #include <AzCore/Platform.h>
@@ -22,6 +23,7 @@
 #include <AzFramework/Process/ProcessCommon.h>
 #include <AzFramework/Process/ProcessWatcher.h>
 #include <AzCore/Utils/Utils.h>
+#include <AzCore/Settings/SettingsRegistry.h>
 
 #include <QVBoxLayout>
 #include <QHBoxLayout>
@@ -270,17 +272,36 @@ namespace O3DE::ProjectManager
             // Add any missing project buttons and restore buttons to default state
             for (const ProjectInfo& project : projectsVector)
             {
+                ProjectButton* currentButton = nullptr;
                 if (!m_projectButtons.contains(QDir::toNativeSeparators(project.m_path)))
                 {
-                    m_projectButtons.insert(QDir::toNativeSeparators(project.m_path), CreateProjectButton(project));
+                    currentButton = CreateProjectButton(project);
+                    m_projectButtons.insert(QDir::toNativeSeparators(project.m_path), currentButton);
                 }
                 else
                 {
                     auto projectButtonIter = m_projectButtons.find(QDir::toNativeSeparators(project.m_path));
                     if (projectButtonIter != m_projectButtons.end())
                     {
-                        projectButtonIter.value()->RestoreDefaultState();
-                        m_projectsFlowLayout->addWidget(projectButtonIter.value());
+                        currentButton = projectButtonIter.value();
+                        currentButton->RestoreDefaultState();
+                        m_projectsFlowLayout->addWidget(currentButton);
+                    }
+                }
+
+                // Check whether project manager has successfully built the project
+                if (currentButton)
+                {
+                    auto settingsRegistry = AZ::SettingsRegistry::Get();
+                    bool projectBuiltSuccessfully = false;
+                    if (settingsRegistry)
+                    {
+                        QString settingsKey = GetProjectBuiltSuccessfullyKey(project.m_projectName);
+                        settingsRegistry->Get(projectBuiltSuccessfully, settingsKey.toStdString().c_str());
+                    }
+                    if (!projectBuiltSuccessfully)
+                    {
+                        currentButton->ShowBuildRequired();
                     }
                 }
             }

+ 18 - 0
Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp

@@ -15,6 +15,9 @@
 #include <UpdateProjectCtrl.h>
 #include <UpdateProjectSettingsScreen.h>
 #include <ProjectUtils.h>
+#include <ProjectManagerSettings.h>
+
+#include <AzCore/Settings/SettingsRegistry.h>
 
 #include <QDialogButtonBox>
 #include <QMessageBox>
@@ -300,6 +303,21 @@ namespace O3DE::ProjectManager
                 }
             }
 
+            if (newProjectSettings.m_projectName != m_projectInfo.m_projectName)
+            {
+                // update reg key
+                QString oldSettingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName);
+                QString newSettingsKey = GetProjectBuiltSuccessfullyKey(newProjectSettings.m_projectName);
+
+                auto settingsRegistry = AZ::SettingsRegistry::Get();
+                bool projectBuiltSuccessfully = false;
+                if (settingsRegistry && settingsRegistry->Get(projectBuiltSuccessfully, oldSettingsKey.toStdString().c_str()))
+                {
+                    settingsRegistry->Set(newSettingsKey.toStdString().c_str(), projectBuiltSuccessfully);
+                    SaveProjectManagerSettings();
+                }
+            }
+
             if (!newProjectSettings.m_newPreviewImagePath.isEmpty())
             {
                 if (!ProjectUtils::ReplaceProjectFile(

+ 2 - 0
Code/Tools/ProjectManager/project_manager_files.cmake

@@ -58,6 +58,8 @@ set(FILES
     Source/CreateProjectCtrl.cpp
     Source/UpdateProjectCtrl.h
     Source/UpdateProjectCtrl.cpp
+    Source/ProjectManagerSettings.h
+    Source/ProjectManagerSettings.cpp
     Source/ProjectsScreen.h
     Source/ProjectsScreen.cpp
     Source/ProjectSettingsScreen.h

+ 44 - 0
Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <aws/cognito-identity/CognitoIdentityClient.h>
+#include <aws/identity-management/auth/CognitoCachingCredentialsProvider.h>
+#include <aws/identity-management/auth/PersistentCognitoIdentityProvider.h>
+
+namespace AWSClientAuth
+{
+    //! Cognito Caching Credentials Provider implementation that is derived from AWS Native SDK.
+    //! For use with authenticated credentials.
+    class AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider
+        : public Aws::Auth::CognitoCachingCredentialsProvider
+    {
+    public:
+        AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider(
+            const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
+            const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient = nullptr);
+
+    protected:
+        Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome GetCredentialsFromCognito() const override;
+    };
+
+    //! Cognito Caching Credentials Provider implementation that is eventually derived from AWS Native SDK.
+    //! For use with anonymous credentials.
+    class AWSClientAuthCachingAnonymousCredsProvider : public AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider
+    {
+    public:
+        AWSClientAuthCachingAnonymousCredsProvider(
+            const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
+            const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient = nullptr);
+
+    protected:
+        Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome GetCredentialsFromCognito() const override;
+    };
+
+} // namespace AWSClientAuth

+ 3 - 2
Gems/AWSClientAuth/Code/Include/Private/Authorization/AWSCognitoAuthorizationController.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <Authorization/AWSCognitoAuthorizationBus.h>
+#include <Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h>
 #include <Authorization/AWSClientAuthPersistentCognitoIdentityProvider.h>
 #include <Authentication/AuthenticationProviderBus.h>
 #include <Credential/AWSCredentialBus.h>
@@ -51,8 +52,8 @@ namespace AWSClientAuth
 
         std::shared_ptr<AWSClientAuthPersistentCognitoIdentityProvider> m_persistentCognitoIdentityProvider;
         std::shared_ptr<AWSClientAuthPersistentCognitoIdentityProvider> m_persistentAnonymousCognitoIdentityProvider;
-        std::shared_ptr<Aws::Auth::CognitoCachingAuthenticatedCredentialsProvider> m_cognitoCachingCredentialsProvider;
-        std::shared_ptr<Aws::Auth::CognitoCachingAnonymousCredentialsProvider> m_cognitoCachingAnonymousCredentialsProvider;
+        std::shared_ptr<AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider> m_cognitoCachingCredentialsProvider;
+        std::shared_ptr<AWSClientAuthCachingAnonymousCredsProvider> m_cognitoCachingAnonymousCredentialsProvider;
 
         AZStd::string m_cognitoIdentityPoolId;
         AZStd::string m_formattedCognitoUserPoolId;

+ 122 - 0
Gems/AWSClientAuth/Code/Source/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.cpp

@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h>
+
+#include <AzCore/Debug/Trace.h>
+
+#include <aws/cognito-identity/CognitoIdentityClient.h>
+#include <aws/cognito-identity/model/GetCredentialsForIdentityRequest.h>
+#include <aws/cognito-identity/model/GetIdRequest.h>
+#include <aws/core/utils/Outcome.h>
+#include <aws/core/utils/logging/LogMacros.h>
+#include <aws/identity-management/auth/CognitoCachingCredentialsProvider.h>
+#include <aws/identity-management/auth/PersistentCognitoIdentityProvider.h>
+
+
+namespace AWSClientAuth
+{
+    static const char* AUTH_LOG_TAG = "AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider";
+    static const char* ANON_LOG_TAG = "AWSClientAuthCachingAnonymousCredsProvider";
+
+    // Modification of https://github.com/aws/aws-sdk-cpp/blob/main/aws-cpp-sdk-identity-management/source/auth/CognitoCachingCredentialsProvider.cpp#L92
+    // to work around account ID requirement. Account id is not required for call to succeed and is not set unless provided.
+    // see: https://github.com/aws/aws-sdk-cpp/issues/1448
+    Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome FetchCredsFromCognito(
+        const Aws::CognitoIdentity::CognitoIdentityClient& cognitoIdentityClient,
+        Aws::Auth::PersistentCognitoIdentityProvider& identityRepository,
+        const char* logTag,
+        bool includeLogins)
+    {
+        auto logins = identityRepository.GetLogins();
+        Aws::Map<Aws::String, Aws::String> cognitoLogins;
+        for (auto& login : logins)
+        {
+            cognitoLogins[login.first] = login.second.accessToken;
+        }
+
+        if (!identityRepository.HasIdentityId())
+        {
+            auto accountId = identityRepository.GetAccountId();
+            auto identityPoolId = identityRepository.GetIdentityPoolId();
+
+            Aws::CognitoIdentity::Model::GetIdRequest getIdRequest;
+            getIdRequest.SetIdentityPoolId(identityPoolId);
+
+            if (!accountId.empty()) // new check
+            {
+                getIdRequest.SetAccountId(accountId);
+                AWS_LOGSTREAM_INFO(logTag, "Identity not found, requesting an id for accountId "
+                    << accountId << " identity pool id "
+                    << identityPoolId << " with logins.");
+            }
+            else
+            {
+                AWS_LOGSTREAM_INFO(
+                    logTag, "Identity not found, requesting an id for identity pool id %s" << identityPoolId << " with logins.");
+            }
+            if (includeLogins)
+            {
+                getIdRequest.SetLogins(cognitoLogins);
+            }
+
+            auto getIdOutcome = cognitoIdentityClient.GetId(getIdRequest);
+            if (getIdOutcome.IsSuccess())
+            {
+                auto identityId = getIdOutcome.GetResult().GetIdentityId();
+                AWS_LOGSTREAM_INFO(logTag, "Successfully retrieved identity: " << identityId);
+                identityRepository.PersistIdentityId(identityId);
+            }
+            else
+            {
+                AWS_LOGSTREAM_ERROR(
+                    logTag,
+                    "Failed to retrieve identity. Error: " << getIdOutcome.GetError().GetExceptionName() << " "
+                                                           << getIdOutcome.GetError().GetMessage());
+                return Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome(getIdOutcome.GetError());
+            }
+        }
+
+        Aws::CognitoIdentity::Model::GetCredentialsForIdentityRequest getCredentialsForIdentityRequest;
+        getCredentialsForIdentityRequest.SetIdentityId(identityRepository.GetIdentityId());
+        if (includeLogins)
+        {
+            getCredentialsForIdentityRequest.SetLogins(cognitoLogins);
+        }
+
+        return cognitoIdentityClient.GetCredentialsForIdentity(getCredentialsForIdentityRequest);
+    }
+
+    AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider::AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider(
+        const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
+        const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient)
+        : CognitoCachingCredentialsProvider(identityRepository, cognitoIdentityClient)
+    {
+    }
+
+    Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome
+    AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider::GetCredentialsFromCognito() const
+    {
+        return FetchCredsFromCognito(*m_cognitoIdentityClient, *m_identityRepository, AUTH_LOG_TAG, true);
+    }
+
+    AWSClientAuthCachingAnonymousCredsProvider::AWSClientAuthCachingAnonymousCredsProvider(
+        const std::shared_ptr<Aws::Auth::PersistentCognitoIdentityProvider>& identityRepository,
+        const std::shared_ptr<Aws::CognitoIdentity::CognitoIdentityClient>& cognitoIdentityClient)
+        : AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider(identityRepository, cognitoIdentityClient)
+    {
+    }
+
+    Aws::CognitoIdentity::Model::GetCredentialsForIdentityOutcome AWSClientAuthCachingAnonymousCredsProvider::
+        GetCredentialsFromCognito() const
+    {
+        return FetchCredsFromCognito(*m_cognitoIdentityClient, *m_identityRepository, ANON_LOG_TAG, false);
+    }
+
+
+} // namespace AWSClientAuth

+ 11 - 4
Gems/AWSClientAuth/Code/Source/Authorization/AWSCognitoAuthorizationController.cpp

@@ -8,6 +8,7 @@
 
 #include <AWSClientAuthBus.h>
 #include <AWSCoreBus.h>
+#include <Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h>
 #include <Authorization/AWSCognitoAuthorizationController.h>
 #include <ResourceMapping/AWSResourceMappingBus.h>
 #include <AWSClientAuthResourceMappingConstants.h>
@@ -38,10 +39,12 @@ namespace AWSClientAuth
         auto identityClient = AZ::Interface<IAWSClientAuthRequests>::Get()->GetCognitoIdentityClient();
 
         m_cognitoCachingCredentialsProvider =
-            std::make_shared<Aws::Auth::CognitoCachingAuthenticatedCredentialsProvider>(m_persistentCognitoIdentityProvider, identityClient);
+            std::make_shared<AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider>(
+            m_persistentCognitoIdentityProvider, identityClient);
 
         m_cognitoCachingAnonymousCredentialsProvider =
-            std::make_shared<Aws::Auth::CognitoCachingAnonymousCredentialsProvider>(m_persistentAnonymousCognitoIdentityProvider, identityClient);
+            std::make_shared<AWSClientAuthCachingAnonymousCredsProvider>(
+            m_persistentAnonymousCognitoIdentityProvider, identityClient);
     }
 
     AWSCognitoAuthorizationController::~AWSCognitoAuthorizationController()
@@ -65,9 +68,13 @@ namespace AWSClientAuth
         AWSCore::AWSResourceMappingRequestBus::BroadcastResult(
             m_cognitoIdentityPoolId, &AWSCore::AWSResourceMappingRequests::GetResourceNameId, CognitoIdentityPoolIdResourceMappingKey);
 
-        if (m_awsAccountId.empty() || m_cognitoIdentityPoolId.empty())
+        if (m_awsAccountId.empty())
+        {
+            AZ_TracePrintf("AWSCognitoAuthorizationController", "AWS account id not not configured. Proceeding without it.");
+        }
+
+        if (m_cognitoIdentityPoolId.empty())
         {
-            AZ_Warning("AWSCognitoAuthorizationController", !m_awsAccountId.empty(), "Missing AWS account id not configured.");
             AZ_Warning("AWSCognitoAuthorizationController", !m_cognitoIdentityPoolId.empty(), "Missing Cognito Identity pool id in resource mappings.");
             return false;
         }

+ 10 - 10
Gems/AWSClientAuth/Code/Tests/Authorization/AWSCognitoAuthorizationControllerTest.cpp

@@ -62,6 +62,14 @@ TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Success)
     ASSERT_TRUE(m_mockController->m_cognitoIdentityPoolId == AWSClientAuthUnitTest::TEST_RESOURCE_NAME_ID);
 }
 
+TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Success_GetAWSAccountEmpty)
+{
+    EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetResourceNameId(testing::_)).Times(2);
+    EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultAccountId()).Times(1).WillOnce(testing::Return(""));
+    EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultRegion()).Times(1);
+    ASSERT_TRUE(m_mockController->Initialize());
+}
+
 TEST_F(AWSCognitoAuthorizationControllerTest, RequestAWSCredentials_WithLogins_Success)
 {
     AWSClientAuth::AuthenticationTokens tokens(
@@ -121,7 +129,7 @@ TEST_F(AWSCognitoAuthorizationControllerTest, MultipleCalls_UsesCacheCredentials
     m_mockController->RequestAWSCredentialsAsync();
 }
 
-TEST_F(AWSCognitoAuthorizationControllerTest, RequestAWSCredentials_Fail_GetIdError)
+TEST_F(AWSCognitoAuthorizationControllerTest, RequestAWSCredentials_Fail_GetIdError) // fail
 {
     AWSClientAuth::AuthenticationTokens cognitoTokens(
         AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN, AWSClientAuthUnitTest::TEST_TOKEN,
@@ -321,7 +329,7 @@ TEST_F(AWSCognitoAuthorizationControllerTest, GetCredentialsProvider_NoPersisted
     EXPECT_TRUE(actualCredentialsProvider == m_mockController->m_cognitoCachingAnonymousCredentialsProvider);
 }
 
-TEST_F(AWSCognitoAuthorizationControllerTest, GetCredentialsProvider_NoPersistedLogins_NoAnonymousCredentials_ResultNullPtr)
+TEST_F(AWSCognitoAuthorizationControllerTest, GetCredentialsProvider_NoPersistedLogins_NoAnonymousCredentials_ResultNullPtr) // fails
 {
     Aws::Client::AWSError<Aws::CognitoIdentity::CognitoIdentityErrors> error;
     error.SetExceptionName(AWSClientAuthUnitTest::TEST_EXCEPTION);
@@ -431,11 +439,3 @@ TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Fail_GetResourceNameEmp
     EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultAccountId()).Times(1);
     ASSERT_FALSE(m_mockController->Initialize());
 }
-
-TEST_F(AWSCognitoAuthorizationControllerTest, Initialize_Fail_GetAWSAccountEmpty)
-{
-    EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetResourceNameId(testing::_)).Times(1);
-    EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultAccountId()).Times(1).WillOnce(testing::Return(""));
-    EXPECT_CALL(m_awsResourceMappingRequestBusMock, GetDefaultRegion()).Times(0);
-    ASSERT_FALSE(m_mockController->Initialize());
-}

+ 2 - 0
Gems/AWSClientAuth/Code/awsclientauth_files.cmake

@@ -24,6 +24,7 @@ set(FILES
     Include/Private/Authorization/AWSCognitoAuthorizationController.h
     Include/Private/Authorization/AWSClientAuthPersistentCognitoIdentityProvider.h
     Include/Private/Authorization/AWSCognitoAuthorizationNotificationBusBehaviorHandler.h
+    Include/Private/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.h
 
     Include/Private/UserManagement/AWSCognitoUserManagementController.h
     Include/Private/UserManagement/UserManagementNotificationBusBehaviorHandler.h
@@ -45,6 +46,7 @@ set(FILES
     Source/Authorization/ClientAuthAWSCredentials.cpp
     Source/Authorization/AWSCognitoAuthorizationController.cpp
     Source/Authorization/AWSClientAuthPersistentCognitoIdentityProvider.cpp
+    Source/Authorization/AWSClientAuthCognitoCachingAuthenticatedCredentialsProvider.cpp
 
     Source/UserManagement/AWSCognitoUserManagementController.cpp
 )

+ 16 - 0
Gems/AWSCore/cdk/README.md

@@ -65,6 +65,22 @@ them to your `setup.py` file and rerun the `pip install -r requirements.txt`
 command.
 
 ## Optional Features
+
+Optional features are activated by passing [runtime context variables](https://docs.aws.amazon.com/cdk/latest/guide/context.html). To use multiple optional features together provide one key-value pair at a time:
+```
+cdk synth --context key1=value1 --context key2=value2 MyStack
+```
+
+### Automatic S3 and DynamoDB Cleanup
+The S3 bucket and Dynamodb created by the sample will be left behind as the CDK defaults to retaining such storage (both have default policies to retain resources on destroy). To delete
+the storage resources created when using CDK destroy, use the following commands to synthesize and destroy the CDK application.
+```
+cdk synth -c remove_all_storage_on_destroy=true --all
+cdk deploy -c remove_all_storage_on_destroy=true --all
+cdk destroy --all
+```
+
+### Server Access Logging
 Server access logging is enabled by default. To disable the feature, use the following commands to synthesize and deploy this CDK application.
 
 ```

+ 9 - 1
Gems/AWSCore/cdk/core/core_stack.py

@@ -86,13 +86,21 @@ class CoreStack(core.Stack):
         # Create an S3 bucket for Amazon S3 server access logging
         # See https://docs.aws.amazon.com/AmazonS3/latest/dev/security-best-practices.html
         if self.node.try_get_context('disable_access_log') != 'true':
+
+            # Auto cleanup bucket and data if requested
+            _remove_storage = self.node.try_get_context('remove_all_storage_on_destroy') == 'true'
+            _removal_policy = core.RemovalPolicy.DESTROY if _remove_storage else core.RemovalPolicy.RETAIN
+
             self._server_access_logs_bucket = s3.Bucket(
                 self,
                 f'{self._project_name}-{self._feature_name}-Access-Log-Bucket',
+                access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE,
+                auto_delete_objects = _remove_storage,
                 block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
                 encryption=s3.BucketEncryption.S3_MANAGED,
-                access_control=s3.BucketAccessControl.LOG_DELIVERY_WRITE
+                removal_policy=_removal_policy
             )
+
             self._server_access_logs_bucket.grant_read(self._admin_group)
 
             # Export access log bucket name

+ 11 - 0
Gems/AWSCore/cdk/example/example_resources_stack.py

@@ -126,11 +126,17 @@ class ExampleResources(core.Stack):
                 core.Fn.import_value(f"{self._project_name}:ServerAccessLogsBucket")
             )
 
+        # Auto cleanup bucket and data if requested
+        _remove_storage = self.node.try_get_context('remove_all_storage_on_destroy') == 'true'
+        _removal_policy = core.RemovalPolicy.DESTROY if _remove_storage else core.RemovalPolicy.RETAIN
+
         example_bucket = s3.Bucket(
             self,
             f'{self._project_name}-{self._feature_name}-Example-S3bucket',
+            auto_delete_objects=_remove_storage,
             block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
             encryption=s3.BucketEncryption.S3_MANAGED,
+            removal_policy=_removal_policy,
             server_access_logs_bucket=
             server_access_logs_bucket if server_access_logs_bucket else None,
             server_access_logs_prefix=
@@ -170,6 +176,11 @@ class ExampleResources(core.Stack):
                 type=dynamo.AttributeType.STRING
             )
         )
+
+        # Auto-delete the table when requested
+        if self.node.try_get_context('remove_all_storage_on_destroy') == 'true':
+            demo_table.apply_removal_policy(core.RemovalPolicy.DESTROY)
+
         return demo_table
 
     def __create_outputs(self) -> None:

+ 0 - 50
Gems/Atom/Asset/ImageProcessingAtom/Config/Albedo.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Albedo.preset

@@ -7,16 +7,6 @@
             "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
             "Name": "Albedo",
             "RGB_Weight": "CIEXYZ",
-            "FileMasks": [
-                "_basecolor",
-                "_diff",
-                "_color",
-                "_col",
-                "_albedo",
-                "_alb",
-                "_bc",
-                "_diffuse"
-            ],
             "PixelFormat": "BC1",
             "DiscardAlpha": true,
             "IsPowerOf2": true,
@@ -29,16 +19,6 @@
                 "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
                 "Name": "Albedo",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_col",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
                 "DiscardAlpha": true,
@@ -51,16 +31,6 @@
                 "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
                 "Name": "Albedo",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_col",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
                 "DiscardAlpha": true,
@@ -73,16 +43,6 @@
                 "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
                 "Name": "Albedo",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_col",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "BC1",
                 "DiscardAlpha": true,
                 "IsPowerOf2": true,
@@ -94,16 +54,6 @@
                 "UUID": "{08A95286-ADB2-41E4-96EB-DB48F4726D6A}",
                 "Name": "Albedo",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_col",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "BC1",
                 "DiscardAlpha": true,
                 "IsPowerOf2": true,

+ 0 - 45
Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithCoverage.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AlbedoWithCoverage.preset

@@ -7,15 +7,6 @@
             "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
             "Name": "AlbedoWithCoverage",
             "RGB_Weight": "CIEXYZ",
-            "FileMasks": [
-                "_diff",
-                "_color",
-                "_albedo",
-                "_alb",
-                "_basecolor",
-                "_bc",
-                "_diffuse"
-            ],
             "PixelFormat": "BC1a",
             "IsPowerOf2": true,
             "MipMapSetting": {
@@ -27,15 +18,6 @@
                 "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
                 "Name": "AlbedoWithCoverage",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "IsPowerOf2": true,
                 "MipMapSetting": {
@@ -46,15 +28,6 @@
                 "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
                 "Name": "AlbedoWithCoverage",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "IsPowerOf2": true,
                 "MipMapSetting": {
@@ -65,15 +38,6 @@
                 "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
                 "Name": "AlbedoWithCoverage",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "BC1a",
                 "IsPowerOf2": true,
                 "MipMapSetting": {
@@ -84,15 +48,6 @@
                 "UUID": "{57ED16B1-407B-4E29-BCFC-D3BAE60F2C85}",
                 "Name": "AlbedoWithCoverage",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "BC1a",
                 "IsPowerOf2": true,
                 "MipMapSetting": {

+ 1 - 51
Gems/Atom/Asset/ImageProcessingAtom/Config/AlbedoWithGenericAlpha.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AlbedoWithGenericAlpha.preset

@@ -7,17 +7,7 @@
             "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
             "Name": "AlbedoWithGenericAlpha",
             "RGB_Weight": "CIEXYZ",
-            "FileMasks": [
-                "_diff",
-                "_color",
-                "_albedo",
-                "_alb",
-                "_basecolor",
-                "_bc",
-                "_diffuse"
-            ],
-            "PixelFormat": "BC3",
-            "IsPowerOf2": true,
+            "PixelFormat": "ASTC_4x4",
             "MipMapSetting": {
                 "MipGenType": "Box"
             }
@@ -27,18 +17,8 @@
                 "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
                 "Name": "AlbedoWithGenericAlpha",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
-                "IsPowerOf2": true,
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }
@@ -47,18 +27,8 @@
                 "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
                 "Name": "AlbedoWithGenericAlpha",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
-                "IsPowerOf2": true,
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }
@@ -67,17 +37,7 @@
                 "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
                 "Name": "AlbedoWithGenericAlpha",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "BC3",
-                "IsPowerOf2": true,
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }
@@ -86,17 +46,7 @@
                 "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
                 "Name": "AlbedoWithGenericAlpha",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_diff",
-                    "_color",
-                    "_albedo",
-                    "_alb",
-                    "_basecolor",
-                    "_bc",
-                    "_diffuse"
-                ],
                 "PixelFormat": "BC3",
-                "IsPowerOf2": true,
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }

+ 0 - 30
Gems/Atom/Asset/ImageProcessingAtom/Config/AmbientOcclusion.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/AmbientOcclusion.preset

@@ -8,12 +8,6 @@
             "Name": "AmbientOcclusion",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_ao",
-                "_ambocc",
-                "_amb",
-                "_ambientocclusion"
-            ],
             "PixelFormat": "BC4"
         },
         "PlatformsPresets": {
@@ -22,12 +16,6 @@
                 "Name": "AmbientOcclusion",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ao",
-                    "_ambocc",
-                    "_amb",
-                    "_ambientocclusion"
-                ],
                 "MaxTextureSize": 2048,
                 "PixelFormat": "ASTC_4x4"
             },
@@ -36,12 +24,6 @@
                 "Name": "AmbientOcclusion",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ao",
-                    "_ambocc",
-                    "_amb",
-                    "_ambientocclusion"
-                ],
                 "MaxTextureSize": 2048,
                 "PixelFormat": "ASTC_4x4"
             },
@@ -50,12 +32,6 @@
                 "Name": "AmbientOcclusion",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ao",
-                    "_ambocc",
-                    "_amb",
-                    "_ambientocclusion"
-                ],
                 "PixelFormat": "BC4"
             },
             "provo": {
@@ -63,12 +39,6 @@
                 "Name": "AmbientOcclusion",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ao",
-                    "_ambocc",
-                    "_amb",
-                    "_ambientocclusion"
-                ],
                 "PixelFormat": "BC4"
             }
         }

+ 0 - 20
Gems/Atom/Asset/ImageProcessingAtom/Config/ConvolvedCubemap.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ConvolvedCubemap.preset

@@ -8,10 +8,6 @@
             "Name": "ConvolvedCubemap",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_ccm",
-                "_convolvedcubemap"
-            ],
             "SuppressEngineReduce": true,
             "PixelFormat": "R9G9B9E5",
             "DiscardAlpha": true,
@@ -35,10 +31,6 @@
                 "Name": "ConvolvedCubemap",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ccm",
-                    "_convolvedcubemap"
-                ],
                 "SuppressEngineReduce": true,
                 "PixelFormat": "R9G9B9E5",
                 "DiscardAlpha": true,
@@ -61,10 +53,6 @@
                 "Name": "ConvolvedCubemap",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ccm",
-                    "_convolvedcubemap"
-                ],
                 "SuppressEngineReduce": true,
                 "PixelFormat": "R9G9B9E5",
                 "DiscardAlpha": true,
@@ -87,10 +75,6 @@
                 "Name": "ConvolvedCubemap",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ccm",
-                    "_convolvedcubemap"
-                ],
                 "SuppressEngineReduce": true,
                 "PixelFormat": "R9G9B9E5",
                 "DiscardAlpha": true,
@@ -113,10 +97,6 @@
                 "Name": "ConvolvedCubemap",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ccm",
-                    "_convolvedcubemap"
-                ],
                 "SuppressEngineReduce": true,
                 "PixelFormat": "R9G9B9E5",
                 "DiscardAlpha": true,

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/Decal_AlbedoWithOpacity.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Decal_AlbedoWithOpacity.preset

@@ -6,9 +6,6 @@
         "DefaultPreset": {
             "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
             "Name": "Decal_AlbedoWithOpacity",
-            "FileMasks": [
-                "_decal"
-            ],
             "PixelFormat": "BC7t",
             "IsPowerOf2": true,
             "MipMapSetting": {
@@ -21,9 +18,6 @@
             "android": {
                 "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
                 "Name": "Decal_AlbedoWithOpacity",
-                "FileMasks": [
-                    "_decal"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "MaxTextureSize": 2048,
                 "IsPowerOf2": true,
@@ -36,9 +30,6 @@
             "ios": {
                 "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
                 "Name": "Decal_AlbedoWithOpacity",
-                "FileMasks": [
-                    "_decal"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "MaxTextureSize": 2048,
                 "IsPowerOf2": true,
@@ -51,9 +42,6 @@
             "mac": {
                 "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
                 "Name": "Decal_AlbedoWithOpacity",
-                "FileMasks": [
-                    "_decal"
-                ],
                 "PixelFormat": "BC3",
                 "IsPowerOf2": true,
                 "MipMapSetting": {
@@ -65,9 +53,6 @@
             "provo": {
                 "UUID": "{E06B5087-2640-49B6-B9BA-D40048162B90}",
                 "Name": "Decal_AlbedoWithOpacity",
-                "FileMasks": [
-                    "_decal"
-                ],
                 "PixelFormat": "BC7t",
                 "IsPowerOf2": true,
                 "MipMapSetting": {

+ 0 - 60
Gems/Atom/Asset/ImageProcessingAtom/Config/Displacement.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Displacement.preset

@@ -8,18 +8,6 @@
             "Name": "Displacement",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_displ",
-                "_disp",
-                "_dsp",
-                "_d",
-                "_dm",
-                "_displacement",
-                "_height",
-                "_hm",
-                "_ht",
-                "_h"
-            ],
             "PixelFormat": "BC4",
             "DiscardAlpha": true,
             "IsPowerOf2": true,
@@ -33,18 +21,6 @@
                 "Name": "Displacement",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_displ",
-                    "_disp",
-                    "_dsp",
-                    "_d",
-                    "_dm",
-                    "_displacement",
-                    "_height",
-                    "_hm",
-                    "_ht",
-                    "_h"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "MaxTextureSize": 2048,
                 "DiscardAlpha": true,
@@ -59,18 +35,6 @@
                 "Name": "Displacement",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_displ",
-                    "_disp",
-                    "_dsp",
-                    "_d",
-                    "_dm",
-                    "_displacement",
-                    "_height",
-                    "_hm",
-                    "_ht",
-                    "_h"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "MaxTextureSize": 2048,
                 "DiscardAlpha": true,
@@ -84,18 +48,6 @@
                 "Name": "Displacement",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_displ",
-                    "_disp",
-                    "_dsp",
-                    "_d",
-                    "_dm",
-                    "_displacement",
-                    "_height",
-                    "_hm",
-                    "_ht",
-                    "_h"
-                ],
                 "PixelFormat": "BC4",
                 "DiscardAlpha": true,
                 "IsPowerOf2": true,
@@ -108,18 +60,6 @@
                 "Name": "Displacement",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_displ",
-                    "_disp",
-                    "_dsp",
-                    "_d",
-                    "_dm",
-                    "_displacement",
-                    "_height",
-                    "_hm",
-                    "_ht",
-                    "_h"
-                ],
                 "PixelFormat": "BC4",
                 "DiscardAlpha": true,
                 "IsPowerOf2": true,

+ 0 - 35
Gems/Atom/Asset/ImageProcessingAtom/Config/Emissive.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Emissive.preset

@@ -7,13 +7,6 @@
             "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
             "Name": "Emissive",
             "RGB_Weight": "CIEXYZ",
-            "FileMasks": [
-                    "_emissive",
-                    "_e",
-                    "_glow",
-                    "_em",
-                    "_emit"
-            ],
             "PixelFormat": "BC7",
             "DiscardAlpha": true
         },
@@ -22,13 +15,6 @@
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
                 "Name": "Emissive",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_emissive",
-                    "_e",
-                    "_glow",
-                    "_em",
-                    "_emit"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
                 "DiscardAlpha": true
@@ -37,13 +23,6 @@
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
                 "Name": "Emissive",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_emissive",
-                    "_e",
-                    "_glow",
-                    "_em",
-                    "_emit"
-                ],
                 "PixelFormat": "ASTC_6x6",
                 "MaxTextureSize": 2048,
                 "DiscardAlpha": true
@@ -52,13 +31,6 @@
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
                 "Name": "Emissive",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_emissive",
-                    "_e",
-                    "_glow",
-                    "_em",
-                    "_emit"
-                ],
                 "PixelFormat": "BC7",
                 "DiscardAlpha": true
             },
@@ -66,13 +38,6 @@
                 "UUID": "{07041D83-E0C3-4726-8735-CA0FE550C9A0}",
                 "Name": "Emissive",
                 "RGB_Weight": "CIEXYZ",
-                "FileMasks": [
-                    "_emissive",
-                    "_e",
-                    "_glow",
-                    "_em",
-                    "_emit"
-                ],
                 "PixelFormat": "BC7",
                 "DiscardAlpha": true
             }

+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/Gradient.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Gradient.preset


+ 5 - 20
Gems/Atom/Asset/ImageProcessingAtom/Config/Greyscale.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Greyscale.preset

@@ -8,11 +8,8 @@
             "Name": "Greyscale",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_mask"
-            ],
             "PixelFormat": "BC4",
-            "IsPowerOf2": true,
+            "Swizzle": "rrr1",
             "MipMapSetting": {
                 "MipGenType": "Box"
             }
@@ -23,11 +20,8 @@
                 "Name": "Greyscale",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_mask"
-                ],
                 "PixelFormat": "ASTC_4x4",
-                "IsPowerOf2": true,
+                "Swizzle": "rrr1",
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }
@@ -37,11 +31,8 @@
                 "Name": "Greyscale",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_mask"
-                ],
                 "PixelFormat": "ASTC_4x4",
-                "IsPowerOf2": true,
+                "Swizzle": "rrr1",
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }
@@ -51,11 +42,8 @@
                 "Name": "Greyscale",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_mask"
-                ],
                 "PixelFormat": "BC4",
-                "IsPowerOf2": true,
+                "Swizzle": "rrr1",
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }
@@ -65,11 +53,8 @@
                 "Name": "Greyscale",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_mask"
-                ],
                 "PixelFormat": "BC4",
-                "IsPowerOf2": true,
+                "Swizzle": "rrr1",
                 "MipMapSetting": {
                     "MipGenType": "Box"
                 }

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLDiffuse.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLDiffuse.preset

@@ -7,9 +7,6 @@
             "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}",
             "Name": "IBLDiffuse",
             "Description": "The input cubemap generates an IBL diffuse output cubemap.",
-            "FileMasks": [
-                "_ibldiffusecm"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -31,9 +28,6 @@
             "android": {
                 "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}",
                 "Name": "IBLDiffuse",
-                "FileMasks": [
-                    "_ibldiffusecm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -54,9 +48,6 @@
             "ios": {
                 "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}",
                 "Name": "IBLDiffuse",
-                "FileMasks": [
-                    "_ibldiffusecm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -77,9 +68,6 @@
             "mac": {
                 "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}",
                 "Name": "IBLDiffuse",
-                "FileMasks": [
-                    "_ibldiffusecm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -100,9 +88,6 @@
             "provo": {
                 "UUID": "{E3706342-BF21-4D9C-AE28-9670EB3EF3C5}",
                 "Name": "IBLDiffuse",
-                "FileMasks": [
-                    "_ibldiffusecm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 0 - 5
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLGlobal.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLGlobal.preset

@@ -8,11 +8,6 @@
             "Name": "IBLGlobal",
             "Description": "The input cubemap generates IBL specular and diffuse cubemaps.",
             "GenerateIBLOnly": true,
-            "FileMasks": [
-                "_iblglobalcm",
-                "_cubemap",
-                "_cm"
-            ],
             "CubemapSettings": {
                 "GenerateIBLSpecular": true,
                 "IBLSpecularPreset": "IBLSpecular",

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSkybox.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSkybox.preset

@@ -7,9 +7,6 @@
             "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}",
             "Name": "IBLSkybox",
             "Description": "The input cubemap generates a skybox, IBL specular, and IBL diffuse output cubemaps.",
-            "FileMasks": [
-                "_iblskyboxcm"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -29,9 +26,6 @@
             "android": {
                 "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}",
                 "Name": "IBLSkybox",
-                "FileMasks": [
-                    "_iblskyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -50,9 +44,6 @@
             "ios": {
                 "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}",
                 "Name": "IBLSkybox",
-                "FileMasks": [
-                    "_iblskyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -71,9 +62,6 @@
             "mac": {
                 "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}",
                 "Name": "IBLSkybox",
-                "FileMasks": [
-                    "_iblskyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -92,9 +80,6 @@
             "provo": {
                 "UUID": "{E6441EAC-9843-484B-8EFC-C03B2935B48D}",
                 "Name": "IBLSkybox",
-                "FileMasks": [
-                    "_iblskyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 0 - 20
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecular.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecular.preset

@@ -7,10 +7,6 @@
             "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
             "Name": "IBLSpecular",
             "Description": "The input cubemap generates an IBL specular output cubemap.",
-            "FileMasks": [
-                "_iblspecularcm",
-                "_iblspecularcm256"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -34,10 +30,6 @@
             "android": {
                 "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
                 "Name": "IBLSpecular",
-                "FileMasks": [
-                    "_iblspecularcm",
-                    "_iblspecularcm256"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -60,10 +52,6 @@
             "ios": {
                 "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
                 "Name": "IBLSpecular",
-                "FileMasks": [
-                    "_iblspecularcm",
-                    "_iblspecularcm256"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -86,10 +74,6 @@
             "mac": {
                 "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
                 "Name": "IBLSpecular",
-                "FileMasks": [
-                    "_iblspecularcm",
-                    "_iblspecularcm256"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -112,10 +96,6 @@
             "provo": {
                 "UUID": "{908DA68C-97FB-4C4A-97BC-5A55F30F14FA}",
                 "Name": "IBLSpecular",
-                "FileMasks": [
-                    "_iblspecularcm",
-                    "_iblspecularcm256"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularHigh.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularHigh.preset

@@ -7,9 +7,6 @@
             "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
             "Name": "IBLSpecularHigh",
             "Description": "The input cubemap generates an IBL specular output cubemap.",
-            "FileMasks": [
-                "_iblspecularcm512"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -33,9 +30,6 @@
             "android": {
                 "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
                 "Name": "IBLSpecularHigh",
-                "FileMasks": [
-                    "_iblspecularcm512"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -58,9 +52,6 @@
             "ios": {
                 "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
                 "Name": "IBLSpecularHigh",
-                "FileMasks": [
-                    "_iblspecularcm512"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -83,9 +74,6 @@
             "mac": {
                 "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
                 "Name": "IBLSpecularHigh",
-                "FileMasks": [
-                    "_iblspecularcm512"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -108,9 +96,6 @@
             "provo": {
                 "UUID": "{B66395E1-8D0E-4159-989B-FC2B9F091B75}",
                 "Name": "IBLSpecularHigh",
-                "FileMasks": [
-                    "_iblspecularcm512"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularLow.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularLow.preset

@@ -7,9 +7,6 @@
             "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
             "Name": "IBLSpecularLow",
             "Description": "The input cubemap generates an IBL specular output cubemap.",
-            "FileMasks": [
-                "_iblspecularcm128"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -33,9 +30,6 @@
             "android": {
                 "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
                 "Name": "IBLSpecularLow",
-                "FileMasks": [
-                    "_iblspecularcm128"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -58,9 +52,6 @@
             "ios": {
                 "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
                 "Name": "IBLSpecularLow",
-                "FileMasks": [
-                    "_iblspecularcm128"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -83,9 +74,6 @@
             "mac": {
                 "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
                 "Name": "IBLSpecularLow",
-                "FileMasks": [
-                    "_iblspecularcm128"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -108,9 +96,6 @@
             "provo": {
                 "UUID": "{7273ACAE-6E34-487C-AF71-99423A6E1CB0}",
                 "Name": "IBLSpecularLow",
-                "FileMasks": [
-                    "_iblspecularcm128"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryHigh.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularVeryHigh.preset

@@ -7,9 +7,6 @@
             "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
             "Name": "IBLSpecularVeryHigh",
             "Description": "The input cubemap generates an IBL specular output cubemap.",
-            "FileMasks": [
-                "_iblspecularcm1024"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -33,9 +30,6 @@
             "android": {
                 "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
                 "Name": "IBLSpecularVeryHigh",
-                "FileMasks": [
-                    "_iblspecularcm1024"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -58,9 +52,6 @@
             "ios": {
                 "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
                 "Name": "IBLSpecularVeryHigh",
-                "FileMasks": [
-                    "_iblspecularcm1024"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -83,9 +74,6 @@
             "mac": {
                 "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
                 "Name": "IBLSpecularVeryHigh",
-                "FileMasks": [
-                    "_iblspecularcm1024"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -108,9 +96,6 @@
             "provo": {
                 "UUID": "{5CD1AFA6-915B-4716-893C-A5B1F4074C22}",
                 "Name": "IBLSpecularVeryHigh",
-                "FileMasks": [
-                    "_iblspecularcm1024"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/IBLSpecularVeryLow.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/IBLSpecularVeryLow.preset

@@ -7,9 +7,6 @@
             "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
             "Name": "IBLSpecularVeryLow",
             "Description": "The input cubemap generates an IBL specular output cubemap.",
-            "FileMasks": [
-                "_iblspecularcm64"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -33,9 +30,6 @@
             "android": {
                 "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
                 "Name": "IBLSpecularVeryLow",
-                "FileMasks": [
-                    "_iblspecularcm64"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -58,9 +52,6 @@
             "ios": {
                 "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
                 "Name": "IBLSpecularVeryLow",
-                "FileMasks": [
-                    "_iblspecularcm64"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -83,9 +74,6 @@
             "mac": {
                 "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
                 "Name": "IBLSpecularVeryLow",
-                "FileMasks": [
-                    "_iblspecularcm64"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -108,9 +96,6 @@
             "provo": {
                 "UUID": "{8293C236-D3E8-4352-8B18-C2E82EEE6547}",
                 "Name": "IBLSpecularVeryLow",
-                "FileMasks": [
-                    "_iblspecularcm64"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 150 - 0
Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ImageBuilder.settings

@@ -0,0 +1,150 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "BuilderSettingManager",
+    "ClassData": {
+        "BuildSettings": {
+            "android": {
+                "GlossScale": 16.0,
+                "GlossBias": 0.0,
+                "Streaming": false,
+                "Enable": true
+            },
+            "ios": {
+                "GlossScale": 16.0,
+                "GlossBias": 0.0,
+                "Streaming": false,
+                "Enable": true
+            },
+            "mac": {
+                "GlossScale": 16.0,
+                "GlossBias": 0.0,
+                "Streaming": false,
+                "Enable": true
+            },
+            "pc": {
+                "GlossScale": 16.0,
+                "GlossBias": 0.0,
+                "Streaming": false,
+                "Enable": true
+            },
+            "linux": {
+                "GlossScale": 16.0,
+                "GlossBias": 0.0,
+                "Streaming": false,
+                "Enable": true
+            },
+            "provo": {
+                "GlossScale": 16.0,
+                "GlossBias": 0.0,
+                "Streaming": false,
+                "Enable": false
+            }
+        },
+        "PresetsByFileMask": {
+            // albedo
+            "_basecolor": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_diff": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_diffuse": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_color": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_col": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_albedo": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_alb": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            "_bc": [ "Albedo", "AlbedoWithGenericAlpha", "AlbedoWithCoverage" ],
+            // normals
+            "_ddn": [ "Normals" ],
+            "_normal": [ "Normals" ],
+            "_normalmap": [ "Normals" ],
+            "_normals": [ "Normals" ],
+            "_norm": [ "Normals" ],
+            "_nor": [ "Normals" ],
+            "_nrm": [ "Normals" ],
+            "_nm": [ "Normals" ],
+            "_n": [ "Normals" ],
+            "_ddna": [ "NormalsWithSmoothness" ],
+            "_normala": [ "NormalsWithSmoothness" ],
+            "_nrma": [ "NormalsWithSmoothness" ],
+            "_nma": [ "NormalsWithSmoothness" ],
+            "_na": [ "NormalsWithSmoothness" ],
+            // refelctance
+            "_spec": [ "Reflectance" ],
+            "_specular": [ "Reflectance" ],
+            "_metallic": [ "Reflectance" ],
+            "_refl": [ "Reflectance" ],
+            "_ref": [ "Reflectance" ],
+            "_rf": [ "Reflectance" ],
+            "_gloss": [ "Reflectance" ],
+            "_g": [ "Reflectance" ],
+            "_f0": [ "Reflectance" ],
+            "_specf0": [ "Reflectance" ],
+            "_metal": [ "Reflectance" ],
+            "_mtl": [ "Reflectance" ],
+            "_m": [ "Reflectance" ],
+            "_mt": [ "Reflectance" ],
+            "_metalness": [ "Reflectance" ],
+            "_rough": [ "Reflectance" ],
+            "_roughness": [ "Reflectance" ],
+            // opacity
+            "_sss": [ "Opacity" ],
+            "_trans": [ "Opacity" ],
+            "_opac": [ "Opacity" ],
+            "_opacity": [ "Opacity" ],
+            "_o": [ "Opacity" ],
+            "_op": [ "Opacity" ],
+            "_mask": [ "Opacity", "Greyscale" ],
+            "_msk": [ "Opacity" ],
+            "_blend": [ "Opacity" ],
+            // AO
+            "_ao": [ "AmbientOcclusion" ],
+            "_ambocc": [ "AmbientOcclusion" ],
+            "_amb": [ "AmbientOcclusion" ],
+            "_ambientocclusion": [ "AmbientOcclusion" ],
+            // emissive
+            "_emissive": [ "Emissive" ],
+            "_e": [ "Emissive" ],
+            "_glow": [ "Emissive" ],
+            "_em": [ "Emissive" ],
+            "_emit": [ "Emissive" ],
+            // displacement
+            "_displ": [ "Emissive" ],
+            "_disp": [ "Emissive" ],
+            "_dsp": [ "Emissive" ],
+            "_d": [ "Emissive" ],
+            "_dm": [ "Emissive" ],
+            "_displacement": [ "Emissive" ],
+            "_height": [ "Emissive" ],
+            "_hm": [ "Emissive" ],
+            "_ht": [ "Emissive" ],
+            "_h": [ "Emissive" ],
+            // cubemap
+            "_ibldiffusecm": [ "IBLDiffuse" ],
+            "_iblskyboxcm": [ "IBLSkybox" ],
+            "_iblspecularcm": [ "IBLSpecular" ],
+            "_iblspecularcm128": [ "IBLSpecularLow" ],
+            "_iblspecularcm256": [ "IBLSpecular" ],
+            "_iblspecularcm512": [ "IBLSpecularHigh" ],
+            "_iblspecularcm1024": [ "IBLSpecularVeryHigh" ],
+            "_skyboxcm": [ "Skybox" ],
+            "_ccm": [ "ConvolvedCubemap" ],
+            "_convolvedcubemap": [ "ConvolvedCubemap" ],
+            "_iblglobalcm": [ "IBLGlobal" ],
+            "_cubemap": [ "IBLGlobal" ],
+            "_cm": [ "IBLGlobal" ],
+            // lut
+            "_lut": [ "LUT_RG8" ],
+            "_lutr32f": [ "LUT_R32F" ],
+            "_lutrgba16": [ "LUT_RGBA16" ],
+            "_lutrg32f": [ "LUT_RG32F" ],
+            "_lutrgba32f": [ "LUT_RGBA32F" ],
+            // layer mask
+            "_layers": [ "LayerMask" ],
+            "_rgbmask": [ "LayerMask" ],
+            // decal
+            "_decal": [ "Decal_AlbedoWithOpacity" ],
+            // ui
+            "_ui": [ "UserInterface_Lossless" ]
+        },
+        "DefaultPreset": "Albedo",
+        "DefaultPresetAlpha": "AlbedoWithGenericAlpha"
+    }
+}

+ 0 - 1
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_R32F.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_R32F.preset

@@ -6,7 +6,6 @@
         "DefaultPreset": {
             "UUID": "{10D4D7D8-23E2-4FC5-BE6A-DA9949D2C603}",
             "Name": "LUT_R32F",
-            "FileMasks": ["_lutr32f"],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "PixelFormat": "R32F"

+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG16.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RG16.preset


+ 0 - 1
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG32F.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RG32F.preset

@@ -6,7 +6,6 @@
         "DefaultPreset": {
             "UUID": "{52470B8B-0798-4E03-B0D3-039D5141CFEC}",
             "Name": "LUT_RG32F",
-            "FileMasks": ["_lutrg32f"],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "PixelFormat": "R32G32F"

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RG8.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RG8.preset

@@ -8,9 +8,6 @@
             "Name": "LUT_RG8",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_lut"
-            ],
             "PixelFormat": "R8G8"
         },
         "PlatformsPresets": {
@@ -19,9 +16,6 @@
                 "Name": "LUT_RG8",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lut"
-                ],
                 "PixelFormat": "R8G8"
             },
             "ios": {
@@ -29,9 +23,6 @@
                 "Name": "LUT_RG8",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lut"
-                ],
                 "PixelFormat": "R8G8"
             },
             "mac": {
@@ -39,9 +30,6 @@
                 "Name": "LUT_RG8",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lut"
-                ],
                 "PixelFormat": "R8G8"
             },
             "provo": {
@@ -49,9 +37,6 @@
                 "Name": "LUT_RG8",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lut"
-                ],
                 "PixelFormat": "R8G8"
             }
         }

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA16.preset

@@ -8,9 +8,6 @@
             "Name": "LUT_RGBA16",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_lutrgba16"
-            ],
             "PixelFormat": "R16G16B16A16"
         },
         "PlatformsPresets": {
@@ -19,9 +16,6 @@
                 "Name": "LUT_RGBA16",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16"
-                ],
                 "PixelFormat": "R16G16B16A16"
             },
             "ios": {
@@ -29,9 +23,6 @@
                 "Name": "LUT_RGBA16",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16"
-                ],
                 "PixelFormat": "R16G16B16A16"
             },
             "osx_gl": {
@@ -39,9 +30,6 @@
                 "Name": "LUT_RGBA16",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16"
-                ],
                 "PixelFormat": "R16G16B16A16"
             },
             "provo": {
@@ -49,9 +37,6 @@
                 "Name": "LUT_RGBA16",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16"
-                ],
                 "PixelFormat": "R16G16B16A16"
             }
         }

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA16F.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA16F.preset

@@ -8,9 +8,6 @@
             "Name": "LUT_RGBA16F",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_lutrgba16f"
-            ],
             "PixelFormat": "R16G16B16A16F"
         },
         "PlatformsPresets": {
@@ -19,9 +16,6 @@
                 "Name": "LUT_RGBA16F",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16f"
-                ],
                 "PixelFormat": "R16G16B16A16F"
             },
             "ios": {
@@ -29,9 +23,6 @@
                 "Name": "LUT_RGBA16F",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16f"
-                ],
                 "PixelFormat": "R16G16B16A16F"
             },
             "osx_gl": {
@@ -39,9 +30,6 @@
                 "Name": "LUT_RGBA16F",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16f"
-                ],
                 "PixelFormat": "R16G16B16A16F"
             },
             "provo": {
@@ -49,9 +37,6 @@
                 "Name": "LUT_RGBA16F",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_lutrgba16f"
-                ],
                 "PixelFormat": "R16G16B16A16F"
             }
         }

+ 0 - 1
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA32F.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA32F.preset

@@ -6,7 +6,6 @@
         "DefaultPreset": {
             "UUID": "{AC4C49D4-2C70-425A-8DBF-E7FB2C61CF8D}",
             "Name": "LUT_RGBA32F",
-            "FileMasks": ["_lutrgba32f"],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "PixelFormat": "R32G32B32A32F"

+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/LUT_RGBA8.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LUT_RGBA8.preset


+ 0 - 20
Gems/Atom/Asset/ImageProcessingAtom/Config/LayerMask.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/LayerMask.preset

@@ -8,10 +8,6 @@
             "Name": "LayerMask",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_layers",
-                "_rgbmask"
-            ],
             "PixelFormat": "R8G8B8X8"
         },
         "PlatformsPresets": {
@@ -20,10 +16,6 @@
                 "Name": "LayerMask",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_layers",
-                    "_rgbmask"
-                ],
                 "PixelFormat": "R8G8B8X8"
             },
             "ios": {
@@ -31,10 +23,6 @@
                 "Name": "LayerMask",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_layers",
-                    "_rgbmask"
-                ],
                 "PixelFormat": "R8G8B8X8"
             },
             "mac": {
@@ -42,10 +30,6 @@
                 "Name": "LayerMask",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_layers",
-                    "_rgbmask"
-                ],
                 "PixelFormat": "R8G8B8X8"
             },
             "provo": {
@@ -53,10 +37,6 @@
                 "Name": "LayerMask",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_layers",
-                    "_rgbmask"
-                ],
                 "PixelFormat": "R8G8B8X8"
             }
         }

+ 0 - 55
Gems/Atom/Asset/ImageProcessingAtom/Config/Normals.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Normals.preset

@@ -8,17 +8,6 @@
             "Name": "Normals",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_ddn",
-                "_normal",
-                "_normalmap",
-                "_normals",
-                "_norm",
-                "_nor",
-                "_nrm",
-                "_nm",
-                "_n"
-            ],
             "PixelFormat": "BC5s",
             "DiscardAlpha": true,
             "IsPowerOf2": true,
@@ -33,17 +22,6 @@
                 "Name": "Normals",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddn",
-                    "_normal",
-                    "_normalmap",
-                    "_normals",
-                    "_norm",
-                    "_nor",
-                    "_nrm",
-                    "_nm",
-                    "_n"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "DiscardAlpha": true,
                 "MaxTextureSize": 1024,
@@ -58,17 +36,6 @@
                 "Name": "Normals",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddn",
-                    "_normal",
-                    "_normalmap",
-                    "_normals",
-                    "_norm",
-                    "_nor",
-                    "_nrm",
-                    "_nm",
-                    "_n"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "DiscardAlpha": true,
                 "MaxTextureSize": 1024,
@@ -83,17 +50,6 @@
                 "Name": "Normals",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddn",
-                    "_normal",
-                    "_normalmap",
-                    "_normals",
-                    "_norm",
-                    "_nor",
-                    "_nrm",
-                    "_nm",
-                    "_n"
-                ],
                 "PixelFormat": "BC5s",
                 "DiscardAlpha": true,
                 "IsPowerOf2": true,
@@ -107,17 +63,6 @@
                 "Name": "Normals",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddn",
-                    "_normal",
-                    "_normalmap",
-                    "_normals",
-                    "_norm",
-                    "_nor",
-                    "_nrm",
-                    "_nm",
-                    "_n"
-                ],
                 "PixelFormat": "BC5s",
                 "DiscardAlpha": true,
                 "IsPowerOf2": true,

+ 0 - 35
Gems/Atom/Asset/ImageProcessingAtom/Config/NormalsWithSmoothness.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/NormalsWithSmoothness.preset

@@ -8,13 +8,6 @@
             "Name": "NormalsWithSmoothness",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_ddna",
-                "_normala",
-                "_nrma",
-                "_nma",
-                "_na"
-            ],
             "PixelFormat": "BC5s",
             "PixelFormatAlpha": "BC4",
             "IsPowerOf2": true,
@@ -30,13 +23,6 @@
                 "Name": "NormalsWithSmoothness",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddna",
-                    "_normala",
-                    "_nrma",
-                    "_nma",
-                    "_na"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "PixelFormatAlpha": "ASTC_4x4",
                 "MaxTextureSize": 2048,
@@ -52,13 +38,6 @@
                 "Name": "NormalsWithSmoothness",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddna",
-                    "_normala",
-                    "_nrma",
-                    "_nma",
-                    "_na"
-                ],
                 "PixelFormat": "ASTC_4x4",
                 "PixelFormatAlpha": "ASTC_4x4",
                 "MaxTextureSize": 2048,
@@ -74,13 +53,6 @@
                 "Name": "NormalsWithSmoothness",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddna",
-                    "_normala",
-                    "_nrma",
-                    "_nma",
-                    "_na"
-                ],
                 "PixelFormat": "BC5s",
                 "PixelFormatAlpha": "BC4",
                 "IsPowerOf2": true,
@@ -95,13 +67,6 @@
                 "Name": "NormalsWithSmoothness",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_ddna",
-                    "_normala",
-                    "_nrma",
-                    "_nma",
-                    "_na"
-                ],
                 "PixelFormat": "BC5s",
                 "PixelFormatAlpha": "BC4",
                 "IsPowerOf2": true,

+ 5 - 60
Gems/Atom/Asset/ImageProcessingAtom/Config/Opacity.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Opacity.preset

@@ -8,19 +8,8 @@
             "Name": "Opacity",
             "SourceColor": "Linear",
             "DestColor": "Linear",
-            "FileMasks": [
-                "_sss",
-                "_trans",
-                "_opac",
-                "_opacity",
-                "_o",
-                "_opac",
-                "_op",
-                "_mask",
-                "_msk",
-                "_blend"
-            ],
             "PixelFormat": "BC4",
+            "Swizzle": "rrr1",
             "IsPowerOf2": true,
             "MipMapSetting": {
                 "MipGenType": "Box"
@@ -32,19 +21,8 @@
                 "Name": "Opacity",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_sss",
-                    "_trans",
-                    "_opac",
-                    "_opacity",
-                    "_o",
-                    "_opac",
-                    "_op",
-                    "_mask",
-                    "_msk",
-                    "_blend"
-                ],
                 "PixelFormat": "ASTC_4x4",
+                "Swizzle": "rrr1",
                 "MaxTextureSize": 2048,
                 "IsPowerOf2": true,
                 "MipMapSetting": {
@@ -56,19 +34,8 @@
                 "Name": "Opacity",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_sss",
-                    "_trans",
-                    "_opac",
-                    "_opacity",
-                    "_o",
-                    "_opac",
-                    "_op",
-                    "_mask",
-                    "_msk",
-                    "_blend"
-                ],
                 "PixelFormat": "ASTC_4x4",
+                "Swizzle": "rrr1",
                 "MaxTextureSize": 2048,
                 "IsPowerOf2": true,
                 "MipMapSetting": {
@@ -80,19 +47,8 @@
                 "Name": "Opacity",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_sss",
-                    "_trans",
-                    "_opac",
-                    "_opacity",
-                    "_o",
-                    "_opac",
-                    "_op",
-                    "_mask",
-                    "_msk",
-                    "_blend"
-                ],
                 "PixelFormat": "BC4",
+                "Swizzle": "rrr1",
                 "IsPowerOf2": true,
                 "MipMapSetting": {
                     "MipGenType": "Box"
@@ -103,19 +59,8 @@
                 "Name": "Opacity",
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
-                "FileMasks": [
-                    "_sss",
-                    "_trans",
-                    "_opac",
-                    "_opacity",
-                    "_o",
-                    "_opac",
-                    "_op",
-                    "_mask",
-                    "_msk",
-                    "_blend"
-                ],
                 "PixelFormat": "BC4",
+                "Swizzle": "rrr1",
                 "IsPowerOf2": true,
                 "MipMapSetting": {
                     "MipGenType": "Box"

+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage.preset


+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinear.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage_HDRLinear.preset


+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_HDRLinearUncompressed.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage_HDRLinearUncompressed.preset


+ 0 - 0
Gems/Atom/Asset/ImageProcessingAtom/Config/ReferenceImage_Linear.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/ReferenceImage_Linear.preset


+ 66 - 0
Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Reflectance.preset

@@ -0,0 +1,66 @@
+{
+    "Type": "JsonSerialization",
+    "Version": 1,
+    "ClassName": "MultiplatformPresetSettings",
+    "ClassData": {
+        "DefaultPreset": {
+            "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
+            "Name": "Reflectance",
+            "SourceColor": "Linear",
+            "DestColor": "Linear",
+            "PixelFormat": "BC1",
+            "IsPowerOf2": true,
+            "MipMapSetting": {
+                "MipGenType": "Box"
+            }
+        },
+        "PlatformsPresets": {
+            "android": {
+                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
+                "Name": "Reflectance",
+                "SourceColor": "Linear",
+                "DestColor": "Linear",
+                "PixelFormat": "ASTC_6x6",
+                "MaxTextureSize": 2048,
+                "IsPowerOf2": true,
+                "MipMapSetting": {
+                    "MipGenType": "Box"
+                }
+            },
+            "ios": {
+                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
+                "Name": "Reflectance",
+                "SourceColor": "Linear",
+                "DestColor": "Linear",
+                "PixelFormat": "ASTC_6x6",
+                "MaxTextureSize": 2048,
+                "IsPowerOf2": true,
+                "MipMapSetting": {
+                    "MipGenType": "Box"
+                }
+            },
+            "mac": {
+                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
+                "Name": "Reflectance",
+                "SourceColor": "Linear",
+                "DestColor": "Linear",
+                "PixelFormat": "BC1",
+                "IsPowerOf2": true,
+                "MipMapSetting": {
+                    "MipGenType": "Box"
+                }
+            },
+            "provo": {
+                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
+                "Name": "Reflectance",
+                "SourceColor": "Linear",
+                "DestColor": "Linear",
+                "PixelFormat": "BC1",
+                "IsPowerOf2": true,
+                "MipMapSetting": {
+                    "MipGenType": "Box"
+                }
+            }
+        }
+    }
+}

+ 0 - 15
Gems/Atom/Asset/ImageProcessingAtom/Config/Skybox.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/Skybox.preset

@@ -6,9 +6,6 @@
         "DefaultPreset": {
             "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}",
             "Name": "Skybox",
-            "FileMasks": [
-                "_skyboxcm"
-            ],
             "SourceColor": "Linear",
             "DestColor": "Linear",
             "SuppressEngineReduce": true,
@@ -24,9 +21,6 @@
             "android": {
                 "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}",
                 "Name": "Skybox",
-                "FileMasks": [
-                    "_skyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -41,9 +35,6 @@
             "ios": {
                 "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}",
                 "Name": "Skybox",
-                "FileMasks": [
-                        "_skyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -58,9 +49,6 @@
             "mac": {
                 "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}",
                 "Name": "Skybox",
-                "FileMasks": [
-                        "_skyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,
@@ -75,9 +63,6 @@
             "provo": {
                 "UUID": "{F359CD3B-37E6-4627-B4F6-2DFC2C0E3C1C}",
                 "Name": "Skybox",
-                "FileMasks": [
-                        "_skyboxcm"
-                ],
                 "SourceColor": "Linear",
                 "DestColor": "Linear",
                 "SuppressEngineReduce": true,

+ 1 - 2
Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Compressed.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/UserInterface_Compressed.preset

@@ -9,8 +9,7 @@
             "SuppressEngineReduce": true,
             "PixelFormat": "R8G8B8A8",
             "SourceColor": "Linear",
-            "DestColor": "Linear",
-            "FileMasks": [ "_ui" ]
+            "DestColor": "Linear"
         },
         "PlatformsPresets": {
             "android": {

+ 1 - 2
Gems/Atom/Asset/ImageProcessingAtom/Config/UserInterface_Lossless.preset → Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/UserInterface_Lossless.preset

@@ -9,8 +9,7 @@
             "SuppressEngineReduce": true,
             "PixelFormat": "R8G8B8A8",
             "SourceColor": "Linear",
-            "DestColor": "Linear",
-            "FileMasks": [ "_ui" ]
+            "DestColor": "Linear"
         },
         "PlatformsPresets": {
             "android": {

+ 272 - 125
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.cpp

@@ -8,6 +8,7 @@
 
 
 #include "BuilderSettingManager.h"
+#include <QCoreApplication>
 #include <QDirIterator>
 #include <QFile>
 #include <QFileInfo>
@@ -17,8 +18,9 @@
 #include <BuilderSettings/CubemapSettings.h>
 #include <BuilderSettings/TextureSettings.h>
 #include <Converters/Cubemap.h>
-#include <Processing/PixelFormatInfo.h>
 #include <Processing/ImageToProcess.h>
+#include <Processing/PixelFormatInfo.h>
+#include <Processing/Utils.h>
 #include <ImageLoader/ImageLoaders.h>
 #include <ImageProcessing_Traits_Platform.h>
 
@@ -41,13 +43,18 @@
 
 namespace ImageProcessingAtom
 {
-    const char* BuilderSettingManager::s_defaultConfigRelativeFolder = "Gems/Atom/Asset/ImageProcessingAtom/Config/";
+    const char* BuilderSettingManager::s_defaultConfigRelativeFolder = "Gems/Atom/Asset/ImageProcessingAtom/Assets/Config/";
     const char* BuilderSettingManager::s_projectConfigRelativeFolder = "Config/AtomImageBuilder/";
     const char* BuilderSettingManager::s_builderSettingFileName = "ImageBuilder.settings";
-    const char* BuilderSettingManager::s_presetFileExtension = ".preset";
+    const char* BuilderSettingManager::s_presetFileExtension = "preset";
 
     const char FileMaskDelimiter = '_';
 
+    namespace
+    {
+        static constexpr const char* const LogWindow = "Image Processing";
+    }
+
 #if defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS)
 #define AZ_RESTRICTED_PLATFORM_EXPANSION(CodeName, CODENAME, codename, PrivateName, PRIVATENAME, privatename, PublicName, PUBLICNAME, publicname, PublicAuxName1, PublicAuxName2, PublicAuxName3) \
     namespace ImageProcess##PrivateName                                                                                                                                                           \
@@ -69,13 +76,15 @@ namespace ImageProcessingAtom
         if (serialize)
         {
             serialize->Class<BuilderSettingManager>()
-                ->Version(1)
-                ->Field("AnalysisFingerprint", &BuilderSettingManager::m_analysisFingerprint)
+                ->Version(2)
                 ->Field("BuildSettings", &BuilderSettingManager::m_builderSettings)
-                ->Field("DefaultPresetsByFileMask", &BuilderSettingManager::m_defaultPresetByFileMask)
+                ->Field("PresetsByFileMask", &BuilderSettingManager::m_presetFilterMap)
                 ->Field("DefaultPreset", &BuilderSettingManager::m_defaultPreset)
                 ->Field("DefaultPresetAlpha", &BuilderSettingManager::m_defaultPresetAlpha)
-                ->Field("DefaultPresetNonePOT", &BuilderSettingManager::m_defaultPresetNonePOT);
+                ->Field("DefaultPresetNonePOT", &BuilderSettingManager::m_defaultPresetNonePOT)
+                // deprecated properties
+                ->Field("DefaultPresetsByFileMask", &BuilderSettingManager::m_defaultPresetByFileMask)
+                ->Field("AnalysisFingerprint", &BuilderSettingManager::m_analysisFingerprint);
         }
     }
 
@@ -122,7 +131,7 @@ namespace ImageProcessingAtom
         s_globalInstance.Reset();
     }
     
-    const PresetSettings* BuilderSettingManager::GetPreset(const PresetName& presetName, const PlatformName& platform, AZStd::string_view* settingsFilePathOut)
+    const PresetSettings* BuilderSettingManager::GetPreset(const PresetName& presetName, const PlatformName& platform, AZStd::string_view* settingsFilePathOut) const
     {
         AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
         auto itr = m_presets.find(presetName);
@@ -137,16 +146,17 @@ namespace ImageProcessingAtom
         return nullptr;
     }
 
-    const BuilderSettings* BuilderSettingManager::GetBuilderSetting(const PlatformName& platform)
+    const BuilderSettings* BuilderSettingManager::GetBuilderSetting(const PlatformName& platform) const
     {
-        if (m_builderSettings.find(platform) != m_builderSettings.end())
+        auto itr = m_builderSettings.find(platform);
+        if (itr != m_builderSettings.end())
         {
-            return &m_builderSettings[platform];
+            return &itr->second;
         }
         return nullptr;
     }
 
-    const PlatformNameList BuilderSettingManager::GetPlatformList()
+    const PlatformNameList BuilderSettingManager::GetPlatformList() const
     {
         PlatformNameList platforms;
 
@@ -161,7 +171,7 @@ namespace ImageProcessingAtom
         return platforms;
     }
 
-    const AZStd::map <FileMask, AZStd::unordered_set<PresetName>>& BuilderSettingManager::GetPresetFilterMap()
+    const AZStd::map <FileMask, AZStd::unordered_set<PresetName>>& BuilderSettingManager::GetPresetFilterMap() const
     {
         AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
         return m_presetFilterMap;
@@ -188,7 +198,6 @@ namespace ImageProcessingAtom
         m_presetFilterMap.clear();
         m_builderSettings.clear();
         m_presets.clear();
-        m_defaultPresetByFileMask.clear();
     }
 
     StringOutcome BuilderSettingManager::LoadConfig()
@@ -198,44 +207,53 @@ namespace ImageProcessingAtom
         auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
         if (fileIoBase == nullptr)
         {
-            return AZ::Failure(AZStd::string("File IO instance needs to be initialized to resolve ImageProcessing builder file aliases"));
+            return AZ::Failure(
+                AZStd::string("File IO instance needs to be initialized to resolve ImageProcessing builder file aliases"));
         }
 
-        // Construct the default setting path
-
-        AZ::IO::FixedMaxPath defaultConfigFolder;
         if (auto engineRoot = fileIoBase->ResolvePath("@engroot@"); engineRoot.has_value())
         {
-            defaultConfigFolder = *engineRoot;
-            defaultConfigFolder /= s_defaultConfigRelativeFolder;
+            m_defaultConfigFolder = *engineRoot;
+            m_defaultConfigFolder /= s_defaultConfigRelativeFolder;
         }
 
-        AZ::IO::FixedMaxPath projectConfigFolder;
         if (auto sourceGameRoot = fileIoBase->ResolvePath("@projectroot@"); sourceGameRoot.has_value())
         {
-            projectConfigFolder = *sourceGameRoot;
-            projectConfigFolder /= s_projectConfigRelativeFolder;
+            m_projectConfigFolder = *sourceGameRoot;
+            m_projectConfigFolder /= s_projectConfigRelativeFolder;
         }
 
         AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
         ClearSettings();
         
-        outcome = LoadSettings((projectConfigFolder / s_builderSettingFileName).Native());
-
-        if (!outcome.IsSuccess())
-        {
-            outcome = LoadSettings((defaultConfigFolder / s_builderSettingFileName).Native());
-        }
+        outcome = LoadSettings();
         
         if (outcome.IsSuccess())
         {
             // Load presets in default folder first, then load from project folder. 
             // The same presets which loaded last will overwrite previous loaded one.
-            LoadPresets(defaultConfigFolder.Native());
-            LoadPresets(projectConfigFolder.Native());
+            LoadPresets(m_defaultConfigFolder.Native());
+            LoadPresets(m_projectConfigFolder.Native());
+        }
+
+        // Collect extra file masks from preset files
+        CollectFileMasksFromPresets();
 
-            // Regenerate file mask mapping after all presets loaded
-            RegenerateMappings();
+        
+        if (QCoreApplication::instance())
+        {
+            m_fileWatcher.reset(new QFileSystemWatcher);
+            // track preset files
+            // Note, the QT signal would only works for AP but not AssetBuilder
+            // We use file time stamp to track preset file change in builder's CreateJob
+            for (auto& preset : m_presets)
+            {
+                m_fileWatcher.data()->addPath(QString(preset.second.m_presetFilePath.c_str()));
+            }
+            m_fileWatcher.data()->addPath(QString(m_defaultConfigFolder.c_str()));
+            m_fileWatcher.data()->addPath(QString(m_projectConfigFolder.c_str()));
+            QObject::connect(m_fileWatcher.data(), &QFileSystemWatcher::fileChanged, this, &BuilderSettingManager::OnFileChanged);
+            QObject::connect(m_fileWatcher.data(), &QFileSystemWatcher::directoryChanged, this, &BuilderSettingManager::OnFolderChanged);
         }
 
         return outcome;
@@ -243,36 +261,84 @@ namespace ImageProcessingAtom
 
     void BuilderSettingManager::LoadPresets(AZStd::string_view presetFolder)
     {
-        AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
-
         QDirIterator it(presetFolder.data(), QStringList() << "*.preset", QDir::Files, QDirIterator::NoIteratorFlags);
         while (it.hasNext())
         {
             QString filePath = it.next();
-            QFileInfo fileInfo = it.fileInfo();
+            LoadPreset(filePath.toUtf8().data());
+        }
+    }
 
-            MultiplatformPresetSettings preset;
-            auto result = AZ::JsonSerializationUtils::LoadObjectFromFile(preset, filePath.toUtf8().data());
-            if (!result.IsSuccess())
-            {
-                AZ_Warning("Image Processing", false, "Failed to load preset file %s. Error: %s",
-                    filePath.toUtf8().data(), result.GetError().c_str());
-            }
+    bool BuilderSettingManager::LoadPreset(const AZStd::string& filePath)
+    {
+        QFileInfo fileInfo (filePath.c_str());
 
-            PresetName presetName(fileInfo.baseName().toUtf8().data());
+        if (!fileInfo.exists())
+        {
+            return false;
+        }
 
-            AZ_Warning("Image Processing", presetName == preset.GetPresetName(), "Preset file name '%s' is not"
-                " same as preset name '%s'. Using preset file name as preset name",
-                filePath.toUtf8().data(), preset.GetPresetName().GetCStr());
+        MultiplatformPresetSettings preset;
+        auto result = AZ::JsonSerializationUtils::LoadObjectFromFile(preset, filePath);
+        if (!result.IsSuccess())
+        {
+            AZ_Warning(LogWindow, false, "Failed to load preset file %s. Error: %s",
+                filePath.c_str(), result.GetError().c_str());
+            return false;
+        }
+
+        PresetName presetName(fileInfo.baseName().toUtf8().data());
 
-            preset.SetPresetName(presetName);
+        AZ_Warning(LogWindow, presetName == preset.GetPresetName(), "Preset file name '%s' is not"
+            " same as preset name '%s'. Using preset file name as preset name",
+            filePath.c_str(), preset.GetPresetName().GetCStr());
 
-            m_presets[presetName] = PresetEntry{preset, filePath.toUtf8().data()};
+        preset.SetPresetName(presetName);
+
+        m_presets[presetName] = PresetEntry{preset, filePath.c_str(), fileInfo.lastModified()};
+        return true;
+    }
+
+    void BuilderSettingManager::ReloadPreset(const PresetName& presetName)
+    {
+        // Find the preset file from project or default config folder
+        AZStd::string presetFileName = AZStd::string::format("%s.%s", presetName.GetCStr(), s_presetFileExtension);
+        AZ::IO::FixedMaxPath filePath = m_projectConfigFolder/presetFileName;
+        QFileInfo fileInfo (filePath.c_str());
+        if (!fileInfo.exists())
+        {
+            filePath = (m_defaultConfigFolder/presetFileName).c_str();
+            fileInfo = QFileInfo(filePath.c_str());
+        }
+        
+        AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
+
+        //Skip the loading if the file wasn't chagned
+        if (fileInfo.exists())
+        {
+            if (m_presets.find(presetName) != m_presets.end())
+            {
+                if (m_presets[presetName].m_lastModifiedTime == fileInfo.lastModified()
+                    && m_presets[presetName].m_presetFilePath == filePath.c_str())
+                {
+                    return;
+                }
+            }
+        }
+
+        // remove preset
+        m_presets.erase(presetName);
+
+        if (fileInfo.exists())
+        {
+            LoadPreset(filePath.c_str());
         }
     }
 
     StringOutcome BuilderSettingManager::LoadConfigFromFolder(AZStd::string_view configFolder)
     {
+        AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
+
         // Load builder settings
         AZStd::string settingFilePath = AZStd::string::format("%.*s%s",  aznumeric_cast<int>(configFolder.size()), 
             configFolder.data(), s_builderSettingFileName);
@@ -282,12 +348,108 @@ namespace ImageProcessingAtom
         if (result.IsSuccess())
         {
             LoadPresets(configFolder);
-            RegenerateMappings();
         }
 
         return result;
     }
 
+    void BuilderSettingManager::ReportDeprecatedSettings()
+    {
+        // reported deprecated attributes in image builder settings
+        if (!m_analysisFingerprint.empty())
+        {
+            AZ_Warning(LogWindow, false, "'AnalysisFingerprint' is deprecated and it should be removed from file [%s]", s_builderSettingFileName);
+        }
+        if (!m_defaultPresetByFileMask.empty())
+        {
+            AZ_Warning(LogWindow, false, "'DefaultPresetsByFileMask' is deprecated and it should be removed from file [%s]. Use PresetsByFileMask instead", s_builderSettingFileName);
+        }
+    }
+
+    StringOutcome BuilderSettingManager::LoadSettings()
+    {
+        // If the project image build setting file exist, it will merge image builder settings from project folder to the settings from default config folder.
+        bool needMerge = false;
+        AZStd::string projectSettingFile{ (m_projectConfigFolder / s_builderSettingFileName).Native() };
+
+        if (AZ::IO::SystemFile::Exists(projectSettingFile.c_str()))
+        {
+            needMerge = true;
+        }
+
+        AZ::Outcome<void, AZStd::string> outcome;
+        AZStd::string defaultSettingFile{ (m_defaultConfigFolder / s_builderSettingFileName).Native() };
+        if (needMerge)
+        {
+            auto outcome1 = AZ::JsonSerializationUtils::ReadJsonFile(defaultSettingFile);
+            auto outcome2 = AZ::JsonSerializationUtils::ReadJsonFile(projectSettingFile);
+
+            // return error if it failed to load default settings
+            if (!outcome1.IsSuccess())
+            {
+                return STRING_OUTCOME_ERROR(outcome1.GetError());
+            }
+
+            // if project config was loaded successfully, apply merge patch
+            rapidjson::Document& originDoc = outcome1.GetValue();
+            if (outcome2.IsSuccess())
+            {
+                const rapidjson::Document& patchDoc = outcome2.GetValue();
+                AZ::JsonSerializationResult::ResultCode result =
+                    AZ::JsonSerialization::ApplyPatch(originDoc, originDoc.GetAllocator(), patchDoc, AZ::JsonMergeApproach::JsonMergePatch);
+
+                if (result.GetProcessing() == AZ::JsonSerializationResult::Processing::Completed)
+                {
+                    AZStd::vector<char> outBuffer;
+                    AZ::IO::ByteContainerStream<AZStd::vector<char>> outStream{ &outBuffer };
+                    AZ::JsonSerializationUtils::WriteJsonStream(originDoc, outStream);
+
+                    outStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
+
+                    outcome = AZ::JsonSerializationUtils::LoadObjectFromStream(*this, outStream);
+                    if (!outcome.IsSuccess())
+                    {
+                        return STRING_OUTCOME_ERROR(outcome.GetError());
+                    }
+                    
+                    ReportDeprecatedSettings();
+
+
+                    // Generate config file fingerprint
+                    outStream.Seek(0, AZ::IO::GenericStream::ST_SEEK_BEGIN);
+                    AZ::u64 hash = AssetBuilderSDK::GetHashFromIOStream(outStream);
+                    m_analysisFingerprint = AZStd::string::format("%llX", hash);
+                }
+                else
+                {
+                    needMerge = false;
+                    AZ_Warning(LogWindow, false, "Failed to fully merge data into image builder settings. Skipping project build setting file [%s]", projectSettingFile.c_str());
+                }
+            }
+            else
+            {
+                AZ_Warning(LogWindow, false, "Failed to load project setting file [%s]. Skipping", projectSettingFile.c_str());
+            }
+        }
+
+        if (!needMerge)
+        {
+            outcome = AZ::JsonSerializationUtils::LoadObjectFromFile(*this, defaultSettingFile);
+            if (!outcome.IsSuccess())
+            {
+                return STRING_OUTCOME_ERROR(outcome.GetError());
+            }
+
+            ReportDeprecatedSettings();
+
+            // Generate config file fingerprint
+            AZ::u64 hash = AssetBuilderSDK::GetFileHash(defaultSettingFile.c_str());
+            m_analysisFingerprint = AZStd::string::format("%llX", hash);
+        }
+
+        return STRING_OUTCOME_SUCCESS;
+    }
+
     StringOutcome BuilderSettingManager::LoadSettings(AZStd::string_view filepath)
     {
         AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
@@ -336,13 +498,13 @@ namespace ImageProcessingAtom
         return m_analysisFingerprint;
     }
 
-    void BuilderSettingManager::RegenerateMappings()
+    void BuilderSettingManager::CollectFileMasksFromPresets()
     {
         AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
 
         AZStd::string noFilter = AZStd::string();
-
-        m_presetFilterMap.clear();
+        
+        AZStd::string extraString;
 
         for (const auto& presetIter : m_presets)
         {
@@ -357,22 +519,31 @@ namespace ImageProcessingAtom
             {
                 if (filemask.empty() || filemask[0] != FileMaskDelimiter)
                 {
-                    AZ_Warning("Image Processing", false, "File mask '%s' is invalid. It must start with '%c'.", filemask.c_str(), FileMaskDelimiter);
+                    AZ_Warning(LogWindow, false, "File mask '%s' is invalid. It must start with '%c'.", filemask.c_str(), FileMaskDelimiter);
                     continue;
                 }
                 else if (filemask.size() < 2)
                 {
-                    AZ_Warning("Image Processing", false, "File mask '%s' is invalid. The '%c' must be followed by at least one other character.", filemask.c_str());
+                    AZ_Warning(LogWindow, false, "File mask '%s' is invalid. The '%c' must be followed by at least one other character.", filemask.c_str());
                     continue;
                 }
                 else if (filemask.find(FileMaskDelimiter, 1) != AZStd::string::npos)
                 {
-                    AZ_Warning("Image Processing", false, "File mask '%s' is invalid. It must contain only a single '%c' character.", filemask.c_str(), FileMaskDelimiter);
+                    AZ_Warning(LogWindow, false, "File mask '%s' is invalid. It must contain only a single '%c' character.", filemask.c_str(), FileMaskDelimiter);
                     continue;
                 }
+
+                extraString += (filemask + preset.m_name.GetCStr());
+
                 m_presetFilterMap[filemask].insert(preset.m_name);
             }
         }
+
+        if (!extraString.empty())
+        {
+            AZ::u64 hash = AZStd::hash<AZStd::string>{}(extraString);
+            m_analysisFingerprint += AZStd::string::format("%llX", hash);
+        }
     }
 
     void BuilderSettingManager::MetafilePathFromImagePath(AZStd::string_view imagePath, AZStd::string& metafilePath)
@@ -419,38 +590,15 @@ namespace ImageProcessingAtom
         return m_presets.find(presetName) != m_presets.end();
     }
 
-    PresetName BuilderSettingManager::GetSuggestedPreset(AZStd::string_view imageFilePath, IImageObjectPtr imageFromFile)
+    PresetName BuilderSettingManager::GetSuggestedPreset(AZStd::string_view imageFilePath) const
     {
         PresetName emptyPreset;
 
-        //load the image to get its size for later use
-        IImageObjectPtr image = imageFromFile;
-        //if the input image is empty we will try to load it from the path
-        if (imageFromFile == nullptr)
-        {
-            image = IImageObjectPtr(LoadImageFromFile(imageFilePath));
-        }
-
-        if (image == nullptr)
-        {
-            return emptyPreset;
-        }
-
         //get file mask of this image file
         AZStd::string fileMask = GetFileMask(imageFilePath);
 
         PresetName outPreset = emptyPreset;
 
-        //check default presets for some file masks
-        if (m_defaultPresetByFileMask.find(fileMask) != m_defaultPresetByFileMask.end())
-        {
-            outPreset = m_defaultPresetByFileMask[fileMask];
-            if (!IsValidPreset(outPreset))
-            {
-                outPreset = emptyPreset;
-            }
-        }
-
         //use the preset filter map to find
         if (outPreset.IsEmpty() && !fileMask.empty())
         {
@@ -461,54 +609,21 @@ namespace ImageProcessingAtom
             }
         }
 
-        const PresetSettings* presetInfo = nullptr;
-
-        if (!outPreset.IsEmpty())
-        {
-            presetInfo = GetPreset(outPreset);
-
-            //special case for cubemap
-            if (presetInfo && presetInfo->m_cubemapSetting)
-            {
-                // If it's not a latitude-longitude map or it doesn't match any cubemap layouts then reset its preset
-                if (!IsValidLatLongMap(image) && CubemapLayout::GetCubemapLayoutInfo(image) == nullptr)
-                {
-                    outPreset = emptyPreset;
-                }
-            }
-        }
-
         if (outPreset == emptyPreset)
         {
-            if (image->GetAlphaContent() == EAlphaContent::eAlphaContent_Absent)
-            {
-                outPreset = m_defaultPreset;
-            }
-            else
-            {
-                outPreset = m_defaultPresetAlpha;
-            }
+            outPreset = m_defaultPreset;
         }
 
-        //get the pixel format for selected preset
-        presetInfo = GetPreset(outPreset);
-
-        if (presetInfo)
-        {
-            //valid whether image size work with pixel format
-            if (CPixelFormats::GetInstance().IsImageSizeValid(presetInfo->m_pixelFormat,
-                image->GetWidth(0), image->GetHeight(0), false))
-            {
-                return outPreset;
-            }
-            else
-            {
-                AZ_Warning("Image Processing", false, "Image dimensions are not compatible with preset '%s'. The default preset will be used.", presetInfo->m_name.GetCStr());
-            }
-        }
+        return outPreset;
+    }
 
-        //uncompressed one which could be used for almost everything
-        return m_defaultPresetNonePOT;
+    AZStd::vector<AZStd::string> BuilderSettingManager::GetPossiblePresetPaths(const PresetName& presetName) const
+    {
+        AZStd::vector<AZStd::string> paths;
+        AZStd::string presetFile = AZStd::string::format("%s.preset", presetName.GetCStr());
+        paths.push_back((m_defaultConfigFolder / presetFile).c_str());
+        paths.push_back((m_projectConfigFolder / presetFile).c_str());
+        return paths;
     }
 
     bool BuilderSettingManager::DoesSupportPlatform(AZStd::string_view platformId)
@@ -526,18 +641,50 @@ namespace ImageProcessingAtom
             AZStd::string filePath;
             if (!AzFramework::StringFunc::Path::Join(outputFolder.data(), fileName.c_str(), filePath))
             {
-                AZ_Warning("Image Processing", false, "Failed to construct path with folder '%.*s' and file: '%s' to save preset",
+                AZ_Warning(LogWindow, false, "Failed to construct path with folder '%.*s' and file: '%s' to save preset",
                     aznumeric_cast<int>(outputFolder.size()), outputFolder.data(), filePath.c_str());
                 continue;
             }
             auto result = AZ::JsonSerializationUtils::SaveObjectToFile(&presetEntry.m_multiPreset, filePath);
             if (!result.IsSuccess())
             {
-                AZ_Warning("Image Processing", false, "Failed to save preset '%s' to file '%s'. Error: %s", 
+                AZ_Warning(LogWindow, false, "Failed to save preset '%s' to file '%s'. Error: %s", 
                     presetEntry.m_multiPreset.GetDefaultPreset().m_name.GetCStr(), filePath.c_str(), result.GetError().c_str());
             }
         }
     }
 
+    void BuilderSettingManager::OnFileChanged(const QString &path)
+    {
+        // handles preset file change
+        // Note: this signal only works with AP but not AssetBuilder
+        AZ_TracePrintf(LogWindow, "File changed %s\n", path.toUtf8().data());
+        QFileInfo info(path);
+
+        // skip if the file is not a preset file
+        // Note: for .settings file change it's handled when restart AP. 
+        if (info.suffix() != s_presetFileExtension)
+        {
+            return;
+        }
+        
+        ReloadPreset(PresetName(info.baseName().toUtf8().data()));
+    }
+    
+    void BuilderSettingManager::OnFolderChanged([[maybe_unused]] const QString &path)
+    {
+        // handles new file added or removed
+        // Note: this signal only works with AP but not AssetBuilder
+        AZ_TracePrintf(LogWindow, "folder changed %s\n", path.toUtf8().data());
 
+        AZStd::lock_guard<AZStd::recursive_mutex> lock(m_presetMapLock);
+        m_presets.clear();
+        LoadPresets(m_defaultConfigFolder.Native());
+        LoadPresets(m_projectConfigFolder.Native());
+
+        for (auto& preset : m_presets)
+        {
+            m_fileWatcher.data()->addPath(QString(preset.second.m_presetFilePath.c_str()));
+        }
+    }
 } // namespace ImageProcessingAtom

+ 52 - 12
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/BuilderSettings/BuilderSettingManager.h

@@ -10,10 +10,15 @@
 
 #include <BuilderSettings/ImageProcessingDefines.h>
 #include <BuilderSettings/BuilderSettings.h>
-#include <AzCore/std/containers/set.h>
 #include <AzCore/base.h>
+#include <AzCore/IO/Path/Path.h>
+#include <AzCore/std/containers/set.h>
 #include <Atom/ImageProcessing/ImageObject.h>
 
+#include <QDateTime>
+#include <QFileSystemWatcher>
+#include <QScopedPointer>
+
 class QSettings;
 class QString;
 
@@ -36,6 +41,7 @@ namespace ImageProcessingAtom
      * Each preset setting may have different values on different platform, but they are using same uuid.
      */
     class BuilderSettingManager
+        : public QObject // required for using QFileSystemWatcher
     {
         friend class ImageProcessingTest;
 
@@ -49,17 +55,17 @@ namespace ImageProcessingAtom
         static void DestroyInstance();
         static void Reflect(AZ::ReflectContext* context);
         
-        const PresetSettings* GetPreset(const PresetName& presetName, const PlatformName& platform = "", AZStd::string_view* settingsFilePathOut = nullptr);
+        const PresetSettings* GetPreset(const PresetName& presetName, const PlatformName& platform = "", AZStd::string_view* settingsFilePathOut = nullptr) const;
 
-        const BuilderSettings* GetBuilderSetting(const PlatformName& platform);
+        const BuilderSettings* GetBuilderSetting(const PlatformName& platform) const;
                 
         //! Return A list of platform supported
-        const PlatformNameList GetPlatformList();
+        const PlatformNameList GetPlatformList() const;
 
         //! Return A map of preset settings based on their filemasks.
         //!      @key filemask string, empty string means no filemask
         //!      @value set of preset setting names supporting the specified filemask
-        const AZStd::map<FileMask, AZStd::unordered_set<PresetName>>& GetPresetFilterMap();
+        const AZStd::map<FileMask, AZStd::unordered_set<PresetName>>& GetPresetFilterMap() const;
 
         //! Find preset name based on the preset id.
         const PresetName GetPresetNameFromId(const AZ::Uuid& presetId);
@@ -68,7 +74,11 @@ namespace ImageProcessingAtom
         StringOutcome LoadConfig();
         
         //! Load configurations files from a folder which includes builder settings and presets
-        StringOutcome LoadConfigFromFolder(AZStd::string_view configFolder);
+        //! Note: this is only used for unit test. Use LoadConfig() for editor or game launcher
+         StringOutcome LoadConfigFromFolder(AZStd::string_view configFolder);
+
+        //! Reload preset from config folders
+        void ReloadPreset(const PresetName& presetName);
                 
         const AZStd::string& GetAnalysisFingerprint() const;
 
@@ -81,7 +91,12 @@ namespace ImageProcessingAtom
         //! @param imageFilePath: Filepath string of the image file. The function may load the image from the path for better detection
         //! @param image: an optional image object which can be used for preset selection if there is no match based file mask.
         //! @return suggested preset name.
-        PresetName GetSuggestedPreset(AZStd::string_view imageFilePath, IImageObjectPtr image = nullptr);
+        PresetName GetSuggestedPreset(AZStd::string_view imageFilePath) const;
+
+        //! Get the possible preset config's full file paths
+        //! This function is only used for setting up image's source dependency if a preset file is missing
+        //! Otherwise, the preset's file path can be retrieved in GetPreset() function
+        AZStd::vector<AZStd::string> GetPossiblePresetPaths(const PresetName& presetName) const;
 
         bool IsValidPreset(PresetName presetName) const;
 
@@ -105,25 +120,41 @@ namespace ImageProcessingAtom
     private: // functions
         AZ_DISABLE_COPY_MOVE(BuilderSettingManager);
 
+        // Write image builder setting to the file specified by filepath
         StringOutcome WriteSettings(AZStd::string_view filepath);
+        // Load image builder settings from the file specified by filepath
         StringOutcome LoadSettings(AZStd::string_view filepath);
 
+        // Load merge image builder settings (project and default)
+        StringOutcome LoadSettings();
+
+        // report warnings for the deprecated properties in image builder setting data
+        void ReportDeprecatedSettings();
+
         // Clear Builder Settings and any cached maps/lists
         void ClearSettings();
 
-        // Regenerate Builder Settings and any cached maps/lists
-        void RegenerateMappings();
+        // collect file masks 
+        void CollectFileMasksFromPresets();
 
         // Functions to save/load preset from a folder
         void SavePresets(AZStd::string_view outputFolder);
         void LoadPresets(AZStd::string_view presetFolder);
 
+        // Load a preset to m_presets and return true if success
+        bool LoadPreset(const AZStd::string& filePath);
+
+        // handle preset files changes
+        void OnFileChanged(const QString &path);
+        void OnFolderChanged(const QString &path);
+
     private: // variables
 
         struct PresetEntry
         {
             MultiplatformPresetSettings m_multiPreset;
             AZStd::string m_presetFilePath; // Can be used for debug output
+            QDateTime m_lastModifiedTime;
         };
 
         // Builder settings for each platform
@@ -131,13 +162,13 @@ namespace ImageProcessingAtom
 
         AZStd::unordered_map<PresetName, PresetEntry> m_presets;
 
-        // Cached list of presets mapped by their file masks.
+        // a list of presets mapped by their file masks.
         // @Key file mask, use empty string to indicate all presets without filtering
         // @Value set of preset names that matches the file mask
         AZStd::map <FileMask, AZStd::unordered_set<PresetName>> m_presetFilterMap;
 
-        // A mutex to protect when modifying any map in this manager        
-        AZStd::recursive_mutex m_presetMapLock;
+        // A mutex to protect when modifying any map in this manager
+        mutable AZStd::recursive_mutex m_presetMapLock;
 
         // Default presets for certain file masks
         AZStd::map <FileMask, PresetName > m_defaultPresetByFileMask;
@@ -153,5 +184,14 @@ namespace ImageProcessingAtom
 
         // Image builder's version
         AZStd::string m_analysisFingerprint;
+
+        // default config folder
+        AZ::IO::FixedMaxPath m_defaultConfigFolder;
+
+        // project config folder
+        AZ::IO::FixedMaxPath m_projectConfigFolder;
+
+        // File system watcher to detect preset file changes
+        QScopedPointer<QFileSystemWatcher> m_fileWatcher;
     };
 } // namespace ImageProcessingAtom

+ 1 - 1
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Editor/EditorCommon.cpp

@@ -171,7 +171,7 @@ namespace ImageProcessingAtomEditor
         if (!preset)
         {
             AZ_Warning("Texture Editor", false, "Cannot find preset %s! Will assign a suggested one for the texture.", presetName.GetCStr());
-            presetName = BuilderSettingManager::Instance()->GetSuggestedPreset(m_fullPath, m_img);
+            presetName = BuilderSettingManager::Instance()->GetSuggestedPreset(m_fullPath);
 
             for (auto& settingIter : m_settingsMap)
             {

+ 66 - 1
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageBuilderComponent.cpp

@@ -221,6 +221,58 @@ namespace ImageProcessingAtom
         m_isShuttingDown = true;
     }
 
+    PresetName GetImagePreset(const AZStd::string& filepath)
+    {
+        // first let preset from asset info
+        TextureSettings textureSettings;
+        StringOutcome output = TextureSettings::LoadTextureSetting(filepath, textureSettings);
+
+        if (!textureSettings.m_preset.IsEmpty())
+        {
+            return textureSettings.m_preset;
+        }
+
+        return BuilderSettingManager::Instance()->GetSuggestedPreset(filepath);
+    }
+
+    void HandlePresetDependency(PresetName presetName, AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceDependencyList)
+    {
+        // Reload preset if it was changed
+        ImageProcessingAtom::BuilderSettingManager::Instance()->ReloadPreset(presetName);
+        
+        AZStd::string_view filePath;
+        auto presetSettings = BuilderSettingManager::Instance()->GetPreset(presetName, /*default platform*/"", &filePath);
+
+        AssetBuilderSDK::SourceFileDependency sourceFileDependency;
+        sourceFileDependency.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute;
+
+        // Need to watch any possibe preset paths 
+        AZStd::vector<AZStd::string> possiblePresetPaths = BuilderSettingManager::Instance()->GetPossiblePresetPaths(presetName);
+        for (const auto& path:possiblePresetPaths)
+        {
+            sourceFileDependency.m_sourceFileDependencyPath = path;
+            sourceDependencyList.push_back(sourceFileDependency);
+        }
+
+        if (presetSettings)
+        {
+            // handle special case here
+            // Cubemap setting may reference some other presets
+            if (presetSettings->m_cubemapSetting)
+            {
+                if (presetSettings->m_cubemapSetting->m_generateIBLDiffuse && !presetSettings->m_cubemapSetting->m_iblDiffusePreset.IsEmpty())
+                {
+                    HandlePresetDependency(presetSettings->m_cubemapSetting->m_iblDiffusePreset, sourceDependencyList);
+                }
+            
+                if (presetSettings->m_cubemapSetting->m_generateIBLSpecular && !presetSettings->m_cubemapSetting->m_iblSpecularPreset.IsEmpty())
+                {
+                    HandlePresetDependency(presetSettings->m_cubemapSetting->m_iblSpecularPreset, sourceDependencyList);
+                }
+            }
+        }
+    }
+
     // this happens early on in the file scanning pass
     // this function should consistently always create the same jobs, and should do no checking whether the job is up to date or not - just be consistent.
     void ImageBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
@@ -242,13 +294,26 @@ namespace ImageProcessingAtom
             if (ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformInfo.m_identifier))
             {
                 AssetBuilderSDK::JobDescriptor descriptor;
-                descriptor.m_jobKey = ext + " Atom Compile";
+                descriptor.m_jobKey = "Image Compile: " + ext;
                 descriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
                 descriptor.m_critical = false;
+                descriptor.m_additionalFingerprintInfo = "";
                 response.m_createJobOutputs.push_back(descriptor);
             }
         }
 
+        // add source dependency for .assetinfo file
+        AssetBuilderSDK::SourceFileDependency sourceFileDependency;
+        sourceFileDependency.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute;
+        sourceFileDependency.m_sourceFileDependencyPath = request.m_sourceFile;
+        AZ::StringFunc::Path::ReplaceExtension(sourceFileDependency.m_sourceFileDependencyPath, TextureSettings::ExtensionName);
+        response.m_sourceFileDependencyList.push_back(sourceFileDependency);
+
+        // add source dependencies for .preset files
+        // Get the preset for this file 
+        auto presetName = GetImagePreset(request.m_sourceFile);
+        HandlePresetDependency(presetName, response.m_sourceFileDependencyList);
+
         response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
         return;
     }

+ 45 - 13
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp

@@ -11,6 +11,7 @@
 #include <Processing/ImageConvert.h>
 #include <Processing/ImageAssetProducer.h>
 #include <Processing/ImageFlags.h>
+#include <Processing/Utils.h>
 #include <Converters/FIR-Weights.h>
 #include <Converters/Cubemap.h>
 #include <Converters/PixelOperation.h>
@@ -229,12 +230,24 @@ namespace ImageProcessingAtom
                 AZStd::unique_ptr<CubemapSettings>& cubemapSettings = m_input->m_presetSetting.m_cubemapSetting;
                 if (cubemapSettings->m_generateIBLSpecular && !cubemapSettings->m_iblSpecularPreset.IsEmpty())
                 {
-                    CreateIBLCubemap(cubemapSettings->m_iblSpecularPreset, SpecularCubemapSuffix, m_iblSpecularCubemapImage);
+                    bool success = CreateIBLCubemap(cubemapSettings->m_iblSpecularPreset, SpecularCubemapSuffix, m_iblSpecularCubemapImage);
+                    if (!success)
+                    {
+                        m_isSucceed = false;
+                        m_isFinished = true;
+                        break;
+                    }
                 }
 
                 if (cubemapSettings->m_generateIBLDiffuse && !cubemapSettings->m_iblDiffusePreset.IsEmpty())
                 {
-                    CreateIBLCubemap(cubemapSettings->m_iblDiffusePreset, DiffuseCubemapSuffix, m_iblDiffuseCubemapImage);
+                    bool success = CreateIBLCubemap(cubemapSettings->m_iblDiffusePreset, DiffuseCubemapSuffix, m_iblDiffuseCubemapImage);
+                    if (!success)
+                    {
+                        m_isSucceed = false;
+                        m_isFinished = true;
+                        break;
+                    }
                 }
             }
 
@@ -251,7 +264,12 @@ namespace ImageProcessingAtom
             {
                 if (m_input->m_presetSetting.m_cubemapSetting->m_requiresConvolve)
                 {
-                    FillCubemapMipmaps();
+                    bool success = FillCubemapMipmaps();
+                    if (!success)
+                    {
+                        m_isSucceed = false;
+                         m_isFinished = true;
+                    }
                 }
             }
             else
@@ -268,9 +286,7 @@ namespace ImageProcessingAtom
             // get gloss from normal for all mipmaps and save to alpha channel
             if (m_input->m_presetSetting.m_glossFromNormals)
             {
-                bool hasAlpha = (m_alphaContent == EAlphaContent::eAlphaContent_OnlyBlack
-                    || m_alphaContent == EAlphaContent::eAlphaContent_OnlyBlackAndWhite
-                    || m_alphaContent == EAlphaContent::eAlphaContent_Greyscale);
+                bool hasAlpha = Utils::NeedAlphaChannel(m_alphaContent);
 
                 m_image->Get()->GlossFromNormals(hasAlpha);
                 // set alpha content so it won't be ignored later.
@@ -347,7 +363,11 @@ namespace ImageProcessingAtom
             }
             else
             {
-                AZ_TracePrintf("Image Processing", "Image converted with preset [%s] [%s] and saved to [%s] (%d bytes) taking %f seconds\n",
+                
+                [[maybe_unused]] const PixelFormatInfo* formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(m_image->Get()->GetPixelFormat());
+                AZ_TracePrintf("Image Processing", "Image [%dx%d] [%s] converted with preset [%s] [%s] and saved to [%s] (%d bytes) taking %f seconds\n",
+                    m_image->Get()->GetWidth(0), m_image->Get()->GetHeight(0),
+                    formatInfo->szName,
                     m_input->m_presetSetting.m_name.GetCStr(),
                     m_input->m_filePath.c_str(),
                     m_input->m_outputFolder.c_str(), sizeTotal, m_processTime);
@@ -421,6 +441,17 @@ namespace ImageProcessingAtom
             outHeight >>= 1;
             outReduce++;
         }
+
+        // resize to min texture size if it's smaller
+        if (outWidth < presetSettings->m_minTextureSize)
+        {
+            outWidth = presetSettings->m_minTextureSize;
+        }
+
+        if (outHeight < presetSettings->m_minTextureSize)
+        {
+            outHeight = presetSettings->m_minTextureSize;
+        }
     }
 
     bool ImageConvertProcess::ConvertToLinear()
@@ -647,7 +678,7 @@ namespace ImageProcessingAtom
         }
         else if (!CPixelFormats::GetInstance().IsImageSizeValid(dstFmt, dwWidth, dwHeight, false))
         {
-            AZ_Warning("Image Processing", false, "Image size will be scaled for pixel format %s", CPixelFormats::GetInstance().GetPixelFormatInfo(dstFmt)->szName);
+            AZ_TracePrintf("Image processing", "Image size will be scaled for pixel format %s\n", CPixelFormats::GetInstance().GetPixelFormatInfo(dstFmt)->szName);
         }
 
 #if defined(AZ_TOOLS_EXPAND_FOR_RESTRICTED_PLATFORMS)
@@ -758,7 +789,7 @@ namespace ImageProcessingAtom
         // in very rare user case, an old texture setting file may not have a preset. We fix it over here too. 
         if (textureSettings.m_preset.IsEmpty())
         {
-            textureSettings.m_preset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilePath, srcImage);
+            textureSettings.m_preset = BuilderSettingManager::Instance()->GetSuggestedPreset(imageFilePath);
         }
 
         // Get preset
@@ -795,7 +826,7 @@ namespace ImageProcessingAtom
         return process;
     }
 
-    void ImageConvertProcess::CreateIBLCubemap(PresetName preset, const char* fileNameSuffix, IImageObjectPtr& cubemapImage)
+    bool ImageConvertProcess::CreateIBLCubemap(PresetName preset, const char* fileNameSuffix, IImageObjectPtr& cubemapImage)
     {
         const AZStd::string& platformId = m_input->m_platform;
         AZStd::string_view filePath;
@@ -803,7 +834,7 @@ namespace ImageProcessingAtom
         if (presetSettings == nullptr)
         {
             AZ_Error("Image Processing", false, "Couldn't find preset for IBL cubemap generation");
-            return;
+            return false;
         }
 
         // generate export file name
@@ -838,14 +869,14 @@ namespace ImageProcessingAtom
         if (!imageConvertProcess)
         {
             AZ_Error("Image Processing", false, "Failed to create image convert process for the IBL cubemap");
-            return;
+            return false;
         }
 
         imageConvertProcess->ProcessAll();
         if (!imageConvertProcess->IsSucceed())
         {
             AZ_Error("Image Processing", false, "Image convert process for the IBL cubemap failed");
-            return;
+            return false;
         }
 
         // append the output products to the job's product list
@@ -853,6 +884,7 @@ namespace ImageProcessingAtom
 
         // store the output cubemap so it can be accessed by unit tests
         cubemapImage = imageConvertProcess->m_image->Get();
+        return true;
     }
 
     bool ConvertImageFile(const AZStd::string& imageFilePath, const AZStd::string& exportDir,

+ 1 - 1
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.h

@@ -160,7 +160,7 @@ namespace ImageProcessingAtom
         bool FillCubemapMipmaps();
 
         //IBL cubemap generation, this creates a separate ImageConvertProcess
-        void CreateIBLCubemap(PresetName preset, const char* fileNameSuffix, IImageObjectPtr& cubemapImage);
+        bool CreateIBLCubemap(PresetName preset, const char* fileNameSuffix, IImageObjectPtr& cubemapImage);
 
         //convert color space to linear with pixel format rgba32f
         bool ConvertToLinear();

+ 1 - 2
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageObjectImpl.cpp

@@ -183,10 +183,9 @@ namespace ImageProcessingAtom
             return EAlphaContent::eAlphaContent_Absent;
         }
 
-        //if it's compressed format, return indeterminate. if user really want to know the content, they may convert the format to ARGB8 first
         if (!CPixelFormats::GetInstance().IsPixelFormatUncompressed(m_pixelFormat))
         {
-            AZ_Assert(false, "the function only works right with uncompressed formats. convert to uncompressed format if you get accurate result");
+            AZ_TracePrintf("Image processing", "GetAlphaContent() was called for compressed format\n");
             return EAlphaContent::eAlphaContent_Indeterminate;
         }
 

+ 7 - 0
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.cpp

@@ -385,6 +385,13 @@ namespace ImageProcessingAtom
             }
             return true;
         }
+
+        bool NeedAlphaChannel(EAlphaContent alphaContent)
+        {
+             return (alphaContent == EAlphaContent::eAlphaContent_OnlyBlack
+                        || alphaContent == EAlphaContent::eAlphaContent_OnlyBlackAndWhite
+                        || alphaContent == EAlphaContent::eAlphaContent_Greyscale);
+        }
     }
 
 } // namespace ImageProcessingAtom

+ 2 - 0
Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/Utils.h

@@ -26,5 +26,7 @@ namespace ImageProcessingAtom
         IImageObjectPtr LoadImageFromImageAsset(const AZ::Data::Asset<AZ::RPI::StreamingImageAsset>& asset);
 
         bool SaveImageToDdsFile(IImageObjectPtr image, AZStd::string_view filePath);
+
+        bool NeedAlphaChannel(EAlphaContent alphaContent);
     }
 }

+ 1 - 1
Gems/Atom/Asset/ImageProcessingAtom/Code/Tests/ImageProcessing_Test.cpp

@@ -203,7 +203,7 @@ namespace UnitTest
             m_gemFolder = AZ::Test::GetEngineRootPath() + "/Gems/Atom/Asset/ImageProcessingAtom/";
             m_outputFolder = m_gemFolder + AZStd::string("Code/Tests/TestAssets/temp/");
 
-            m_defaultSettingFolder = m_gemFolder + AZStd::string("Config/");
+            m_defaultSettingFolder = m_gemFolder + AZStd::string("Assets/Config/");
             m_testFileFolder = m_gemFolder + AZStd::string("Code/Tests/TestAssets/");
 
             InitialImageFilenames();

+ 0 - 67
Gems/Atom/Asset/ImageProcessingAtom/Config/ImageBuilder.settings

@@ -1,67 +0,0 @@
-{
-    "Type": "JsonSerialization",
-    "Version": 1,
-    "ClassName": "BuilderSettingManager",
-    "ClassData": {
-        "AnalysisFingerprint": "2",
-        "BuildSettings": {
-            "android": {
-                "GlossScale": 16.0,
-                "GlossBias": 0.0,
-                "Streaming": false,
-                "Enable": true
-            },
-            "ios": {
-                "GlossScale": 16.0,
-                "GlossBias": 0.0,
-                "Streaming": false,
-                "Enable": true
-            },
-            "mac": {
-                "GlossScale": 16.0,
-                "GlossBias": 0.0,
-                "Streaming": false,
-                "Enable": true
-            },
-            "pc": {
-                "GlossScale": 16.0,
-                "GlossBias": 0.0,
-                "Streaming": false,
-                "Enable": true
-            },
-            "linux": {
-                "GlossScale": 16.0,
-                "GlossBias": 0.0,
-                "Streaming": false,
-                "Enable": true
-            },
-            "provo": {
-                "GlossScale": 16.0,
-                "GlossBias": 0.0,
-                "Streaming": false,
-                "Enable": false
-            }
-        },
-        "DefaultPresetsByFileMask": {
-            "_basecolor": "Albedo",
-            "_diff": "Albedo",
-            "_diffuse": "Albedo",
-            "_ddn": "Normals",
-            "_normal": "Normals",
-            "_ddna": "NormalsWithSmoothness",
-            "_glossness": "Reflectance",
-            "_spec": "Reflectance",
-            "_specular": "Reflectance",
-            "_metallic": "Reflectance",
-            "_refl": "Reflectance",
-            "_roughness": "Reflectance",
-            "_ibldiffusecm": "IBLDiffuse",
-            "_iblskyboxcm": "IBLSkybox",
-            "_iblspecularcm": "IBLSpecular",
-            "_skyboxcm": "Skybox"
-        },
-        "DefaultPreset": "Albedo",
-        "DefaultPresetAlpha": "AlbedoWithGenericAlpha",
-        "DefaultPresetNonePOT": "ReferenceImage"
-    }
-}

+ 0 - 157
Gems/Atom/Asset/ImageProcessingAtom/Config/Reflectance.preset

@@ -1,157 +0,0 @@
-{
-    "Type": "JsonSerialization",
-    "Version": 1,
-    "ClassName": "MultiplatformPresetSettings",
-    "ClassData": {
-        "DefaultPreset": {
-            "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
-            "Name": "Reflectance",
-            "SourceColor": "Linear",
-            "DestColor": "Linear",
-            "FileMasks": [
-                "_spec",
-                "_refl",
-                "_ref",
-                "_rf",
-                "_gloss",
-                "_g",
-                "_f0",
-                "_specf0",
-                "_specular",
-                "_metal",
-                "_mtl",
-                "_m",
-                "_mt",
-                "_metalness",
-                "_metallic",
-                "_roughness",
-                "_rough"
-            ],
-            "PixelFormat": "BC1",
-            "IsPowerOf2": true,
-            "MipMapSetting": {
-                "MipGenType": "Box"
-            }
-        },
-        "PlatformsPresets": {
-            "android": {
-                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
-                "Name": "Reflectance",
-                "SourceColor": "Linear",
-                "DestColor": "Linear",
-                "FileMasks": [
-                    "_spec",
-                    "_refl",
-                    "_ref",
-                    "_rf",
-                    "_gloss",
-                    "_g",
-                    "_f0",
-                    "_specf0",
-                    "_metal",
-                    "_mtl",
-                    "_m",
-                    "_mt",
-                    "_metalness",
-                    "_metallic",
-                    "_roughness",
-                    "_rough"
-                ],
-                "PixelFormat": "ASTC_6x6",
-                "MaxTextureSize": 2048,
-                "IsPowerOf2": true,
-                "MipMapSetting": {
-                    "MipGenType": "Box"
-                }
-            },
-            "ios": {
-                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
-                "Name": "Reflectance",
-                "SourceColor": "Linear",
-                "DestColor": "Linear",
-                "FileMasks": [
-                    "_spec",
-                    "_refl",
-                    "_ref",
-                    "_rf",
-                    "_gloss",
-                    "_g",
-                    "_f0",
-                    "_specf0",
-                    "_metal",
-                    "_mtl",
-                    "_m",
-                    "_mt",
-                    "_metalness",
-                    "_metallic",
-                    "_roughness",
-                    "_rough"
-                ],
-                "PixelFormat": "ASTC_6x6",
-                "MaxTextureSize": 2048,
-                "IsPowerOf2": true,
-                "MipMapSetting": {
-                    "MipGenType": "Box"
-                }
-            },
-            "mac": {
-                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
-                "Name": "Reflectance",
-                "SourceColor": "Linear",
-                "DestColor": "Linear",
-                "FileMasks": [
-                    "_spec",
-                    "_refl",
-                    "_ref",
-                    "_rf",
-                    "_gloss",
-                    "_g",
-                    "_f0",
-                    "_specf0",
-                    "_metal",
-                    "_mtl",
-                    "_m",
-                    "_mt",
-                    "_metalness",
-                    "_metallic",
-                    "_roughness",
-                    "_rough"
-                ],
-                "PixelFormat": "BC1",
-                "IsPowerOf2": true,
-                "MipMapSetting": {
-                    "MipGenType": "Box"
-                }
-            },
-            "provo": {
-                "UUID": "{7A3CC95E-0A0C-4CA1-8357-5712B028B77D}",
-                "Name": "Reflectance",
-                "SourceColor": "Linear",
-                "DestColor": "Linear",
-                "FileMasks": [
-                    "_spec",
-                    "_refl",
-                    "_ref",
-                    "_rf",
-                    "_gloss",
-                    "_g",
-                    "_f0",
-                    "_specf0",
-                    "_metal",
-                    "_mtl",
-                    "_m",
-                    "_mt",
-                    "_metalness",
-                    "_metallic",
-                    "_roughness",
-                    "_rough"
-                ],
-                "PixelFormat": "BC1",
-                "IsPowerOf2": true,
-                "MipMapSetting": {
-                    "MipGenType": "Box"
-                }
-            }
-        }
-    }
-}

+ 8 - 1
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli

@@ -14,6 +14,7 @@
 #include <Atom/RPI/Math.azsli>
 #include "BicubicPcfFilters.azsli"
 #include "Shadow.azsli"
+#include "NormalOffsetShadows.azsli"
 
 // ProjectedShadow calculates shadowed area projected from a light.
 class ProjectedShadow
@@ -123,6 +124,7 @@ float ProjectedShadow::GetThickness(uint shadowIndex, float3 worldPosition)
 
     ProjectedShadow shadow;
     shadow.m_worldPosition = worldPosition;
+    shadow.m_normalVector = 0; // The normal vector is used to reduce acne, this is not an issue when using the shadowmap to determine thickness. 
     shadow.m_shadowIndex = shadowIndex;
     shadow.SetShadowPosition();
     return shadow.GetThickness();
@@ -317,8 +319,13 @@ bool ProjectedShadow::IsShadowed(float3 shadowPosition)
 
 void ProjectedShadow::SetShadowPosition()
 {
+    const float normalBias = ViewSrg::m_projectedShadows[m_shadowIndex].m_normalShadowBias;
+    const float shadowmapSize = ViewSrg::m_projectedFilterParams[m_shadowIndex].m_shadowmapSize;
+    const float3 shadowOffset = ComputeNormalShadowOffset(normalBias, m_normalVector, shadowmapSize);
     const float4x4 depthBiasMatrix = ViewSrg::m_projectedShadows[m_shadowIndex].m_depthBiasMatrix;
-    float4 shadowPositionHomogeneous = mul(depthBiasMatrix, float4(m_worldPosition, 1));
+    
+    float4 shadowPositionHomogeneous = mul(depthBiasMatrix, float4(m_worldPosition + shadowOffset, 1));
+         
     m_shadowPosition = shadowPositionHomogeneous.xyz / shadowPositionHomogeneous.w;
     
     m_bias = ViewSrg::m_projectedShadows[m_shadowIndex].m_bias / shadowPositionHomogeneous.w;

+ 2 - 0
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h

@@ -86,6 +86,8 @@ namespace AZ
             virtual void SetShadowsEnabled(LightHandle handle, bool enabled) = 0;
             //! Sets the shadow bias
             virtual void SetShadowBias(LightHandle handle, float bias) = 0;
+            //! Sets the normal shadow bias
+            virtual void SetNormalShadowBias(LightHandle handle, float bias) = 0;
             //! Sets the shadowmap size (width and height) of the light.
             virtual void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) = 0;
             //! Specifies filter method of shadows.

+ 2 - 0
Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/PointLightFeatureProcessorInterface.h

@@ -74,6 +74,8 @@ namespace AZ
             virtual void SetFilteringSampleCount(LightHandle handle, uint16_t count) = 0;
             //! Sets the Esm exponent to use. Higher values produce a steeper falloff in the border areas between light and shadow.
             virtual void SetEsmExponent(LightHandle handle, float exponent) = 0;
+            //! Sets the normal shadow bias. Reduces acne by biasing the shadowmap lookup along the geometric normal.
+            virtual void SetNormalShadowBias(LightHandle handle, float bias) = 0;
             //! Sets all of the the point data for the provided LightHandle.
             virtual void SetPointData(LightHandle handle, const PointLightData& data) = 0;
         };

+ 8 - 29
Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.cpp

@@ -343,7 +343,6 @@ namespace AZ
                 m_shadowBufferNeedsUpdate = true;
 
                 m_shadowProperties.GetData(index).m_cameraConfigurations[nullptr] = {};
-                m_shadowProperties.GetData(index).m_cameraTransforms[nullptr] = Transform::CreateIdentity();
 
                 const LightHandle handle(index);
                 m_shadowingLightHandle = handle; // only the recent light has shadows.
@@ -495,20 +494,10 @@ namespace AZ
 
         void DirectionalLightFeatureProcessor::SetCameraTransform(
             LightHandle handle,
-            const Transform& cameraTransform,
-            const RPI::RenderPipelineId& renderPipelineId)
+            const Transform&,
+            const RPI::RenderPipelineId&)
         {
             ShadowProperty& property = m_shadowProperties.GetData(handle.GetIndex());
-
-            if (RPI::RenderPipeline* renderPipeline = GetParentScene()->GetRenderPipeline(renderPipelineId).get())
-            {
-                const RPI::View* cameraView = renderPipeline->GetDefaultView().get();
-                property.m_cameraTransforms[cameraView] = cameraTransform;
-            }
-            else
-            {
-                property.m_cameraTransforms[nullptr] = cameraTransform;
-            }
             property.m_shadowmapViewNeedsUpdate = true;
         }
 
@@ -934,17 +923,6 @@ namespace AZ
             return property.m_cameraConfigurations.at(nullptr);
         }
 
-        const Transform& DirectionalLightFeatureProcessor::GetCameraTransform(LightHandle handle, const RPI::View* cameraView) const
-        {
-            const ShadowProperty& property = m_shadowProperties.GetData(handle.GetIndex());
-            const auto findIt = property.m_cameraTransforms.find(cameraView);
-            if (findIt != property.m_cameraTransforms.end())
-            {
-                return findIt->second;
-            }
-            return property.m_cameraTransforms.at(nullptr);
-        }
-
         void DirectionalLightFeatureProcessor::UpdateFrustums(
             LightHandle handle)
         {
@@ -1365,10 +1343,11 @@ namespace AZ
             // If we used an AABB whose Y-direction range is from a segment,
             // the depth value on the shadowmap saturated to 0 or 1,
             // and we could not draw shadow correctly.
+            const Transform cameraTransform = cameraView->GetCameraTransform();
             const Vector3 entireFrustumCenterLight =
-                lightTransform.GetInverseFast() * (GetCameraTransform(handle, cameraView).TransformPoint(property.m_entireFrustumCenterLocal));
+                lightTransform.GetInverseFast() * (cameraTransform.TransformPoint(property.m_entireFrustumCenterLocal));
             const float entireCenterY = entireFrustumCenterLight.GetElement(1);
-            const Vector3 cameraLocationWorld = GetCameraTransform(handle, cameraView).GetTranslation();
+            const Vector3 cameraLocationWorld = cameraTransform.GetTranslation();
             const Vector3 cameraLocationLight = lightTransformInverse * cameraLocationWorld;
             // Extend light view frustum by camera depth far in order to avoid shadow lacking behind camera.
             const float cameraBehindMinY = cameraLocationLight.GetElement(1) - GetCameraConfiguration(handle, cameraView).GetDepthFar();
@@ -1428,8 +1407,8 @@ namespace AZ
                 GetCameraConfiguration(handle, cameraView).GetDepthCenter(depthNear, depthFar),
                 depthFar);
 
-            const Vector3 localCenter{ 0.f, depthCenter, 0.f };
-            return GetCameraTransform(handle, cameraView).TransformPoint(localCenter);
+            const Vector3 localCenter{ 0.f, depthCenter, 0.f };            
+            return cameraView->GetCameraTransform().TransformPoint(localCenter);
         }
 
         float DirectionalLightFeatureProcessor::GetRadius(
@@ -1483,7 +1462,7 @@ namespace AZ
             const ShadowProperty& property = m_shadowProperties.GetData(handle.GetIndex());
             const Vector3& boundaryCenter = GetWorldCenterPosition(handle, cameraView, depthNear, depthFar);
             const CascadeShadowCameraConfiguration& cameraConfiguration = GetCameraConfiguration(handle, cameraView);
-            const Transform& cameraTransform = GetCameraTransform(handle, cameraView);
+            const Transform cameraTransform = cameraView->GetCameraTransform();
             const Vector3& cameraFwd = cameraTransform.GetBasis(1);
             const Vector3& cameraUp = cameraTransform.GetBasis(2);
             const Vector3 cameraToBoundaryCenter = boundaryCenter - cameraTransform.GetTranslation();

+ 0 - 8
Gems/Atom/Feature/Common/Code/Source/CoreLights/DirectionalLightFeatureProcessor.h

@@ -134,9 +134,6 @@ namespace AZ
                 // Default far depth of each cascade.
                 AZStd::array<float, Shadow::MaxNumberOfCascades> m_defaultFarDepths;
 
-                // Transforms of camera who offers view frustum for each camera view.
-                AZStd::unordered_map<const RPI::View*, Transform> m_cameraTransforms;
-
                 // Configuration offers shape of the camera view frustum for each camera view.
                 AZStd::unordered_map<const RPI::View*, CascadeShadowCameraConfiguration> m_cameraConfigurations;
 
@@ -259,11 +256,6 @@ namespace AZ
             //! it returns one of the fallback render pipeline ID.
             const CascadeShadowCameraConfiguration& GetCameraConfiguration(LightHandle handle, const RPI::View* cameraView) const;
 
-            //! This returns the camera transform.
-            //! If it has not been registered for the given camera view.
-            //! it returns one of the fallback render pipeline ID.
-            const Transform& GetCameraTransform(LightHandle handle, const RPI::View* cameraView) const;
-
             //! This update view frustum of camera.
             void UpdateFrustums(LightHandle handle);
 

+ 5 - 0
Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.cpp

@@ -313,6 +313,11 @@ namespace AZ
             SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowBias, bias);
         }
 
+        void DiskLightFeatureProcessor::SetNormalShadowBias(LightHandle handle, float bias)
+        {
+            SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetNormalShadowBias, bias);
+        }
+
         void DiskLightFeatureProcessor::SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize)
         {
             SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution, shadowmapSize);

+ 1 - 0
Gems/Atom/Feature/Common/Code/Source/CoreLights/DiskLightFeatureProcessor.h

@@ -51,6 +51,7 @@ namespace AZ
             void SetConeAngles(LightHandle handle, float innerDegrees, float outerDegrees) override;
             void SetShadowsEnabled(LightHandle handle, bool enabled) override;
             void SetShadowBias(LightHandle handle, float bias) override;
+            void SetNormalShadowBias(LightHandle handle, float bias) override;
             void SetShadowmapMaxResolution(LightHandle handle, ShadowmapSize shadowmapSize) override;
             void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
             void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;

+ 5 - 0
Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.cpp

@@ -302,5 +302,10 @@ namespace AZ
             SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetEsmExponent, esmExponent);
         }
 
+        void PointLightFeatureProcessor::SetNormalShadowBias(LightHandle handle, float bias)
+        {
+            SetShadowSetting(handle, &ProjectedShadowFeatureProcessor::SetNormalShadowBias, bias);
+        }
+
     } // namespace Render
 } // namespace AZ

+ 1 - 0
Gems/Atom/Feature/Common/Code/Source/CoreLights/PointLightFeatureProcessor.h

@@ -52,6 +52,7 @@ namespace AZ
             void SetShadowFilterMethod(LightHandle handle, ShadowFilterMethod method) override;
             void SetFilteringSampleCount(LightHandle handle, uint16_t count) override;
             void SetEsmExponent(LightHandle handle, float esmExponent) override;
+            void SetNormalShadowBias(LightHandle handle, float bias) override;
             void SetPointData(LightHandle handle, const PointLightData& data) override;
 
             const Data::Instance<RPI::Buffer>  GetLightBuffer() const;

+ 3 - 2
Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.cpp

@@ -155,8 +155,9 @@ namespace AZ::Render
     {
         AZ_Assert(id.IsValid(), "Invalid ShadowId passed to ProjectedShadowFeatureProcessor::SetNormalShadowBias().");
 
-        ShadowProperty& shadowProperty = GetShadowPropertyFromShadowId(id);
-        shadowProperty.m_normalShadowBias = normalShadowBias;
+        ShadowData& shadowData = m_shadowData.GetElement<ShadowDataIndex>(id.GetIndex());
+        shadowData.m_normalShadowBias = normalShadowBias;
+        m_deviceBufferNeedsUpdate = true;
     }
 
     void ProjectedShadowFeatureProcessor::SetShadowmapMaxResolution(ShadowId id, ShadowmapSize size)

+ 1 - 2
Gems/Atom/Feature/Common/Code/Source/Shadows/ProjectedShadowFeatureProcessor.h

@@ -68,7 +68,7 @@ namespace AZ::Render
             uint32_t m_filteringSampleCount = 0;
             AZStd::array<float, 2> m_unprojectConstants = { {0, 0} };
             float m_bias;
-            float m_normalShadowBias;
+            float m_normalShadowBias = 0;
             float m_esmExponent = 87.0f;
             float m_padding[3];
         };
@@ -79,7 +79,6 @@ namespace AZ::Render
             ProjectedShadowDescriptor m_desc;
             RPI::ViewPtr m_shadowmapView;
             float m_bias = 0.1f;
-            float m_normalShadowBias = 0.0f;
             ShadowId m_shadowId;
         };
 

+ 1 - 0
Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/__init__.py

@@ -163,6 +163,7 @@ _LOGGER.debug('Invoking __init__.py for {0}.'.format({_PACKAGENAME}))
 # -------------------------------------------------------------------------
 
 
+# -------------------------------------------------------------------------
 def get_datadir() -> pathlib.Path:
     """
     persistent application data.

+ 19 - 11
Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/capture_displaymapperpassthrough.py → Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/capture_displaymapper.py

@@ -10,9 +10,7 @@
 """Frame capture of the Displaymapper Passthrough (outputs .dds image)"""
 # ------------------------------------------------------------------------
 import logging as _logging
-from env_bool import env_bool
 
-# ------------------------------------------------------------------------
 _MODULENAME = 'ColorGrading.capture_displaymapperpassthrough'
 
 import ColorGrading.initialize
@@ -27,25 +25,35 @@ _LOGGER.debug('Initializing: {0}.'.format({_MODULENAME}))
 import azlmbr.bus
 import azlmbr.atom
 
-default_passtree = ["Root",
+# This requires the level to have the DisplayMapper component added
+# and configured to 'Passthrough'
+# but now we can capture the parent input 
+# so this is here for reference for how it previously worked
+passtree_displaymapperpassthrough = ["Root",
+                                     "MainPipeline_0",
+                                     "MainPipeline",
+                                     "PostProcessPass",
+                                     "LightAdaptation",
+                                     "DisplayMapperPass",
+                                     "DisplayMapperPassthrough"]
+
+# we can grad the parent pass input to the displaymapper directly
+passtree_default = ["Root",
                     "MainPipeline_0",
                     "MainPipeline",
                     "PostProcessPass",
                     "LightAdaptation",
-                    "DisplayMapperPass",
-                    "DisplayMapperPassthrough"]
-
-default_path = "FrameCapture\DisplayMapperPassthrough.dds"
+                    "DisplayMapperPass"]
 
-# To Do: we should try to set display mapper to passthrough,
-# then back after capture?
+default_path = "FrameCapture\DisplayMappeInput.dds"
 
 # To Do: we can wrap this, to call from a PySide2 GUI
 
 def capture(command="CapturePassAttachment",
-            passtree=default_passtree,
-            pass_type="Output",
+            passtree=passtree_default,
+            pass_type="Input",
             output_path=default_path):
+    """Writes frame capture into project cache"""
     azlmbr.atom.FrameCaptureRequestBus(azlmbr.bus.Broadcast,
                                        command,
                                        passtree,

+ 7 - 12
Gems/Atom/Feature/Common/Editor/Scripts/ColorGrading/initialize.py

@@ -32,10 +32,7 @@ if DCCSI_GDEBUG:
     DCCSI_LOGLEVEL = int(10)
 
 # set up logger with both console and file _logging
-if DCCSI_GDEBUG:
-    _LOGGER = initialize_logger(_PACKAGENAME, log_to_file=True, default_log_level=DCCSI_LOGLEVEL)
-else:
-    _LOGGER = initialize_logger(_PACKAGENAME, log_to_file=False, default_log_level=DCCSI_LOGLEVEL)
+_LOGGER = initialize_logger(_PACKAGENAME, log_to_file=DCCSI_GDEBUG, default_log_level=DCCSI_LOGLEVEL)
         
 _LOGGER.debug('Initializing: {0}.'.format({_MODULENAME}))
 
@@ -46,7 +43,7 @@ if DCCSI_DEV_MODE:
     APPDATA = get_datadir()  # os APPDATA
     APPDATA_WING = Path(APPDATA, f"Wing Pro {DCCSI_WING_VERSION_MAJOR}").resolve()
     if APPDATA_WING.exists():
-        site.addsitedir(pathlib.PureWindowsPath(APPDATA_WING).as_posix())
+        site.addsitedir(APPDATA_WING.resolve())
         import wingdbstub as debugger
         try:
             debugger.Ensure()
@@ -75,8 +72,7 @@ def start():
 
         try:
             _O3DE_DEV = Path(os.getenv('O3DE_DEV'))
-            _O3DE_DEV = _O3DE_DEV.resolve()
-            os.environ['O3DE_DEV'] = pathlib.PureWindowsPath(_O3DE_DEV).as_posix()
+            os.environ['O3DE_DEV'] = _O3DE_DEV.as_posix()
             _LOGGER.debug(f'O3DE_DEV is: {_O3DE_DEV}')
         except EnvironmentError as e:
             _LOGGER.error('O3DE engineroot not set or found')
@@ -86,23 +82,22 @@ def start():
             _TAG_LY_BUILD_PATH = os.getenv('TAG_LY_BUILD_PATH', 'build')
             _DEFAULT_BIN_PATH = Path(str(_O3DE_DEV), _TAG_LY_BUILD_PATH, 'bin', 'profile')
             _O3DE_BIN_PATH = Path(os.getenv('O3DE_BIN_PATH', _DEFAULT_BIN_PATH))
-            _O3DE_BIN_PATH = _O3DE_BIN_PATH.resolve()
-            os.environ['O3DE_BIN_PATH'] = pathlib.PureWindowsPath(_O3DE_BIN_PATH).as_posix()
+            os.environ['O3DE_BIN_PATH'] = _O3DE_BIN_PATH.as_posix()
             _LOGGER.debug(f'O3DE_BIN_PATH is: {_O3DE_BIN_PATH}')
-            site.addsitedir(pathlib.PureWindowsPath(_O3DE_BIN_PATH).as_posix())
+            site.addsitedir(_O3DE_BIN_PATH.resolve())
         except EnvironmentError as e:
             _LOGGER.error('O3DE bin folder not set or found')
             raise e
         
         if running_editor:
             _O3DE_DEV = Path(os.getenv('O3DE_DEV', Path(azlmbr.paths.engroot)))
-            os.environ['O3DE_DEV'] = pathlib.PureWindowsPath(_O3DE_DEV).as_posix()
+            os.environ['O3DE_DEV'] = _O3DE_DEV.as_posix()
             _LOGGER.debug(_O3DE_DEV)
         
             _O3DE_BIN_PATH = Path(str(_O3DE_DEV),Path(azlmbr.paths.executableFolder))
         
             _O3DE_BIN = Path(os.getenv('O3DE_BIN', _O3DE_BIN_PATH.resolve()))
-            os.environ['O3DE_BIN'] = pathlib.PureWindowsPath(_O3DE_BIN).as_posix()
+            os.environ['O3DE_BIN'] = _O3DE_BIN_PATH.as_posix()
         
             _LOGGER.debug(_O3DE_BIN)
         

+ 3 - 3
Gems/Atom/Feature/Common/Tools/ColorGrading/cmdline/CMD_ColorGradingTools.bat

@@ -34,15 +34,15 @@ SETLOCAL ENABLEDELAYEDEXPANSION
 IF EXIST "%~dp0User_Env.bat" CALL %~dp0User_Env.bat
 
 :: Initialize env
-echo 
+echo.
 echo     ... calling Env_Core.bat
 CALL %~dp0\Env_Core.bat
 
-echo 
+echo.
 echo     ... calling Env_Python.bat
 CALL %~dp0\Env_Python.bat
 
-echo 
+echo.
 echo     ... calling Env_Tools.bat
 CALL %~dp0\Env_Tools.bat
 

+ 10 - 36
Gems/Atom/Feature/Common/Tools/ColorGrading/cmdline/Env_Python.bat

@@ -27,37 +27,19 @@ echo ~    O3DE Color Grading Python Env ...
 echo _____________________________________________________________________
 echo.
 
-:: Python Version
-:: Ideally these are set to match the O3DE python distribution
-:: <O3DE>\python\runtime
-IF "%DCCSI_PY_VERSION_MAJOR%"=="" (set DCCSI_PY_VERSION_MAJOR=3)
-echo     DCCSI_PY_VERSION_MAJOR = %DCCSI_PY_VERSION_MAJOR%
-
-:: PY version Major
-IF "%DCCSI_PY_VERSION_MINOR%"=="" (set DCCSI_PY_VERSION_MINOR=7)
-echo     DCCSI_PY_VERSION_MINOR = %DCCSI_PY_VERSION_MINOR%
-
-IF "%DCCSI_PY_VERSION_RELEASE%"=="" (set DCCSI_PY_VERSION_RELEASE=10)
-echo     DCCSI_PY_VERSION_RELEASE = %DCCSI_PY_VERSION_RELEASE%
-
-:: shared location for 64bit python 3.7 DEV location
-:: this defines a DCCsi sandbox for lib site-packages by version
-:: <O3DE>\Gems\AtomLyIntegration\TechnicalArt\DccScriptingInterface\3rdParty\Python\Lib
-set DCCSI_PYTHON_PATH=%DCCSIG_PATH%\3rdParty\Python
-echo     DCCSI_PYTHON_PATH = %DCCSI_PYTHON_PATH%
-
-:: add access to a Lib location that matches the py version (example: 3.7.x)
-:: switch this for other python versions like maya (2.7.x)
-IF "%DCCSI_PYTHON_LIB_PATH%"=="" (set DCCSI_PYTHON_LIB_PATH=%DCCSI_PYTHON_PATH%\Lib\%DCCSI_PY_VERSION_MAJOR%.x\%DCCSI_PY_VERSION_MAJOR%.%DCCSI_PY_VERSION_MINOR%.x\site-packages)
-echo     DCCSI_PYTHON_LIB_PATH = %DCCSI_PYTHON_LIB_PATH%
-
-:: add to the PATH
-SET PATH=%DCCSI_PYTHON_LIB_PATH%;%PATH%
-
 :: shared location for default O3DE python location
 set DCCSI_PYTHON_INSTALL=%O3DE_DEV%\Python
 echo     DCCSI_PYTHON_INSTALL = %DCCSI_PYTHON_INSTALL%
 
+:: Warning, many DCC tools (like Maya) include thier own versioned python interpretter.
+:: Some apps may not operate correctly if PYTHONHOME is set/propogated.
+:: This is definitely the case with Maya, doing so causes Maya to not boot.
+FOR /F "tokens=* USEBACKQ" %%F IN (`%DCCSI_PYTHON_INSTALL%\python.cmd %DCCSI_PYTHON_INSTALL%\get_python_path.py`) DO (SET PYTHONHOME=%%F)
+echo     PYTHONHOME - is now the folder containing O3DE python executable
+echo     PYTHONHOME = %PYTHONHOME% 
+
+SET PYTHON=%PYTHONHOME%\python.exe
+
 :: location for O3DE python 3.7 location 
 set DCCSI_PY_BASE=%DCCSI_PYTHON_INSTALL%\python.cmd
 echo     DCCSI_PY_BASE = %DCCSI_PY_BASE%
@@ -65,10 +47,7 @@ echo     DCCSI_PY_BASE = %DCCSI_PY_BASE%
 :: ide and debugger plug
 set DCCSI_PY_DEFAULT=%DCCSI_PY_BASE%
 
-IF "%DCCSI_PY_REV%"=="" (set DCCSI_PY_REV=rev2)
-IF "%DCCSI_PY_PLATFORM%"=="" (set DCCSI_PY_PLATFORM=windows)
-
-set DCCSI_PY_IDE=%DCCSI_PYTHON_INSTALL%\runtime\python-%DCCSI_PY_VERSION_MAJOR%.%DCCSI_PY_VERSION_MINOR%.%DCCSI_PY_VERSION_RELEASE%-%DCCSI_PY_REV%-%DCCSI_PY_PLATFORM%\python
+set DCCSI_PY_IDE=%PYTHONHOME%
 echo     DCCSI_PY_IDE = %DCCSI_PY_IDE%
 
 :: Wing and other IDEs probably prefer access directly to the python.exe
@@ -91,11 +70,6 @@ SET PATH=%DCCSI_PYTHON_INSTALL%;%DCCSI_PY_IDE%;%DCCSI_PY_IDE_PACKAGES%;%DCCSI_PY
 set PYTHONPATH=%DCCSIG_PATH%;%DCCSI_PYTHON_LIB_PATH%;%O3DE_BIN_PATH%;%DCCSI_COLORGRADING_SCRIPTS%;%DCCSI_FEATURECOMMON_SCRIPTS%;%PYTHONPATH%
 echo     PYTHONPATH = %PYTHONPATH%
 
-:: used for debugging in WingIDE (but needs to be here)
-IF "%TAG_USERNAME%"=="" (set TAG_USERNAME=NOT_SET)
-echo     TAG_USERNAME = %TAG_USERNAME%
-IF "%TAG_USERNAME%"=="NOT_SET" (echo        Add TAG_USERNAME to User_Env.bat)
-
 :: Set flag so we don't initialize dccsi environment twice
 SET O3DE_ENV_PY_INIT=1
 GOTO END_OF_FILE

+ 0 - 5
Gems/Atom/Feature/Common/Tools/ColorGrading/cmdline/User_Env.bat.template

@@ -25,11 +25,6 @@ SET TAG_LY_BUILD_PATH=build
 SET DCCSI_GDEBUG=True
 SET DCCSI_DEV_MODE=True
 
-:: set the your user name here for windows path
-SET TAG_USERNAME=NOT_SET
-SET DCCSI_PY_REV=rev1
-SET DCCSI_PY_PLATFORM=windows
-
 :: Set flag so we don't initialize dccsi environment twice
 SET O3DE_USER_ENV_INIT=1
 GOTO END_OF_FILE

+ 2 - 0
Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.h

@@ -9,11 +9,13 @@
 #pragma once
 
 #include <AzCore/Module/Module.h>
+#include <AzToolsFramework/API/PythonLoader.h>
 
 namespace AtomToolsFramework
 {
     class AtomToolsFrameworkModule
         : public AZ::Module
+        , public AzToolsFramework::EmbeddedPython::PythonLoader
     {
     public:
         AZ_RTTI(AtomToolsFrameworkModule, "{B58B7CA8-98C9-4DC8-8607-E094989BBBE2}", AZ::Module);

+ 7 - 0
Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightBus.h

@@ -132,6 +132,13 @@ namespace AZ
             //! Sets the Esm exponent. Higher values produce a steeper falloff between light and shadow.
             virtual void SetEsmExponent(float exponent) = 0;
 
+            //! Reduces acne by biasing the shadowmap lookup along the geometric normal.
+            //! @return Returns the amount of bias to apply.
+            virtual float GetNormalShadowBias() const = 0;
+
+            //! Reduces acne by biasing the shadowmap lookup along the geometric normal.
+            //! @param normalShadowBias Sets the amount of normal shadow bias to apply.
+            virtual void SetNormalShadowBias(float normalShadowBias) = 0;
         };
 
         //! The EBus for requests to for setting and getting light component properties.

+ 1 - 0
Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/CoreLights/AreaLightComponentConfig.h

@@ -57,6 +57,7 @@ namespace AZ
             // Shadows (only used for supported shapes)
             bool m_enableShadow = false;
             float m_bias = 0.1f;
+            float m_normalShadowBias = 0.0f;
             ShadowmapSize m_shadowmapMaxSize = ShadowmapSize::Size256;
             ShadowFilterMethod m_shadowFilterMethod = ShadowFilterMethod::None;
             uint16_t m_filteringSampleCount = 12;

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است