Browse Source

Stabilization/2310 @ 9f9829b26a47c2f255959b8e7edac24ac081c651 -> Development (#16844)

* Enable the DPE by default

Signed-off-by: Chris Galvan <[email protected]>

* Fixed asset picker automated test with DPE enabled

Signed-off-by: Chris Galvan <[email protected]>

* type fixing for WrapperType

Signed-off-by: Alex Montgomery <[email protected]>

* fixes from code review

Signed-off-by: Alex Montgomery <[email protected]>

* Fix missing support for multi-collider articulation links (#16713)

* Fix missing support for multi-collider articulation links
* Adjust to review
---------
Signed-off-by: Michał Pełka <[email protected]>

* addressing PR feedback

Signed-off-by: TJ Kotha <[email protected]>

* addressed tweaks and nitpicks

Signed-off-by: TJ Kotha <[email protected]>

* addressing more feedback

Signed-off-by: TJ Kotha <[email protected]>

* adjusted spacing

Signed-off-by: TJ Kotha <[email protected]>

* addressing chgalvan's feedback

Signed-off-by: TJ Kotha <[email protected]>

* Skip test that is intermittently failing with DPE enabled

Signed-off-by: Chris Galvan <[email protected]>

* Fix for failing to open text files when running from a snap install (#16717)

* Fix for failing to open text files when running from a snap install

- Update Linux Qt packages to use the fixed revision (rev9) that fixes the snap/open local file issue
- Corrected use of `QUrl("file:///" + ` with the correct way: `QUrl("file:///" + `
- Added warning logs in Project Manager in case QDesktopServices::openUrl(<local file>) fails.
- Fix error causing 'show logs' not to work

Signed-off-by: Steve Pham <[email protected]>

* fix for opaque wrapper types (#16732)

* type fixing for WrapperType

Signed-off-by: Alex Montgomery <[email protected]>

* fixes from code review

Signed-off-by: Alex Montgomery <[email protected]>

---------

Signed-off-by: Alex Montgomery <[email protected]>

* turn on override support in the Inspector by default

Signed-off-by: Alex Montgomery <[email protected]>

* Fixed crash in DPE due to events being sent to widgets that have been deleted

Signed-off-by: Chris Galvan <[email protected]>

* remove unnecessary overrides, revert to new default

Signed-off-by: Alex Montgomery <[email protected]>

* Material Canvas: Updating material graph shaders to not optimize out material SRG

This change updates the material graph template material SRGs to include a placeholder variable. Including and referencing this variable ensures that the material SRG always exists across all shaders that might use it. This initial resolved one assert that triggered when adding or removing material inputs from a material graph, consequently adding or removing variables from the material SRG and corresponding connections in the material type. As the AP processes the material and shader changes, the runtime may receive individual shader updates with inconsistent SRG data until everything is fully compiled and synchronized.

This change may be reverted after subsequent changes that may also address the issue.

Relates to https://github.com/o3de/o3de/issues/16034
Relates to https://github.com/o3de/o3de/issues/16735
Relates to https://github.com/o3de/o3de/issues/14772

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 16376e4323167ddac4346a224e64c59c02140abb)
(cherry picked from commit a3cbcbb3bd369f2d80e0c715b62c0a82780d13c5)

* Material Canvas: Create new material instance in viewport after changes

This change updates the material assignment class with a flag to force it to create a new material instance regardless of property overrides. The material canvas viewport uses this flag to guarantee the most recent version of the material asset and instance are reflected in the viewport.

Relates to https://github.com/o3de/o3de/issues/16034

Relates to https://github.com/o3de/o3de/issues/16735

Relates to https://github.com/o3de/o3de/issues/14772

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 07df9b3a7a64a4982b08034f1647b1a22ddc30f9)
(cherry picked from commit 2275ceed1e929995b75a02030657cee6b26b5113)

* Material Canvas: Option to delete previously generated files before creating new ones

This change provides an option to delete files that were previously generated using the current template before creating new ones. The option was useful while debugging and may also be useful if switching between different material output nodes that might use the same file names. The option is disabled by default.

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 015e0076fddd25c2b387d272aa8f6ede4b59d24d)
(cherry picked from commit fcdd7ee9e5ef1476bf62bebbca57c57738e88170)

* updating display name and tooltip for delete generated files option

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 8888dff582310ad57ee4330a23f4f526cf7b9443)
(cherry picked from commit 8bc60150ecdd6a7be950cf718902750de6271ab7)

* Update shader and SRG instance ID creation functions

This change updates these functions to generate unique instance IDs that take the content or the version of the data into account. The asset data pointer is used to make the value more unique, relative to the asset, but could be replaced with some other explicit version value or a hash of the data. Ensuring that the instance ID is unique relative to the version of the data will permit new instances to be created or new versions of the asset with the same asset ID.

Relates to https://github.com/o3de/o3de/issues/16034

Relates to https://github.com/o3de/o3de/issues/16735

Relates to https://github.com/o3de/o3de/issues/14772

Relates to https://github.com/o3de/o3de/issues/15794’

Signed-off-by: gadams3 <[email protected]>

* Converted instance ID data byte array to structure after PR feedback

Signed-off-by: gadams3 <[email protected]>

* Changing shader resource group instance ID generation to only use the shader resource group name and layout hashes

Signed-off-by: gadams3 <[email protected]>

* Renaming instance ID function to MakeSRGPoolInstanceId

Signed-off-by: gadams3 <[email protected]>

* Reverting material related changes from this PR and moving to separate PR

Signed-off-by: gadams3 <[email protected]>

* Reverting but cleaning up changes to make instance ID for shader resource group pool

Signed-off-by: gadams3 <[email protected]>

* Fixed unit tests after enabling prefab overrides

Signed-off-by: Chris Galvan <[email protected]>

* Change PR checklist GHA event type (#16765)

* Fixing warning as error caused by unused variable

Signed-off-by: gadams3 <[email protected]>

* Correcting spelling of "Default" in material component

Signed-off-by: gadams3 <[email protected]>

* Restored function within correct spelling and added deprecation notice

Signed-off-by: gadams3 <[email protected]>

* SMC: Allow access to generated shaders and materials in intermediate assets folder

Replaced explicit checks for ignoring the cache folder with configurable settings so that SMC could access generated shaders and materials

Signed-off-by: gadams3 <[email protected]>

* Changed the ignored paths pattern settings to use regular expressions.
Added a faster, direct setting for ignoring the cache holder.
Added the ignore cache folder setting to the settings dialog.
Added a settings registry handler to monitor changes to the new settings and update the asset browser.

Signed-off-by: gadams3 <[email protected]>

* Atom tools, SMC, MC: Atom tools asset browser support for extension based file filters

SMC, MC, and other tools work primarily with source files that are not directly tied to asset types. This change implements support in the atom tools asset browser for setting up extension based file filters in lieu of the asset group based filters.

Signed-off-by: gadams3 <[email protected]>

* Updating comments and adding switch statement braces per PR feedback

Signed-off-by: gadams3 <[email protected]>

* fixed a few opaque types, where possible

Signed-off-by: Alex Montgomery <[email protected]>

* Fix reading of the GenericValueList values that contain enums

The GenericValueListAttributeDefinition has been updated to use the
`Dom::Utils::ValueFromType` function to properly read the attribute from
a Dom::Value

The GenericComboBoxCtrl::ConsumeAttribute reading of the
GenericValueList attribute has been updated to try to read a vector of every integer type `AZStd::pair<int-type, AZStd::string>` type.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updating material canvas asset status reporting to be non blocking with multiple documents open simultaneously

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit d1db12012509a1b4dfaa0778819d7d0d07256c78)
Signed-off-by: Guthrie Adams <[email protected]>

* Removing asset handling from material class that reported success even though the class has not been initialized

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 7a7a7c3868324625c2b0388cfcc26eb2c0f76621)
Signed-off-by: Guthrie Adams <[email protected]>

* updated erroneous comments in graph view

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit c34dcb70ddfc7f0667ef74d0d75cbd2c3988f103)
Signed-off-by: Guthrie Adams <[email protected]>

* Fix missing texture reference

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit e9302d7f31f7ad9064e4ac874f300d5a4ac818e7)
Signed-off-by: Guthrie Adams <[email protected]>

* Centralized logic and settings for deleting previously generated files

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit b3cf1c0fbbf5d880d3e3947eaf48ab6bcc4d223b)
Signed-off-by: Guthrie Adams <[email protected]>

* Updating comments for registering shader source data document

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 6a0e647028504ce5a15156e50272d2c59314e4bc)
Signed-off-by: Guthrie Adams <[email protected]>

* Restoring settings to clear viewport material when graph compilation starts

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 2820309965dd4f403935de3285df5cfe16de0f00)
Signed-off-by: Guthrie Adams <[email protected]>

* Updated comments

Signed-off-by: gadams3 <[email protected]>

updating messaging to make assets less acidic

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit c1bec429b984165607ac4c63a0ef39040e03f9b0)
Signed-off-by: Guthrie Adams <[email protected]>

* Updating comments

Signed-off-by: gadams3 <[email protected]>
(cherry picked from commit 29a623a6eed6718839933b481865ccbfe438a2eb)
Signed-off-by: Guthrie Adams <[email protected]>

* removing reverted changes

Signed-off-by: Guthrie Adams <[email protected]>

* This change fix a diffuse probe grid bake issue with pc which doesn't support raytracing (#16733)

* This change fix a diffuse probe grid bake issue with pc which doesn't support raytracing

Signed-off-by: VickyAtAZ <[email protected]>
Co-authored-by: Mike Balfour <[email protected]>

* Fixed comparison of file paths with '.' using the AZ Path API (#16674) (#16798)

Windows Paths such as `C:/foo/bar/./baz` now correctly compare equal to
`C:/foo/bar/baz` and POSIX paths such as `/baz/bar/./foo` now compare
equal to `/baz/bar/foo`

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fix for atom tools not launching with all gems enabled

All of the atom tools that run independently of the main editor have registry settings to disable specific gems within those tools.
This is done to speed up launch times and reduce memory utilization by not loading or activating systems that will never be used within those tools.
Occasionally, a new gem is added or activated that may depend on one of the disabled gem modules.
In this case, the virtual game pad was activated by the game project and relied upon ly shine, which is disabled by those tools.
So the virtual game pad was added to the disabled list.
Alternatively, the auto load or disabled gem registry settings files can be completely removed to let all of the project runtime dependencies load in the tools.
I attempted this but certain project specific gems, like those from the multiplayer sample, will still need to be disabled.
Script canvas will also need to be disabled because it fires several errors and warnings while, I assume, trying to load graph data.

Signed-off-by: gadams3 <[email protected]>

* made a new CVar for AssetEditor DPE support (#16782)

Signed-off-by: Alex Montgomery <[email protected]>

* Disable o3de-extras automated testing for stabilization (#16801)

Signed-off-by: Mike Chang <[email protected]>

* Update the splash screen for o3de editor. (#16812)

Signed-off-by: VickyAtAZ <[email protected]>

* Fixed prefab overrides not working with DPE enabled

Signed-off-by: Chris Galvan <[email protected]>

* Fix DPE resizing on Linux (#16813)

* made a new CVar for AssetEditor DPE support

Signed-off-by: Alex Montgomery <[email protected]>

* fix for Linux DPEs not automatically resizing

Signed-off-by: Alex Montgomery <[email protected]>

---------

Signed-off-by: Alex Montgomery <[email protected]>

* Document Property Editor Slider fixes (#16814)

* DOM ValueFromType and ValueToType json serialization support

The DomUtils.h/.cpp `ValueFromType` and `ValueToType` functions have
been updated to use the JSON Serialization system when possible to
convert an `AZ::Dom::Value` to/from a C++ type.

This is the preferred way to store a aggregate type into
`AZ::Dom::Value` as it maintains the C++/C object structure in a Dom
Object/Array structure.

If the type doesn't support JSON Serialization, then it fallback to
being stored using an `AZStd::any` inside of the Dom::Value as an opaque
type.
The limitations of using an opaque type, is that there is no support for
comparing two `AZStd::any` objects to each other.
The only way for comparison, is the compare the address where the
`AZStd::any` objects are stored.

Due to that, the Dom::Value::operator= and AZ::DeepCompareIsEqual can
only compare the internal `AZStd::shared_ptr<AZStd::any>` instance
memory address to compare if two opaque types are equal and not a deep
compare of the fields of the stored aggregate type.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Adding an `IsTypeSerializable` impl for JsonSerialization

The `IsTypeSerializable` function checks if an AZ TypeId has either a
serializer registered with the Json Registration Context or is reflected
to the SerializeContext.

This allows checking to see if a type can be serialized before calling
either `JsonSerialization::Load` or `JsonSerialization::Store`

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Added check for serializability to the Dom Value <-> Json Serialization functions

The Dom Utils `LoadViaJsonSerialization` and `StoreViaJsonSerialization`
functions now check if the AZ TypeId is reflected with either the
JsonRegistrationContext or SerializeContext before attempting to use the
Json Serialization system to marshal a Dom Value to/from a C++ object.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* The Dom Patch creation logic now performs a deep compare of array fields

Because the Dom Patch creation did not perform a deep compare previously
of it's fields, any Node, Object or Array fields contained within an
Array field performed a pointer comparison between the shared_ptr
address containing those objects, instead of comparing the properties
and children of those complex types.

Therefore patch changes were generated for Dom entrys that were
value-equivalent, but not address equivalent.

This would cause Node children such as Labels fields to compare unequal,
even when the label string was equivalent.

fixes #16742

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Adding a Json Serializer for the EditContext EnumConstant

With an JsonSerializer for the EditContext EnumConstant, it is now
possible to marshal an enum constant to a `Dom::Value` using Json
Serialization.
Therefore the `Dom::Value` would store an "Object" instead of an
"OpaqueType" which is comparable without a value hash.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed the DOM Utils `ValueToType` function Json Serialization flow

By default using JsonSerialization doesn't store full default values to
the serialized rapidjson object, so only the non-default value fields
were being added to the Dom::Values

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Changed the Dom Utils MarshalOpaqueValue and ValueToType functions to
use a no-op Json issue reporter callback to prevent log spam to the
Editor console.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Modified the ReflectionAdapter `VisitObjectBegin` function
`StoreValueIntoPointer` lambda to account for marshaling Dom ObjectTypes

The `StoreValueIntoPointer` value was only accounting for visiting
pointers to C++ types through the TryMarshalValueToPointer function
call.

Now that the Dom Utils `ValueFromType` function has been updated to
write C++ structs using Json Serialization to the Dom::Value when
possible, the scenario where a Dom::Value which is an "Object" is being
visited.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Moved `TryMarshalValueToPointer` logic earlier in the ExtractOpaqueValue lambda

The flow now attempts to check if the Dom::Value is a Dom::Object which
has a pointer field that can be deserialized first.
If that fails, then the JSON Serialization system is used to load Dom
Value object into a C++ type.

This is needed as the AssetJsonSerializer needs to suceed in its
deserialization operation, when all the relavant asset fields are
missing such as the "assetId" field.

The result of the operation indicates that JSON Serialization
has used the default value to intialize the Asset.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Updated the SetValueFromDom function to always use ValueToType.

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Update Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp

Fixed return statement in DomUtils.cpp

Signed-off-by: lumberyard-employee-dm <[email protected]>

---------

Signed-off-by: lumberyard-employee-dm <[email protected]>

* Fixed crash when closing Script Canvas

Signed-off-by: Chris Galvan <[email protected]>

* Fixed node serialized path generation to ignore base class path

Signed-off-by: Chris Galvan <[email protected]>

* Remove developer preview title from Shortcuts and Bootstrapper WiX templates (#16825)

Signed-off-by: Mike Chang <[email protected]>

* Switch override prefs from setreg to CVars, and default the DPE Inspector to "off" (#16824)

* made a new CVar for AssetEditor DPE support

Signed-off-by: Alex Montgomery <[email protected]>

* fix for Linux DPEs not automatically resizing

Signed-off-by: Alex Montgomery <[email protected]>

* switch override flags from setreg to CVars

Signed-off-by: Alex Montgomery <[email protected]>

* remove overarchng DPE flag, and add an Inspector-specific one

Signed-off-by: Alex Montgomery <[email protected]>

* Update Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabEditorPreferences.cpp

Co-authored-by: lumberyard-employee-dm <[email protected]>
Signed-off-by: Alex Montgomery <[email protected]>

* addressed PR feedback

Signed-off-by: Alex Montgomery <[email protected]>

* default outliner overrides to false

Signed-off-by: Alex Montgomery <[email protected]>

* fix Inspector override tests

Signed-off-by: Alex Montgomery <[email protected]>

* remove unnecessary extern

Signed-off-by: Alex Montgomery <[email protected]>

* fix more tests

Signed-off-by: Alex Montgomery <[email protected]>

* fix some missed tests

Signed-off-by: Alex Montgomery <[email protected]>

* fix tests to save/restore cvar settings

Signed-off-by: Alex Montgomery <[email protected]>

* stupid copy pasta fix

Signed-off-by: Alex Montgomery <[email protected]>

* string fix

Signed-off-by: Alex Montgomery <[email protected]>

---------

Signed-off-by: Alex Montgomery <[email protected]>
Co-authored-by: lumberyard-employee-dm <[email protected]>

* Reverting o3de-extras changes

Signed-off-by: Olex Lozitskiy <[email protected]>

---------

Signed-off-by: Chris Galvan <[email protected]>
Signed-off-by: Alex Montgomery <[email protected]>
Signed-off-by: TJ Kotha <[email protected]>
Signed-off-by: Steve Pham <[email protected]>
Signed-off-by: gadams3 <[email protected]>
Signed-off-by: lumberyard-employee-dm <[email protected]>
Signed-off-by: Guthrie Adams <[email protected]>
Signed-off-by: VickyAtAZ <[email protected]>
Signed-off-by: Mike Chang <[email protected]>
Signed-off-by: Olex Lozitskiy <[email protected]>
Co-authored-by: Chris Galvan <[email protected]>
Co-authored-by: Alex Montgomery <[email protected]>
Co-authored-by: Michał Pełka <[email protected]>
Co-authored-by: TJ Kotha <[email protected]>
Co-authored-by: Steve Pham <[email protected]>
Co-authored-by: T.J. Kotha <[email protected]>
Co-authored-by: gadams3 <[email protected]>
Co-authored-by: Mike Chang <[email protected]>
Co-authored-by: lumberyard-employee-dm <[email protected]>
Co-authored-by: Guthrie Adams <[email protected]>
Co-authored-by: Qing Tao <[email protected]>
Co-authored-by: Mike Balfour <[email protected]>
Olex Lozitskiy 1 year ago
parent
commit
4ea6d8710b
84 changed files with 1261 additions and 449 deletions
  1. 1 1
      .github/workflows/stabilization-pr-checklist.yaml
  2. 3 3
      AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_DPEOverrides.py
  3. 6 6
      AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py
  4. 1 1
      AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Periodic.py
  5. 2 4
      AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetPicker_UI_UX.py
  6. 1 1
      AutomatedTesting/Gem/PythonTests/editor/EditorScripts/DPE_AllComponentPropertyTypesEditable.py
  7. 1 1
      AutomatedTesting/Gem/PythonTests/editor/EditorScripts/DPE_AllComponentsAddedRemoved.py
  8. 1 1
      AutomatedTesting/Gem/PythonTests/editor/TestSuite_DPE.py
  9. 1 0
      AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py
  10. 1 9
      AutomatedTesting/Registry/editorpreferences.setreg
  11. 63 88
      Code/Editor/o3de_logo.svg
  12. 2 2
      Code/Editor/splashscreen_background.png
  13. 2 1
      Code/Framework/AzCore/AzCore/DOM/DomComparison.cpp
  14. 142 0
      Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp
  15. 97 14
      Code/Framework/AzCore/AzCore/DOM/DomUtils.h
  16. 2 1
      Code/Framework/AzCore/AzCore/RTTI/TypeInfo.h
  17. 9 0
      Code/Framework/AzCore/AzCore/Serialization/EditContext.cpp
  18. 18 9
      Code/Framework/AzCore/AzCore/Serialization/EditContext.h
  19. 119 0
      Code/Framework/AzCore/AzCore/Serialization/EnumConstantJsonSerializer.cpp
  20. 39 0
      Code/Framework/AzCore/AzCore/Serialization/EnumConstantJsonSerializer.h
  21. 46 0
      Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp
  22. 28 0
      Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.h
  23. 5 0
      Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp
  24. 2 0
      Code/Framework/AzCore/AzCore/azcore_files.cmake
  25. 25 23
      Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/DocumentSchema.h
  26. 1 1
      Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h
  27. 8 1
      Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp
  28. 110 53
      Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.cpp
  29. 15 4
      Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorTab.cpp
  30. 45 38
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/DocumentPropertyEditor/PrefabComponentAdapter.cpp
  31. 21 9
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabEditorPreferences.cpp
  32. 0 2
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabEditorPreferences.h
  33. 7 19
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.cpp
  34. 0 5
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.h
  35. 25 4
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp
  36. 58 2
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.inl
  37. 2 40
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h
  38. 2 2
      Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntitySelectionTests.cpp
  39. 2 2
      Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntityTests.cpp
  40. 8 0
      Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp
  41. 1 0
      Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h
  42. 14 0
      Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionFixture.h
  43. 10 3
      Code/Framework/AzToolsFramework/Tests/Prefab/Overrides/PrefabInspectorOverrideTestFixture.cpp
  44. 2 0
      Code/Framework/AzToolsFramework/Tests/Prefab/Overrides/PrefabInspectorOverrideTestFixture.h
  45. 8 4
      Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp
  46. 5 1
      Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp
  47. 1 1
      Gems/Atom/Feature/Common/Assets/Scripts/material_find_overrides_demo.lua
  48. 1 1
      Gems/Atom/Feature/Common/Assets/Scripts/material_property_overrides_demo.lua
  49. 1 1
      Gems/Atom/Feature/Common/Assets/Scripts/material_property_overrides_demo.py
  50. 13 0
      Gems/Atom/Tools/MaterialCanvas/Registry/gem_autoload.materialcanvas.setreg
  51. 13 0
      Gems/Atom/Tools/MaterialEditor/Registry/gem_autoload.materialeditor.setreg
  52. 13 0
      Gems/Atom/Tools/PassCanvas/Registry/gem_autoload.passcanvas.setreg
  53. 13 0
      Gems/Atom/Tools/ShaderManagementConsole/Registry/gem_autoload.shadermanagementconsole.setreg
  54. 1 1
      Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialAssignment.h
  55. 14 2
      Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h
  56. 2 2
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp
  57. 2 2
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp
  58. 1 1
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialAssignment.cpp
  59. 3 3
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp
  60. 1 1
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h
  61. 2 2
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp
  62. 1 1
      Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h
  63. 2 2
      Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp
  64. 1 1
      Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h
  65. 2 3
      Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py
  66. 2 1
      Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/ui.py
  67. 1 0
      Gems/DiffuseProbeGrid/Code/Include/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h
  68. 5 0
      Gems/DiffuseProbeGrid/Code/Source/Components/DiffuseProbeGridComponentController.cpp
  69. 1 0
      Gems/DiffuseProbeGrid/Code/Source/Components/DiffuseProbeGridComponentController.h
  70. 11 0
      Gems/DiffuseProbeGrid/Code/Source/EditorComponents/EditorDiffuseProbeGridComponent.cpp
  71. 6 0
      Gems/DiffuseProbeGrid/Code/Source/Render/DiffuseProbeGridFeatureProcessor.cpp
  72. 1 0
      Gems/DiffuseProbeGrid/Code/Source/Render/DiffuseProbeGridFeatureProcessor.h
  73. 42 37
      Gems/PhysX/Code/Source/Articulation.cpp
  74. 3 3
      Gems/PhysX/Code/Source/Articulation.h
  75. 8 14
      Gems/PhysX/Code/Source/Pipeline/PhysicsPrefabProcessor.cpp
  76. 4 4
      Gems/ScriptCanvas/Assets/TranslationAssets/EBus/Senders/MaterialComponentRequestBus.names
  77. 0 2
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/Logger.cpp
  78. 1 3
      Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/Logger.h
  79. 1 1
      cmake/3rdParty/Platform/Linux/BuiltInPackages_linux_aarch64.cmake
  80. 1 1
      cmake/3rdParty/Platform/Linux/BuiltInPackages_linux_x86_64.cmake
  81. 1 1
      cmake/Platform/Windows/Packaging/Bootstrapper.wxs
  82. 1 1
      cmake/Platform/Windows/Packaging/Shortcuts.wxs
  83. 122 0
      scripts/o3de/ExportScripts/export_source_ios_xcode.py
  84. 2 2
      scripts/o3de/o3de/export_project.py

+ 1 - 1
.github/workflows/stabilization-pr-checklist.yaml

@@ -1,7 +1,7 @@
 name: Add stabilization PR checklist
 
 on:
-  pull_request:
+  pull_request_target:
     types: [opened, reopened]
     branches:
     - stabilization/**

+ 3 - 3
AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_DPEOverrides.py

@@ -17,9 +17,9 @@ class TestAutomationOverridesEnabled(EditorTestSuite):
 
     # These tests will execute with Outliner Overrides/Inspector DPE/Inspector Overrides enabled
     EditorTestSuite.global_extra_cmdline_args.extend(
-        [f"--regset=/O3DE/Preferences/Prefabs/EnableOutlinerOverrideManagement=true",
-         f"--regset=/O3DE/Preferences/Prefabs/EnableInspectorOverrideManagement=true",
-         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableDPE=true"])
+        [f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement=true",
+         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableInspectorOverrideManagement=true",
+         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableDPEInspector=true"])
 
     # Add Entity Tests
     class test_AddEntity_UnderAnotherEntity(EditorBatchedTest):

+ 6 - 6
AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Main.py

@@ -17,9 +17,9 @@ class TestAutomationOverridesDisabled(EditorTestSuite):
 
     # These tests will execute with Outliner Overrides/Inspector DPE/Inspector Overrides disabled
     EditorTestSuite.global_extra_cmdline_args.extend(
-        [f"--regset=/O3DE/Preferences/Prefabs/EnableOutlinerOverrideManagement=false",
-         f"--regset=/O3DE/Preferences/Prefabs/EnableInspectorOverrideManagement=false",
-         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableDPE=false"])
+        [f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement=false",
+         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableInspectorOverrideManagement=false",
+         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableDPEInspector=false"])
 
     # Add Entity Tests
     class test_AddEntity_UnderAnotherEntity(EditorBatchedTest):
@@ -182,9 +182,9 @@ class TestAutomationOverrides(EditorTestSuite):
 
     # These tests will execute with Outliner Overrides/Inspector DPE/Inspector Overrides enabled
     EditorTestSuite.global_extra_cmdline_args.extend(
-        [f"--regset=/O3DE/Preferences/Prefabs/EnableOutlinerOverrideManagement=true",
-         f"--regset=/O3DE/Preferences/Prefabs/EnableInspectorOverrideManagement=true",
-         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableDPE=true"])
+        [f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement=true",
+         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableInspectorOverrideManagement=true",
+         f"--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableDPEInspector=true"])
 
     # Overrides Tests
 

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Prefab/TestSuite_Periodic.py

@@ -16,7 +16,7 @@ from ly_test_tools.o3de.editor_test import EditorBatchedTest, EditorSingleTest,
 class TestAutomationNoOverrides(EditorTestSuite):
 
     # These tests will execute with prefab outliner overrides disabled
-    EditorTestSuite.global_extra_cmdline_args.append("--regset=O3DE/Preferences/Prefabs/EnableOutlinerOverrideManagement=false")
+    EditorTestSuite.global_extra_cmdline_args.append("--regset=/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement=false")
 
     @pytest.mark.skip(reason="Single test case to avoid suite failure. Can be removed when other tests are added.")
     class test_DummyTest(EditorSingleTest):

+ 2 - 4
AutomatedTesting/Gem/PythonTests/editor/EditorScripts/AssetPicker_UI_UX.py

@@ -230,8 +230,7 @@ def AssetPicker_UI_UX():
         # 4) Click on Asset Picker (Model Asset)
         general.select_object("TestEntity")
         general.idle_wait(0.5)
-        model_asset = component_list_widget.findChildren(QtWidgets.QFrame, "Model Asset")[0]
-        attached_button = model_asset.findChildren(QtWidgets.QPushButton, "attached-button")[0]
+        attached_button = component_list_widget.findChildren(QtWidgets.QPushButton, "attached-button")[0]
 
         # Assign Model Asset via OK button
         pyside_utils.click_button_async(attached_button)
@@ -254,8 +253,7 @@ def AssetPicker_UI_UX():
         hydra.get_set_test(entity, 0, "Controller|Configuration|Model Asset", None)
         general.select_object("TestEntity")
         general.idle_wait(0.5)
-        model_asset = component_list_widget.findChildren(QtWidgets.QFrame, "Model Asset")[0]
-        attached_button = model_asset.findChildren(QtWidgets.QPushButton, "attached-button")[0]
+        attached_button = component_list_widget.findChildren(QtWidgets.QPushButton, "attached-button")[0]
 
         # Assign Model Asset via Enter
         pyside_utils.click_button_async(attached_button)

+ 1 - 1
AutomatedTesting/Gem/PythonTests/editor/EditorScripts/DPE_AllComponentPropertyTypesEditable.py

@@ -67,7 +67,7 @@ def DPE_AllComponentPropertyTypesEditable():
         TestHelper.open_level("", "Base")
 
         # Verify the DPE is enabled
-        Report.critical_result(Tests.dpe_enabled, general.get_cvar("ed_enableDPE") == "true")
+        Report.critical_result(Tests.dpe_enabled, general.get_cvar("ed_enableDPEInspector") == "true")
 
         # Create several new entities to hold various components
         dither_entity = EditorEntity.create_editor_entity("DitherComponentTestEntity")

+ 1 - 1
AutomatedTesting/Gem/PythonTests/editor/EditorScripts/DPE_AllComponentsAddedRemoved.py

@@ -348,7 +348,7 @@ def DPE_AllComponentsAddedRemoved():
         TestHelper.open_level("", "Base")
 
         # Verify the DPE is enabled
-        Report.critical_result(Tests.dpe_enabled, general.get_cvar("ed_enableDPE") == "true")
+        Report.critical_result(Tests.dpe_enabled, general.get_cvar("ed_enableDPEInspector") == "true")
 
         # Loop through list of components
         for component_category in all_component_categories:

+ 1 - 1
AutomatedTesting/Gem/PythonTests/editor/TestSuite_DPE.py

@@ -16,7 +16,7 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorTestSuite
 class TestAutomationDPE(EditorTestSuite):
 
     # Disable Batch Mode and autotest mode and enable the DPE
-    global_extra_cmdline_args = ["-ed_enableDPE=true"]
+    global_extra_cmdline_args = ["-ed_enableDPEInspector=true"]
 
     @pytest.mark.xfail(reason="https://github.com/o3de/o3de/issues/15718, https://github.com/o3de/o3de/issues/15704, "
                               "https://github.com/o3de/o3de/issues/15695, https://github.com/o3de/o3de/issues/15579")

+ 1 - 0
AutomatedTesting/Gem/PythonTests/editor/TestSuite_Main.py

@@ -26,6 +26,7 @@ class TestAutomationNoAutoTestMode(EditorTestSuite):
         file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
                            True, True)
 
+    @pytest.mark.skip(reason="Skipped for intermittently failing.")
     class test_AssetPicker_UI_UX(EditorBatchedTest):
         from .EditorScripts import AssetPicker_UI_UX as test_module
 

+ 1 - 9
AutomatedTesting/Registry/editorpreferences.setreg

@@ -3,13 +3,5 @@
         "Preferences": {
             "EnablePrefabSystem": true
         }
-    },
-    "O3DE": {
-        "Preferences": {
-            "Prefabs": {
-                "EnableOutlinerOverrideManagement": true,
-                "EnableInspectorOverrideManagement": false
-            }
-        }
     }
-}
+}

+ 63 - 88
Code/Editor/o3de_logo.svg

@@ -1,98 +1,73 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!-- Generator: Adobe Illustrator 27.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-	 viewBox="0 0 350 133" style="enable-background:new 0 0 350 133;" xml:space="preserve">
+	 viewBox="0 0 1500 600" style="enable-background:new 0 0 1500 600;" xml:space="preserve">
 <style type="text/css">
 	.st0{fill:#FFFFFF;}
-	.st1{fill:#1E70EB;}
-	.st2{fill:#4B8DEF;}
-	.st3{fill:#D2E2FB;}
-	.st4{fill:#A5C6F7;}
-	.st5{fill:#78A9F3;}
 </style>
 <g>
-	<g id="Subtitle_00000171697014716552087890000008568781844654944905_">
-		<path class="st0" d="M125.6,115.8c5,0,8.4,2.9,8.4,6.8s-3.3,6.8-8.4,6.8c-5.1,0-8.4-2.9-8.4-6.8S120.5,115.8,125.6,115.8z
-			 M125.6,116.8c-4.2,0-6.8,2.4-6.8,5.7c0,3.3,2.6,5.7,6.8,5.7c4.1,0,6.8-2.4,6.8-5.7C132.4,119.2,129.7,116.8,125.6,116.8z"/>
-		<path class="st0" d="M140.2,116h6.5c3.3,0,5.2,1.8,5.2,3.8c0,2.1-1.9,3.8-5.2,3.8h-5v5.4h-1.5V116z M146.5,117h-4.8v5.6h4.8
-			c2.3,0,3.8-1.2,3.8-2.8C150.2,118.2,148.7,117,146.5,117z"/>
-		<path class="st0" d="M157.5,116h10.9v1H159v4.8h9.3v1H159v5.2h9.4v1h-10.9V116z"/>
-		<path class="st0" d="M176.1,117.8v11.3h-1.5V116h1.5l10.8,11.1V116h1.5v13.1h-1.4L176.1,117.8z"/>
-		<path class="st0" d="M204.2,126.5c1,1,2.6,1.8,4.8,1.8c2.7,0,4.3-1.1,4.3-2.8c0-1.9-2-2.6-4.6-2.6c-0.7,0-1.3,0-1.6,0v-1.1
-			c0.3,0,0.9,0,1.6,0c2.3,0,4.4-0.7,4.4-2.5c0-1.6-1.9-2.5-4.1-2.5c-2,0-3.4,0.6-4.6,1.7l-0.9-0.7c1.2-1.1,3.1-2,5.6-2
-			c3,0,5.5,1.2,5.5,3.4c0,2-2.2,2.9-3.7,3.1c1.5,0.1,3.9,1.1,3.9,3.3c0,2.2-2.1,3.7-5.8,3.7c-2.8,0-4.9-1-5.9-2.2L204.2,126.5z"/>
-		<path class="st0" d="M221.1,116h5.5c5.3,0,8.7,3,8.7,6.5c0,3.6-3.4,6.5-8.7,6.5h-5.5V116z M226.6,128c4.5,0,7.1-2.4,7.1-5.5
-			s-2.6-5.5-7.1-5.5h-4v11H226.6z"/>
-		<path class="st0" d="M250.9,116h10.9v1h-9.4v4.8h9.3v1h-9.3v5.2h9.4v1h-10.9V116z"/>
-		<path class="st0" d="M269.5,117.8v11.3H268V116h1.5l10.8,11.1V116h1.5v13.1h-1.4L269.5,117.8z"/>
-		<path class="st0" d="M296.7,115.8c2.8,0,4.9,0.9,6.5,2.2l-1.1,0.6c-1.2-1.1-3.2-1.8-5.4-1.8c-4.1,0-7.2,2.3-7.2,5.7
-			c0,3.4,3.1,5.8,7.2,5.8c2.3,0,4.1-0.9,5.1-1.6v-3.1h-6.5v-1h8v4.6c-1.5,1.3-3.8,2.2-6.5,2.2c-4.9,0-8.7-2.7-8.7-6.8
-			C288,118.5,291.8,115.8,296.7,115.8z"/>
-		<path class="st0" d="M309.6,116h1.5v13.1h-1.5V116z"/>
-		<path class="st0" d="M319.4,117.8v11.3h-1.5V116h1.5l10.8,11.1V116h1.5v13.1h-1.4L319.4,117.8z"/>
-		<path class="st0" d="M338.5,116h10.9v1H340v4.8h9.3v1H340v5.2h9.4v1h-10.9V116z"/>
+	<g>
+		<path class="st0" d="M1098.6,145.1c-35-32.7-83-50.1-139.4-50.1H832.1c-9.5,0-17.5,8.1-17.5,18v328.8c0,9.9,8.1,18,17.5,18h127.1
+			c56.4,0,104.5-17.2,139.4-49.8c35.4-33,54.1-78.7,54.1-132.3C1152.6,224.1,1133.9,178.2,1098.6,145.1 M1037.2,351.6
+			c-19.2,20.1-46.2,30.7-78.2,30.7h-53.3c-3.8,0-6.8-3.1-6.8-6.8V179.3c0-3.8,3.1-6.8,6.8-6.8H959c65.4,0,106,40.3,106,105.2
+			C1065,306.5,1055.1,332.8,1037.2,351.6"/>
+		<path class="st0" d="M1298.1,172.5h188.2c7.6,0,13.7-6.2,13.7-13.7v-47c0-9.2-7.5-16.7-19.1-16.7h-258.1c-7.5,0-15.6,8.1-15.6,18
+			v328.8c0,9.9,8.1,18,15.6,18h258.1c11.6,0,19.1-7.5,19.1-16.7v-47c0-7.6-6.1-13.7-13.7-13.7h-188.2c-3.8,0-6.8-3.1-6.8-6.8V322
+			c0-3.8,3.1-6.8,6.8-6.8h186c7.6,0,13.7-6.2,13.7-13.7v-50c0-7.6-6.2-13.7-13.7-13.7h-186c-3.8,0-6.8-3.1-6.8-6.8v-51.6
+			C1291.2,175.6,1294.3,172.5,1298.1,172.5"/>
+		<path class="st0" d="M736,288.5c-8.1-7.2-18.1-13.2-29.9-17.7c-3.4-1.3-3.8-5.9-0.7-7.7c8.6-5.1,16.1-11.2,22.4-18.2
+			c15.3-16.5,23.1-37.8,23.1-61.7c0-28.9-12.4-51.7-37-67.9c-23.3-15.3-54.9-23-94.1-23c-35.6,0-67.3,6.8-95.9,20.2
+			c-6.9,3.2-11,11.5-7.8,20c0.3,0.7,0.6,1.4,1,2c7.6,13.2,14.2,27.4,19.6,42c2.2,6,8,10,14.4,10c2.4,0,4.9-0.6,7.1-1.7
+			c8.5-4.4,16.4-7.7,23.7-9.8c11-3.4,21.4-5.1,32.7-5.1c37.3,0,42.1,15.6,42.1,29.1c0,13.2-4.6,22.5-14.4,27.9
+			c-11.6,6.2-30.8,9.4-57.1,9.4h-16.8c-1.2,0-2.5,0.1-3.6,0.4c-7.9,2-12.4,9.3-11.8,16.6c0.7,8.2,1.1,16.6,1.1,25
+			c0,5.1-0.2,10.5-0.5,16c-0.2,4.3,1.3,8.5,4.2,11.6c2.9,3.1,7,4.9,11.2,4.9h15.6c28.8,0,49.8,3,62.3,9c10.7,5.2,15.4,13,15.4,25.5
+			c0,14.8-4.4,23.9-13.8,30.2c-10.4,6.9-28,10.6-50.8,10.6c-15.6,0-30.7-2-46.7-6.1c-1.3-0.4-2.7-0.6-4.1-0.6
+			c-6.2,0-11.7,3.7-14.1,9.5c-5.6,13.4-12.3,26.6-20,39.2c-2.6,4.2-3,9.3-1.2,14c1.8,4.6,5.8,7.9,10.5,9.3
+			c25.6,7.4,53.2,11.1,84,11.1c48.8,0,87.1-9.8,113.9-29.2c27.8-20.2,41.9-47.9,41.9-83.8C761.7,324.2,753.1,303.6,736,288.5"/>
+		<path class="st0" d="M244.5,33C109.7,33,0,142.6,0,277.4s109.7,244.5,244.5,244.5s244.5-109.7,244.5-244.5S379.3,33,244.5,33
+			 M350.8,277.4c0,58.6-47.7,106.3-106.3,106.3S138.1,336,138.1,277.4c0-58.6,47.7-106.3,106.3-106.3S350.8,218.8,350.8,277.4
+			 M209.3,455.3c11.4,2.3,23.2,3.5,35.2,3.5c12,0,23.8-1.2,35.2-3.5l0.3,0.2L244.5,517l-35.5-61.5L209.3,455.3z M416.1,219
+			c-7.7-22.6-19.8-43.3-35.2-60.9l0.1-0.6H452l-35.6,61.6L416.1,219z M377.3,154.2c-16-17.2-35.2-31.3-56.8-41.3l-0.1-0.5l64.9-28.9
+			l-7.4,70.7H377.3z M319.4,109.2l-21.8-67c31.4,7.1,60.5,20.3,86,38.4L319.4,109.2z M316.3,110.6l-0.4,0.2
+			c-21.2-9.1-44.5-14.3-68.9-14.6l-0.3-0.3L294.3,43L316.3,110.6z M241.9,96.2c-24.4,0.3-47.7,5.5-68.9,14.6l-0.4-0.2l22-67.6
+			l47.6,52.9L241.9,96.2z M169.6,109.2l-64.2-28.6c25.5-18.1,54.6-31.3,86-38.4L169.6,109.2z M168.5,112.4l-0.1,0.4
+			c-21.5,10-40.8,24.1-56.8,41.3H111l-7.4-70.7L168.5,112.4z M108.1,158.1c-15.4,17.6-27.5,38.2-35.2,60.9l-0.4,0.2l-35.6-61.6H108
+			L108.1,158.1z M69.4,220.5L5.1,249.2c3.8-32.2,13.9-62.5,29.2-89.6L69.4,220.5z M71.1,223.4l0.2,0.3c-5.3,17-8.1,35-8.1,53.7
+			c0,5.5,0.3,11,0.8,16.4l-0.3,0.3L6.1,252.4L71.1,223.4z M64.5,298.9c2.9,24,10.4,46.6,21.7,66.8l-0.2,0.5l-69.5-14.8l47.6-52.8
+			L64.5,298.9z M85,369.4l-21.7,66.8c-20.7-23.6-36.8-51.2-47.1-81.5L85,369.4z M88.3,370.1l0.5,0.1c12.2,20.3,28.2,38.1,47.1,52.3
+			l0.1,0.5l-69.6,14.8L88.3,370.1z M140.1,425.5c19,13.5,40.8,23.4,64.3,28.7l0.2,0.4L147,496.4l-7.4-70.8L140.1,425.5z
+			 M206.2,457.5l35.2,61c-32.7-0.4-63.8-7.3-92.2-19.6L206.2,457.5z M282.7,457.5l57,41.4c-28.3,12.2-59.5,19.2-92.2,19.6
+			L282.7,457.5z M284.4,454.6l0.2-0.4c23.5-5.3,45.2-15.2,64.2-28.7l0.5,0.1l-7.4,70.7L284.4,454.6z M353,422.5
+			c18.9-14.2,34.9-32,47.1-52.3l0.5-0.1l22,67.6L353,423L353,422.5z M403.9,369.4l68.9-14.6c-10.3,30.3-26.5,57.9-47.1,81.5
+			L403.9,369.4z M402.9,366.2l-0.2-0.5c11.3-20.2,18.9-42.8,21.7-66.8l0.4-0.3l47.5,52.8L402.9,366.2z M425,293.8
+			c0.5-5.4,0.8-10.9,0.8-16.4c0-18.7-2.8-36.7-8.1-53.7l0.2-0.3l65,28.9l-57.5,41.8L425,293.8z M419.5,220.5l35.2-61
+			c15.2,27.1,25.4,57.4,29.2,89.6L419.5,220.5z M451.6,154.2h-70.4l7.4-70C413.9,103.2,435.4,127,451.6,154.2 M291.6,41l-47.2,52.4
+			L197.3,41c15.3-3,31-4.7,47.2-4.7C260.6,36.3,276.4,37.9,291.6,41 M100.3,84.2l7.4,70H37.3C53.6,127,75,103.2,100.3,84.2
+			 M4.4,255.3l57,41.4l-47.2,52.4c-7.1-22.6-10.9-46.7-10.9-71.7C3.3,269.9,3.7,262.6,4.4,255.3 M67.5,440.9l68.8-14.6l7.4,70
+			C114.7,483,88.9,464.1,67.5,440.9 M345.3,496.4l7.4-70l68.8,14.6C400.1,464.1,374.2,483,345.3,496.4 M474.7,349.1l-47.2-52.4
+			l57-41.4c0.7,7.3,1.1,14.7,1.1,22.1C485.6,302.3,481.8,326.4,474.7,349.1"/>
 	</g>
-	<g id="Gem">
-		<g>
-			<g>
-				<path class="st1" d="M95.6,84.4l2.8,8.6l2.8,8.6c5.4-6,9.5-13.1,12.1-20.9l-8.9,1.9L95.6,84.4z"/>
-				<path class="st1" d="M67.1,105.1l-4.5,7.8l-4.5,7.8c8.4,0,16.4-1.8,23.6-5l-7.3-5.3L67.1,105.1z"/>
-				<path class="st1" d="M83.5,97.8l-0.9,8.9l-0.9,9c7.5-3.3,14.1-8.2,19.5-14.2l-8.8-1.9L83.5,97.8z"/>
-			</g>
-			<g>
-				<path class="st2" d="M20.5,84.4l-8.8-1.9l-8.9-1.9c2.5,7.8,6.7,14.9,12.1,20.9l2.8-8.6L20.5,84.4z"/>
-				<path class="st2" d="M101.2,67.2l6,6.7l6.1,6.7c1.8-5.6,2.8-11.7,2.8-17.9c0-2-0.1-4.1-0.3-6.1l-7.3,5.3L101.2,67.2z"/>
-				<path class="st2" d="M99.3,49.3l8.2,3.7l8.3,3.7c-0.9-8.3-3.5-16.1-7.5-22.9l-4.5,7.8L99.3,49.3z"/>
-				<path class="st2" d="M32.6,97.8l-8.8,1.9l-8.8,1.9c5.4,6,12,10.9,19.5,14.2l-0.9-9L32.6,97.8z"/>
-				<path class="st2" d="M49,105.1l-7.3,5.3l-7.3,5.3c7.2,3.2,15.2,5,23.6,5c0,0,0,0,0,0l-4.5-7.8L49,105.1z"/>
-				<path class="st2" d="M67.1,105.1L67.1,105.1l7.3,5.3l7.3,5.3l0.9-9l0.9-8.9l0,0C78.7,101.3,73.1,103.8,67.1,105.1z"/>
-				<path class="st2" d="M95.6,84.4L95.6,84.4c-3.1,5.3-7.2,9.8-12.1,13.4l0,0l8.8,1.9l8.8,1.9c0,0,0,0,0,0l-2.8-8.6L95.6,84.4z"/>
-			</g>
-			<g>
-				<path class="st3" d="M75.7,23L75.7,23l-2.8-8.6l-2.8-8.6l-6.1,6.7l-6,6.7l0,0C64.3,19.3,70.3,20.6,75.7,23z"/>
-				<path class="st3" d="M58,19.3L58,19.3l-6-6.7L46,5.9l-2.8,8.6L40.4,23l0,0C45.8,20.6,51.8,19.3,58,19.3z"/>
-				<path class="st3" d="M25.8,33.6L25.8,33.6c4.1-4.5,9-8.1,14.6-10.6l0,0l-8.2-3.7l-8.3-3.7c0,0,0,0,0,0l0.9,9L25.8,33.6z"/>
-				<path class="st3" d="M25.8,33.7L25.8,33.7l-9,0h-9l4.5,7.8l4.5,7.8l0,0C18.7,43.4,21.8,38.1,25.8,33.7z"/>
-				<path class="st3" d="M0.3,56.6l7.3,5.3l7.3,5.3l0,0c-0.2-1.5-0.2-3-0.2-4.5c0-4.7,0.7-9.2,2.1-13.4l0,0l-8.2,3.7L0.3,56.6
-					C0.3,56.6,0.3,56.6,0.3,56.6z"/>
-			</g>
-			<g>
-				<path class="st4" d="M25.8,33.6L25.8,33.6L25.8,33.6l-0.9-8.9l-0.9-9c-6.6,4.8-12.1,10.9-16.1,17.9h9H25.8z"/>
-				<path class="st4" d="M8.6,52.9l8.2-3.7l0,0l0,0l-4.5-7.8l-4.5-7.8c-4,6.9-6.6,14.7-7.5,22.9L8.6,52.9z"/>
-				<path class="st4" d="M40.4,23l2.8-8.6L46,5.9c-8.1,1.7-15.6,5.1-22,9.8l8.3,3.7L40.4,23z"/>
-				<path class="st4" d="M58,19.3l6-6.7l6.1-6.7C66.2,5.1,62.2,4.6,58,4.6c-4.1,0-8.2,0.4-12.1,1.3l6.1,6.7L58,19.3z"/>
-				<path class="st4" d="M99.3,49.3L99.3,49.3l4.5-7.8l4.5-7.8h-9h-9l0,0C94.3,38.1,97.4,43.4,99.3,49.3z"/>
-				<path class="st4" d="M75.7,23L75.7,23c5.6,2.5,10.5,6.1,14.6,10.6h0l0.9-8.9l0.9-9c0,0,0,0,0,0l-8.3,3.7L75.7,23z"/>
-				<path class="st4" d="M14.9,67.2L14.9,67.2l-6,6.7l-6.1,6.7l8.8,1.9l8.8,1.9l0,0C17.5,79.2,15.6,73.4,14.9,67.2z"/>
-				<path class="st4" d="M32.6,97.8L32.6,97.8c-4.9-3.6-9-8.1-12.1-13.4l0,0l-2.8,8.6l-2.8,8.6c0,0,0,0,0,0l8.8-1.9L32.6,97.8z"/>
-			</g>
-			<g>
-				<path class="st5" d="M14.9,67.2l-7.3-5.3l-7.3-5.3c-0.2,2-0.3,4-0.3,6.1C0,68.9,1,75,2.8,80.6l6.1-6.7L14.9,67.2z"/>
-				<path class="st5" d="M90.3,33.6h9l9,0c-4.1-7-9.6-13.2-16.1-17.9l-0.9,9L90.3,33.6z"/>
-				<path class="st5" d="M75.7,23l8.2-3.7l8.3-3.7c-6.5-4.7-13.9-8.1-22-9.8l2.8,8.6L75.7,23z"/>
-				<path class="st5" d="M67.1,105.1L67.1,105.1c-2.9,0.6-5.9,0.9-9,0.9c-3.1,0-6.1-0.3-9-0.9l0,0l4.5,7.8l4.5,7.8l4.5-7.8
-					L67.1,105.1z"/>
-				<path class="st5" d="M95.6,84.3L95.6,84.3l8.8-1.9l8.8-1.9l-6.1-6.7l-6-6.7l0,0C100.5,73.4,98.6,79.2,95.6,84.3z"/>
-				<path class="st5" d="M101.4,62.7c0,1.5-0.1,3-0.2,4.5l0,0l7.3-5.3l7.3-5.3c0,0,0,0,0,0l-8.3-3.7l-8.2-3.7l0,0
-					C100.7,53.5,101.4,58,101.4,62.7z"/>
-				<path class="st5" d="M32.6,97.8L32.6,97.8l0.9,8.9l0.9,9l7.3-5.3l7.3-5.3l0,0C43,103.8,37.4,101.3,32.6,97.8z"/>
-			</g>
-		</g>
-	</g>
-	<g id="Letters">
-		<path class="st0" d="M189.1,19.5h34c27,0,45.7,17.2,45.7,43.2s-18.7,43-45.7,43h-34V19.5z M223,90.1c17.4,0,27.6-12.6,27.6-27.5
-			c0-15.6-9.5-27.6-27.6-27.6h-16v55.1H223z"/>
-		<g>
-			<path class="st0" d="M281,19.5h68.6v15.8h-50.3V54h50.3v15.8h-50.3v20h50.3v15.9H281V19.5z"/>
-		</g>
-		<path class="st0" d="M171.2,66.4c-3.3-2.9-8-4.9-14-5.9c5-1.5,9-3.8,11.9-7c3.5-3.7,5.2-8.3,5.2-13.7c0-6.4-2.8-11.5-8.3-15.1
-			c-5.4-3.5-12.9-5.3-22.2-5.3c-9.5,0-17.9,2-25.2,5.8c2.9,4.6,5.2,9.6,7,14.8c3-1.7,5.8-2.9,8.2-3.7c2.9-0.9,5.9-1.3,8.7-1.3
-			c8,0,11.9,2.8,11.9,8.7c0,3.8-1.4,6.4-4.4,8c-3.1,1.6-8,2.4-14.8,2.4h-6.5c0.3,2.8,0.5,5.7,0.5,8.6c0,2.1-0.1,4.2-0.3,6.3h6.2
-			c7.3,0,12.7,0.8,16,2.4c3.2,1.5,4.7,4,4.7,7.6c0,4.1-1.3,7-4.1,8.9c-2.8,1.9-7.3,2.8-13.3,2.8c-4,0-8-0.5-12.1-1.5
-			c-0.6-0.2-1.3-0.3-1.9-0.5c-1.9,4.9-4.3,9.5-7.2,13.8c6.9,2.3,14.7,3.5,23.2,3.5c11.6,0,20.7-2.3,27-6.8c6.4-4.6,9.6-11,9.6-19.2
-			C177,74.3,175,69.7,171.2,66.4z"/>
-		<path class="st0" d="M58,19.3c-24,0-43.4,19.4-43.4,43.4S34.1,106,58,106s43.4-19.4,43.4-43.4S82,19.3,58,19.3z M58,90.8
-			c-15.5,0-28.1-12.6-28.1-28.1S42.5,34.5,58,34.5s28.1,12.6,28.1,28.1S73.6,90.8,58,90.8z"/>
+	<g>
+		<path class="st0" d="M511.8,557.9c0-16.8,13.2-30.1,30.2-30.1c17.1,0,30.2,13.3,30.2,30c0,16.6-13,29.9-30.3,29.9
+			C524.7,587.7,511.8,574.5,511.8,557.9z M558.8,557.9c-0.1-10.1-7.6-17.2-16.8-17.2c-9.3,0-16.8,7.3-16.8,17.2
+			c0,9.8,7.4,17.1,16.8,17.1C551.6,574.9,558.9,567.7,558.8,557.9z"/>
+		<path class="st0" d="M648,548.5c0,10.1-6.2,19.1-21.9,19.1h-8.3v19.2h-12.9v-57.9H627C642.1,528.9,648,538.5,648,548.5z
+			 M634.8,548.3c0-4-2.8-7.5-7.7-7.5h-9.3v15.1h8.8C632.3,555.9,634.8,552.1,634.8,548.3z"/>
+		<path class="st0" d="M679.3,528.9h40.4v12.1h-27.4v10.7h25.4v12h-25.4v11h27.3v12.1h-40.3V528.9z"/>
+		<path class="st0" d="M803.8,528.9v57.9h-9.3l-28.3-33.5v33.5h-12.8v-57.9h8.8l28.9,33.8v-33.8H803.8z"/>
+		<path class="st0" d="M877.1,582l5.3-10.3c5.3,3.1,9.7,4.2,13.1,4.2c5.6,0,8.6-2.7,8.6-6.3c0-3.6-3.1-6.1-7.8-6.1h-7.4v-11.6h6
+			c5.1,0,8-2.5,8-6c0-3.7-3.2-5.8-7.2-5.8c-3.4,0-6.5,1.2-11.1,4.3l-6.1-9.3c4.6-4.2,11.6-7.1,18.9-7.1c10.5,0,18.6,6.5,18.6,15
+			c0,6.4-4.5,11-9.7,12.9v0.3c7.4,1.6,11.4,7.5,11.4,14.3c0,10.2-8.7,17.3-21.2,17.3C889.2,587.7,883,585.6,877.1,582z"/>
+		<path class="st0" d="M950.5,528.9h23.1c17.8,0,28.2,11.2,28.2,29c0,17.8-10.7,28.9-28.6,28.9h-22.7V528.9z M973.8,574.3
+			c9.8,0,14.7-7.7,14.7-16.5c0-8.9-4.9-16.5-14.7-16.5h-10.3v33H973.8z"/>
+		<path class="st0" d="M1077.5,528.9h40.4v12.1h-27.4v10.7h25.4v12h-25.4v11h27.3v12.1h-40.3V528.9z"/>
+		<path class="st0" d="M1202.1,528.9v57.9h-9.3l-28.3-33.5v33.5h-12.8v-57.9h8.8l28.9,33.8v-33.8H1202.1z"/>
+		<path class="st0" d="M1234.7,557.9c0-17.6,13.6-30,30.8-30c9.7,0,18.1,3.9,23.1,9l-8.3,9.4c-3.8-3.6-9.1-6-14.9-6
+			c-10.2,0-17.5,7.5-17.5,17.4c0,9.6,6.9,17.6,17.4,17.6c5.9,0,11.4-2.5,14.4-5.5v-6.7H1265V552h26.5v24.4
+			c-7.1,7.1-16.1,11.3-26.4,11.3C1247.3,587.6,1234.7,575,1234.7,557.9z"/>
+		<path class="st0" d="M1337.7,528.9v57.9h-13v-57.9H1337.7z"/>
+		<path class="st0" d="M1423.8,528.9v57.9h-9.3l-28.3-33.5v33.5h-12.8v-57.9h8.8l28.9,33.8v-33.8H1423.8z"/>
+		<path class="st0" d="M1459.6,528.9h40.4v12.1h-27.4v10.7h25.4v12h-25.4v11h27.3v12.1h-40.3V528.9z"/>
 	</g>
 </g>
 </svg>

+ 2 - 2
Code/Editor/splashscreen_background.png

@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:07268873b6fa8c882c14d1d5098996e6fc561fab8377281fcdfbde4a3b33ed7c
-size 2278310
+oid sha256:746adf79fe5d0b1cc99e529f9280b79bf9de4d3a2b99f42c6b30ca55f46a35cd
+size 3390470

+ 2 - 1
Code/Framework/AzCore/AzCore/DOM/DomComparison.cpp

@@ -7,6 +7,7 @@
  */
 
 #include <AzCore/DOM/DomComparison.h>
+#include <AzCore/DOM/DomUtils.h>
 #include <AzCore/std/containers/queue.h>
 #include <AzCore/std/containers/unordered_set.h>
 
@@ -84,7 +85,7 @@ namespace AZ::Dom
                 const size_t entriesToEnumerate = AZStd::min(beforeSize, afterSize);
                 for (size_t i = 0; i < entriesToEnumerate; ++i)
                 {
-                    if (before[i] != after[i])
+                    if (!Utils::DeepCompareIsEqual(before[i], after[i]))
                     {
                         ++changedValueCount;
                         if (changedValueCount >= params.m_replaceThreshold)

+ 142 - 0
Code/Framework/AzCore/AzCore/DOM/DomUtils.cpp

@@ -123,9 +123,74 @@ namespace AZ::Dom::Utils
         }
     }
 
+    bool CanLoadViaJsonSerialization(
+        const AZ::TypeId& typeId, const Value& root, JsonDeserializerSettings settings)
+    {
+        // A serializeContext is required for making the temporary AZStd::any for storage
+        // so if the supplied serialize context is nullptr, query the one associated with the ComponentApplication
+        auto componentApplicationInterface = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+        if (settings.m_serializeContext == nullptr)
+        {
+            settings.m_serializeContext = componentApplicationInterface != nullptr ? componentApplicationInterface->GetSerializeContext() : nullptr;
+        }
+        if (settings.m_serializeContext == nullptr)
+        {
+            return false;
+        }
+
+        if (settings.m_registrationContext == nullptr)
+        {
+            settings.m_registrationContext = componentApplicationInterface != nullptr ? componentApplicationInterface->GetJsonRegistrationContext() : nullptr;
+        }
+        if (settings.m_registrationContext == nullptr)
+        {
+            return false;
+        }
+
+        JsonSerializerSettings serializerSettings;
+        serializerSettings.m_serializeContext = settings.m_serializeContext;
+        serializerSettings.m_registrationContext = settings.m_registrationContext;
+        // Check if the type is serializable before moving on converting the Dom Value into a temporary JSON document
+        if (!JsonSerialization::IsTypeSerializable(typeId, serializerSettings))
+        {
+            return false;
+        }
+
+        rapidjson::Document jsonViewOfDomValue;
+        auto WriteDomFieldToJson = [&root](Visitor& visitor)
+        {
+            const bool copyStrings = false;
+            return root.Accept(visitor, copyStrings);
+        };
+        if (auto convertToJsonResult = Json::WriteToRapidJsonValue(jsonViewOfDomValue, jsonViewOfDomValue.GetAllocator(), AZStd::move(WriteDomFieldToJson));
+            !convertToJsonResult)
+        {
+            return false;
+        }
+
+        AZStd::any dryRunStorage = settings.m_serializeContext->CreateAny(typeId);
+        // CreateAny will fail if the type is not default constructible or not reflected to the Serialize Context
+        if (dryRunStorage.empty())
+        {
+            return false;
+        }
+        JsonSerializationResult::ResultCode loadResult = JsonSerialization::Load(AZStd::any_cast<void>(&dryRunStorage), typeId, jsonViewOfDomValue, settings);
+        return loadResult.GetProcessing() != JsonSerializationResult::Processing::Halted;
+    }
+
     JsonSerializationResult::ResultCode LoadViaJsonSerialization(
         void* object, const AZ::TypeId& typeId, const Value& root, const JsonDeserializerSettings& settings)
     {
+        // Check if the Type is serializable before attempting to load into the object pointer
+        JsonSerializerSettings serializerSettings;
+        serializerSettings.m_serializeContext = settings.m_serializeContext;
+        serializerSettings.m_registrationContext = settings.m_registrationContext;
+        if (!JsonSerialization::IsTypeSerializable(typeId, serializerSettings))
+        {
+            return JsonSerializationResult::ResultCode{ JsonSerializationResult::Tasks::Convert,
+                                                        JsonSerializationResult::Outcomes::Catastrophic };
+        }
+
         rapidjson::Document buffer;
         auto convertToRapidjsonResult = Json::WriteToRapidJsonValue(buffer, buffer.GetAllocator(), [&root](Visitor& visitor)
             {
@@ -142,6 +207,13 @@ namespace AZ::Dom::Utils
     JsonSerializationResult::ResultCode StoreViaJsonSerialization(
         const void* object, const void* defaultObject, const AZ::TypeId& typeId, Value& output, const JsonSerializerSettings& settings)
     {
+        // Check if the Type is serializable before attempting to store the object address into the Dom Value
+        if (!JsonSerialization::IsTypeSerializable(typeId, settings))
+        {
+            return JsonSerializationResult::ResultCode{ JsonSerializationResult::Tasks::Convert,
+                                                        JsonSerializationResult::Outcomes::Catastrophic };
+        }
+
         rapidjson::Document buffer;
         auto result = JsonSerialization::Store(buffer, buffer.GetAllocator(), object, defaultObject, typeId, settings);
         auto outputWriter = output.GetWriteHandler();
@@ -409,6 +481,76 @@ namespace AZ::Dom::Utils
         }
         else
         {
+            // For the non-pointer case source object is copied into the Dom::Value
+            // First try to use Json Serialization system if available to leverage
+            // the SerializeContext and JsonRegistrationContext for writing the value to a Dom::Value
+            // The ideal scenario is trying to replicate the data structure into the Dom Value as if
+            // it is a JSON Object.
+            // For example a C++ struct such as follows
+            /*
+             ```c++
+             struct DiceComponentConfig
+             {
+                int m_sides;
+                AZStd::vector<double> m_probabilities;
+                AZStd::string m_name;
+             };
+             ```
+             */
+            // Which contains the data ina C++ psuedo-layout of DiceComponentConfig{ 6, [1/6, 1/6, 1/6, 1/6, 1/6, 1/6], "Six-Sided Die" }
+            // Could map to JSON Object as follows if JSON Serialization is available
+            /*
+            ```JSON
+            {
+                "m_sides": 6,
+                "m_probabilities": [
+                    0.166667,
+                    0.166667
+                    0.166667
+                    0.166667
+                    0.166667
+                    0.166667
+                ],
+                "m_name": "Six-Sided Die"
+            }
+            ```
+            */
+            // That could then map into a Dom::Value of the following layout
+            // Dom::Value
+            // -> Object
+            //   1. Field: "m_sides" -> Int
+            //   2. Field: "m_probabilities" -> Array
+            //      Indices: 0-6 -> int
+            //   3. Field: "m_name": -> String
+            //
+            // However if JSON Serialization is not available, the data will be stored in an AZStd::any as an opaque
+            // type in which the data structure is opaque to the Dom::Value
+            // i.e
+            // Dom::Value
+            // -> Opaque = <value>
+            //
+            // The drawbacks of an opaque type is that the Dom::Value can only shallow compare two opaque values
+            // via looking at their memory address
+            // It can't actually compare the data
+            // Therefore two opaque values with the same data, but that different are aprt of different objects
+            // will always compare unequal. This can result in-efficient behavior such as creating more Dom Patches than necessary
+            AZ::JsonSerializerSettings storeSettings;
+            // Defaults should be kept in the Dom::Value to make sure a complete object is written to the Dom
+            storeSettings.m_keepDefaults = true;
+
+            // Create a pass no-op issue reporting to skip the DefaultIssueReporter logging AZ_Warnings
+            storeSettings.m_reporting = [](AZStd::string_view, JsonSerializationResult::ResultCode result, AZStd::string_view)
+            {
+                return result;
+            };
+            Value newValue;
+            if (auto storeViaSerializationResult = StoreViaJsonSerialization(valueAddress, nullptr, typeTraits.m_typeId, newValue, storeSettings);
+                storeViaSerializationResult.GetProcessing() != JsonSerializationResult::Processing::Halted)
+            {
+                return newValue;
+            }
+
+            // The data will be stored in AZStd::any as a fail-safe
             AZStd::any::type_info typeInfo;
             typeInfo.m_id = typeTraits.m_typeId;
             typeInfo.m_handler = AZStd::move(actionHandler);

+ 97 - 14
Code/Framework/AzCore/AzCore/DOM/DomUtils.h

@@ -25,7 +25,7 @@ namespace AZ::Dom::Utils
     struct ComparisonParameters
     {
         //! If set, opaque values will only be compared by type and not contents
-        //! This can be useful when comparing opaque values that aren't equal in-memory but shouldn't constitue a
+        //! This can be useful when comparing opaque values that aren't equal in-memory but shouldn't constitute a
         //! comparison failure (e.g. comparing callbacks)
         bool m_treatOpaqueValuesOfSameTypeAsEqual = false;
     };
@@ -33,6 +33,16 @@ namespace AZ::Dom::Utils
     bool DeepCompareIsEqual(const Value& lhs, const Value& rhs, const ComparisonParameters& parameters = {});
     Value TypeIdToDomValue(const AZ::TypeId& typeId);
     AZ::TypeId DomValueToTypeId(const AZ::Dom::Value& value, const AZ::TypeId* baseClassId = nullptr);
+    //! Runs a dry-run JSON Serializer over the Dom::Value to check if it can be converted to the type associated
+    //! with the AZ TypeId
+    //! @param typeId TypeInfo ID associated with C++ type to determine if the Dom Value can be deserialized into
+    //! @param root Dom Value to be check to see if it can be converted to the C++ TypeInfo ID
+    //! @param settings Json Deserializer Settings which is used to query the serialize context to use for loading the raw object
+    //!        data from the Dom Value
+    //! @return true if the Dom Value can be deserialized into the type associated with the TypeInfo ID
+    bool CanLoadViaJsonSerialization(
+        const AZ::TypeId& typeId, const Value& root, JsonDeserializerSettings settings = {});
+
     JsonSerializationResult::ResultCode LoadViaJsonSerialization(
         void* object, const AZ::TypeId& typeId, const Value& root, const JsonDeserializerSettings& settings = {});
     JsonSerializationResult::ResultCode StoreViaJsonSerialization(
@@ -213,6 +223,8 @@ namespace AZ::Dom::Utils
         }
         else
         {
+            // For pointer types, the pointer marshaling logic is used
+            // to extract a pointer address from the Object with the Dom::Value
             if constexpr (AZStd::is_pointer_v<WrapperType>)
             {
                 if (TryMarshalValueToPointer(value) != nullptr)
@@ -220,12 +232,37 @@ namespace AZ::Dom::Utils
                     return true;
                 }
             }
+
+            // If the Dom::Value can be loaded into the WrapperType using JSON
+            // Serialization, then the Value is convertible to the C++ type
+            JsonDeserializerSettings loadSettings;
+            // Create a no-op issue reporter to suppress AZ_Warnings
+            loadSettings.m_reporting = [](AZStd::string_view, JsonSerializationResult::ResultCode result, AZStd::string_view)
+            {
+                return result;
+            };
+            if (CanLoadViaJsonSerialization(azrtti_typeid<WrapperType>(), value, loadSettings))
+            {
+                return true;
+            }
+
             if (!value.IsOpaqueValue())
             {
                 return false;
             }
+
             const AZStd::any& opaqueValue = value.GetOpaqueValue();
-            return opaqueValue.is<WrapperType>();
+
+            // Then the original type should be check and not the wrapper type
+            // if the WrapperType is a std::reference_wrapper
+            if constexpr (AZStd::is_reference_wrapper<WrapperType>::value)
+            {
+                return opaqueValue.is<AZStd::remove_reference_t<T>>() || opaqueValue.is<WrapperType>();
+            }
+            else
+            {
+                return opaqueValue.is<WrapperType>();
+            }
         }
     }
 
@@ -297,16 +334,72 @@ namespace AZ::Dom::Utils
             {
                 if constexpr (AZStd::is_pointer_v<WrapperType>)
                 {
+                    // When the Wrapped C++ Type is a pointer
+                    // then the attempt to read the pointer address
+                    // from the Dom Value object
                     void* valuePointer = TryMarshalValueToPointer(value);
                     if (valuePointer != nullptr)
                     {
                         return reinterpret_cast<WrapperType>(valuePointer);
                     }
                 }
-                if (value.IsOpaqueValue())
+
+                if (!value.IsOpaqueValue())
+                {
+                    // For a non-opaque type attempt to first check if it Dom Object
+                    // which represents a pointer type and try to marshal that value to the pointer type
+                    void* valuePointer = TryMarshalValueToPointer(value);
+                    if (valuePointer != nullptr)
+                    {
+                        return WrapperType(*reinterpret_cast<WrapperType*>(valuePointer));
+                    }
+
+                    // If the Dom is not storing an object that is a pointer
+                    // attempt to use Json Serialization to load into the WrapperType if it is default constructible
+                    if constexpr (AZStd::is_constructible_v<WrapperType>)
+                    {
+                        // Attempt to deserialize the type into T using JSON Serialization if possible
+                        WrapperType typeValue;
+                        // Create a pass no-op issue reporting to skip the DefaultIssueReporter logging AZ_Warnings
+                        JsonDeserializerSettings loadSettings;
+                        loadSettings.m_reporting = [](AZStd::string_view, JsonSerializationResult::ResultCode result, AZStd::string_view)
+                            {
+                                return result;
+                            };
+                        if (auto loadViaJsonSerializationResult = LoadViaJsonSerialization(typeValue, value, loadSettings);
+                            loadViaJsonSerializationResult.GetProcessing() != JsonSerializationResult::Processing::Halted)
+                        {
+                            return typeValue;
+                        }
+                    }
+
+                    return {};
+                }
+                // At this point, the type must be an opaque type which is an AZStd::any stored within the Dom::Value
+                else
                 {
                     const AZStd::any& opaqueValue = value.GetOpaqueValue();
-                    if (!opaqueValue.is<WrapperType>())
+                    // If the wrapper type is a reference wrapper, then the original type needs to be extracted
+                    // from the opaque object and then constructed into a reference_wrapper.
+                    // This is because the implementation of std::reference_wrapper might store a pointer to T or a reference to T
+                    // and that cannot be relied on
+                    if constexpr (AZStd::is_reference_wrapper<WrapperType>::value)
+                    {
+                        if (auto instanceValue = AZStd::any_cast<AZStd::remove_reference_t<T>>(&opaqueValue);
+                            instanceValue != nullptr)
+                        {
+                            // Construct a reference_wrapper using a deferenced value to the opaque type
+                            return WrapperType(const_cast<T>(*instanceValue));
+                        }
+
+                        // Check if The Dom Value is actually storing an reference_wrapper<T> and return that if possible
+                        else if (auto referenceWrapperValue = AZStd::any_cast<WrapperType>(&opaqueValue); referenceWrapperValue != nullptr)
+                        {
+                            // return the reference_wrapper<T> directly
+                            return *referenceWrapperValue;
+                        }
+                    }
+                    else if (!opaqueValue.is<WrapperType>())
                     {
                         // Marshal void* into our type - CanConvertToType will not register this as correct,
                         // but this is an important safety hatch for marshalling out non-primitive UI elements in the DocumentPropertyEditor
@@ -318,16 +411,6 @@ namespace AZ::Dom::Utils
                     }
                     return AZStd::any_cast<WrapperType>(opaqueValue);
                 }
-                else
-                {
-                    void* valuePointer = TryMarshalValueToPointer(value);
-                    if (valuePointer != nullptr)
-                    {
-                        return WrapperType(*reinterpret_cast<WrapperType*>(valuePointer));
-                    }
-                    return {};
-                }
-
             };
 
             return ExtractOpaqueValue();

+ 2 - 1
Code/Framework/AzCore/AzCore/RTTI/TypeInfo.h

@@ -459,7 +459,8 @@ namespace AZ
     inline AZ::TypeId GetO3deTypeId(AZ::Adl, AZStd::type_identity<std::reference_wrapper<T>>)
     {
         // Return the type id of the non-reference type for the reference wrapper
-        return AZ::AzTypeInfo<T>::Uuid();
+        constexpr TemplateId referenceWrapperId{ "{49A51B21-8302-4E63-8EE8-A4BF51B72FFC}" };
+        return referenceWrapperId + AZ::AzTypeInfo<T>::Uuid();
     }
 } // namespace AZ
 

+ 9 - 0
Code/Framework/AzCore/AzCore/Serialization/EditContext.cpp

@@ -417,6 +417,15 @@ namespace AZ
 
 } // namespace AZ
 
+namespace AZ::Edit
+{
+    AZ::TemplateId GetO3deTemplateId(AZ::Adl, AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<EnumConstant>)
+    {
+        return AZ::TemplateId(EnumConstantTypeId);
+    }
+}
+
+
 // pre-instantiate the extremely common ones
 template AZ::EditContext::ClassBuilder* AZ::EditContext::ClassBuilder::Attribute<AZ::Crc32>(const char *, AZ::Crc32);
 template AZ::EditContext::ClassBuilder* AZ::EditContext::ClassBuilder::Attribute<AZ::Crc32>(AZ::Crc32, AZ::Crc32);

+ 18 - 9
Code/Framework/AzCore/AzCore/Serialization/EditContext.h

@@ -805,30 +805,39 @@ namespace AZ
     //=========================================================================
     namespace Edit
     {
+        struct EnumConstantBase
+        {
+            // Store using a u64 under the hood so this can be safely cast to any valid enum-range value
+            AZ::u64 m_value{};
+            AZStd::string m_description;
+        };
+
+        constexpr const char* EnumConstantTypeId = "{4CDFEE70-7271-4B27-833B-F8F72AA64C40}";
+
         template <class EnumType>
         struct EnumConstant
+            : EnumConstantBase
         {
-            AZ_TYPE_INFO(EnumConstant, "{4CDFEE70-7271-4B27-833B-F8F72AA64C40}");
+            AZ_TYPE_INFO(EnumConstant, EnumConstantTypeId);
 
-            using UnderlyingType = AZStd::RemoveEnumT<EnumType>;
+            // Bring the base class constructors into scope
+            using EnumConstantBase::EnumConstantBase;
 
-            EnumConstant() {}
             EnumConstant(EnumType first, AZStd::string_view description)
-                : m_value(static_cast<AZ::u64>(first))
-                , m_description(description)
+                : EnumConstantBase{ static_cast<AZ::u64>(first), description }
             {
             }
+            using UnderlyingType = AZStd::RemoveEnumT<EnumType>;
 
             AZStd::pair<UnderlyingType, AZStd::string> operator()() const
             {
                 return { static_cast<UnderlyingType>(m_value), m_description };
             }
-
-            // Store using a u64 under the hood so this can be safely cast to any valid enum-range value
-            AZ::u64 m_value;
-            AZStd::string m_description;
         };
 
+        // Overload to allow querying TypeInfo when specifying the EnumConstant class template
+        AZ::TemplateId GetO3deTemplateId(AZ::Adl, AZ::AzGenericTypeInfo::Internal::TemplateIdentityTypes<EnumConstant>);
+
         // Automatically generate a list of EnumConstant from AzEnumTraits
         template<typename EnumType, typename UnderlyingType = AZStd::underlying_type_t<EnumType>>
         AZStd::vector<EnumConstant<UnderlyingType>> GetEnumConstantsFromTraits()

+ 119 - 0
Code/Framework/AzCore/AzCore/Serialization/EnumConstantJsonSerializer.cpp

@@ -0,0 +1,119 @@
+/*
+ * 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 <AzCore/Serialization/EnumConstantJsonSerializer.h>
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Serialization/EditContext.h>
+
+namespace AZ::EnumConstantJsonSerializerInternal
+{
+    constexpr const char* ValueField = "value";
+    constexpr const char* DescriptionField = "description";
+} // namespace AZ::EnumConstantSerializerInternal
+
+namespace AZ
+{
+    AZ_TYPE_INFO_WITH_NAME_IMPL(EnumConstantJsonSerializer, "EnumConstantJsonSerializer", "{A231A314-A4EB-4485-835F-A58A9C3C5F08}");
+    AZ_RTTI_NO_TYPE_INFO_IMPL(EnumConstantJsonSerializer, BaseJsonSerializer);
+    AZ_CLASS_ALLOCATOR_IMPL(EnumConstantJsonSerializer, SystemAllocator);
+
+    JsonSerializationResult::Result EnumConstantJsonSerializer::Load(
+        void* outputValue, const Uuid&, const rapidjson::Value& inputValue, JsonDeserializerContext& context)
+    {
+        namespace JSR = JsonSerializationResult;
+
+        JSR::ResultCode result(JSR::Tasks::ReadField);
+
+        switch (inputValue.GetType())
+        {
+        case rapidjson::kObjectType:
+        {
+            auto enumConstant = reinterpret_cast<Edit::EnumConstantBase*>(outputValue);
+            if (auto valueIt = inputValue.FindMember(EnumConstantJsonSerializerInternal::ValueField); valueIt != inputValue.MemberEnd())
+            {
+                using ValueType = decltype(enumConstant->m_value);
+                result.Combine(ContinueLoading(&enumConstant->m_value, azrtti_typeid<ValueType>(), valueIt->value, context));
+
+                if (result.GetProcessing() != JSR::Processing::Completed)
+                {
+                    result.Combine(context.Report(result, "Failed to read enum value for EnumConstant<EnumType>."));
+                }
+            }
+            if (auto descIt = inputValue.FindMember(EnumConstantJsonSerializerInternal::DescriptionField); descIt != inputValue.MemberEnd())
+            {
+                using DescriptionType = decltype(enumConstant->m_description);
+                result.Combine(ContinueLoading(&enumConstant->m_description, azrtti_typeid<DescriptionType>(), descIt->value, context));
+
+                if (result.GetProcessing() != JSR::Processing::Completed)
+                {
+                    result.Combine(context.Report(result, "Failed to read enum description for EnumConstant<EnumType>."));
+                }
+            }
+
+            return context.Report(
+                result,
+                result.GetOutcome() <= JSR::Outcomes::PartialSkip ? "Successfully loaded data into EnumConstant<EnumType>."
+                                                                     : "Failed to load data into EnumConstant<EnumType>.");
+        }
+        case rapidjson::kArrayType: // fall through
+        case rapidjson::kNullType: // fall through
+        case rapidjson::kStringType: // fall through
+        case rapidjson::kFalseType: // fall through
+        case rapidjson::kTrueType: // fall through
+        case rapidjson::kNumberType:
+            return context.Report(
+                JSR::Tasks::ReadField,
+                JSR::Outcomes::Unsupported,
+                "Unsupported type. EnumConstant<EnumType> can only be read from an object.");
+
+        default:
+            return context.Report(
+                JSR::Tasks::ReadField, JSR::Outcomes::Unknown, "Unknown json type encountered for EnumConstant<EnumType>.");
+        }
+    }
+
+    JsonSerializationResult::Result EnumConstantJsonSerializer::Store(
+        rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid&, JsonSerializerContext& context)
+    {
+        namespace JSR = JsonSerializationResult;
+
+        JSR::ResultCode result(JSR::Tasks::WriteValue);
+
+        auto enumConstant = reinterpret_cast<const Edit::EnumConstantBase*>(inputValue);
+        auto defaultEnumConstant = reinterpret_cast<const Edit::EnumConstantBase*>(defaultValue);
+
+        if (!outputValue.IsObject())
+        {
+            outputValue.SetObject();
+        }
+
+        using ValueType = decltype(enumConstant->m_value);
+        using DescriptionType = decltype(enumConstant->m_description);
+
+        auto* enumValue = &enumConstant->m_value;
+        auto* defaultEnumValue = defaultEnumConstant != nullptr ? &defaultEnumConstant->m_value : nullptr;
+        result.Combine(ContinueStoringToJsonObjectField(
+            outputValue, rapidjson::StringRef(EnumConstantJsonSerializerInternal::ValueField), enumValue, defaultEnumValue, azrtti_typeid<ValueType>(), context));
+
+        auto* enumDescription = &enumConstant->m_description;
+        auto* defaultEnumDescription = defaultEnumConstant != nullptr ? &defaultEnumConstant->m_description : nullptr;
+        result.Combine(ContinueStoringToJsonObjectField(
+            outputValue,
+            rapidjson::StringRef(EnumConstantJsonSerializerInternal::DescriptionField),
+            enumDescription,
+            defaultEnumDescription,
+            azrtti_typeid<DescriptionType>(),
+            context));
+
+        return context.Report(
+            result,
+            result.GetProcessing() == JSR::Processing::Completed ? "EnumConstant<EnumType> stored successfully."
+            : "Failed to store EnumConstant<EnumType>.");
+    }
+} // namespace AZ

+ 39 - 0
Code/Framework/AzCore/AzCore/Serialization/EnumConstantJsonSerializer.h

@@ -0,0 +1,39 @@
+/*
+ * 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 <AzCore/Memory/Memory_fwd.h>
+#include <AzCore/RTTI/RTTIMacros.h>
+#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
+
+namespace AZ
+{
+    //! JSON serializer for EnumConstant<EnumType>
+    //! This is only used for marshaling the EnumConstant to and from a Dom Value
+    //! in-memory. EnumConstant's are an editor only concept and shouldn't be persisted to the filesystem
+    class EnumConstantJsonSerializer : public BaseJsonSerializer
+    {
+    public:
+        AZ_TYPE_INFO_WITH_NAME_DECL(EnumConstantJsonSerializer);
+        AZ_RTTI_NO_TYPE_INFO_DECL();
+        AZ_CLASS_ALLOCATOR_DECL;
+
+        JsonSerializationResult::Result Load(
+            void* outputValue,
+            const Uuid& outputValueTypeId,
+            const rapidjson::Value& inputValue,
+            JsonDeserializerContext& context) override;
+        JsonSerializationResult::Result Store(
+            rapidjson::Value& outputValue,
+            const void* inputValue,
+            const void* defaultValue,
+            const Uuid& valueTypeId,
+            JsonSerializerContext& context) override;
+    };
+} // namespace AZ

+ 46 - 0
Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.cpp

@@ -89,6 +89,14 @@ namespace AZ
         }
     } // namespace JsonSerializationInternal
 
+
+    // RegisteredReflectionContextResult struct boolean conversion implementation
+    RegisteredReflectionContextResult::operator bool() const
+    {
+        // A positive value indicates that the type is reflected with at least one reflection context
+        return m_reflectContextValue > RegisteredReflectionContext::None;
+    }
+
     JsonSerializationResult::ResultCode JsonSerialization::ApplyPatch(
         rapidjson::Value& target, rapidjson::Document::AllocatorType& allocator, const rapidjson::Value& patch, JsonMergeApproach approach,
         const JsonApplyPatchSettings& settings)
@@ -444,6 +452,44 @@ namespace AZ
         return JsonImportResolver::RestoreImports(jsonDoc, allocator, settings);
     }
 
+    RegisteredReflectionContextResult JsonSerialization::IsTypeSerializable(const AZ::TypeId& typeId, JsonSerializerSettings settings)
+    {
+        auto componentApplicationInterface = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+        if (settings.m_registrationContext == nullptr)
+        {
+            // Look up the JSON registration context in the ComponentApplication interface if not supplied
+            settings.m_registrationContext =
+                componentApplicationInterface != nullptr ? componentApplicationInterface->GetJsonRegistrationContext() : nullptr;
+        }
+
+        // Check if the typeID is registered with the JSON Registration Context first
+        const BaseJsonSerializer* jsonSerializer{ settings.m_registrationContext != nullptr
+                                            ? settings.m_registrationContext->GetSerializerForType(typeId)
+                                            : nullptr };
+        if (jsonSerializer != nullptr)
+        {
+            return { RegisteredReflectionContext::JsonRegistrationContext };
+        }
+
+        if (settings.m_serializeContext == nullptr)
+        {
+            // Look up the serialize context in the ComponentApplication interface if not supplied
+            settings.m_serializeContext =
+                componentApplicationInterface != nullptr ? componentApplicationInterface->GetSerializeContext() : nullptr;
+        }
+
+        // Next check if the type is registered with the SerializeContext
+        const SerializeContext::ClassData* classData{ settings.m_serializeContext != nullptr
+                                                          ? settings.m_serializeContext->FindClassData(typeId)
+                                                          : nullptr };
+        if (classData != nullptr)
+        {
+            return { RegisteredReflectionContext::SerializeContext };
+        }
+
+        return { RegisteredReflectionContext::None };
+    }
+
     JsonSerializationResult::ResultCode JsonSerialization::DefaultIssueReporter(AZStd::string& scratchBuffer,
         AZStd::string_view message, JsonSerializationResult::ResultCode result, AZStd::string_view path)
     {

+ 28 - 0
Code/Framework/AzCore/AzCore/Serialization/Json/JsonSerialization.h

@@ -34,6 +34,27 @@ namespace AZ
         Greater,
         Error
     };
+
+    //! Represents entries of which reflection context a type is registered
+    //!
+    //! Only one value is returned.
+    //! The values are NOT bitwise-ORed together if the type is registered with multiple context,
+    //! the JsonRegistrationContext is set before the SerializeContext
+    enum class RegisteredReflectionContext
+    {
+        None = 0,
+        JsonRegistrationContext = 1,
+        SerializeContext,
+    };
+    //! structure which stores the result of an operation to query if an AZ::TypeID
+    //! is registered with a reflection context
+    struct RegisteredReflectionContextResult
+    {
+        //! @return true if the type is reflected with any reflection context
+        explicit operator bool() const;
+
+        RegisteredReflectionContext m_reflectContextValue{ RegisteredReflectionContext::None };
+    };
     
     //! Core class to handle serialization to and from json documents.
     //! The Json Serialization works by taking a default constructed object and then apply the information found in the JSON document
@@ -303,6 +324,13 @@ namespace AZ
         static JsonSerializationResult::ResultCode RestoreImports(
             rapidjson::Value& jsonDoc, rapidjson::Document::AllocatorType& allocator, JsonImportSettings& settings);
 
+        //! Returns an result structure indicating if the type is reflected using either the SerializeContext or JsonRegistrationContext
+        //! If the type is reflected in both contexts, then the JsonRegistrationContext is given preference in that case
+        //! @param typeId AZ TypeInfo to query in the SerializeContext or JSON RegistrationContext
+        //! @return result structure which is convertible to bool. If the type is either reflected in SerializeContext or JsonRegistrationContext
+        //! then it converts to true, otherwise the result structure converts to false
+        static RegisteredReflectionContextResult IsTypeSerializable(const AZ::TypeId& typeId, JsonSerializerSettings settings = {});
+
     private:
         JsonSerialization() = delete;
         ~JsonSerialization() = delete;

+ 5 - 0
Code/Framework/AzCore/AzCore/Serialization/Json/JsonSystemComponent.cpp

@@ -22,6 +22,8 @@
 #include <AzCore/Serialization/Json/TupleSerializer.h>
 #include <AzCore/Serialization/Json/UnorderedSetSerializer.h>
 #include <AzCore/Serialization/Json/UnsupportedTypesSerializer.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/EnumConstantJsonSerializer.h>
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/Settings/ConfigurableStack.h>
 #include <AzCore/std/any.h>
@@ -42,6 +44,7 @@
 #include <AzCore/std/smart_ptr/shared_ptr.h>
 #include <AzCore/std/smart_ptr/unique_ptr.h>
 
+
 namespace AZ
 {
     void JsonSystemComponent::Activate()
@@ -110,6 +113,8 @@ namespace AZ
                 ->HandlesType<AZStd::optional>();
             jsonContext->Serializer<JsonBitsetSerializer>()
                 ->HandlesType<AZStd::bitset>();
+            jsonContext->Serializer<EnumConstantJsonSerializer>()
+                ->HandlesType<AZ::Edit::EnumConstant>();
 
             MathReflect(jsonContext);
         }

+ 2 - 0
Code/Framework/AzCore/AzCore/azcore_files.cmake

@@ -550,6 +550,8 @@ set(FILES
     Serialization/AZStdAnyDataContainer.inl
     Serialization/DynamicSerializableField.cpp
     Serialization/DynamicSerializableField.h
+    Serialization/EnumConstantJsonSerializer.cpp
+    Serialization/EnumConstantJsonSerializer.h
     Serialization/EditContext.cpp
     Serialization/EditContext.h
     Serialization/EditContext.inl

+ 25 - 23
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/DocumentSchema.h

@@ -280,7 +280,16 @@ namespace AZ::DocumentPropertyEditor
         Dom::Value ValueToDom(const GenericValuePair& attribute) const override
         {
             Dom::Value result(Dom::Type::Object);
-            result[EntryValueKey] = Dom::Value::FromOpaqueValue(AZStd::make_any<GenericValueType>(attribute.first));
+            if constexpr (AZStd::is_constructible_v<Dom::Value, GenericValueType>)
+            {
+                // this type is already constructible as a normal Dom::Value, create it as-is
+                result[EntryValueKey] = Dom::Value(attribute.first);
+            }
+            else
+            {
+                // type doesn't fit directly into a Dom::Value, construct it as an opaque value
+                result[EntryValueKey] = Dom::Value::FromOpaqueValue(AZStd::make_any<GenericValueType>(attribute.first));
+            }
             result[EntryDescriptionKey] = Dom::Value(attribute.second, true);
             return result;
         }
@@ -319,11 +328,9 @@ namespace AZ::DocumentPropertyEditor
                 return {};
             }
 
-            const AZStd::any* opaqueValue = &value[EntryValueKey].GetOpaqueValue();
-            auto genericValue = AZStd::any_cast<GenericValueType>(opaqueValue);
-            if (genericValue != nullptr)
+            if (auto genericValueOpt = Dom::Utils::ValueToType<GenericValueType>(value[EntryValueKey]); genericValueOpt.has_value())
             {
-                return GenericValuePair{ *genericValue, AZStd::string(value[EntryDescriptionKey].GetString()) };
+                return GenericValuePair{ genericValueOpt.value(), AZStd::string(value[EntryDescriptionKey].GetString()) };
             }
 
             return {};
@@ -353,7 +360,16 @@ namespace AZ::DocumentPropertyEditor
             for (const auto& entry : attribute)
             {
                 Dom::Value entryDom(Dom::Type::Object);
-                entryDom[EntryValueKey] = Dom::Value::FromOpaqueValue(AZStd::make_any<GenericValueType>(entry.first));
+                if constexpr (AZStd::is_constructible_v<Dom::Value, GenericValueType>)
+                {
+                    // this type is already constructible as a normal Dom::Value, create it as-is
+                    entryDom[EntryValueKey] = Dom::Value(entry.first);
+                }
+                else
+                {
+                    // type doesn't fit directly into a Dom::Value, construct it as an opaque value
+                    entryDom[EntryValueKey] = Dom::Value::FromOpaqueValue(AZStd::make_any<GenericValueType>(entry.first));
+                }
                 entryDom[EntryDescriptionKey] = Dom::Value(entry.second, true);
                 result.ArrayPushBack(AZStd::move(entryDom));
             }
@@ -392,27 +408,13 @@ namespace AZ::DocumentPropertyEditor
                     continue;
                 }
 
-                if (!entryDom[EntryValueKey].IsOpaqueValue())
+                if (auto genericValueOpt = Dom::Utils::ValueToType<GenericValueType>(entryDom[EntryValueKey]); genericValueOpt.has_value())
                 {
-                    continue;
-                }
-
-                const AZStd::any* opaqueValue = &entryDom[EntryValueKey].GetOpaqueValue();
-                auto genericValue = AZStd::any_cast<GenericValueType>(opaqueValue);
-                if (opaqueValue->is<GenericValueType>() && genericValue)
-                {
-                    result.emplace_back(AZStd::make_pair(*genericValue, entryDom[EntryDescriptionKey].GetString()));
+                    result.emplace_back(genericValueOpt.value(), entryDom[EntryDescriptionKey].GetString());
                 }
             }
 
-            if (result.empty())
-            {
-                return {};
-            }
-            else
-            {
-                return result;
-            }
+            return !result.empty() ? AZStd::make_optional(AZStd::move(result)) : AZStd::nullopt;
         }
     };
 

+ 1 - 1
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/PropertyEditorNodes.h

@@ -129,7 +129,7 @@ namespace AZ::DocumentPropertyEditor::Nodes
         static constexpr auto OnChanged = CallbackAttributeDefinition<void(const Dom::Value&, ValueChangeType)>("OnChanged");
         static constexpr auto Value = AttributeDefinition<AZ::Dom::Value>("Value");
         static constexpr auto ValueType = TypeIdAttributeDefinition("ValueType");
-        static constexpr auto ValueHashed = AttributeDefinition<AZ::Uuid>("ValueHashed");
+        static constexpr auto ValueHashed = AttributeDefinition<AZ::u64>("ValueHashed");
         static constexpr auto ParentValue = AttributeDefinition<AZ::Dom::Value>("ParentValue");
 
         //! If set to true, specifies that this PropertyEditor shouldn't be allocated its own column, but instead appended

+ 8 - 1
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/Reflection/LegacyReflectionBridge.cpp

@@ -319,7 +319,14 @@ namespace AZ::Reflection
                     path.append("/");
                     path.append(AZStd::string::format("%zu", parentData.m_childElementIndex));
                 }
-                else if (nodeData.m_classElement)
+                // If we have a class element, we skip appending to the SerializedPath if its a base class
+                // because base class information is ignored by the JSON serializer in JsonSerializer::StoreWithClassElement.
+                // The name for these look like "BaseClass1", "BaseClass2", etc... are defined in c_serializeBaseClassStrings
+                // and won't be present once serialized so if we don't ignore them then certain logic that attempts to
+                // match against the SerializedPath won't be accurate.
+                else if (
+                    nodeData.m_classElement &&
+                    ((nodeData.m_classElement->m_flags & AZ::SerializeContext::ClassElement::FLG_BASE_CLASS) == 0))
                 {
                     AZStd::string_view elementName = nodeData.m_classElement->m_name;
 

+ 110 - 53
Code/Framework/AzFramework/AzFramework/DocumentPropertyEditor/ReflectionAdapter.cpp

@@ -7,6 +7,7 @@
  */
 
 #include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/Console/IConsole.h>
 #include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
 #include <AzCore/DOM/DomPrefixTree.h>
 #include <AzCore/DOM/DomUtils.h>
@@ -27,8 +28,6 @@ namespace AZ::DocumentPropertyEditor
         // Look-up table of onChanged callbacks for handling property changes
         AZ::Dom::DomPrefixTree<AZStd::function<Dom::Value(const Dom::Value&)>> m_onChangedCallbacks;
 
-        static constexpr AZStd::string_view InspectorOverrideManagementKey = "/O3DE/Preferences/Prefabs/EnableInspectorOverrideManagement";
-
         //! This represents a container or associative container instance and has methods
         //! for interacting with the container.
         struct BoundContainer
@@ -383,7 +382,7 @@ namespace AZ::DocumentPropertyEditor
             {
                 m_builder.Attribute(
                     Nodes::PropertyEditor::ValueHashed,
-                    AZ::Uuid::CreateData(static_cast<AZStd::byte*>(instance), valueSize));
+                    static_cast<AZ::u64>(AZStd::hash<AZ::Uuid>{}((AZ::Uuid::CreateData(static_cast<AZStd::byte*>(instance), valueSize)))));
             }
             m_builder.EndPropertyEditor();
 
@@ -424,19 +423,64 @@ namespace AZ::DocumentPropertyEditor
                 attributes,
                 [valuePointer, valueType, this](const Dom::Value& newValue)
                 {
-                    void* marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType);
-                    rapidjson::Document serializedValue;
-                    JsonSerialization::Store(serializedValue, serializedValue.GetAllocator(), marshalledPointer, nullptr, valueType);
+                    AZ::JsonSerializationResult::ResultCode resultCode(AZ::JsonSerializationResult::Tasks::ReadField);
+                    // marshal this new value into a pointer for use by the Json serializer if a pointer is being stored
+                    if (auto marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType); marshalledPointer != nullptr)
+                    {
+                        rapidjson::Document buffer;
+                        JsonSerializerSettings serializeSettings;
+                        JsonDeserializerSettings deserializeSettings;
+                        serializeSettings.m_serializeContext = m_serializeContext;
+                        deserializeSettings.m_serializeContext = m_serializeContext;
+
+                        // serialize the new value to Json, using the original valuePointer as a reference object to generate a minimal
+                        // diff
+                        resultCode = JsonSerialization::Store(
+                            buffer, buffer.GetAllocator(), marshalledPointer, valuePointer, valueType, serializeSettings);
+
+                        if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                        {
+                            AZ_Error(
+                                "ReflectionAdapter",
+                                false,
+                                "Storing new property editor value to JSON for copying to instance has failed with error %s",
+                                resultCode.ToString("").c_str());
+                            return newValue;
+                        }
 
-                    JsonDeserializerSettings deserializeSettings;
-                    deserializeSettings.m_serializeContext = m_serializeContext;
-                    // now deserialize that value into the original location
-                    JsonSerialization::Load(valuePointer, valueType, serializedValue, deserializeSettings);
+                        // now deserialize that value into the original location
+                        resultCode = JsonSerialization::Load(valuePointer, valueType, buffer, deserializeSettings);
+                        if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                        {
+                            AZ_Error(
+                                "ReflectionAdapter",
+                                false,
+                                "Loading JSON value containing new property editor into instance has failed with error %s",
+                                resultCode.ToString("").c_str());
+                            return newValue;
+                        }
+
+                    }
+                    else
+                    {
+                        // Otherwise use Json Serialization to copy the Dom Value directly into the valuePointer address
+                        resultCode = AZ::Dom::Utils::LoadViaJsonSerialization(valuePointer, valueType, newValue);
+                        if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                        {
+                            AZ_Error(
+                                "ReflectionAdapter",
+                                false,
+                                "Loading new DOMValue directly into instance has failed with error %s",
+                                resultCode.ToString("").c_str());
+                            return newValue;
+                        }
+                    }
 
                     AZ::Dom::Value newInstancePointerValue;
-                    auto outputWriter = newInstancePointerValue.GetWriteHandler();
-                    auto convertToAzDomResult =
-                        AZ::Dom::Json::VisitRapidJsonValue(serializedValue, *outputWriter, AZ::Dom::Lifetime::Temporary);
+                    AZ::JsonSerializerSettings storeSettings;
+                    // Defaults must be kept to make sure a complete object is written to the Dom::Value
+                    storeSettings.m_keepDefaults = true;
+                    AZ::Dom::Utils::StoreViaJsonSerialization(valuePointer, nullptr, valueType, newInstancePointerValue, storeSettings);
                     return newInstancePointerValue;
                 },
                 false,
@@ -446,12 +490,10 @@ namespace AZ::DocumentPropertyEditor
         bool IsInspectorOverrideManagementEnabled()
         {
             bool isInspectorOverrideManagementEnabled = false;
-
-            if (auto* registry = AZ::SettingsRegistry::Get())
+            if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
             {
-                registry->Get(isInspectorOverrideManagementEnabled, InspectorOverrideManagementKey);
+                console->GetCvarValue("ed_enableInspectorOverrideManagement", isInspectorOverrideManagementEnabled);
             }
-
             return isInspectorOverrideManagementEnabled;
         }
 
@@ -753,12 +795,9 @@ namespace AZ::DocumentPropertyEditor
                 }
 
                 AZ::Dom::Value instancePointerValue = AZ::Dom::Utils::MarshalTypedPointerToValue(access.Get(), access.GetType());
-                bool hashValue = false;
-                const AZ::Name PointerTypeFieldName = AZ::Dom::Utils::PointerTypeFieldName;
-                if (instancePointerValue.IsOpaqueValue() || instancePointerValue.FindMember(PointerTypeFieldName))
-                {
-                    hashValue = true;
-                }
+                // Only hash an opaque value
+                // A value that is not opaque, but is a pointer will have it's members visited in a recursive call to this method
+                const bool hashValue = instancePointerValue.IsOpaqueValue();
 
                 // The IsInspectorOverrideManagementEnabled() check is only temporary until the inspector override management feature set
                 // is fully developed. Since the original utils funtion is in AzToolsFramework and we can't access it from here, we are
@@ -784,42 +823,60 @@ namespace AZ::DocumentPropertyEditor
                     // this needs to write the value back into the reflected object via Json serialization
                     auto StoreValueIntoPointer = [valuePointer = access.Get(), valueType = access.GetType(), this](const Dom::Value& newValue)
                     {
-                        // marshal this new value into a pointer for use by the Json serializer
-                        auto marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType);
-
-                        rapidjson::Document buffer;
-                        JsonSerializerSettings serializeSettings;
-                        JsonDeserializerSettings deserializeSettings;
-                        serializeSettings.m_serializeContext = m_serializeContext;
-                        deserializeSettings.m_serializeContext = m_serializeContext;
+                        AZ::JsonSerializationResult::ResultCode resultCode(AZ::JsonSerializationResult::Tasks::ReadField);
+                        // marshal this new value into a pointer for use by the Json serializer if a pointer is being stored
+                        if (auto marshalledPointer = AZ::Dom::Utils::TryMarshalValueToPointer(newValue, valueType); marshalledPointer != nullptr)
+                        {
+                            rapidjson::Document buffer;
+                            JsonSerializerSettings serializeSettings;
+                            JsonDeserializerSettings deserializeSettings;
+                            serializeSettings.m_serializeContext = m_serializeContext;
+                            deserializeSettings.m_serializeContext = m_serializeContext;
+
+                            // serialize the new value to Json, using the original valuePointer as a reference object to generate a minimal
+                            // diff
+                            resultCode = JsonSerialization::Store(
+                                buffer, buffer.GetAllocator(), marshalledPointer, valuePointer, valueType, serializeSettings);
+
+                            if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                            {
+                                AZ_Error(
+                                    "ReflectionAdapter",
+                                    false,
+                                    "Storing new property editor value to JSON for copying to instance has failed with error %s",
+                                    resultCode.ToString("").c_str());
+                                return newValue;
+                            }
 
-                        // serialize the new value to Json, using the original valuePointer as a reference object to generate a minimal
-                        // diff
-                        AZ::JsonSerializationResult::ResultCode resultCode = JsonSerialization::Store(
-                            buffer, buffer.GetAllocator(), marshalledPointer, valuePointer, valueType, serializeSettings);
+                            // now deserialize that value into the original location
+                            resultCode = JsonSerialization::Load(valuePointer, valueType, buffer, deserializeSettings);
+                            if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                            {
+                                AZ_Error(
+                                    "ReflectionAdapter",
+                                    false,
+                                    "Loading JSON value containing new property editor into instance has failed with error %s",
+                                    resultCode.ToString("").c_str());
+                                return newValue;
+                            }
 
-                        if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
-                        {
-                            AZ_Error(
-                                "ReflectionAdapter",
-                                false,
-                                "Storing new property editor value to JSON for copying to instance has failed with error %s",
-                                resultCode.ToString("").c_str());
-                            return newValue;
                         }
-
-                        // now deserialize that value into the original location
-                        resultCode = JsonSerialization::Load(valuePointer, valueType, buffer, deserializeSettings);
-                        if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                        else
                         {
-                            AZ_Error(
-                                "ReflectionAdapter",
-                                false,
-                                "Loading JSON value containing new property editor into instance has failed with error %s",
-                                resultCode.ToString("").c_str());
-                            return newValue;
+                            // Otherwise use Json Serialization to copy the Dom Value directly into the valuePointer address
+                            resultCode = AZ::Dom::Utils::LoadViaJsonSerialization(valuePointer, valueType, newValue);
+                            if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+                            {
+                                AZ_Error(
+                                    "ReflectionAdapter",
+                                    false,
+                                    "Loading new DOMValue directly into instance has failed with error %s",
+                                    resultCode.ToString("").c_str());
+                                return newValue;
+                            }
                         }
 
+
                         // NB: the returned value for serialized pointer values is instancePointerValue, but since this is passed by
                         // pointer, it will not actually detect a changed dom value. Since we are already writing directly to the DOM
                         // before this step, it won't affect the calling DPE, however, other DPEs pointed at the same adapter would be

+ 15 - 4
Code/Framework/AzToolsFramework/AzToolsFramework/AssetEditor/AssetEditorTab.cpp

@@ -57,10 +57,16 @@ AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 'QFileInfo::d_ptr':
 AZ_POP_DISABLE_WARNING
 #include <QAction>
 
-AZ_CVAR_EXTERNED(bool, ed_enableDPE);
-
 namespace AzToolsFramework
 {
+    AZ_CVAR(
+        bool,
+        ed_enableDPEAssetEditor,
+        false,
+        nullptr,
+        AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::DontDuplicate,
+        "If set, enables experimental Document Property Editor for the AssetEditor");
+
     namespace AssetEditor
     {
         // Amount to add on to the save confirm dialog text width to account for the icon etc when padding.
@@ -159,8 +165,13 @@ namespace AzToolsFramework
             setObjectName("AssetEditorTab");
 
             QWidget* propertyEditor = nullptr;
-            // TODO: Re-enable the DPE in the Asset Editor
-            //m_useDPE = DocumentPropertyEditor::ShouldReplaceRPE();
+
+            // use the DPE version of the AssetEditor if ed_enableDPEAssetEditor is enabled
+            if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
+            {
+                console->GetCvarValue("ed_enableDPEAssetEditor", m_useDPE);
+            }
+
             if (!m_useDPE)
             {
                 m_propertyEditor = new ReflectedPropertyEditor(this);

+ 45 - 38
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/DocumentPropertyEditor/PrefabComponentAdapter.cpp

@@ -114,11 +114,8 @@ namespace AzToolsFramework::Prefab
     {
         if (propertyChangeInfo.changeType == AZ::DocumentPropertyEditor::Nodes::ValueChangeType::InProgressEdit)
         {
-            if (auto instanceUpdateExecutorInterface = AZ::Interface<Prefab::InstanceUpdateExecutorInterface>::Get())
-            {
-                instanceUpdateExecutorInterface->SetShouldPauseInstancePropagation(true);
-            }
-
+            // The Begin/ResumeUndoBatch call will come from PropertyManagerComponent::RequestWrite which gets invoked
+            // just before this, so we just need to retrieve the undo batch.
             AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(
                 m_currentUndoBatch, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::GetCurrentUndoBatch);
             if (!m_currentUndoBatch)
@@ -132,6 +129,17 @@ namespace AzToolsFramework::Prefab
                     m_currentUndoBatch, &AzToolsFramework::ToolsApplicationRequests::BeginUndoBatch, "Modify Component Property");
             }
 
+            // But we do need to mark our entity as dirty. In the RPE, this is handled by EntityPropertyEditor::BeforePropertyModified,
+            // but the DPE doesn't use those notification triggers.
+            AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
+                &AzToolsFramework::ToolsApplicationRequests::AddDirtyEntity, m_entityId);
+
+            if (auto instanceUpdateExecutorInterface = AZ::Interface<Prefab::InstanceUpdateExecutorInterface>::Get())
+            {
+                instanceUpdateExecutorInterface->SetShouldPauseInstancePropagation(true);
+            }
+
+
             AZ::Dom::Path serializedPath = propertyChangeInfo.path / AZ::Reflection::DescriptorAttributes::SerializedPath;
 
             AZ::Dom::Path relativePathFromOwningPrefab(PrefabDomUtils::EntitiesName);
@@ -147,42 +155,39 @@ namespace AzToolsFramework::Prefab
             auto prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
             if (prefabFocusPublicInterface->IsOwningPrefabBeingFocused(m_entityId))
             {
-                if (CreateAndApplyComponentEditPatch(relativePathFromOwningPrefab.ToString(), propertyChangeInfo))
-                {
-                    NotifyContentsChanged(
-                        { AZ::Dom::PatchOperation::ReplaceOperation(propertyChangeInfo.path / "Value", propertyChangeInfo.newValue) });
-                }
+                // Normal component edit, so invoke the base behavior that updates the source template.
+                ComponentAdapter::UpdateDomContents(propertyChangeInfo);
             }
             else if (prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(m_entityId))
             {
-                if (CreateAndApplyComponentOverridePatch(relativePathFromOwningPrefab, propertyChangeInfo))
-                {
-                    AZ::Dom::Patch patches(
-                        { AZ::Dom::PatchOperation::ReplaceOperation(propertyChangeInfo.path / "Value", propertyChangeInfo.newValue) });
-
-                    AZ::Dom::Path pathToProperty = propertyChangeInfo.path;
-
-                    // Get the path to parent row and its value.
-                    pathToProperty.Pop();
-                    AZ::Dom::Value propertyRowValue = GetContents()[pathToProperty];
-
-                    AZ_Assert(
-                        propertyRowValue.IsNode() &&
-                            propertyRowValue.GetNodeName().GetStringView() == AZ::DocumentPropertyEditor::Nodes::Row::Name,
-                        "PrefabComponentAdapter::UpdateDomContents - Parent path to property doesn't map to a 'Row' node. ");
-
-                    AZ::Dom::Value firstRowElement = propertyRowValue[0];
-                    AZ_Assert(
-                        firstRowElement.IsNode() &&
-                            firstRowElement.GetNodeName().GetStringView() == AZ::DocumentPropertyEditor::Nodes::PropertyEditor::Name &&
-                            firstRowElement["Type"].GetString() == PrefabPropertyEditorNodes::PrefabOverrideLabel::Name,
-                        "PrefabComponentAdapter::UpdateDomContents - First element in the property row is not a 'PrefabOverrideLabel'.");
-
-                    // Patch the first child in the row, which is going to the PrefabOverrideLabel.
-                    patches.PushBack(AZ::Dom::PatchOperation::ReplaceOperation(
-                        pathToProperty / 0 / PrefabPropertyEditorNodes::PrefabOverrideLabel::IsOverridden.GetName(), AZ::Dom::Value(true)));
-                    NotifyContentsChanged(patches);
-                }
+                // This is for an override, so in addition to the default replace operation,
+                // we need to also patch in the PrefabOverrideLabel in case the change doesn't
+                // trigger a refresh in the adapter.
+                AZ::Dom::Patch patches(
+                    { AZ::Dom::PatchOperation::ReplaceOperation(propertyChangeInfo.path / "Value", propertyChangeInfo.newValue) });
+
+                AZ::Dom::Path pathToProperty = propertyChangeInfo.path;
+
+                // Get the path to parent row and its value.
+                pathToProperty.Pop();
+                AZ::Dom::Value propertyRowValue = GetContents()[pathToProperty];
+
+                AZ_Assert(
+                    propertyRowValue.IsNode() &&
+                    propertyRowValue.GetNodeName().GetStringView() == AZ::DocumentPropertyEditor::Nodes::Row::Name,
+                    "PrefabComponentAdapter::UpdateDomContents - Parent path to property doesn't map to a 'Row' node. ");
+
+                AZ::Dom::Value firstRowElement = propertyRowValue[0];
+                AZ_Assert(
+                    firstRowElement.IsNode() &&
+                    firstRowElement.GetNodeName().GetStringView() == AZ::DocumentPropertyEditor::Nodes::PropertyEditor::Name &&
+                    firstRowElement["Type"].GetString() == PrefabPropertyEditorNodes::PrefabOverrideLabel::Name,
+                    "PrefabComponentAdapter::UpdateDomContents - First element in the property row is not a 'PrefabOverrideLabel'.");
+
+                // Patch the first child in the row, which is going to the PrefabOverrideLabel.
+                patches.PushBack(AZ::Dom::PatchOperation::ReplaceOperation(
+                    pathToProperty / 0 / PrefabPropertyEditorNodes::PrefabOverrideLabel::IsOverridden.GetName(), AZ::Dom::Value(true)));
+                NotifyContentsChanged(patches);
             }
         }
         else if (propertyChangeInfo.changeType == AZ::DocumentPropertyEditor::Nodes::ValueChangeType::FinishedEdit)
@@ -192,6 +197,8 @@ namespace AzToolsFramework::Prefab
                 instanceUpdateExecutorInterface->SetShouldPauseInstancePropagation(false);
             }
 
+            // The EndUndoBatch will get called from PropertyManagerComponent::OnEditingFinished, so we can just clear
+            // out our reference to the undo batch here.
             m_currentUndoBatch = nullptr;
         }
     }

+ 21 - 9
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabEditorPreferences.cpp

@@ -6,14 +6,29 @@
  *
  */
 
+#include <AzCore/Console/IConsole.h>
 #include <AzCore/Settings/SettingsRegistry.h>
 #include <AzToolsFramework/Prefab/PrefabEditorPreferences.h>
 
+AZ_CVAR(
+    bool,
+    ed_enableInspectorOverrideManagement,
+    false,
+    nullptr,
+    AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::DontDuplicate,
+    "If set, enables experimental prefab override support in the Entity Inspector");
+
+AZ_CVAR(
+    bool,
+    ed_enableOutlinerOverrideManagement,
+    false,
+    nullptr,
+    AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::DontDuplicate,
+    "If set, enables experimental prefab override support in the Outliner");
+
 namespace AzToolsFramework::Prefab
 {
     const AZStd::string_view HotReloadToggleKey = "/O3DE/Preferences/Prefabs/EnableHotReloading";
-    const AZStd::string_view InspectorOverrideManagementKey = "/O3DE/Preferences/Prefabs/EnableInspectorOverrideManagement";
-    const AZStd::string_view OutlinerOverrideManagementKey = "/O3DE/Preferences/Prefabs/EnableOutlinerOverrideManagement";
 
     bool IsHotReloadingEnabled()
     {
@@ -30,23 +45,20 @@ namespace AzToolsFramework::Prefab
     bool IsOutlinerOverrideManagementEnabled()
     {
         bool isOutlinerOverrideManagementEnabled = false;
-        if (auto* registry = AZ::SettingsRegistry::Get())
+        if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
         {
-            registry->Get(isOutlinerOverrideManagementEnabled, OutlinerOverrideManagementKey);
+            console->GetCvarValue("ed_enableOutlinerOverrideManagement", isOutlinerOverrideManagementEnabled);
         }
-
         return isOutlinerOverrideManagementEnabled;
     }
 
     bool IsInspectorOverrideManagementEnabled()
     {
         bool isInspectorOverrideManagementEnabled = false;
-
-        if (auto* registry = AZ::SettingsRegistry::Get())
+        if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
         {
-            registry->Get(isInspectorOverrideManagementEnabled, InspectorOverrideManagementKey);
+            console->GetCvarValue("ed_enableInspectorOverrideManagement", isInspectorOverrideManagementEnabled);
         }
-
         return isInspectorOverrideManagementEnabled;
     }
 

+ 0 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabEditorPreferences.h

@@ -13,8 +13,6 @@
 namespace AzToolsFramework::Prefab
 {
     extern const AZStd::string_view HotReloadToggleKey;
-    extern const AZStd::string_view InspectorOverrideManagementKey;
-    extern const AZStd::string_view OutlinerOverrideManagementKey;
     
     //! Checks if hot reloading for prefab files is enabled.
     bool IsHotReloadingEnabled();

+ 7 - 19
Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.cpp

@@ -23,14 +23,6 @@
 #include <AzToolsFramework/UI/DPEDebugViewer/DPEDebugWindow.h>
 #include <AzToolsFramework/UI/DocumentPropertyEditor/KeyQueryDPE.h>
 
-AZ_CVAR(
-    bool,
-    ed_enableDPE,
-    false,
-    nullptr,
-    AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::DontDuplicate,
-    "If set, enables experimental Document Property Editor support, replacing the Reflected Property Editor where possible");
-
 AZ_CVAR(
     bool,
     ed_enableCVarDPE,
@@ -1326,6 +1318,7 @@ namespace AzToolsFramework
         {
             // only save our expander state if our expanse/collapse was user-driven
             dpe->SetSavedExpanderStateForRow(BuildDomPath(), isExpanded);
+            dpe->updateGeometry();
             dpe->ExpanderChangedByUser();
         }
     }
@@ -1600,16 +1593,6 @@ namespace AzToolsFramework
         m_spawnDebugView = shouldSpawn;
     }
 
-    bool DocumentPropertyEditor::ShouldReplaceRPE()
-    {
-        bool dpeEnabled = false;
-        if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
-        {
-            console->GetCvarValue(GetEnableDPECVarName(), dpeEnabled);
-        }
-        return dpeEnabled;
-    }
-
     bool DocumentPropertyEditor::ShouldReplaceCVarEditor()
     {
         bool dpeCVarEditorEnabled = false;
@@ -1701,6 +1684,7 @@ namespace AzToolsFramework
             }
         }
         m_layout->addStretch();
+        updateGeometry();
         emit RequestSizeUpdate();
     }
 
@@ -1726,7 +1710,11 @@ namespace AzToolsFramework
             {
                 HandleReset();
             }
-            emit RequestSizeUpdate();
+            else
+            {
+                updateGeometry();
+                emit RequestSizeUpdate();
+            }
         }
     }
 

+ 0 - 5
Code/Framework/AzToolsFramework/AzToolsFramework/UI/DocumentPropertyEditor/DocumentPropertyEditor.h

@@ -221,11 +221,6 @@ namespace AzToolsFramework
         // but can be overridden here
         void SetSpawnDebugView(bool shouldSpawn);
 
-        static constexpr const char* GetEnableDPECVarName()
-        {
-            return "ed_enableDPE";
-        }
-        static bool ShouldReplaceRPE();
         static bool ShouldReplaceCVarEditor();
 
         static constexpr const char* GetEnableCVarEditorName()

+ 25 - 4
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.cpp

@@ -18,6 +18,7 @@ AZ_POP_DISABLE_WARNING
 #include <AzCore/Component/Entity.h>
 #include <AzCore/Component/TickBus.h>
 #include <AzCore/Component/TransformBus.h>
+#include <AzCore/Console/IConsole.h>
 #include <AzCore/Debug/Profiler.h>
 #include <AzCore/IO/FileIO.h>
 #include <AzCore/Serialization/SerializeContext.h>
@@ -129,6 +130,26 @@ namespace AzToolsFramework
     constexpr const char* kPropertyEditorMenuActionMoveUp("editor/propertyEditorMoveUp");
     constexpr const char* kPropertyEditorMenuActionMoveDown("editor/propertyEditorMoveDown");
 
+    static constexpr const char* enableDPECVarName = "ed_enableDPEInspector";
+
+    AZ_CVAR(
+        bool,
+        ed_enableDPEInspector,
+        false,
+        nullptr,
+        AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::DontDuplicate,
+        "If set, enables experimental Document Property Editor support for the Entity Inspector");
+
+    static bool ShouldUseDPE()
+    {
+        bool dpeEnabled = false;
+        if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
+        {
+            console->GetCvarValue(enableDPECVarName, dpeEnabled);
+        }
+        return dpeEnabled;
+    }
+
     //since component editors are spaced apart to make room for drop indicator,
     //giving drop logic simple buffer so drops between editors don't go to the bottom
     static const int kComponentEditorDropTargetPrecision = 6;
@@ -510,7 +531,7 @@ namespace AzToolsFramework
         , m_commandInvokedHandler(
               [this](AZStd::string_view command, const AZ::ConsoleCommandContainer&, AZ::ConsoleFunctorFlags, AZ::ConsoleInvokedFrom)
               {
-                  if (command == AzToolsFramework::DocumentPropertyEditor::GetEnableDPECVarName())
+                  if (command == enableDPECVarName)
                   {
                       ClearInstances();
                       for (auto componentEditor : m_componentEditors)
@@ -1816,7 +1837,7 @@ namespace AzToolsFramework
             }
 
             // Set up other entity property editor customization
-            if (DocumentPropertyEditor::ShouldReplaceRPE() && Prefab::IsInspectorOverrideManagementEnabled())
+            if (ShouldUseDPE() && Prefab::IsInspectorOverrideManagementEnabled())
             {
                 // Set up visualization for overrides on the component
                 UpdateOverrideVisualization(*componentEditor);
@@ -1906,7 +1927,7 @@ namespace AzToolsFramework
             ComponentEditor::ComponentAdapterFactory adapterFactory =
                 [&]() -> AZStd::shared_ptr<AZ::DocumentPropertyEditor::ComponentAdapter>
             {
-                if (DocumentPropertyEditor::ShouldReplaceRPE())
+                if (ShouldUseDPE())
                 {
                     return (
                         Prefab::IsInspectorOverrideManagementEnabled()
@@ -1939,7 +1960,7 @@ namespace AzToolsFramework
             componentEditor->GetPropertyEditor()->SetHiddenQueryFunction([this](const InstanceDataNode* node) { return QueryInstanceDataNodeHiddenStatus(node); });
             componentEditor->GetPropertyEditor()->SetIndicatorQueryFunction([this](const InstanceDataNode* node) { return GetAppropriateIndicator(node); });
 
-            if (DocumentPropertyEditor::ShouldReplaceRPE() && Prefab::IsInspectorOverrideManagementEnabled())
+            if (ShouldUseDPE() && Prefab::IsInspectorOverrideManagementEnabled())
             {
                 // Connect to the component icon's click event to display override context menu
                 connect(

+ 58 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/GenericComboBoxCtrl.inl

@@ -311,13 +311,69 @@ namespace AzToolsFramework
         else if (attrib == AZ::Edit::Attributes::GenericValueList)
         {
             ValueTypeContainer values;
-            if (attrReader->Read<ValueTypeContainer>(values))
+            bool readSuccess = attrReader->Read<ValueTypeContainer>(values);
+
+            if (!readSuccess)
+            {
+                // If the type is integral type
+                // try different combinations of pair<integral type, string>
+                if constexpr (AZStd::is_integral_v<T>)
+                {
+                    auto ReadIntegralStringVectorPair = [&values, attrReader](auto typeIdentity)
+                    {
+                        using IntegralType = typename decltype(typeIdentity)::type;
+
+                        // The AZStd::vector<T, AZStd::string> has been read in before the lambda was called and failed
+                        // so skip re-reading it again
+                        if constexpr (AZStd::is_same_v<T, IntegralType>)
+                        {
+                            return false;
+                        }
+                        else
+                        {
+                            using IntegralValueType = AZStd::pair<IntegralType, AZStd::string>;
+                            using IntegralValueTypeContainer = AZStd::vector<IntegralValueType>;
+                            IntegralValueTypeContainer integralValues;
+                            bool readContainer{};
+                            if (readContainer = attrReader->Read<IntegralValueTypeContainer>(integralValues); readContainer)
+                            {
+                                for (auto&& [value, description] : integralValues)
+                                {
+                                    values.emplace_back(static_cast<T>(value), AZStd::move(description));
+                                }
+                            }
+
+                            return readContainer;
+                        }
+                    };
+
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<char>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<unsigned char>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<signed char>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<short>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<unsigned short>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<int>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<unsigned int>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<long>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<unsigned long>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<AZ::s64>{});
+                    readSuccess = readSuccess || ReadIntegralStringVectorPair(AZStd::type_identity<AZ::u64>{});
+                }
+            }
+
+            if (readSuccess)
             {
                 genericGUI->setElements(values);
             }
             else
             {
-                AZ_WarningOnce("AzToolsFramework", false, "Failed to read 'GenericValueList' attribute from property '%s' into generic combo box. Expected a vector of pair<%s, string>.", debugName, AZ::AzTypeInfo<typename GenericComboBoxHandler::property_t>::Name());
+                AZ_WarningOnce(
+                    "AzToolsFramework",
+                    false,
+                    "Failed to read 'GenericValueList' attribute from property '%s' into generic combo box. Expected a vector of pair<%s, "
+                    "string>.",
+                    debugName,
+                    AZ::AzTypeInfo<typename GenericComboBoxHandler::property_t>::Name());
             }
         }
         else if (attrib == AZ::Edit::Attributes::PostChangeNotify)

+ 2 - 40
Code/Framework/AzToolsFramework/AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h

@@ -206,7 +206,7 @@ namespace AzToolsFramework
         {
             if (m_widget)
             {
-                delete m_widget;
+                m_widget->deleteLater();
             }
             IndividualPropertyHandlerEditNotifications::Bus::Handler::BusDisconnect();
         }
@@ -357,45 +357,7 @@ namespace AzToolsFramework
             auto value = AZ::DocumentPropertyEditor::Nodes::PropertyEditor::Value.ExtractFromDomNode(node);
             if (value.has_value())
             {
-                if (!Prefab::IsInspectorOverrideManagementEnabled())
-                {
-                    m_proxyValue = AZ::Dom::Utils::ValueToType<WrappedType>(value.value()).value_or(m_proxyValue);
-                }
-                else
-                {
-                    // Assign a custom reporting callback to ignore unregistered types
-                    AZ::JsonDeserializerSettings settings;
-                    AZStd::string scratchBuffer;
-                    settings.m_reporting = [&scratchBuffer](
-                        AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, AZStd::string_view path) -> auto
-                    {
-                        // Unregistered types are acceptable and do not require a warning
-                        if (result.GetTask() != AZ::JsonSerializationResult::Tasks::RetrieveInfo ||
-                            result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::Unknown)
-                        {
-                            // Default Json serialization issue reporting
-                            if (result.GetProcessing() != AZ::JsonSerializationResult::Processing::Completed)
-                            {
-                                scratchBuffer.append(message.begin(), message.end());
-                                scratchBuffer.append("\n    Reason: ");
-                                result.AppendToString(scratchBuffer, path);
-                                scratchBuffer.append(".");
-                                AZ_Warning("JSON Serialization", false, "%s", scratchBuffer.c_str());
-
-                                scratchBuffer.clear();
-                            }
-                        }
-
-                        return result;
-                    };
-
-                    AZ::JsonSerializationResult::ResultCode loadResult =
-                        AZ::Dom::Utils::LoadViaJsonSerialization(m_proxyValue, value.value(), settings);
-                    if (loadResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
-                    {
-                        m_proxyValue = AZ::Dom::Utils::ValueToType<WrappedType>(value.value()).value_or(m_proxyValue);
-                    }
-                }
+                m_proxyValue = AZ::Dom::Utils::ValueToType<WrappedType>(value.value()).value_or(m_proxyValue);
             }
 
             AZ::SerializeContext* serializeContext = nullptr;

+ 2 - 2
Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntitySelectionTests.cpp

@@ -52,7 +52,7 @@ namespace UnitTest
         // Verify the correct entity is selected
         auto selectedEntitiesAfter = GetSelectedEntities();
         EXPECT_EQ(selectedEntitiesAfter.size(), 1);
-        EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
+        EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]);
 
         // Restore default state for other tests.
         m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);
@@ -90,7 +90,7 @@ namespace UnitTest
         // Verify the correct entity is selected
         auto selectedEntitiesAfter = GetSelectedEntities();
         EXPECT_EQ(selectedEntitiesAfter.size(), 1);
-        EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[StreetEntityName]);
+        EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CityEntityName]);
 
         // Restore default state for other tests.
         m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);

+ 2 - 2
Code/Framework/AzToolsFramework/Tests/FocusMode/ContainerEntityTests.cpp

@@ -255,7 +255,7 @@ namespace UnitTest
         m_containerEntityInterface->SetContainerOpen(m_entityMap[SportsCarEntityName], true);
 
         AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
-        EXPECT_EQ(selectedEntityId, m_entityMap[Passenger2EntityName]);
+        EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]);
 
         // Restore default state for other tests.
         m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[SportsCarEntityName]);
@@ -283,7 +283,7 @@ namespace UnitTest
         m_containerEntityInterface->SetContainerOpen(m_entityMap[StreetEntityName], true);
 
         AZ::EntityId selectedEntityId = m_containerEntityInterface->FindHighestSelectableEntity(m_entityMap[Passenger2EntityName]);
-        EXPECT_EQ(selectedEntityId, m_entityMap[SportsCarEntityName]);
+        EXPECT_EQ(selectedEntityId, m_entityMap[StreetEntityName]);
 
         // Restore default state for other tests.
         m_containerEntityInterface->UnregisterEntityAsContainer(m_entityMap[StreetEntityName]);

+ 8 - 0
Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.cpp

@@ -42,6 +42,12 @@ namespace UnitTest
         m_focusModeInterface = AZ::Interface<AzToolsFramework::FocusModeInterface>::Get();
         ASSERT_TRUE(m_focusModeInterface != nullptr);
 
+        if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
+        {
+            console->GetCvarValue("ed_enableOutlinerOverrideManagement", m_ed_enableOutlinerOverrideManagement);
+            console->PerformCommand("ed_enableOutlinerOverrideManagement true");
+        }
+
         // register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
         GetApplication()->RegisterComponentDescriptor(UnitTest::BoundsTestComponent::CreateDescriptor());
 
@@ -67,6 +73,8 @@ namespace UnitTest
 
         // Clear selection
         ClearSelectedEntities();
+        AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
+        registry->Set("/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement", m_ed_enableOutlinerOverrideManagement);
     }
 
     void EditorFocusModeFixture::GenerateTestHierarchy()

+ 1 - 0
Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeFixture.h

@@ -32,6 +32,7 @@ namespace UnitTest
 
         AzToolsFramework::ContainerEntityInterface* m_containerEntityInterface = nullptr;
         AzToolsFramework::FocusModeInterface* m_focusModeInterface = nullptr;
+        bool m_ed_enableOutlinerOverrideManagement = false;
 
     public:
         AzToolsFramework::EntityIdList GetSelectedEntities();

+ 14 - 0
Code/Framework/AzToolsFramework/Tests/FocusMode/EditorFocusModeSelectionFixture.h

@@ -25,6 +25,7 @@
 #include <AzToolsFramework/Manipulators/LinearManipulator.h>
 #include <AzToolsFramework/Manipulators/ManipulatorManager.h>
 #include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
+#include <AzToolsFramework/Prefab/PrefabEditorPreferences.h>
 
 namespace UnitTest
 {
@@ -33,10 +34,20 @@ namespace UnitTest
     public:
         void SetUpEditorFixtureImpl() override
         {
+            AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
+            m_formerOutlinerOverrideSetting = AzToolsFramework::Prefab::IsOutlinerOverrideManagementEnabled();
+            registry->Set("/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement", true);
             IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>::SetUpEditorFixtureImpl();
             m_viewportManipulatorInteraction->GetViewportInteraction().SetIconsVisible(false);
         }
 
+        void TearDownEditorFixtureImpl() override
+        {
+            AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
+            IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>::TearDownEditorFixtureImpl();
+            registry->Set("/O3DE/Autoexec/ConsoleCommands/ed_enableOutlinerOverrideManagement", m_formerOutlinerOverrideSetting);
+        }
+
         void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
         {
             // Calculate the world position in screen space
@@ -60,5 +71,8 @@ namespace UnitTest
                 ->MousePosition(endingPositionWorldBoxSelect)
                 ->MouseLButtonUp();
         }
+
+    protected:
+        bool m_formerOutlinerOverrideSetting = false;
     };
 } // namespace UnitTest

+ 10 - 3
Code/Framework/AzToolsFramework/Tests/Prefab/Overrides/PrefabInspectorOverrideTestFixture.cpp

@@ -15,10 +15,14 @@ namespace UnitTest
     void PrefabInspectorOverrideTestFixture::SetUpEditorFixtureImpl()
     {
         // Enable feature flags for DPE and InspectorOverrideManagement
-        AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
-        registry->Set(InspectorOverrideManagementKey, true);
-        registry->Set("/O3DE/Autoexec/ConsoleCommands/ed_enableDPE", true);
+        if (auto* console = AZ::Interface<AZ::IConsole>::Get(); console != nullptr)
+        {
+            console->GetCvarValue("ed_enableInspectorOverrideManagement", m_ed_enableInspectorOverrideManagement);
+            console->GetCvarValue("ed_enableDPEInspector", m_ed_enableDPEInspector);
+            console->PerformCommand("ed_enableInspectorOverrideManagement true");
+            console->PerformCommand("ed_enableDPEInspector true");
 
+        }
         PrefabOverrideTestFixture::SetUpEditorFixtureImpl();
 
         m_testEntityPropertyEditor = AZStd::make_unique<AzToolsFramework::EntityPropertyEditor>(nullptr, Qt::WindowFlags(), false);
@@ -28,6 +32,9 @@ namespace UnitTest
     {
         m_testEntityPropertyEditor.reset();
         PrefabOverrideTestFixture::TearDownEditorFixtureImpl();
+        AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
+        registry->Set("/O3DE/Autoexec/ConsoleCommands/ed_enableDPEInspector", m_ed_enableDPEInspector);
+        registry->Set("/O3DE/Autoexec/ConsoleCommands/ed_enableInspectorOverrideManagement", m_ed_enableInspectorOverrideManagement);
     }
 
     void PrefabInspectorOverrideTestFixture::GenerateComponentAdapterDoms(AZ::EntityId entityId)

+ 2 - 0
Code/Framework/AzToolsFramework/Tests/Prefab/Overrides/PrefabInspectorOverrideTestFixture.h

@@ -29,5 +29,7 @@ namespace UnitTest
             const AzToolsFramework::ComponentEditor::VisitComponentAdapterContentsCallback& callback);
 
         AZStd::unique_ptr<AzToolsFramework::EntityPropertyEditor> m_testEntityPropertyEditor;
+        bool m_ed_enableDPEInspector = false;
+        bool m_ed_enableInspectorOverrideManagement = false;
     };
 } // namespace UnitTest

+ 8 - 4
Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp

@@ -12,6 +12,7 @@
 #include <SettingsInterface.h>
 
 #include <QMessageBox>
+#include <QDebug>
 #include <QDesktopServices>
 #include <QUrl>
 
@@ -55,7 +56,7 @@ namespace O3DE::ProjectManager
         if (projectButton)
         {
             projectButton->SetProjectButtonAction(tr("Cancel"), [this] { HandleCancel(); });
-            projectButton->SetBuildLogsLink(m_worker->GetLogFilePath());
+            projectButton->SetBuildLogsLink(QUrl::fromLocalFile(m_worker->GetLogFilePath()));
             projectButton->SetState(ProjectButtonState::Building);
 
             if (!m_lastLine.isEmpty())
@@ -95,11 +96,14 @@ namespace O3DE::ProjectManager
                 if (openLog == QMessageBox::Yes)
                 {
                     // Open application assigned to this file type
-                    QDesktopServices::openUrl(QUrl("file:///" + m_worker->GetLogFilePath()));
+                    if (!QDesktopServices::openUrl(QUrl::fromLocalFile(m_worker->GetLogFilePath())))
+                    {
+                        qDebug() << "QDesktopServices::openUrl failed to open " << m_projectInfo.m_logUrl.toString() << "\n";
+                    }
                 }
 
                 m_projectInfo.m_buildFailed = true;
-                m_projectInfo.m_logUrl = QUrl("file:///" + m_worker->GetLogFilePath());
+                m_projectInfo.m_logUrl = QUrl::fromLocalFile(m_worker->GetLogFilePath());
                 emit NotifyBuildProject(m_projectInfo);
             }
             else
@@ -107,7 +111,7 @@ namespace O3DE::ProjectManager
                 QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result);
 
                 m_projectInfo.m_buildFailed = true;
-                m_projectInfo.m_logUrl = QUrl("file:///" + m_worker->GetLogFilePath());
+                m_projectInfo.m_logUrl = QUrl::fromLocalFile(m_worker->GetLogFilePath());
                 emit NotifyBuildProject(m_projectInfo);
             }
 

+ 5 - 1
Code/Tools/ProjectManager/Source/ProjectButtonWidget.cpp

@@ -25,6 +25,7 @@
 #include <QMenu>
 #include <QSpacerItem>
 #include <QProgressBar>
+#include <QDebug>
 #include <QDir>
 #include <QFileInfo>
 #include <QDesktopServices>
@@ -403,7 +404,10 @@ namespace O3DE::ProjectManager
 
     void ProjectButton::ShowLogs()
     {
-        QDesktopServices::openUrl(m_projectInfo.m_logUrl);
+        if (!QDesktopServices::openUrl(m_projectInfo.m_logUrl))
+        {
+            qDebug() << "QDesktopServices::openUrl failed to open " << m_projectInfo.m_logUrl.toString() << "\n";
+        }
     }
 
     void ProjectButton::SetEngine(const EngineInfo& engine)

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Scripts/material_find_overrides_demo.lua

@@ -136,7 +136,7 @@ end
 function FindMaterialAssignmentTest:OnTick(deltaTime, timePoint)
 
     if(nil == self.assignmentIds) then
-        local originalAssignments = MaterialComponentRequestBus.Event.GetDefautMaterialMap(self.entityId)
+        local originalAssignments = MaterialComponentRequestBus.Event.GetDefaultMaterialMap(self.entityId)
         if(nil == originalAssignments or #originalAssignments <= 1) then -- There is always 1 entry for the default assignment; a loaded model will have at least 2 assignments
             return
         end

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Scripts/material_property_overrides_demo.lua

@@ -135,7 +135,7 @@ end
 function PropertyValueTest:OnTick(deltaTime, timePoint)
 
     if(nil == self.assignmentIds) then
-        local originalAssignments = MaterialComponentRequestBus.Event.GetDefautMaterialMap(self.entityId)
+        local originalAssignments = MaterialComponentRequestBus.Event.GetDefaultMaterialMap(self.entityId)
         if(nil == originalAssignments or #originalAssignments <= 1) then -- There is always 1 entry for the default assignment; a loaded model will have at least 2 assignments
             return
         end

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Scripts/material_property_overrides_demo.py

@@ -45,7 +45,7 @@ def assetIdToPath(assetId):
 # List all slot ids
 def getIds(entityId):
     print("Get material ids")
-    return azlmbr.render.MaterialComponentRequestBus(azlmbr.bus.Event, 'GetDefautMaterialMap', entityId).keys()
+    return azlmbr.render.MaterialComponentRequestBus(azlmbr.bus.Event, 'GetDefaultMaterialMap', entityId).keys()
 
 def printMaterials(entityId):
     materials = azlmbr.render.MaterialComponentRequestBus(azlmbr.bus.Event, 'GetMaterialMap', entityId)

+ 13 - 0
Gems/Atom/Tools/MaterialCanvas/Registry/gem_autoload.materialcanvas.setreg

@@ -34,6 +34,19 @@
                     }
                 }
             },
+            "VirtualGamepad": {
+                "Targets": {
+                    "VirtualGamepad": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Clients": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Tools": {
+                        "AutoLoad": false
+                    }
+                }
+            },
             "LyShine": {
                 "Targets": {
                     "LyShine": {

+ 13 - 0
Gems/Atom/Tools/MaterialEditor/Registry/gem_autoload.materialeditor.setreg

@@ -34,6 +34,19 @@
                     }
                 }
             },
+            "VirtualGamepad": {
+                "Targets": {
+                    "VirtualGamepad": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Clients": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Tools": {
+                        "AutoLoad": false
+                    }
+                }
+            },
             "LyShine": {
                 "Targets": {
                     "LyShine": {

+ 13 - 0
Gems/Atom/Tools/PassCanvas/Registry/gem_autoload.passcanvas.setreg

@@ -34,6 +34,19 @@
                     }
                 }
             },
+            "VirtualGamepad": {
+                "Targets": {
+                    "VirtualGamepad": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Clients": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Tools": {
+                        "AutoLoad": false
+                    }
+                }
+            },
             "LyShine": {
                 "Targets": {
                     "LyShine": {

+ 13 - 0
Gems/Atom/Tools/ShaderManagementConsole/Registry/gem_autoload.shadermanagementconsole.setreg

@@ -34,6 +34,19 @@
                     }
                 }
             },
+            "VirtualGamepad": {
+                "Targets": {
+                    "VirtualGamepad": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Clients": {
+                        "AutoLoad": false
+                    },
+                    "VirtualGamepad.Tools": {
+                        "AutoLoad": false
+                    }
+                }
+            },
             "LyShine": {
                 "Targets": {
                     "LyShine": {

+ 1 - 1
Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialAssignment.h

@@ -76,7 +76,7 @@ namespace AZ
             const MaterialAssignmentMap& materials, const MaterialAssignmentId& id);
 
         //! Utility function for generating a set of available material assignments in a model
-        MaterialAssignmentMap GetDefautMaterialMapFromModelAsset(const Data::Asset<AZ::RPI::ModelAsset> modelAsset);
+        MaterialAssignmentMap GetDefaultMaterialMapFromModelAsset(const Data::Asset<AZ::RPI::ModelAsset> modelAsset);
 
         //! Get material slot labels from a model
         MaterialAssignmentLabelMap GetMaterialSlotLabelsFromModelAsset(const Data::Asset<AZ::RPI::ModelAsset> modelAsset);

+ 14 - 2
Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h

@@ -19,7 +19,13 @@ namespace AZ
         {
         public:
             //! Get a map representing the default layout and values for all material assignment slots on the source model or object
-            virtual MaterialAssignmentMap GetDefautMaterialMap() const = 0;
+            virtual MaterialAssignmentMap GetDefaultMaterialMap() const = 0;
+
+            // O3DE_DEPRECATION_NOTICE(GHI-16783) Function is being replaced by GetDefaultMaterialMap
+            virtual MaterialAssignmentMap GetDefautMaterialMap() const
+            {
+                return GetDefaultMaterialMap();
+            }
 
             //! Search for a material assignment ID matching the lod and label parameters
             //! @param lod Index of the LOD to be searched for the material assignment ID. -1 is used to search the default material and
@@ -214,7 +220,13 @@ namespace AZ
             virtual MaterialAssignmentLabelMap GetMaterialLabels() const = 0;
 
             //! Returns the available material slots and default assigned materials
-            virtual MaterialAssignmentMap GetDefautMaterialMap() const = 0;
+            virtual MaterialAssignmentMap GetDefaultMaterialMap() const = 0;
+
+            // O3DE_DEPRECATION_NOTICE(GHI-16783) Function is being replaced by GetDefaultMaterialMap
+            virtual MaterialAssignmentMap GetDefautMaterialMap() const
+            {
+                return GetDefaultMaterialMap();
+            }
 
             //! Returns a map of UV Overridable UV channel names
             virtual AZStd::unordered_set<AZ::Name> GetModelUvNames() const = 0;

+ 2 - 2
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponent.cpp

@@ -315,7 +315,7 @@ namespace AZ
             // Get the known material assignment slots from the associated model or other source
             MaterialAssignmentMap originalMaterials;
             MaterialComponentRequestBus::EventResult(
-                originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefautMaterialMap);
+                originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
                         
             // Generate the table of editable materials using the source data to define number of groups, elements, and initial values
             for (const auto& materialPair : originalMaterials)
@@ -371,7 +371,7 @@ namespace AZ
         {
             MaterialAssignmentMap originalMaterials;
             MaterialComponentRequestBus::EventResult(
-                originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefautMaterialMap);
+                originalMaterials, GetEntityId(), &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
 
             // Generate a unique set of all material asset IDs that will be used for source data generation
             AZStd::unordered_map<AZ::Data::AssetId, AZStd::string> assetIdToSlotNameMap;

+ 2 - 2
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentUtil.cpp

@@ -240,7 +240,7 @@ namespace AZ
             {
                 MaterialAssignmentMap primaryMaterialSlots;
                 MaterialComponentRequestBus::EventResult(
-                    primaryMaterialSlots, primaryEntityId, &MaterialComponentRequestBus::Events::GetDefautMaterialMap);
+                    primaryMaterialSlots, primaryEntityId, &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
 
                 return AZStd::all_of(
                     secondaryEntityIds.begin(), secondaryEntityIds.end(),
@@ -249,7 +249,7 @@ namespace AZ
                         MaterialAssignmentMap secondaryMaterialSlots;
                         MaterialComponentRequestBus::EventResult(
                             secondaryMaterialSlots, secondaryEntityId,
-                            &MaterialComponentRequestBus::Events::GetDefautMaterialMap);
+                            &MaterialComponentRequestBus::Events::GetDefaultMaterialMap);
 
                         if (primaryMaterialSlots.size() != secondaryMaterialSlots.size())
                         {

+ 1 - 1
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialAssignment.cpp

@@ -236,7 +236,7 @@ namespace AZ
             return DefaultMaterialAssignment;
         }
 
-        MaterialAssignmentMap GetDefautMaterialMapFromModelAsset(const Data::Asset<AZ::RPI::ModelAsset> modelAsset)
+        MaterialAssignmentMap GetDefaultMaterialMapFromModelAsset(const Data::Asset<AZ::RPI::ModelAsset> modelAsset)
         {
             MaterialAssignmentMap materials;
             materials[DefaultMaterialAssignmentId] = MaterialAssignment();

+ 3 - 3
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.cpp

@@ -36,7 +36,7 @@ namespace AZ
                     ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
                     ->Attribute(AZ::Script::Attributes::Category, "render")
                     ->Attribute(AZ::Script::Attributes::Module, "render")
-                    ->Event("GetDefautMaterialMap", &MaterialComponentRequestBus::Events::GetDefautMaterialMap, "GetOriginalMaterialAssignments")
+                    ->Event("GetDefaultMaterialMap", &MaterialComponentRequestBus::Events::GetDefaultMaterialMap, "GetDefautMaterialMap")
                     ->Event("FindMaterialAssignmentId", &MaterialComponentRequestBus::Events::FindMaterialAssignmentId)
                     ->Event("GetActiveMaterialAssetId", &MaterialComponentRequestBus::Events::GetMaterialAssetId) // This function is now redundant but cannot be marked deprecated or removed in case it's still referenced in script
                     ->Event("GetDefaultMaterialAssetId", &MaterialComponentRequestBus::Events::GetDefaultMaterialAssetId)
@@ -238,7 +238,7 @@ namespace AZ
             ReleaseMaterials();
 
             MaterialConsumerRequestBus::EventResult(
-                m_defaultMaterialMap, m_entityId, &MaterialConsumerRequestBus::Events::GetDefautMaterialMap);
+                m_defaultMaterialMap, m_entityId, &MaterialConsumerRequestBus::Events::GetDefaultMaterialMap);
 
             // Build tables of all referenced materials so that we can load and look up defaults
             for (const auto& [materialAssignmentId, materialAssignment] : m_defaultMaterialMap)
@@ -344,7 +344,7 @@ namespace AZ
             }
         }
 
-        MaterialAssignmentMap MaterialComponentController::GetDefautMaterialMap() const
+        MaterialAssignmentMap MaterialComponentController::GetDefaultMaterialMap() const
         {
             return m_defaultMaterialMap;
         }

+ 1 - 1
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/MaterialComponentController.h

@@ -46,7 +46,7 @@ namespace AZ
             const MaterialComponentConfig& GetConfiguration() const;
 
             //! MaterialComponentRequestBus overrides...
-            MaterialAssignmentMap GetDefautMaterialMap() const override;
+            MaterialAssignmentMap GetDefaultMaterialMap() const override;
             MaterialAssignmentId FindMaterialAssignmentId(const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
             AZ::Data::AssetId GetDefaultMaterialAssetId(const MaterialAssignmentId& materialAssignmentId) const override;
             AZStd::string GetMaterialLabel(const MaterialAssignmentId& materialAssignmentId) const override;

+ 2 - 2
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.cpp

@@ -348,9 +348,9 @@ namespace AZ
             return GetMaterialSlotIdFromModelAsset(GetModelAsset(), lod, label);
         }
 
-        MaterialAssignmentMap MeshComponentController::GetDefautMaterialMap() const
+        MaterialAssignmentMap MeshComponentController::GetDefaultMaterialMap() const
         {
-            return GetDefautMaterialMapFromModelAsset(GetModelAsset());
+            return GetDefaultMaterialMapFromModelAsset(GetModelAsset());
         }
 
         AZStd::unordered_set<AZ::Name> MeshComponentController::GetModelUvNames() const

+ 1 - 1
Gems/AtomLyIntegration/CommonFeatures/Code/Source/Mesh/MeshComponentController.h

@@ -155,7 +155,7 @@ namespace AZ
             MaterialAssignmentId FindMaterialAssignmentId(
                 const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
             MaterialAssignmentLabelMap GetMaterialLabels() const override;
-            MaterialAssignmentMap GetDefautMaterialMap() const override;
+            MaterialAssignmentMap GetDefaultMaterialMap() const override;
             AZStd::unordered_set<AZ::Name> GetModelUvNames() const override;
 
             // MaterialComponentNotificationBus::Handler overrides ...

+ 2 - 2
Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.cpp

@@ -215,9 +215,9 @@ namespace AZ::Render
         return GetMaterialSlotIdFromModelAsset(GetModelAsset(), lod, label);
     }
 
-    MaterialAssignmentMap AtomActorInstance::GetDefautMaterialMap() const
+    MaterialAssignmentMap AtomActorInstance::GetDefaultMaterialMap() const
     {
-        return GetDefautMaterialMapFromModelAsset(GetModelAsset());
+        return GetDefaultMaterialMapFromModelAsset(GetModelAsset());
     }
 
     AZStd::unordered_set<AZ::Name> AtomActorInstance::GetModelUvNames() const

+ 1 - 1
Gems/AtomLyIntegration/EMotionFXAtom/Code/Source/AtomActorInstance.h

@@ -126,7 +126,7 @@ namespace AZ
             MaterialAssignmentId FindMaterialAssignmentId(
                 const MaterialAssignmentLodIndex lod, const AZStd::string& label) const override;
             MaterialAssignmentLabelMap GetMaterialLabels() const override;
-            MaterialAssignmentMap GetDefautMaterialMap() const override;
+            MaterialAssignmentMap GetDefaultMaterialMap() const override;
             AZStd::unordered_set<AZ::Name> GetModelUvNames() const override;
 
             /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ 2 - 3
Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/bootstrap.py

@@ -160,9 +160,6 @@ from PySide2 import QtGui
 from PySide2.QtCore import Slot, QObject, QUrl
 from shiboken2 import wrapInstance, getCppPointer
 
-# import additional O3DE QtForPython Gem modules
-import az_qt_helpers
-
 # additional DCCsi imports that utilize PySide2
 from DccScriptingInterface.Editor.Scripts.ui import start_service
 from DccScriptingInterface.Editor.Scripts.ui import hook_register_action_sampleui
@@ -346,6 +343,8 @@ def click_action_dccsi_about():
     """Open DCCsi About Dialog"""
     _LOGGER.debug(f'Clicked: click_action_dccsi_about')
 
+    # import additional O3DE QtForPython Gem modules
+    import az_qt_helpers
     EDITOR_MAIN_WINDOW = az_qt_helpers.get_editor_main_window()
 
     about_dialog = DccsiAbout(EDITOR_MAIN_WINDOW)

+ 2 - 1
Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Editor/Scripts/ui.py

@@ -45,7 +45,6 @@ import DccScriptingInterface.azpy.shared.ui.samples
 from DccScriptingInterface.azpy.shared.ui.samples import SampleUI
 
 # O3DE imports
-import az_qt_helpers
 import azlmbr
 import azlmbr.bus
 import azlmbr.paths
@@ -65,6 +64,8 @@ def click_action_sampleui():
     """
     _LOGGER.debug(f'Clicked: click_action_sampleui')
 
+    # import additional O3DE QtForPython Gem modules
+    import az_qt_helpers
     ui = SampleUI(parent=az_qt_helpers.get_editor_main_window(),
                   title='Dccsi: SampleUI')
     ui.show()

+ 1 - 0
Gems/DiffuseProbeGrid/Code/Include/DiffuseProbeGrid/DiffuseProbeGridFeatureProcessorInterface.h

@@ -151,6 +151,7 @@ namespace AZ
             virtual void SetVisualizationShowInactiveProbes(const DiffuseProbeGridHandle& probeGrid, bool visualizationShowInactiveProbes) = 0;
             virtual void SetVisualizationSphereRadius(const DiffuseProbeGridHandle& probeGrid, float visualizationSphereRadius) = 0;
 
+            virtual bool CanBakeTextures() = 0;
             virtual void BakeTextures(
                 const DiffuseProbeGridHandle& probeGrid,
                 DiffuseProbeGridBakeTexturesCallback callback,

+ 5 - 0
Gems/DiffuseProbeGrid/Code/Source/Components/DiffuseProbeGridComponentController.cpp

@@ -482,6 +482,11 @@ namespace AZ
             m_featureProcessor->SetVisualizationSphereRadius(m_handle, m_configuration.m_visualizationSphereRadius);
         }
 
+        bool DiffuseProbeGridComponentController::CanBakeTextures()
+        {
+        return m_featureProcessor && m_featureProcessor->CanBakeTextures();
+        }
+
         void DiffuseProbeGridComponentController::BakeTextures(DiffuseProbeGridBakeTexturesCallback callback)
         {
             if (!m_featureProcessor)

+ 1 - 0
Gems/DiffuseProbeGrid/Code/Source/Components/DiffuseProbeGridComponentController.h

@@ -124,6 +124,7 @@ namespace AZ
 
             // Bake the diffuse probe grid textures to assets
             void BakeTextures(DiffuseProbeGridBakeTexturesCallback callback);
+            bool CanBakeTextures();
 
             // Update the baked texture assets from the configuration
             void UpdateBakedTextures();

+ 11 - 0
Gems/DiffuseProbeGrid/Code/Source/EditorComponents/EditorDiffuseProbeGridComponent.cpp

@@ -518,6 +518,17 @@ namespace AZ
                 return AZ::Edit::PropertyRefreshLevels::None;
             }
 
+            if (!m_controller.CanBakeTextures())
+            {
+                QMessageBox::information(
+                    QApplication::activeWindow(),
+                    "Diffuse Probe Grid",
+                    "Can't bake the textures. Diffuse probe calculations require GPU raytracing support",
+                    QMessageBox::Ok);
+
+                return AZ::Edit::PropertyRefreshLevels::None;
+            }
+
             DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration;
 
             // retrieve the source image paths from the configuration

+ 6 - 0
Gems/DiffuseProbeGrid/Code/Source/Render/DiffuseProbeGridFeatureProcessor.cpp

@@ -464,6 +464,12 @@ namespace AZ
             probeGrid->SetUseDiffuseIbl(useDiffuseIbl);
         }
 
+        bool DiffuseProbeGridFeatureProcessor::CanBakeTextures()
+        {
+            RHI::Ptr<RHI::Device> device = RHI::RHISystemInterface::Get()->GetDevice();
+            return device->GetFeatures().m_rayTracing;
+        }
+
         void DiffuseProbeGridFeatureProcessor::BakeTextures(
             const DiffuseProbeGridHandle& probeGrid,
             DiffuseProbeGridBakeTexturesCallback callback,

+ 1 - 0
Gems/DiffuseProbeGrid/Code/Source/Render/DiffuseProbeGridFeatureProcessor.h

@@ -61,6 +61,7 @@ namespace AZ
             void SetVisualizationShowInactiveProbes(const DiffuseProbeGridHandle& probeGrid, bool visualizationShowInactiveProbes) override;
             void SetVisualizationSphereRadius(const DiffuseProbeGridHandle& probeGrid, float visualizationSphereRadius) override;
 
+            bool CanBakeTextures() override;
             void BakeTextures(
                 const DiffuseProbeGridHandle& probeGrid,
                 DiffuseProbeGridBakeTexturesCallback callback,

+ 42 - 37
Gems/PhysX/Code/Source/Articulation.cpp

@@ -23,9 +23,9 @@ namespace PhysX
         if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
         {
             serializeContext->Class<ArticulationLinkData>()
-                ->Version(1)
+                ->Version(2)
                 ->Field("LinkConfiguration", &ArticulationLinkData::m_articulationLinkConfiguration)
-                ->Field("ShapeColliderPair", &ArticulationLinkData::m_shapeColliderConfiguration)
+                ->Field("ShapeColliderPairList", &ArticulationLinkData::m_shapeColliderConfigurationList)
                 ->Field("LocalTransform", &ArticulationLinkData::m_localTransform)
                 ->Field("JointLeadLocalFrame", &ArticulationLinkData::m_jointLeadLocalFrame)
                 ->Field("JointFollowerLocalFrame", &ArticulationLinkData::m_jointFollowerLocalFrame)
@@ -62,50 +62,55 @@ namespace PhysX
 
     void ArticulationLink::AddCollisionShape(const ArticulationLinkData& thisLinkData)
     {
-        const Physics::ColliderConfiguration* colliderConfiguration = thisLinkData.m_shapeColliderConfiguration.first.get();
-        const Physics::ShapeConfiguration* shapeConfiguration = thisLinkData.m_shapeColliderConfiguration.second.get();
-
-        if (shapeConfiguration && colliderConfiguration)
+        m_physicsShapes.clear();
+        for (auto& [colliderConfiguration, shapeConfiguration] : thisLinkData.m_shapeColliderConfigurationList)
         {
-            if (shapeConfiguration->GetShapeType() == Physics::ShapeType::PhysicsAsset)
+            if (shapeConfiguration && colliderConfiguration)
             {
-                const auto* physicsAssetShapeConfiguration =
-                    static_cast<const Physics::PhysicsAssetShapeConfiguration*>(shapeConfiguration);
-                if (!physicsAssetShapeConfiguration->m_asset.IsReady())
+                if (shapeConfiguration->GetShapeType() == Physics::ShapeType::PhysicsAsset)
                 {
-                    const_cast<Physics::PhysicsAssetShapeConfiguration*>(physicsAssetShapeConfiguration)->m_asset.BlockUntilLoadComplete();
+                    const auto* physicsAssetShapeConfiguration =
+                        static_cast<const Physics::PhysicsAssetShapeConfiguration*>(shapeConfiguration.get());
+                    if (!physicsAssetShapeConfiguration->m_asset.IsReady())
+                    {
+                        auto asset_status = const_cast<Physics::PhysicsAssetShapeConfiguration*>(physicsAssetShapeConfiguration)
+                                                ->m_asset.BlockUntilLoadComplete();
+                        AZ_Error(
+                            "PhysX",
+                            asset_status == AZ::Data::AssetData::AssetStatus::Ready,
+                            "Failed to load physics asset %s ",
+                            physicsAssetShapeConfiguration->m_asset.GetHint().c_str());
+                        if (asset_status != AZ::Data::AssetData::AssetStatus::Ready)
+                        {
+                            continue;
+                        }
+                    }
+                    const bool hasNonUniformScale = !Physics::Utils::HasUniformScale(physicsAssetShapeConfiguration->m_assetScale) ||
+                        (AZ::NonUniformScaleRequestBus::FindFirstHandler(GetEntityId()) != nullptr);
+                    AZStd::vector<AZStd::shared_ptr<Physics::Shape>> assetShapes;
+                    Utils::CreateShapesFromAsset(
+                        *physicsAssetShapeConfiguration,
+                        *colliderConfiguration,
+                        hasNonUniformScale,
+                        physicsAssetShapeConfiguration->m_subdivisionLevel,
+                        assetShapes);
+                    AZStd::copy(assetShapes.begin(), assetShapes.end(), AZStd::back_inserter(m_physicsShapes));
                 }
-
-                const bool hasNonUniformScale = !Physics::Utils::HasUniformScale(physicsAssetShapeConfiguration->m_assetScale) ||
-                    (AZ::NonUniformScaleRequestBus::FindFirstHandler(GetEntityId()) != nullptr);
-                AZStd::vector<AZStd::shared_ptr<Physics::Shape>> assetShapes;
-                Utils::CreateShapesFromAsset(
-                    *physicsAssetShapeConfiguration,
-                    *colliderConfiguration,
-                    hasNonUniformScale,
-                    physicsAssetShapeConfiguration->m_subdivisionLevel,
-                    assetShapes);
-
-                if (!assetShapes.empty())
+                else
                 {
-                    m_physicsShape = assetShapes[0];
-                    AZ_Warning(
-                        "PhysX",
-                        assetShapes.size() == 1,
-                        "Articulation %s has a link with physics mesh with more than 1 shape",
-                        m_pxLink->getName());
+                    m_physicsShapes.push_back(
+                        AZ::Interface<Physics::System>::Get()->CreateShape(*colliderConfiguration, *shapeConfiguration));
                 }
             }
-            else
-            {
-                m_physicsShape =
-                    AZ::Interface<Physics::System>::Get()->CreateShape(*colliderConfiguration, *shapeConfiguration);
-            }
         }
-
-        if (m_physicsShape)
+        AZ_Printf(
+            "PhysX", "ArticulationLink::AddCollisionShape: %zu shapes added to link %s\n", m_physicsShapes.size(), m_pxLink->getName());
+        for (const auto& shapePtr : m_physicsShapes)
         {
-            m_pxLink->attachShape(*static_cast<physx::PxShape*>(m_physicsShape->GetNativePointer()));
+            if (shapePtr && shapePtr->GetNativePointer())
+            {
+                m_pxLink->attachShape(*static_cast<physx::PxShape*>(shapePtr->GetNativePointer()));
+            }
         }
     }
 

+ 3 - 3
Gems/PhysX/Code/Source/Articulation.h

@@ -46,8 +46,8 @@ namespace PhysX
         //! This data comes from Articulation Link Component in the Editor.
         ArticulationLinkConfiguration m_articulationLinkConfiguration;
 
-        //! Data related to the collision shape for this link. 
-        AzPhysics::ShapeColliderPair m_shapeColliderConfiguration;
+        //! Data related to the collision shapes for this link.
+        AzPhysics::ShapeColliderPairList m_shapeColliderConfigurationList;
 
         //! Cached local transform of this link relative to its parent.
         //! This is needed because at the time of constructing the articulation
@@ -95,7 +95,7 @@ namespace PhysX
         physx::PxArticulationLink* m_pxLink = nullptr;
 
         ActorData m_actorData;
-        AZStd::shared_ptr<Physics::Shape> m_physicsShape;
+        AZStd::vector<AZStd::shared_ptr<Physics::Shape>> m_physicsShapes;
     };
 
     ArticulationLink* CreateArticulationLink(const ArticulationLinkConfiguration* articulationConfig);

+ 8 - 14
Gems/PhysX/Code/Source/Pipeline/PhysicsPrefabProcessor.cpp

@@ -33,21 +33,15 @@ namespace PhysX
         linkData->m_localTransform = transformComponent->GetLocalTM();
 
         const auto& components = entity->GetComponents();
-        auto baseColliderComponentIt = AZStd::find_if(
-            components.begin(),
-            components.end(),
-            [](AZ::Component* component)
-            {
-                return azdynamic_cast<BaseColliderComponent*>(component) != nullptr;
-            });
-
-        if (baseColliderComponentIt != components.end())
+        for (const auto& component : components)
         {
-            auto* baseColliderComponent = azdynamic_cast<BaseColliderComponent*>(*baseColliderComponentIt);
-            AzPhysics::ShapeColliderPairList shapeColliderPairList = baseColliderComponent->GetShapeConfigurations();
-            AZ_Assert(!shapeColliderPairList.empty(), "Collider component with no shape configurations");
-
-            linkData->m_shapeColliderConfiguration = shapeColliderPairList[0];
+            if (auto* baseColliderComponent = azdynamic_cast<BaseColliderComponent*>(component))
+            {
+                AzPhysics::ShapeColliderPairList shapeColliderPairList = baseColliderComponent->GetShapeConfigurations();
+                AZ_Assert(!shapeColliderPairList.empty(), "Collider component with no shape configurations");
+                linkData->m_shapeColliderConfigurationList.insert(
+                    linkData->m_shapeColliderConfigurationList.end(), shapeColliderPairList.begin(), shapeColliderPairList.end());
+            }
         }
 
         auto* articulationLinkComponent = entity->FindComponent<ArticulationLinkComponent>();

+ 4 - 4
Gems/ScriptCanvas/Assets/TranslationAssets/EBus/Senders/MaterialComponentRequestBus.names

@@ -923,17 +923,17 @@
                     ]
                 },
                 {
-                    "base": "GetDefautMaterialMap",
+                    "base": "GetDefaultMaterialMap",
                     "entry": {
                         "name": "In",
-                        "tooltip": "When signaled, this will invoke Get Defaut Material Map"
+                        "tooltip": "When signaled, this will invoke Get Default Material Map"
                     },
                     "exit": {
                         "name": "Out",
-                        "tooltip": "Signaled after Get Defaut Material Map is invoked"
+                        "tooltip": "Signaled after Get Default Material Map is invoked"
                     },
                     "details": {
-                        "name": "Get Defaut Material Map"
+                        "name": "Get Default Material Map"
                     },
                     "results": [
                         {

+ 0 - 2
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/Logger.cpp

@@ -34,9 +34,7 @@ namespace ScriptCanvas
 
         void Logger::ClearLog()
         {
-#if defined(SC_EXECUTION_TRACE_ENABLED) 
             m_logAsset.GetData().Clear();
-#endif
         }
 
         void Logger::ClearLogExecutionOverride()

+ 1 - 3
Gems/ScriptCanvas/Code/Include/ScriptCanvas/Debugger/Logger.h

@@ -68,8 +68,8 @@ namespace ScriptCanvas
             {
 #if defined(SC_EXECUTION_TRACE_ENABLED) 
                 SCRIPT_CANVAS_DEBUGGER_TRACE_CLIENT("Logging: %s", loggableEvent.ToString().data());
-                m_logAsset.GetData().m_events.emplace_back(loggableEvent.Duplicate());
 #endif
+                m_logAsset.GetData().m_events.emplace_back(loggableEvent.Duplicate());
             }
 
         private:
@@ -78,9 +78,7 @@ namespace ScriptCanvas
             bool m_logExecutionOverrideEnabled = false;
             bool m_logExecutionOverride = false;
 
-#if defined(SC_EXECUTION_TRACE_ENABLED) 
             ExecutionLogAsset m_logAsset;
-#endif
             ScriptCanvas::Debugger::Target m_target;
         };
     }

+ 1 - 1
cmake/3rdParty/Platform/Linux/BuiltInPackages_linux_aarch64.cmake

@@ -27,7 +27,7 @@ ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.1-rev1-linux-aarch64
 ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux-aarch64                            TARGETS mikkelsen                   PACKAGE_HASH 62f3f316c971239a2b86d7c47a68fee9be744de3a4f9b00533b32f33a4764f8b)
 ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux-aarch64                        TARGETS googletest                  PACKAGE_HASH 01e236a9b5992da2494227ce23ba2f9aa6ea73a232aeceb3606fbf41954dc6d0)
 ly_associate_package(PACKAGE_NAME googlebenchmark-1.7.0-rev1-linux-aarch64                   TARGETS GoogleBenchmark             PACKAGE_HASH 06fbfeaba2aeae20197da631019e52105dc1f69e702151a76c6aba2c27c03acb)
-ly_associate_package(PACKAGE_NAME qt-5.15.2-rev8-linux-aarch64                               TARGETS Qt                          PACKAGE_HASH c437ee1c7a4fe84002352a2f8ed230c822a13dcc80735a4fecf3b3af6e34bb63)
+ly_associate_package(PACKAGE_NAME qt-5.15.2-rev9-linux-aarch64                               TARGETS Qt                          PACKAGE_HASH da80840ecd3f7a074edecbb3dedb1ff36c568cfe4943e18d9559e9fca9f151bc)
 ly_associate_package(PACKAGE_NAME png-1.6.37-rev2-linux-aarch64                              TARGETS PNG                         PACKAGE_HASH fcf646c1b1b4163000efdb56d7c8f086b6ce0a520da5c8d3ffce4e1329ae798a)
 ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux-aarch64                     TARGETS libsamplerate               PACKAGE_HASH 751484da1527432cd19263909f69164d67b25644f87ec1d4ec974a343defacea)
 ly_associate_package(PACKAGE_NAME openimageio-opencolorio-2.3.17-rev2-linux-aarch64          TARGETS OpenImageIO OpenColorIO OpenColorIO::Runtime OpenImageIO::Tools::Binaries OpenImageIO::Tools::PythonPlugins PACKAGE_HASH 2bc6a43f60c8206b2606a65738e0fcf3b3b17e0db16089404d8389d337c85ad6)

+ 1 - 1
cmake/3rdParty/Platform/Linux/BuiltInPackages_linux_x86_64.cmake

@@ -27,7 +27,7 @@ ly_associate_package(PACKAGE_NAME mcpp-2.7.2_az.2-rev1-linux
 ly_associate_package(PACKAGE_NAME mikkelsen-1.0.0.4-linux                           TARGETS mikkelsen                   PACKAGE_HASH 5973b1e71a64633588eecdb5b5c06ca0081f7be97230f6ef64365cbda315b9c8)
 ly_associate_package(PACKAGE_NAME googletest-1.8.1-rev4-linux                       TARGETS googletest                  PACKAGE_HASH 7b7ad330f369450c316a4c4592d17fbb4c14c731c95bd8f37757203e8c2bbc1b)
 ly_associate_package(PACKAGE_NAME googlebenchmark-1.7.0-rev1-linux                  TARGETS GoogleBenchmark             PACKAGE_HASH 230e1881e31490820f0bd2059df4741455b52809ac73367e278e1e821ac89c9b)
-ly_associate_package(PACKAGE_NAME qt-5.15.2-rev8-linux                              TARGETS Qt                          PACKAGE_HASH 613d6a404b305ce0e715c57c936dc00318fb9f0d2d3f6609f8454c198f993095)
+ly_associate_package(PACKAGE_NAME qt-5.15.2-rev9-linux                              TARGETS Qt                          PACKAGE_HASH db4bcd2003262f4d8c7d7da832758824fc24e53da5895edef743f67a64a5c734)
 ly_associate_package(PACKAGE_NAME png-1.6.37-rev2-linux                             TARGETS PNG                         PACKAGE_HASH 5c82945a1648905a5c4c5cee30dfb53a01618da1bf58d489610636c7ade5adf5)
 ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-linux                    TARGETS libsamplerate               PACKAGE_HASH 41643c31bc6b7d037f895f89d8d8d6369e906b92eff42b0fe05ee6a100f06261)
 ly_associate_package(PACKAGE_NAME openimageio-opencolorio-2.3.17-rev2-linux         TARGETS OpenImageIO OpenColorIO OpenColorIO::Runtime OpenImageIO::Tools::Binaries OpenImageIO::Tools::PythonPlugins PACKAGE_HASH c8a9f1d9d6c9f8c3defdbc3761ba391d175b1cb62a70473183af1eaeaef70c36)

+ 1 - 1
cmake/Platform/Windows/Packaging/Bootstrapper.wxs

@@ -5,7 +5,7 @@
 <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
     xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
 
-    <Bundle Name="$(var.CPACK_PACKAGE_NAME) [Developer Preview]"
+    <Bundle Name="$(var.CPACK_PACKAGE_NAME)"
         Version="$(var.CPACK_PACKAGE_VERSION)"
         Manufacturer="$(var.CPACK_PACKAGE_VENDOR)"
         UpgradeCode="$(var.CPACK_BOOTSTRAP_UPGRADE_GUID)"

+ 1 - 1
cmake/Platform/Windows/Packaging/Shortcuts.wxs

@@ -7,7 +7,7 @@
 
         <DirectoryRef Id="TARGETDIR">
             <Directory Id="ProgramMenuFolder">
-                <Directory Id="ApplicationProgramsFolder" Name="$(var.CPACK_PACKAGE_NAME) [Developer Preview]">
+                <Directory Id="ApplicationProgramsFolder" Name="$(var.CPACK_PACKAGE_NAME)">
                     <Directory Id="VersionedApplicationProgramsFolder" Name="$(var.CPACK_PACKAGE_VERSION)"/>
                 </Directory>
             </Directory>

+ 122 - 0
scripts/o3de/ExportScripts/export_source_ios_xcode.py

@@ -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
+#
+#
+
+import argparse
+import logging
+import sys
+
+import o3de.export_project as exp
+import pathlib
+
+def export_ios_xcode_project(ctx: exp.O3DEScriptExportContext,
+                             tools_build_folder: pathlib.Path,
+                             ios_build_folder: pathlib.Path,
+                             should_build_tools:bool = True,
+                             skip_asset_processing: bool = False,
+                             logger: logging.Logger|None = None):
+    """
+    This function serves as an initial exporter for project to the iOS platform. The steps in this code will generate
+    an Xcode project file containing all necessary build information to produce iOS executables. 
+    In order to build and deploy projects, it is recommended to open the project file in Xcode to configure and build accordingly.
+
+    Note: In order to use this functionality, you must be running this script from a MacOS machine with a valid copy of Xcode. 
+    This will also require setting up an AppleID and provisioning profile in Xcode when the resulting xcode-project file is generated before deploying to device.
+
+    Instructions to handle iOS projects in Xcode will be provided soon. This export function is currently experimental.
+
+    :param ctx:                                     The O3DE Script context provided by the export-command
+    :param tools_build_folder:                      Optional build path to build the tools. (see default_tools_path below)
+    :param ios_build_folder:                        The base output path of the generated Xcode Project file for iOS
+    :param should_build_tools:                      Option to build the export process dependent tools (AssetProcessor, AssetBundlerBatch, and dependencies)
+    :param should_build_all_assets:                 Option to process all the assets for the game
+    :param logger:                                  Optional logger to use to log the process and errors
+    """
+    
+    # Use a provided logger or get the current system one
+    if not logger:
+        logger = logging.getLogger()
+        logger.setLevel(logging.ERROR)
+
+    tools_build_folder_str = str(tools_build_folder)
+    ios_build_folder_str = str(ios_build_folder)
+
+    # Optionally build the toolchain needed to process the assets
+    if should_build_tools:
+        exp.process_command(["cmake", "-B", tools_build_folder_str, '-G', "Xcode"], cwd=ctx.project_path)
+
+        exp.process_command(["cmake", "--build", tools_build_folder_str, "--target", "AssetProcessorBatch", "AssetBundlerBatch", "--config", "profile"],
+                    cwd=ctx.project_path)
+        
+    # Optionally process the assets
+    if not skip_asset_processing:
+        asset_processor_batch_path = exp.get_asset_processor_batch_path(tools_build_folder, True)
+        exp.process_command([ str(asset_processor_batch_path), '--platforms=ios',
+                        '--project-path', ctx.project_path ], cwd=ctx.project_path)
+
+    # Generate the Xcode project file for the O3DE project
+    cmake_toolchain_path = ctx.engine_path / 'cmake/Platform/iOS/Toolchain_ios.cmake'
+
+    exp.process_command(['cmake', '-B', ios_build_folder_str, '-G', "Xcode", f'-DCMAKE_TOOLCHAIN_FILE={str(cmake_toolchain_path)}', '-DLY_MONOLITHIC_GAME=1'],
+                    cwd= ctx.project_path)
+
+
+    logger.info(f"Xcode project file should be generated now. Please check {ios_build_folder_str}")
+
+
+
+# This code is only run by the 'export-project' O3DE CLI command
+if "o3de_context" in globals():
+    global o3de_context
+    global o3de_logger
+
+    def parse_args(o3de_context: exp.O3DEScriptExportContext):
+        parser = argparse.ArgumentParser(
+                    prog=f'o3de.py export-project -es {__file__}',
+                    description="Exports a project as a generated iOS Xcode project in the project directory. "
+                                "In order to use this script, the engine and project must be setup and registered beforehand. ",
+                    epilog=exp.CUSTOM_CMAKE_ARG_HELP_EPILOGUE,
+                    formatter_class=argparse.RawTextHelpFormatter,
+                    add_help=False
+        )
+
+        default_tools_path = o3de_context.project_path / 'build/tools'
+        default_ios_path = o3de_context.project_path / 'build/game_ios'
+        parser.add_argument(exp.CUSTOM_SCRIPT_HELP_ARGUMENT, default=False, action='store_true', help='Show this help message and exit.')
+        parser.add_argument('-bt', '--build-tools', default=True, action='store_true',
+                            help="Specifies whether to build O3DE toolchain executables. This will build AssetBundlerBatch, AssetProcessorBatch.")
+        parser.add_argument('-tbp', '--tools-build-path', type=pathlib.Path, default=default_tools_path,
+                                help=f'Designates where the build files for the O3DE toolchain are generated. If not specified, default is {default_tools_path}.')
+        parser.add_argument('-ibp', '--ios-build-path', type=pathlib.Path, default=default_ios_path,
+                                help=f'Designates where the build files for the O3DE toolchain are generated. If not specified, default is {default_ios_path}.')
+        parser.add_argument('-assets', '--skip-asset-processing', default=False, action='store_true',
+                                help='Toggles processing all assets for the iOS build.')
+        parser.add_argument('-q', '--quiet', action='store_true', help='Suppresses logging information unless an error occurs.')
+        if o3de_context is None:
+            parser.print_help()
+            exit(0)
+        
+        parsed_args = parser.parse_args(o3de_context.args)
+        if parsed_args.script_help:
+            parser.print_help()
+            exit(0)
+
+        return parsed_args
+    
+    args = parse_args(o3de_context)
+    if args.quiet:
+        o3de_logger.setLevel(logging.ERROR)
+    try:
+        export_ios_xcode_project(ctx=o3de_context,
+                                 tools_build_folder=args.tools_build_path,
+                                 ios_build_folder=args.ios_build_path,
+                                 should_build_tools=args.build_tools,
+                                 skip_asset_processing=args.skip_asset_processing,
+                                 logger=o3de_logger)
+    except exp.ExportProjectError as err:
+        print(err)
+        sys.exit(1)

+ 2 - 2
scripts/o3de/o3de/export_project.py

@@ -35,10 +35,10 @@ if CURRENT_PLATFORM == 'windows':
     ADDITIONAL_PLATFORM_IGNORE_FILES = ['*.pdb', '*.lock']
 
 elif CURRENT_PLATFORM == 'darwin':
-    # Test if Ninja is available from the command line to determine the generator and multi-config capability
+    # Test if Xcode is available from the command line to determine the generator
     test_xcode_result = None
     try:
-        test_xcode_result = subprocess.run(['xcodebuild', '--version'])
+        test_xcode_result = subprocess.run(['xcodebuild', '-version'])
     except FileNotFoundError:
         pass