ソースを参照

Convert prefab to temp spawnable assets (#6179)

* Convert prefab to temp in-memory spawnable assets

Signed-off-by: chiyenteng <[email protected]>
chiyenteng 3 年 前
コミット
ca49eedc5f
55 ファイル変更752 行追加296 行削除
  1. 4 1
      AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_test_utils.py
  2. 0 1
      AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py
  3. 5 1
      AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py
  4. 9 3
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py
  5. 8 3
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py
  6. 6 3
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py
  7. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py
  8. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py
  9. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py
  10. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py
  11. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py
  12. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py
  13. 8 8
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py
  14. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py
  15. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py
  16. 4 4
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py
  17. 4 4
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py
  18. 4 4
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py
  19. 4 4
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py
  20. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py
  21. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py
  22. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py
  23. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py
  24. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py
  25. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py
  26. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py
  27. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py
  28. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py
  29. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py
  30. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py
  31. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py
  32. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py
  33. 2 2
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py
  34. 4 4
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py
  35. 4 4
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py
  36. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py
  37. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py
  38. 1 1
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py
  39. 21 16
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py
  40. 3 3
      AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py
  41. 40 4
      AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py
  42. 2 1
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h
  43. 38 159
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp
  44. 4 7
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h
  45. 30 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h
  46. 73 4
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp
  47. 10 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h
  48. 275 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.cpp
  49. 69 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.h
  50. 20 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConverterStackProfileNames.h
  51. 3 0
      Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
  52. 27 21
      Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp
  53. 21 0
      Registry/prefab.test.setreg
  54. 8 2
      Tools/LyTestTools/ly_test_tools/o3de/editor_test.py
  55. 9 0
      Tools/LyTestTools/tests/unit/test_o3de_editor_test.py

+ 4 - 1
AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/hydra_test_utils.py

@@ -58,7 +58,10 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
     if null_renderer:
         editor.args.extend(["-rhi=Null"])
     if enable_prefab_system:
-        editor.args.extend(["--regset=/Amazon/Preferences/EnablePrefabSystem=true"])
+        from os import path
+        editor.args.extend([
+            "--regset=/Amazon/Preferences/EnablePrefabSystem=true",
+            f"--regset-file={os.path.join(workspace.paths.engine_root(), 'Registry', 'prefab.test.setreg')}"])
     else:
         editor.args.extend(["--regset=/Amazon/Preferences/EnablePrefabSystem=false"])
 

+ 0 - 1
AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/prefab_utils.py

@@ -39,7 +39,6 @@ def get_prefab_file_path(prefab_path):
         prefab_path = name + ".prefab"
     return prefab_path
 
-
 def get_all_entity_ids():
     return entity.SearchBus(bus.Broadcast, 'SearchEntities', entity.SearchFilter())
 

+ 5 - 1
AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py

@@ -12,6 +12,8 @@ import sys
 import pytest
 import time
 
+from os import path
+
 import ly_test_tools.environment.file_system as file_system
 import ly_test_tools.environment.process_utils as process_utils
 import ly_test_tools.environment.waiter as waiter
@@ -98,7 +100,9 @@ class TestAutomationBase:
         if autotest_mode:
             pycmd += ["-autotest_mode"]
         if enable_prefab_system:
-            pycmd += ["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
+            pycmd += [
+                "--regset=/Amazon/Preferences/EnablePrefabSystem=true",
+                f"--regset-file={path.join(workspace.paths.engine_root(), 'Registry', 'prefab.test.setreg')}"]
         else:
             pycmd += ["--regset=/Amazon/Preferences/EnablePrefabSystem=false"]
 

+ 9 - 3
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude.py

@@ -51,19 +51,22 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude():
 
     import os
 
+    import azlmbr.asset as asset
     import azlmbr.editor as editor
     import azlmbr.legacy.general as general
     import azlmbr.bus as bus
     import azlmbr.math as math
+    import azlmbr.prefab as prefab
 
     import editor_python_test_tools.hydra_editor_utils as hydra
+    from editor_python_test_tools.prefab_utils import Prefab
     from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
     from editor_python_test_tools.utils import Report
     from editor_python_test_tools.utils import TestHelper as helper
 
     # 1) Open an existing simple level
     helper.init_idle()
-    helper.open_level("Physics", "Base")
+    helper.open_level("", "Base")
 
     # Set view of planting area for visual debugging
     general.set_current_view_position(512.0, 500.0, 38.0)
@@ -71,8 +74,11 @@ def AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude():
 
     # 2) Create a new entity with required vegetation area components
     center_point = math.Vector3(512.0, 512.0, 32.0)
-    asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
+
+    flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel")
+    flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0]
+
+    spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, flower_prefab)
 
     # Add a Vegetation Altitude Filter
     spawner_entity.add_component("Vegetation Altitude Filter")

+ 8 - 3
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_FilterStageToggle.py

@@ -32,7 +32,9 @@ def AltitudeFilter_FilterStageToggle():
     import os
 
     import azlmbr.legacy.general as general
+    import azlmbr.bus as bus
     import azlmbr.math as math
+    import azlmbr.prefab as prefab
 
     import editor_python_test_tools.hydra_editor_utils as hydra
     from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg
@@ -44,13 +46,16 @@ def AltitudeFilter_FilterStageToggle():
 
     # Open an existing simple level
     helper.init_idle()
-    helper.open_level("Physics", "Base")
+    helper.open_level("", "Base")
     general.set_current_view_position(512.0, 480.0, 38.0)
 
     # Create basic vegetation entity
     position = math.Vector3(512.0, 512.0, 32.0)
-    asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
+
+    flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel")
+    flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0]
+
+    vegetation = dynveg.create_prefab_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, flower_prefab)
 
     # Add a Vegetation Altitude Filter to the vegetation area entity
     vegetation.add_component("Vegetation Altitude Filter")

+ 6 - 3
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude.py

@@ -57,7 +57,7 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude():
 
     # 1) Open an existing simple level
     helper.init_idle()
-    helper.open_level("Physics", "Base")
+    helper.open_level("", "Base")
 
     # Set view of planting area for visual debugging
     general.set_current_view_position(512.0, 500.0, 38.0)
@@ -65,8 +65,11 @@ def AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude():
 
     # 2) Create a new entity with required vegetation area components
     center_point = math.Vector3(512.0, 512.0, 32.0)
-    asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, asset_path)
+
+    flower_asset_path = os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel")
+    flower_prefab = dynveg.create_temp_mesh_prefab(flower_asset_path, "PinkFlower")[0]
+
+    spawner_entity = dynveg.create_prefab_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 16.0, flower_prefab)
 
     # Add a Vegetation Altitude Filter
     spawner_entity.add_component("Vegetation Altitude Filter")

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea.py

@@ -111,7 +111,7 @@ def AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea():
 
     # 4) Create a spawner using a Vegetation Asset List Combiner component and a Weight Selector, and disallow
     # spawning empty assets
-    spawner_entity = dynveg.create_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", center_point, 16.0, 16.0, 16.0, None)
     spawner_entity.remove_component("Vegetation Asset List")
     spawner_entity.add_component("Vegetation Asset List Combiner")
     spawner_entity.add_component("Vegetation Asset Weight Selector")

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/AssetWeightSelector_InstancesExpressBasedOnWeight.py

@@ -67,8 +67,8 @@ def AssetWeightSelector_InstancesExpressBasedOnWeight():
     # valid slice entity, and one set to None
     spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
+                                                                               asset_path)
     desc_asset = hydra.get_component_property_value(spawner_entity.components[2],
                                                     "Configuration|Embedded Assets")[0]
     desc_list = [desc_asset, desc_asset]

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius.py

@@ -72,8 +72,8 @@ def DistanceBetweenFilterOverrides_InstancesPlantAtSpecifiedRadius():
     # 2) Create a new entity with required vegetation area components
     spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
     asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
+                                                                               asset_path)
 
     # 3) Create a surface to plant on
     surface_center_point = math.Vector3(512.0, 512.0, 32.0)

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius.py

@@ -70,8 +70,8 @@ def DistanceBetweenFilter_InstancesPlantAtSpecifiedRadius():
     # 2) Create a new entity with required vegetation area components
     spawner_center_point = math.Vector3(520.0, 520.0, 32.0)
     asset_path = os.path.join("Slices", "1m_cube.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
+                                                                               asset_path)
 
     # 3) Create a surface to plant on
     surface_center_point = math.Vector3(512.0, 512.0, 32.0)

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynamicSliceInstanceSpawner_Embedded_E2E.py

@@ -80,7 +80,7 @@ def DynamicSliceInstanceSpawner_Embedded_E2E():
     # 2) Create a new entity with required vegetation area components and Script Canvas component for launcher test
     center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0, asset_path)
     spawner_entity.add_component("Script Canvas")
     instance_counter_path = os.path.join("scriptcanvas", "instance_counter.scriptcanvas")
     instance_counter_script = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", instance_counter_path,

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/InstanceSpawnerPriority_LayerAndSubPriority.py

@@ -70,8 +70,8 @@ def InstanceSpawnerPriority_LayerAndSubPriority():
     # 2) Create overlapping areas: 1 instance spawner area, and 1 blocker area
     spawner_center_point = math.Vector3(508.0, 508.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 1.0,
+                                                                               asset_path)
     blocker_center_point = math.Vector3(516.0, 516.0, 32.0)
     blocker_entity = dynveg.create_blocker_area("Instance Blocker", blocker_center_point, 16.0, 16.0, 1.0)
 

+ 8 - 8
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlender_E2E_Editor.py

@@ -86,17 +86,17 @@ def LayerBlender_E2E_Editor():
     # 2) Create 2 vegetation areas with different meshes
     purple_position = math.Vector3(504.0, 512.0, 32.0)
     purple_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity_1 = dynveg.create_vegetation_area("Purple Spawner",
-                                                     purple_position,
-                                                     16.0, 16.0, 1.0,
-                                                     purple_asset_path)
+    spawner_entity_1 = dynveg.create_dynamic_slice_vegetation_area("Purple Spawner",
+                                                                                 purple_position,
+                                                                                 16.0, 16.0, 1.0,
+                                                                                 purple_asset_path)
 
     pink_position = math.Vector3(520.0, 512.0, 32.0)
     pink_asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity_2 = dynveg.create_vegetation_area("Pink Spawner",
-                                                     pink_position,
-                                                     16.0, 16.0, 1.0,
-                                                     pink_asset_path)
+    spawner_entity_2 = dynveg.create_dynamic_slice_vegetation_area("Pink Spawner",
+                                                                                 pink_position,
+                                                                                 16.0, 16.0, 1.0,
+                                                                                 pink_asset_path)
 
     base_position = math.Vector3(512.0, 512.0, 32.0)
     dynveg.create_surface_entity("Surface Entity",

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerBlocker_InstancesBlockedInConfiguredArea.py

@@ -68,8 +68,8 @@ def LayerBlocker_InstancesBlockedInConfiguredArea():
     # 2) Create a new instance spawner entity
     spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
+                                                                               asset_path)
 
     # 3) Create surface for planting on
     dynveg.create_surface_entity("Surface Entity", spawner_center_point, 32.0, 32.0, 1.0)

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_FilterStageToggle.py

@@ -51,7 +51,7 @@ def LayerSpawner_FilterStageToggle():
     # Create a vegetation area with all needed components
     position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    vegetation_entity = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
+    vegetation_entity = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
     vegetation_entity.add_component("Vegetation Altitude Filter")
     vegetation_entity.add_component("Vegetation Position Modifier")
 

+ 4 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesPlantInAllSupportedShapes.py

@@ -62,10 +62,10 @@ def LayerSpawner_InstancesPlantInAllSupportedShapes():
     # 2) Create basic vegetation area entity and set the properties
     entity_position = math.Vector3(125.0, 136.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    vegetation = dynveg.create_vegetation_area("Instance Spawner",
-                                               entity_position,
-                                               10.0, 10.0, 10.0,
-                                               asset_path)
+    vegetation = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
+                                                                           entity_position,
+                                                                           10.0, 10.0, 10.0,
+                                                                           asset_path)
     vegetation.remove_component("Box Shape")
     vegetation.add_component("Shape Reference")
 

+ 4 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/LayerSpawner_InstancesRefreshUsingCorrectViewportCamera.py

@@ -101,10 +101,10 @@ def LayerSpawner_InstancesRefreshUsingCorrectViewportCamera():
 
     # Create the two vegetation areas
     test_slice_asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    first_veg_entity = dynveg.create_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size,
-                                                     box_size, test_slice_asset_path)
-    second_veg_entity = dynveg.create_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size,
-                                                      box_size, test_slice_asset_path)
+    first_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 1", first_entity_center_point, box_size, box_size,
+                                                                                 box_size, test_slice_asset_path)
+    second_veg_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area 2", second_entity_center_point, box_size, box_size,
+                                                                                  box_size, test_slice_asset_path)
 
     # When the first viewport is active, the first area should be full of instances, and the second should be empty
     general.set_active_viewport(0)

+ 4 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMesh.py

@@ -59,10 +59,10 @@ def MeshBlocker_InstancesBlockedByMesh():
     # Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
-                                                   entity_position,
-                                                   10.0, 10.0, 10.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
+                                                                               entity_position,
+                                                                               10.0, 10.0, 10.0,
+                                                                               asset_path)
 
     # Create surface entity to plant on
     dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)

+ 4 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/MeshBlocker_InstancesBlockedByMeshHeightTuning.py

@@ -62,10 +62,10 @@ def MeshBlocker_InstancesBlockedByMeshHeightTuning():
     # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
-                                                   entity_position,
-                                                   10.0, 10.0, 10.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
+                                                                               entity_position,
+                                                                               10.0, 10.0, 10.0,
+                                                                               asset_path)
 
     # 3) Create surface entity to plant on
     dynveg.create_surface_entity("Surface Entity", entity_position, 10.0, 10.0, 1.0)

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PhysXColliderSurfaceTagEmitter_E2E_Editor.py

@@ -91,7 +91,7 @@ def PhysXColliderSurfaceTagEmitter_E2E_Editor():
 
     # Create a new entity with required vegetation area components
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Veg Area", entity_center_point, 32.0, 32.0, 32.0, asset_path)
 
     # Add a Vegetation Surface Mask Filter component to the spawner entity and set it to include the "test" tag
     spawner_entity.add_component("Vegetation Surface Mask Filter")

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_AutoSnapToSurfaceWorks.py

@@ -74,8 +74,8 @@ def PositionModifier_AutoSnapToSurfaceWorks():
     # 2) Create a new entity with required vegetation area components and a Position Modifier
     spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
+                                                                               asset_path)
 
     # Add a Vegetation Position Modifier and set offset values to 0
     spawner_entity.add_component("Vegetation Position Modifier")

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets.py

@@ -111,7 +111,7 @@ def PositionModifier_ComponentAndOverrides_InstancesPlantAtSpecifiedOffsets():
     # 2) Create a new entity with required vegetation area components
     spawner_center_point = math.Vector3(16.0, 16.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 1.0, 1.0, 1.0, asset_path)
 
     # Add a Vegetation Position Modifier and set offset values to 0
     spawner_entity.add_component("Vegetation Position Modifier")

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifierOverrides_InstancesRotateWithinRange.py

@@ -88,7 +88,7 @@ def RotationModifierOverrides_InstancesRotateWithinRange():
     # 2) Create vegetation entity and add components
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0, asset_path)
     spawner_entity.add_component("Vegetation Rotation Modifier")
     # Our default vegetation settings places 20 instances per 16 meters, so we expect 20 * 20 total instances.
     num_expected = 20 * 20

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/RotationModifier_InstancesRotateWithinRange.py

@@ -126,7 +126,7 @@ def RotationModifier_InstancesRotateWithinRange():
 
     # 2) Set up vegetation entities
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", LEVEL_CENTER, 2.0, 2.0, 2.0, asset_path)
 
     additional_components = [
         "Vegetation Rotation Modifier"

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifierOverrides_InstancesProperlyScale.py

@@ -100,7 +100,7 @@ def ScaleModifierOverrides_InstancesProperlyScale():
     # 2) Create a new entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 10.0, asset_path)
 
     # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes
     dynveg.create_surface_entity("Surface Entity", entity_position, 20.0, 20.0, 1.0)

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ScaleModifier_InstancesProperlyScale.py

@@ -94,8 +94,8 @@ def ScaleModifier_InstancesProperlyScale():
     # Vegetation Scale Modifier
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Entity", entity_position, 16.0, 16.0, 16.0,
+                                                                               asset_path)
     spawner_entity.add_component("Vegetation Scale Modifier")
 
     # Create a surface to plant on and add a Vegetation Debugger Level component to allow refreshes

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_FilterStageToggle.py

@@ -57,7 +57,7 @@ def ShapeIntersectionFilter_FilterStageToggle():
     # Create basic vegetation entity
     position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
+    vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 16.0, asset_path)
 
     # Create Surface for instances to plant on
     dynveg.create_surface_entity("Surface_Entity_Parent", position, 16.0, 16.0, 1.0)

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/ShapeIntersectionFilter_InstancesPlantInAssignedShape.py

@@ -70,8 +70,8 @@ def ShapeIntersectionFilter_InstancesPlantInAssignedShape():
     # 2) Create a new entity with required vegetation area components and Vegetation Shape Intersection Filter
     center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 1.0,
+                                                                               asset_path)
     spawner_entity.add_component("Vegetation Shape Intersection Filter")
 
     # Create a planting surface

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment.py

@@ -66,7 +66,7 @@ def SlopeAlignmentModifierOverrides_InstanceSurfaceAlignment():
     # Create a spawner entity setup with all needed components
     center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
 
     # Create a sloped mesh surface for the instances to plant on
     center_point = math.Vector3(502.0, 512.0, 24.0)

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeAlignmentModifier_InstanceSurfaceAlignment.py

@@ -67,7 +67,7 @@ def SlopeAlignmentModifier_InstanceSurfaceAlignment():
     # Create a spawner entity setup with all needed components
     center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 16.0, 16.0, 32.0, asset_path)
 
     # Create a sloped mesh surface for the instances to plant on
     center_point = math.Vector3(502.0, 512.0, 24.0)

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlope.py

@@ -72,7 +72,7 @@ def SlopeFilter_ComponentAndOverrides_InstancesPlantOnValidSlopes():
     # 2) Create a new entity with required vegetation area components
     center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", center_point, 32.0, 32.0, 32.0, asset_path)
 
     # Add a Vegetation Slope Filter
     spawner_entity.add_component("Vegetation Slope Filter")

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SpawnerSlices_SliceCreationAndVisibilityToggleWorks.py

@@ -66,7 +66,7 @@ def SpawnerSlices_SliceCreationAndVisibilityToggleWorks():
     # 2.1) Create basic vegetation entity
     position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    veg_1 = dynveg.create_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path)
+    veg_1 = dynveg.create_dynamic_slice_vegetation_area("vegetation_1", position, 16.0, 16.0, 16.0, asset_path)
 
     # 2.2) Create slice from the entity
     slice_path = os.path.join("slices", "TestSlice_1.slice")
@@ -94,7 +94,7 @@ def SpawnerSlices_SliceCreationAndVisibilityToggleWorks():
 
     # 4) C2627905 A slice containing the Vegetation Layer Blender component can be created.
     # 4.1) Create another vegetation entity to add to blender component
-    veg_2 = dynveg.create_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "")
+    veg_2 = dynveg.create_dynamic_slice_vegetation_area("vegetation_2", position, 1.0, 1.0, 1.0, "")
 
     # 4.2) Create entity with Vegetation Layer Blender
     components_to_add = ["Box Shape", "Vegetation Layer Blender"]

+ 2 - 2
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected.py

@@ -77,8 +77,8 @@ def SurfaceMaskFilterOverrides_MultipleDescriptorOverridesPlantAsExpected():
     # 2) Create a new instance spawner entity with multiple Dynamic Slice Instance Spawner descriptors
     spawner_center_point = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner", spawner_center_point, 16.0, 16.0, 16.0,
+                                                                               asset_path)
     asset_list_component = spawner_entity.components[2]
     desc_asset = hydra.get_component_property_value(asset_list_component,
                                                     "Configuration|Embedded Assets")[0]

+ 4 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_ExclusionList.py

@@ -99,10 +99,10 @@ def SurfaceMaskFilter_ExclusionList():
     # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
-                                                   entity_position,
-                                                   10.0, 10.0, 10.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
+                                                                               entity_position,
+                                                                               10.0, 10.0, 10.0,
+                                                                               asset_path)
 
     # 3) Add a Vegetation Surface Mask Filter component to the entity.
     spawner_entity.add_component("Vegetation Surface Mask Filter")

+ 4 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SurfaceMaskFilter_InclusionList.py

@@ -100,10 +100,10 @@ def SurfaceMaskFilter_InclusionList():
     # 2) Create entity with components "Vegetation Layer Spawner", "Vegetation Asset List", "Box Shape"
     entity_position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Instance Spawner",
-                                                   entity_position,
-                                                   10.0, 10.0, 10.0,
-                                                   asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Instance Spawner",
+                                                                               entity_position,
+                                                                               10.0, 10.0, 10.0,
+                                                                               asset_path)
 
     # 3) Add a Vegetation Surface Mask Filter component to the entity.
     spawner_entity.add_component("Vegetation Surface Mask Filter")

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorPointDensity.py

@@ -55,7 +55,7 @@ def SystemSettings_SectorPointDensity():
     # Create basic vegetation entity
     position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
+    dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
     dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
 
     # Count the number of vegetation instances in the vegetation area

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/SystemSettings_SectorSize.py

@@ -51,7 +51,7 @@ def SystemSettings_SectorSize():
     # Create basic vegetation entity
     position = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PinkFlower.dynamicslice")
-    vegetation = dynveg.create_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
+    vegetation = dynveg.create_dynamic_slice_vegetation_area("vegetation", position, 16.0, 16.0, 1.0, asset_path)
     dynveg.create_surface_entity("Surface_Entity", position, 16.0, 16.0, 1.0)
 
     # Add the Vegetation Debugger component to the Level Inspector

+ 1 - 1
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/VegetationInstances_DespawnWhenOutOfRange.py

@@ -54,7 +54,7 @@ def VegetationInstances_DespawnWhenOutOfRange():
     # Create vegetation layer spawner
     world_center = math.Vector3(512.0, 512.0, 32.0)
     asset_path = os.path.join("Slices", "PurpleFlower.dynamicslice")
-    spawner_entity = dynveg.create_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path)
+    spawner_entity = dynveg.create_dynamic_slice_vegetation_area("Spawner Instance", world_center, 16.0, 16.0, 16.0, asset_path)
 
     # Create a surface to spawn on
     dynveg.create_surface_entity("Spawner Entity", world_center, 16.0, 16.0, 1.0)

+ 21 - 16
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Main_Optimized.py

@@ -16,6 +16,20 @@ from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, E
 @pytest.mark.parametrize("launcher_platform", ['windows_editor'])
 @pytest.mark.parametrize("project", ["AutomatedTesting"])
 class TestAutomation(EditorTestSuite):
+    class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
+        from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
+    
+    class test_AltitudeFilter_FilterStageToggle(EditorParallelTest):
+        from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
+
+    class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
+        from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
+
+
[email protected]_main
[email protected]("launcher_platform", ['windows_editor'])
[email protected]("project", ["AutomatedTesting"])
+class TestAutomation_PrefabNotEnabled(EditorTestSuite):
 
     enable_prefab_system = False
 
@@ -36,19 +50,10 @@ class TestAutomation(EditorTestSuite):
     class test_EmptyInstanceSpawner_EmptySpawnerWorks(EditorParallelTest):
         from .EditorScripts import EmptyInstanceSpawner_EmptySpawnerWorks as test_module
 
-    class test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
-        from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
-
-    class test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(EditorParallelTest):
-        from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
-
-    class test_AltitudeFilter_FilterStageToggle(EditorParallelTest):
-        from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
-
     class test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(EditorSingleTest):
         # Custom teardown to remove slice asset created during test
         def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_slices(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_slices(self, workspace)
         from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module
 
     class test_AssetListCombiner_CombinedDescriptorsExpressInConfiguredArea(EditorParallelTest):
@@ -161,27 +166,27 @@ class TestAutomation(EditorTestSuite):
 
         # Custom setup/teardown to remove test level created during test
         def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_level(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
 
         def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_level(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
 
     class test_DynamicSliceInstanceSpawner_External_E2E_Editor(EditorSingleTest):
         from .EditorScripts import DynamicSliceInstanceSpawner_External_E2E as test_module
 
         # Custom setup/teardown to remove test level created during test
         def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_level(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
 
         def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_level(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
 
     class test_LayerBlender_E2E_Editor(EditorSingleTest):
         from .EditorScripts import LayerBlender_E2E_Editor as test_module
 
         # Custom setup/teardown to remove test level created during test
         def setup(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_level(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)
 
         def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
-            TestAutomation.cleanup_test_level(self, workspace)
+            TestAutomation_PrefabNotEnabled.cleanup_test_level(self, workspace)

+ 3 - 3
AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic.py

@@ -52,15 +52,15 @@ class TestAutomation(TestAutomationBase):
 
     def test_AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
         from .EditorScripts import AltitudeFilter_ComponentAndOverrides_InstancesPlantAtSpecifiedAltitude as test_module
-        self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
+        self._run_test(request, workspace, editor, test_module)
 
     def test_AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude(self, request, workspace, editor, launcher_platform):
         from .EditorScripts import AltitudeFilter_ShapeSample_InstancesPlantAtSpecifiedAltitude as test_module
-        self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
+        self._run_test(request, workspace, editor, test_module)
 
     def test_AltitudeFilter_FilterStageToggle(self, request, workspace, editor, launcher_platform):
         from .EditorScripts import AltitudeFilter_FilterStageToggle as test_module
-        self._run_test(request, workspace, editor, test_module, enable_prefab_system=False)
+        self._run_test(request, workspace, editor, test_module)
 
     def test_SpawnerSlices_SliceCreationAndVisibilityToggleWorks(self, request, workspace, editor, remove_test_slice, launcher_platform):
         from .EditorScripts import SpawnerSlices_SliceCreationAndVisibilityToggleWorks as test_module

+ 40 - 4
AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py

@@ -8,14 +8,16 @@
 
 import os
 import sys
+from pathlib import Path
 
+import azlmbr.areasystem as areasystem
 import azlmbr.asset as asset
 import azlmbr.bus as bus
 import azlmbr.components as components
 import azlmbr.math as math
-import azlmbr.vegetation as vegetation
-import azlmbr.areasystem as areasystem
 import azlmbr.paths
+import azlmbr.prefab as prefab
+import azlmbr.vegetation as vegetation
 
 sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests'))
 import editor_python_test_tools.hydra_editor_utils as hydra
@@ -91,7 +93,7 @@ def create_mesh_surface_entity_with_slopes(name, center_point, uniform_scale):
     return surface_entity
 
 
-def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, dynamic_slice_asset_path):
+def create_dynamic_slice_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, dynamic_slice_asset_path):
     # Create a vegetation area entity to use as our test vegetation spawner
     spawner_entity = hydra.Entity(name)
     spawner_entity.create_entity(
@@ -104,14 +106,48 @@ def create_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_
                                                                                           box_size_z))
 
     # Set the vegetation area to a Dynamic Slice spawner with a specific slice asset selected
+    descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
     dynamic_slice_spawner = vegetation.DynamicSliceInstanceSpawner()
     dynamic_slice_spawner.SetSliceAssetPath(dynamic_slice_asset_path)
-    descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
     descriptor.spawner = dynamic_slice_spawner
     spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
     return spawner_entity
 
 
+def create_prefab_vegetation_area(name, center_point, box_size_x, box_size_y, box_size_z, target_prefab):
+    # Create a vegetation area entity to use as our test vegetation spawner
+    spawner_entity = hydra.Entity(name)
+    spawner_entity.create_entity(
+        center_point,
+        ["Vegetation Layer Spawner", "Box Shape", "Vegetation Asset List"]
+        )
+    if spawner_entity.id.IsValid():
+        print(f"'{spawner_entity.name}' created")
+    spawner_entity.get_set_test(1, "Box Shape|Box Configuration|Dimensions", math.Vector3(box_size_x, box_size_y,
+                                                                                          box_size_z))
+    # Get the in-memory spawnable asset id if exists
+    spawnable_name = Path(target_prefab.file_path).stem
+    spawnable_asset_id = prefab.PrefabPublicRequestBus(bus.Broadcast, 'GetInMemorySpawnableAssetId', 
+                                                      spawnable_name)
+
+    # Create the in-memory spawnable asset from given prefab if the spawnable does not exist
+    if not spawnable_asset_id.is_valid():
+        create_spawnable_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreateInMemorySpawnableAsset', 
+                                                                target_prefab.file_path, 
+                                                                spawnable_name)
+        assert create_spawnable_result.IsSuccess(), \
+            f"Prefab operation 'CreateInMemorySpawnableAssets' failed. Error: {create_spawnable_result.GetError()}"
+        spawnable_asset_id = create_spawnable_result.GetValue()
+
+    # Set the vegetation area to a prefab instance spawner with a specific prefab asset selected
+    descriptor = hydra.get_component_property_value(spawner_entity.components[2], 'Configuration|Embedded Assets|[0]')
+    prefab_spawner = vegetation.PrefabInstanceSpawner()
+    prefab_spawner.SetPrefabAssetId(spawnable_asset_id)
+    descriptor.spawner = prefab_spawner
+    spawner_entity.get_set_test(2, "Configuration|Embedded Assets|[0]", descriptor)
+    return spawner_entity
+
+
 def create_blocker_area(name, center_point, box_size_x, box_size_y, box_size_z):
     # Create a Vegetation Layer Blocker area
     blocker_entity = hydra.Entity(name)

+ 2 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h

@@ -15,6 +15,7 @@
 #include <AzCore/std/string/string.h>
 
 #include <AzToolsFramework/Prefab/Instance/Instance.h>
+#include <AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.h>
 
 namespace AzToolsFramework
 {
@@ -47,7 +48,7 @@ namespace AzToolsFramework
 
         //! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G)
         //! /return The vector of Assets generated by Prefab processing
-        virtual const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() = 0;
+        virtual const Prefab::PrefabConversionUtils::InMemorySpawnableAssetContainer::SpawnableAssets& GetPlayInEditorAssetData() const = 0;
 
         virtual bool LoadFromStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;
         virtual bool SaveToStream(AZ::IO::GenericStream& stream, AZStd::string_view filename) = 0;

+ 38 - 159
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp

@@ -25,6 +25,7 @@
 #include <AzToolsFramework/Prefab/PrefabLoader.h>
 #include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
 #include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
+#include <AzToolsFramework/Prefab/Spawnable/PrefabConverterStackProfileNames.h>
 
 namespace AzToolsFramework
 {
@@ -379,9 +380,9 @@ namespace AzToolsFramework
         return m_rootInstance ? m_rootInstance->GetTemplateId() : Prefab::InvalidTemplateId;
     }
 
-    const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
+    const Prefab::PrefabConversionUtils::InMemorySpawnableAssetContainer::SpawnableAssets& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData() const
     {
-        return m_playInEditorData.m_assets;
+        return m_playInEditorData.m_assetsCache.GetAllInMemorySpawnableAssets();
     }
 
     void PrefabEditorEntityOwnershipService::OnEntityRemoved(AZ::EntityId entityId)
@@ -405,176 +406,55 @@ namespace AzToolsFramework
         m_validateEntitiesCallback = AZStd::move(validateEntitiesCallback);
     }
 
-    void PrefabEditorEntityOwnershipService::LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets)
+    void PrefabEditorEntityOwnershipService::StartPlayInEditor()
     {
-        // Start our loads on all assets by calling GetAsset from the AssetManager
-        for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
-        {
-            if (!asset.GetId().IsValid())
-            {
-                AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
-                continue;
-            }
-
-            const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
-
-            if (loadBehavior == AZ::Data::AssetLoadBehavior::NoLoad)
-            {
-                continue;
-            }
-
-            AZ::Data::AssetId assetId = asset.GetId();
-            AZ::Data::AssetType assetType = asset.GetType();
-
-            asset = AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, loadBehavior);
-
-            if (!asset.GetId().IsValid())
-            {
-                AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
-                continue;
-            }
-        }
+        // This is a workaround until the replacement for GameEntityContext is done
+        AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
 
-        // For all Preload assets we block until they're ready
-        // We do this as a seperate pass so that we don't interrupt queuing up all other asset loads
-        for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
+        if (m_rootInstance && !m_playInEditorData.m_isEnabled)
         {
-            if (!asset.GetId().IsValid())
+            // Construct the runtime entities and products
+            bool readyToCreateRootSpawnable = m_playInEditorData.m_assetsCache.IsActivated();
+            if (!readyToCreateRootSpawnable &&
+                !m_playInEditorData.m_assetsCache.Activate(Prefab::PrefabConversionUtils::PlayInEditor))
             {
-                AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
-                continue;
+                AZ_Error("Prefab", false, "Failed to create a prefab processing stack from key '%.*s'.", AZ_STRING_ARG(Prefab::PrefabConversionUtils::PlayInEditor));
+                return;
             }
 
-            const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
-
-            if (loadBehavior != AZ::Data::AssetLoadBehavior::PreLoad)
+            auto createRootSpawnableResult = m_playInEditorData.m_assetsCache.CreateInMemorySpawnableAsset(m_rootInstance->GetTemplateId(), DefaultMainSpawnableName, true);
+            if (createRootSpawnableResult.IsSuccess())
             {
-                continue;
-            }
+                // make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of 
+                // (load asset) -> (notify) -> (init) -> (activate)
+                AZ::Data::AssetManager::Instance().DispatchEvents();
 
-            asset.BlockUntilLoadComplete();
+                m_playInEditorData.m_entities.Reset(createRootSpawnableResult.GetValue());
+                m_playInEditorData.m_entities.SpawnAllEntities();
 
-            if (asset.IsError())
+                // This is a workaround until the replacement for GameEntityContext is done
+                AzFramework::GameEntityContextEventBus::Broadcast(
+                    &AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
+            }
+            else
             {
-                AZ_Error("Prefab", false, "Asset with id %s failed to preload while entering game mode",
-                    asset.GetId().ToString<AZStd::string>().c_str());
-
-                continue;
+                AZ_Error("Prefab", false, "Failed to create the root spawnable due to the error: '%.*s'", AZ_STRING_ARG(createRootSpawnableResult.GetError()));
+                return;
             }
-        }
-    }
-
-    void PrefabEditorEntityOwnershipService::StartPlayInEditor()
-    {
-        // This is a workaround until the replacement for GameEntityContext is done
-        AzFramework::GameEntityContextEventBus::Broadcast(&AzFramework::GameEntityContextEventBus::Events::OnPreGameEntitiesStarted);
 
-        if (m_rootInstance && !m_playInEditorData.m_isEnabled)
-        {
-            // Construct the runtime entities and products 
-            Prefab::TemplateReference templateReference = m_prefabSystemComponent->FindTemplate(m_rootInstance->GetTemplateId());
-            if (templateReference.has_value())
+            m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr<AZ::Entity>& entity)
             {
-                bool converterLoaded = m_playInEditorData.m_converter.IsLoaded();
-                if (!converterLoaded)
+                AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
+                if (entity->GetState() == AZ::Entity::State::Active)
                 {
-                    converterLoaded = m_playInEditorData.m_converter.LoadStackProfile("PlayInEditor");
-                }
-                if (converterLoaded)
-                {
-                    // Use a random uuid as this is only a temporary source.
-                    AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext context(AZ::Uuid::CreateRandom());
-                    Prefab::PrefabDom copy;
-                    copy.CopyFrom(templateReference->get().GetPrefabDom(), copy.GetAllocator(), false);
-                    context.AddPrefab(DefaultMainSpawnableName, AZStd::move(copy));
-                    m_playInEditorData.m_converter.ProcessPrefab(context);
-                    if (context.HasCompletedSuccessfully() && !context.GetProcessedObjects().empty())
-                    {
-                        static constexpr size_t NoRootSpawnable = AZStd::numeric_limits<size_t>::max();
-                        size_t rootSpawnableIndex = NoRootSpawnable;
-
-                        // Create temporary assets from the processed data.
-                        for (auto& product : context.GetProcessedObjects())
-                        {
-                            if (product.GetAssetType() == AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid() &&
-                                product.GetId() ==
-                                    AZStd::string::format("%s.%s", DefaultMainSpawnableName, AzFramework::Spawnable::FileExtension))
-                            {
-                                rootSpawnableIndex = m_playInEditorData.m_assets.size();
-                            }
-
-                            AZ::Data::AssetInfo info;
-                            info.m_assetId = product.GetAsset().GetId();
-                            info.m_assetType = product.GetAssetType();
-                            info.m_relativePath = product.GetId();
-
-                            AZ::Data::AssetCatalogRequestBus::Broadcast(
-                                &AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
-                            m_playInEditorData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
-
-                            // Ensure the product asset is registered with the AssetManager
-                            // Hold on to the returned asset to keep ref count alive until we assign it the latest data
-                            AZ::Data::Asset<AZ::Data::AssetData> asset =
-                                AZ::Data::AssetManager::Instance().FindOrCreateAsset(info.m_assetId, info.m_assetType, AZ::Data::AssetLoadBehavior::Default);
-
-                            // Update the asset registered in the AssetManager with the data of our product from the Prefab Processor
-                            AZ::Data::AssetManager::Instance().AssignAssetData(m_playInEditorData.m_assets.back());
-                        }
-
-                        for (auto& product : context.GetProcessedObjects())
-                        {
-                            LoadReferencedAssets(product.GetReferencedAssets());
-                        }
-
-                        // make sure that PRE_NOTIFY assets get their notify before we activate, so that we can preserve the order of 
-                        // (load asset) -> (notify) -> (init) -> (activate)
-                        AZ::Data::AssetManager::Instance().DispatchEvents();
-
-                        if (rootSpawnableIndex != NoRootSpawnable)
-                        {
-                            m_playInEditorData.m_entities.Reset(m_playInEditorData.m_assets[rootSpawnableIndex]);
-                            m_playInEditorData.m_entities.SpawnAllEntities();
-                        }
-                        else
-                        {
-                            AZ_Error("Prefab", false,
-                                "Processing of the level prefab failed to produce a root spawnable while entering game mode. "
-                                "Unable to fully enter game mode.");
-                            return;
-                        }
-
-                        // This is a workaround until the replacement for GameEntityContext is done
-                        AzFramework::GameEntityContextEventBus::Broadcast(
-                            &AzFramework::GameEntityContextEventBus::Events::OnGameEntitiesStarted);
-                    }
-                    else
-                    {
-                        AZ_Error("Prefab", false,
-                            "Failed to convert the prefab into assets. "
-                            "Confirm that the 'PlayInEditor' prefab processor stack is capable of producing a useable product asset.");
-                        return;
-                    }
-                }
-                else
-                {
-                    AZ_Error("Prefab", false, "Failed to create a prefab processing stack from key 'PlayInEditor'.");
-                    return;
+                    entity->Deactivate();
+                    m_playInEditorData.m_deactivatedEntities.push_back(entity.get());
                 }
+                return true;
+            });
 
-                m_rootInstance->GetAllEntitiesInHierarchy([this](AZStd::unique_ptr<AZ::Entity>& entity)
-                    {
-                        AZ_Assert(entity, "Invalid entity found in root instance while starting play in editor.");
-                        if (entity->GetState() == AZ::Entity::State::Active)
-                        {
-                            entity->Deactivate();
-                            m_playInEditorData.m_deactivatedEntities.push_back(entity.get());
-                        }
-                        return true;
-                    });
-            }
+            m_playInEditorData.m_isEnabled = true;
         }
-
-        m_playInEditorData.m_isEnabled = true;
     }
 
     void PrefabEditorEntityOwnershipService::StopPlayInEditor()
@@ -588,7 +468,7 @@ namespace AzToolsFramework
 
             m_playInEditorData.m_entities.DespawnAllEntities();
             m_playInEditorData.m_entities.Alert(
-                [assets = AZStd::move(m_playInEditorData.m_assets),
+                [allSpawnableAssetData = m_playInEditorData.m_assetsCache.MoveAllInMemorySpawnableAssets(),
                  deactivatedEntities = AZStd::move(m_playInEditorData.m_deactivatedEntities)]([[maybe_unused]] uint32_t generation) mutable
                 {
                     auto end = deactivatedEntities.rend();
@@ -598,11 +478,10 @@ namespace AzToolsFramework
                         (*it)->Activate();
                     }
 
-                    for (auto& asset : assets)
+                    for (auto& [spawnableName, spawnableAssetData] : allSpawnableAssetData)
                     {
-                        if (asset)
+                        for (auto& asset : spawnableAssetData.m_assets)
                         {
-                            // Explicitly release because this needs to happen before the asset is unregistered.
                             asset.Release();
                             AZ::Data::AssetCatalogRequestBus::Broadcast(
                                 &AZ::Data::AssetCatalogRequestBus::Events::UnregisterAsset, asset.GetId());

+ 4 - 7
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h

@@ -14,7 +14,7 @@
 #include <AzFramework/Spawnable/SpawnableEntitiesContainer.h>
 #include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
 #include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
-#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
+#include <AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.h>
 
 namespace AzToolsFramework
 {
@@ -176,8 +176,7 @@ namespace AzToolsFramework
     private:
         struct PlayInEditorData
         {
-            AzToolsFramework::Prefab::PrefabConversionUtils::PrefabConversionPipeline m_converter;
-            AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>> m_assets;
+            AzToolsFramework::Prefab::PrefabConversionUtils::InMemorySpawnableAssetContainer m_assetsCache;
             AZStd::vector<AZ::Entity*> m_deactivatedEntities;
             AzFramework::SpawnableEntitiesContainer m_entities;
             bool m_isEnabled{ false };
@@ -195,14 +194,12 @@ namespace AzToolsFramework
 
         Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
         Prefab::TemplateId GetRootPrefabTemplateId() override;
-        
-        const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
+
+        const Prefab::PrefabConversionUtils::InMemorySpawnableAssetContainer::SpawnableAssets& GetPlayInEditorAssetData() const override;
         //////////////////////////////////////////////////////////////////////////
 
         void OnEntityRemoved(AZ::EntityId entityId);
 
-        void LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets);
-
         OnEntitiesAddedCallback m_entitiesAddedCallback;
         OnEntitiesRemovedCallback m_entitiesRemovedCallback;
         ValidateEntitiesCallback m_validateEntitiesCallback;

+ 30 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestBus.h

@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include <AzCore/Asset/AssetCommon.h>
 #include <AzCore/Component/EntityId.h>
 #include <AzCore/EBus/EBus.h>
 #include <AzCore/IO/Path/Path.h>
@@ -27,6 +28,7 @@ namespace AzToolsFramework
         using InstantiatePrefabResult = AZ::Outcome<AZ::EntityId, AZStd::string>;
         using DuplicatePrefabResult = AZ::Outcome<EntityIdList, AZStd::string>;
         using PrefabOperationResult = AZ::Outcome<void, AZStd::string>;
+        using CreateSpawnableResult = AZ::Outcome<AZ::Data::AssetId, AZStd::string>;
 
         /**
         * The primary purpose of this bus is to facilitate writing automated tests for prefabs.
@@ -93,6 +95,34 @@ namespace AzToolsFramework
              * Returns the path to the prefab, or an empty path if the entity is owned by the level.
              */
             virtual AZStd::string GetOwningInstancePrefabPath(AZ::EntityId entityId) const = 0;
+
+            /**
+             * Convert a prefab on given file path with given name to in-memory spawnable asset. 
+             * Returns the asset id of the produced spawnable if creation succeeded;
+             * on failure, it comes with an error message detailing the cause of the error.
+             */
+            virtual CreateSpawnableResult CreateInMemorySpawnableAsset(AZStd::string_view prefabFilePath, AZStd::string_view spawnableName) = 0;
+
+            /**
+             * Remove in-memory spawnable asset with given name.
+             * Return an outcome object; on failure, it comes with an error message detailing the cause of the error.
+             */
+            virtual PrefabOperationResult RemoveInMemorySpawnableAsset(AZStd::string_view spawnableName) = 0;
+
+            /**
+             * Return whether an in-memory spawnable with given name exists or not.
+             */
+            virtual bool HasInMemorySpawnableAsset(AZStd::string_view spawnableName) const = 0;
+
+            /**
+             * Return an asset id of a spawnalbe with given name. Invalid asset id will be returned if the spawnable doesn't exist.
+             */
+            virtual AZ::Data::AssetId GetInMemorySpawnableAssetId(AZStd::string_view spawnableName) const = 0;
+
+            /**
+             * Remove all the in-memory spawnable assets.
+             */
+            virtual void RemoveAllInMemorySpawnableAssets() = 0;
         };
 
         using PrefabPublicRequestBus = AZ::EBus<PrefabPublicRequests>;

+ 73 - 4
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.cpp

@@ -8,10 +8,16 @@
 
 #include <AzToolsFramework/Prefab/PrefabPublicRequestHandler.h>
 
-#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
-
+#include <AzCore/Asset/AssetManager.h>
+#include <AzCore/Asset/AssetManagerBus.h>
 #include <AzCore/RTTI/BehaviorContext.h>
 
+#include <AzToolsFramework/Prefab/PrefabLoader.h>
+#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
+#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
+#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
+#include <AzToolsFramework/Prefab/Spawnable/PrefabConverterStackProfileNames.h>
+
 namespace AzToolsFramework
 {
     namespace Prefab
@@ -31,13 +37,18 @@ namespace AzToolsFramework
                     ->Event("DetachPrefab", &PrefabPublicRequests::DetachPrefab)
                     ->Event("DuplicateEntitiesInInstance", &PrefabPublicRequests::DuplicateEntitiesInInstance)
                     ->Event("GetOwningInstancePrefabPath", &PrefabPublicRequests::GetOwningInstancePrefabPath)
+                    ->Event("CreateInMemorySpawnableAsset", &PrefabPublicRequests::CreateInMemorySpawnableAsset)
+                    ->Event("RemoveInMemorySpawnableAsset", &PrefabPublicRequests::RemoveInMemorySpawnableAsset)
+                    ->Event("HasInMemorySpawnableAsset", &PrefabPublicRequests::HasInMemorySpawnableAsset)
+                    ->Event("GetInMemorySpawnableAssetId", &PrefabPublicRequests::GetInMemorySpawnableAssetId)
+                    ->Event("RemoveAllInMemorySpawnableAssets", &PrefabPublicRequests::RemoveAllInMemorySpawnableAssets)
                     ;
             }
         }
 
         void PrefabPublicRequestHandler::Connect()
         {
-            m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
+            m_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
             AZ_Assert(m_prefabPublicInterface, "PrefabPublicRequestHandler - Could not retrieve instance of PrefabPublicInterface");
 
             PrefabPublicRequestBus::Handler::BusConnect();
@@ -46,7 +57,7 @@ namespace AzToolsFramework
         void PrefabPublicRequestHandler::Disconnect()
         {
             PrefabPublicRequestBus::Handler::BusDisconnect();
-
+            m_spawnableAssetContainer.Deactivate();
             m_prefabPublicInterface = nullptr;
         }
 
@@ -79,5 +90,63 @@ namespace AzToolsFramework
         {
             return m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId).Native();
         }
+
+        bool PrefabPublicRequestHandler::TryActivateSpawnableAssetContainer()
+        {
+            bool activated = m_spawnableAssetContainer.IsActivated();
+            if (!activated)
+            {
+                activated = m_spawnableAssetContainer.Activate(PrefabConversionUtils::IntegrationTests);
+            }
+
+            return activated;
+        }
+
+        CreateSpawnableResult PrefabPublicRequestHandler::CreateInMemorySpawnableAsset(AZStd::string_view prefabFilePath, AZStd::string_view spawnableName)
+        {
+            if (!TryActivateSpawnableAssetContainer())
+            {
+                return AZ::Failure(AZStd::string("Failed to activate Spawnable Asset Container"));
+            }
+
+            auto result = m_spawnableAssetContainer.CreateInMemorySpawnableAsset(prefabFilePath, spawnableName);
+            if (result.IsSuccess())
+            {
+                return AZ::Success(result.GetValue().GetId());
+            }
+            else
+            {
+                return AZ::Failure(result.TakeError());
+            }
+        }
+
+        PrefabOperationResult PrefabPublicRequestHandler::RemoveInMemorySpawnableAsset(AZStd::string_view spawnableName)
+        {
+            auto result = m_spawnableAssetContainer.RemoveInMemorySpawnableAsset(spawnableName);
+            if (result.IsSuccess())
+            {
+                return AZ::Success();
+            }
+            else
+            {
+                return AZ::Failure(result.TakeError());
+            }
+        }
+
+        bool PrefabPublicRequestHandler::HasInMemorySpawnableAsset(AZStd::string_view spawnableName) const
+        {
+            return m_spawnableAssetContainer.HasInMemorySpawnableAsset(spawnableName);
+        }
+
+        AZ::Data::AssetId PrefabPublicRequestHandler::GetInMemorySpawnableAssetId(AZStd::string_view spawnableName) const
+        {
+            return m_spawnableAssetContainer.GetInMemorySpawnableAssetId(spawnableName);
+        }
+
+        void PrefabPublicRequestHandler::RemoveAllInMemorySpawnableAssets()
+        {
+            m_spawnableAssetContainer.ClearAllInMemorySpawnableAssets();
+        }
+
     } // namespace Prefab
 } // namespace AzToolsFramework

+ 10 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicRequestHandler.h

@@ -12,6 +12,7 @@
 #include <AzCore/RTTI/RTTI.h>
 
 #include <AzToolsFramework/Prefab/PrefabPublicRequestBus.h>
+#include <AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.h>
 
 namespace AzToolsFramework
 {
@@ -37,9 +38,18 @@ namespace AzToolsFramework
             PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) override;
             DuplicatePrefabResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override;
             AZStd::string GetOwningInstancePrefabPath(AZ::EntityId entityId) const override;
+            CreateSpawnableResult CreateInMemorySpawnableAsset(AZStd::string_view prefabFilePath, AZStd::string_view spawnableName) override;
+            PrefabOperationResult RemoveInMemorySpawnableAsset(AZStd::string_view spawnableName) override;
+            bool HasInMemorySpawnableAsset(AZStd::string_view spawnableName) const override;
+            AZ::Data::AssetId GetInMemorySpawnableAssetId(AZStd::string_view spawnableName) const override;
+            void RemoveAllInMemorySpawnableAssets() override;
 
         private:
+            bool TryActivateSpawnableAssetContainer();
+
+            PrefabConversionUtils::InMemorySpawnableAssetContainer m_spawnableAssetContainer;
             PrefabPublicInterface* m_prefabPublicInterface = nullptr;
+
         };
     } // namespace Prefab
 } // namespace AzToolsFramework

+ 275 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.cpp

@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.h>
+
+#include <AzCore/Asset/AssetManager.h>
+#include <AzCore/Asset/AssetManagerBus.h>
+#include <AzToolsFramework/Prefab/PrefabLoader.h>
+#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
+#include <AzToolsFramework/Prefab/Spawnable/PrefabConverterStackProfileNames.h>
+
+namespace AzToolsFramework::Prefab::PrefabConversionUtils
+{
+    InMemorySpawnableAssetContainer::~InMemorySpawnableAssetContainer()
+    {
+        Deactivate();
+    }
+
+    bool InMemorySpawnableAssetContainer::Activate(AZStd::string_view stackProfile)
+    {
+        AZ_Assert(!IsActivated(),
+            "InMemorySpawnableAssetContainer - Unable to activate an instance of InMemorySpawnableAssetContainer as the instance is already active.");
+
+        m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
+        AZ_Assert(m_prefabSystemComponentInterface, "InMemorySpawnableAssetContainer - Could not retrieve instance of PrefabSystemComponentInterface");
+
+        m_loaderInterface = AZ::Interface<Prefab::PrefabLoaderInterface>::Get();
+        AZ_Assert(m_loaderInterface, "InMemorySpawnableAssetContainer - Could not retrieve instance of PrefabLoaderInterface");
+
+        m_stockProfile = stackProfile;
+        return m_converter.LoadStackProfile(m_stockProfile);
+    }
+
+    void InMemorySpawnableAssetContainer::Deactivate()
+    {
+        ClearAllInMemorySpawnableAssets();
+        m_stockProfile = "";
+        m_loaderInterface = nullptr;
+        m_prefabSystemComponentInterface = nullptr;
+    }
+
+    bool InMemorySpawnableAssetContainer::IsActivated() const
+    {
+        return m_converter.IsLoaded();
+    }
+
+    AZStd::string_view InMemorySpawnableAssetContainer::GetStockProfile() const
+    {
+        return m_stockProfile;
+    }
+
+    bool InMemorySpawnableAssetContainer::HasInMemorySpawnableAsset(AZStd::string_view spawnableName) const
+    {
+        return m_spawnableAssets.find(spawnableName) != m_spawnableAssets.end();
+    }
+
+    AZ::Data::AssetId InMemorySpawnableAssetContainer::GetInMemorySpawnableAssetId(AZStd::string_view spawnableName) const
+    {
+        auto found = m_spawnableAssets.find(spawnableName);
+        if (found != m_spawnableAssets.end())
+        {
+            return found->second.m_spawnableAssetId;
+        }
+        else
+        {
+            return  AZ::Data::AssetId();
+        }
+    }
+
+    auto InMemorySpawnableAssetContainer::RemoveInMemorySpawnableAsset(AZStd::string_view spawnableName) -> RemoveSpawnableResult
+    {
+        auto found = m_spawnableAssets.find(spawnableName);
+        if (found == m_spawnableAssets.end())
+        {
+            return AZ::Failure(AZStd::string::format("In-memory Spawnable '%.*s' doesn't exists.", AZ_STRING_ARG(spawnableName)));
+        }
+
+        for (auto& asset : found->second.m_assets)
+        {
+            asset.Release();
+            AZ::Data::AssetCatalogRequestBus::Broadcast(
+                &AZ::Data::AssetCatalogRequestBus::Events::UnregisterAsset, asset.GetId());
+        }
+
+        m_spawnableAssets.erase(found);
+        return AZ::Success();
+    }
+
+    InMemorySpawnableAssetContainer::CreateSpawnableResult InMemorySpawnableAssetContainer::CreateInMemorySpawnableAsset(
+        AzToolsFramework::Prefab::TemplateId templateId, AZStd::string_view spawnableName, bool loadReferencedAssets)
+    {
+        if (!IsActivated())
+        {
+            return AZ::Failure(AZStd::string::format("Failed to create a prefab processing stack from key '%.*s'.", AZ_STRING_ARG(m_stockProfile)));
+        }
+
+        if (HasInMemorySpawnableAsset(spawnableName))
+        {
+            return AZ::Failure(AZStd::string::format("In-memory Spawnable '%.*s' already exists.", AZ_STRING_ARG(spawnableName)));
+        }
+
+        TemplateReference templateReference = m_prefabSystemComponentInterface->FindTemplate(templateId);
+        if (!templateReference.has_value())
+        {
+            return AZ::Failure(AZStd::string::format("Could not get Template DOM for given Template's id %llu .", templateId));
+        }
+
+        // Use a random uuid as this is only a temporary source.
+        PrefabConversionUtils::PrefabProcessorContext context(AZ::Uuid::CreateRandom());
+        PrefabDom copy;
+        copy.CopyFrom(templateReference->get().GetPrefabDom(), copy.GetAllocator(), false);
+        context.AddPrefab(spawnableName, AZStd::move(copy));
+        m_converter.ProcessPrefab(context);
+
+        if (!context.HasCompletedSuccessfully() || context.GetProcessedObjects().empty())
+        {
+            return AZ::Failure(AZStd::string::format(
+                "Failed to convert the prefab into assets. Please confirm that the '%.*s' prefab processor stack is capable of producing a usable product asset.",
+                AZ_STRING_ARG(PrefabConversionUtils::IntegrationTests)));
+        }
+
+        static constexpr size_t NoTargetSpawnable = AZStd::numeric_limits<size_t>::max();
+        size_t targetSpawnableIndex = NoTargetSpawnable;
+        AZStd::vector<AZ::Data::AssetId> assetIds;
+        SpawnableAssetData spawnableAssetData;
+        AZStd::string rootProductId(spawnableName);
+        rootProductId += AzFramework::Spawnable::DotFileExtension;
+
+        // Create temporary assets from the processed data.
+        for (auto& product : context.GetProcessedObjects())
+        {
+            if (product.GetAssetType() == azrtti_typeid<AzFramework::Spawnable>() && product.GetId() == rootProductId)
+            {
+                targetSpawnableIndex = spawnableAssetData.m_assets.size();
+            }
+
+            AZ::Data::AssetInfo info;
+            info.m_assetId = product.GetAsset().GetId();
+            info.m_assetType = product.GetAssetType();
+            info.m_relativePath = product.GetId();
+
+            AZ::Data::AssetCatalogRequestBus::Broadcast(
+                &AZ::Data::AssetCatalogRequestBus::Events::RegisterAsset, info.m_assetId, info);
+            spawnableAssetData.m_assets.emplace_back(product.ReleaseAsset().release(), AZ::Data::AssetLoadBehavior::Default);
+
+            // Ensure the product asset is registered with the AssetManager
+            // Hold on to the returned asset to keep ref count alive until we assign it the latest data
+            AZ::Data::Asset<AZ::Data::AssetData> asset =
+                AZ::Data::AssetManager::Instance().FindOrCreateAsset(info.m_assetId, info.m_assetType, AZ::Data::AssetLoadBehavior::Default);
+
+            // Update the asset registered in the AssetManager with the data of our product from the Prefab Processor
+            AZ::Data::AssetManager::Instance().AssignAssetData(spawnableAssetData.m_assets.back());
+        }
+
+        if (targetSpawnableIndex == NoTargetSpawnable)
+        {
+            return AZ::Failure(AZStd::string::format("Failed to produce the target spawnable '%.*s'.", AZ_STRING_ARG(spawnableName)));
+        }
+        
+        if (loadReferencedAssets)
+        {
+            for (auto& product : context.GetProcessedObjects())
+            {
+                LoadReferencedAssets(product.GetReferencedAssets());
+            }
+        }
+
+        auto& spawnableAssetDataAdded = m_spawnableAssets.emplace(spawnableName, spawnableAssetData).first->second;
+        spawnableAssetDataAdded.m_spawnableAssetId = spawnableAssetDataAdded.m_assets[targetSpawnableIndex].GetId();
+        return AZ::Success(spawnableAssetDataAdded.m_assets[targetSpawnableIndex]);
+    }
+
+    InMemorySpawnableAssetContainer::CreateSpawnableResult InMemorySpawnableAssetContainer::CreateInMemorySpawnableAsset(
+        AZStd::string_view prefabFilePath, AZStd::string_view spawnableName, bool loadReferencedAssets)
+    {
+        AZ::IO::Path relativePath = m_loaderInterface->GenerateRelativePath(prefabFilePath);
+        auto templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
+        if (templateId == InvalidTemplateId)
+        {
+            return AZ::Failure(AZStd::string::format("Template with source path '%.*s' is not found.", AZ_STRING_ARG(prefabFilePath)));
+        }
+
+        return CreateInMemorySpawnableAsset(templateId, spawnableName, loadReferencedAssets);
+    }
+
+    void InMemorySpawnableAssetContainer::ClearAllInMemorySpawnableAssets()
+    {
+        for (auto& [spawnableName, spawnableAssetData] : m_spawnableAssets)
+        {
+            for (auto& asset : spawnableAssetData.m_assets)
+            {
+                asset.Release();
+                AZ::Data::AssetCatalogRequestBus::Broadcast(
+                    &AZ::Data::AssetCatalogRequestBus::Events::UnregisterAsset, asset.GetId());
+            }
+        }
+        
+        m_spawnableAssets.clear();
+    }
+
+    InMemorySpawnableAssetContainer::SpawnableAssets&& InMemorySpawnableAssetContainer::MoveAllInMemorySpawnableAssets()
+    {
+        return AZStd::move(m_spawnableAssets);
+    }
+
+    const InMemorySpawnableAssetContainer::SpawnableAssets& InMemorySpawnableAssetContainer::GetAllInMemorySpawnableAssets() const
+    {
+        return m_spawnableAssets;
+    }
+
+    void InMemorySpawnableAssetContainer::LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets)
+    {
+        // Start our loads on all assets by calling GetAsset from the AssetManager
+        for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
+        {
+            if (!asset.GetId().IsValid())
+            {
+                AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
+                continue;
+            }
+
+            const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
+
+            if (loadBehavior == AZ::Data::AssetLoadBehavior::NoLoad)
+            {
+                continue;
+            }
+
+            AZ::Data::AssetId assetId = asset.GetId();
+            AZ::Data::AssetType assetType = asset.GetType();
+
+            asset = AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, loadBehavior);
+
+            if (!asset.GetId().IsValid())
+            {
+                AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
+                continue;
+            }
+        }
+
+        // For all Preload assets we block until they're ready
+        // We do this as a separate pass so that we don't interrupt queuing up all other asset loads
+        for (AZ::Data::Asset<AZ::Data::AssetData>& asset : referencedAssets)
+        {
+            if (!asset.GetId().IsValid())
+            {
+                AZ_Error("Prefab", false, "Invalid asset found referenced in scene while entering game mode");
+                continue;
+            }
+
+            const AZ::Data::AssetLoadBehavior loadBehavior = asset.GetAutoLoadBehavior();
+
+            if (loadBehavior != AZ::Data::AssetLoadBehavior::PreLoad)
+            {
+                continue;
+            }
+
+            asset.BlockUntilLoadComplete();
+
+            if (asset.IsError())
+            {
+                AZ_Error("Prefab", false, "Asset with id %s failed to preload while entering game mode",
+                    asset.GetId().ToString<AZStd::string>().c_str());
+
+                continue;
+            }
+        }
+    }
+
+} // namespace AzToolsFramework::Prefab::PrefabConversionUtils

+ 69 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/InMemorySpawnableAssetContainer.h

@@ -0,0 +1,69 @@
+/*
+ * 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/SystemAllocator.h>
+#include <AzCore/std/containers/vector.h>
+#include <AzCore/std/string/string_view.h>
+#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
+
+namespace AzToolsFramework::Prefab
+{
+    class PrefabSystemComponentInterface;
+    class PrefabLoaderInterface;
+}
+
+namespace AzToolsFramework::Prefab::PrefabConversionUtils
+{
+
+    class InMemorySpawnableAssetContainer
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(InMemorySpawnableAssetContainer, AZ::SystemAllocator, 0);
+
+        using Assets = AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>;
+        using CreateSpawnableResult = AZ::Outcome<AZ::Data::Asset<AZ::Data::AssetData>&, AZStd::string>;
+        using RemoveSpawnableResult = AZ::Outcome<void, AZStd::string>;
+
+        struct SpawnableAssetData
+        {
+            Assets m_assets;
+            AZ::Data::AssetId m_spawnableAssetId;
+        };
+        using SpawnableAssets = AZStd::unordered_map<AZStd::string, SpawnableAssetData>;
+
+        ~InMemorySpawnableAssetContainer();
+
+        bool Activate(AZStd::string_view stackProfile);
+        void Deactivate();
+        bool IsActivated() const;
+        AZStd::string_view GetStockProfile() const;
+
+        CreateSpawnableResult CreateInMemorySpawnableAsset(
+            AZStd::string_view prefabFilePath, AZStd::string_view spawnableName, bool loadReferencedAssets = false);
+        CreateSpawnableResult CreateInMemorySpawnableAsset(
+            AzToolsFramework::Prefab::TemplateId templateId, AZStd::string_view spawnableName, bool loadReferencedAssets = false);
+        RemoveSpawnableResult RemoveInMemorySpawnableAsset(AZStd::string_view spawnableName);
+        AZ::Data::AssetId GetInMemorySpawnableAssetId(AZStd::string_view spawnableName) const;
+        bool HasInMemorySpawnableAsset(AZStd::string_view spawnableName) const;
+
+        void ClearAllInMemorySpawnableAssets();
+        SpawnableAssets&& MoveAllInMemorySpawnableAssets();
+        const SpawnableAssets& GetAllInMemorySpawnableAssets() const;
+
+    private:
+        void LoadReferencedAssets(AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& referencedAssets);
+
+        SpawnableAssets m_spawnableAssets;
+        PrefabConversionUtils::PrefabConversionPipeline m_converter;
+        AZStd::string_view m_stockProfile;
+        PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
+        PrefabLoaderInterface* m_loaderInterface = nullptr;
+    };
+} // namespace AzToolsFramework::Prefab::PrefabConversionUtils

+ 20 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/PrefabConverterStackProfileNames.h

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Serialization/Json/JsonSerialization.h>
+#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessorContext.h>
+
+namespace AzToolsFramework::Prefab::PrefabConversionUtils
+{
+    inline static constexpr AZStd::string_view PlayInEditor = "PlayInEditor";
+    inline static constexpr AZStd::string_view IntegrationTests = "IntegrationTests";
+
+} // namespace AzToolsFramework::Prefab::PrefabConversionUtils

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

@@ -720,10 +720,13 @@ set(FILES
     Prefab/Spawnable/EditorOnlyEntityHandler/UiEditorOnlyEntityHandler.cpp
     Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.h
     Prefab/Spawnable/EditorOnlyEntityHandler/WorldEditorOnlyEntityHandler.cpp
+    Prefab/Spawnable/InMemorySpawnableAssetContainer.h
+    Prefab/Spawnable/InMemorySpawnableAssetContainer.cpp
     Prefab/Spawnable/PrefabCatchmentProcessor.h
     Prefab/Spawnable/PrefabCatchmentProcessor.cpp
     Prefab/Spawnable/PrefabConversionPipeline.h
     Prefab/Spawnable/PrefabConversionPipeline.cpp
+    Prefab/Spawnable/PrefabConverterStackProfileNames.h
     Prefab/Spawnable/ProcesedObjectStore.h
     Prefab/Spawnable/ProcesedObjectStore.cpp
     Prefab/Spawnable/PrefabProcessor.h

+ 27 - 21
Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp

@@ -277,22 +277,25 @@ namespace Multiplayer
         }
 
         // BeginGameMode and Prefab Processing have completed at this point
-        const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
+        const auto& allAssetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
         
         AZStd::vector<uint8_t> buffer;
         AZ::IO::ByteContainerStream byteStream(&buffer);
 
         // Serialize Asset information and AssetData into a potentially large buffer
-        for (const auto& asset : assetData)
+        for (auto& [spawnableName, spawnableAssetData] : allAssetData)
         {
-            AZ::Data::AssetId assetId = asset.GetId();
-            AZStd::string assetHint = asset.GetHint();
-            uint32_t hintSize = aznumeric_cast<uint32_t>(assetHint.size());
-
-            byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast<void*>(&assetId));
-            byteStream.Write(sizeof(uint32_t), reinterpret_cast<void*>(&hintSize));
-            byteStream.Write(assetHint.size(), assetHint.data());
-            AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType());
+            for (auto& asset : spawnableAssetData.m_assets)
+            {
+                AZ::Data::AssetId assetId = asset.GetId();
+                AZStd::string assetHint = asset.GetHint();
+                uint32_t hintSize = aznumeric_cast<uint32_t>(assetHint.size());
+
+                byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast<void*>(&assetId));
+                byteStream.Write(sizeof(uint32_t), reinterpret_cast<void*>(&hintSize));
+                byteStream.Write(assetHint.size(), assetHint.data());
+                AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType());
+            }
         }
 
         const AZ::CVarFixedString remoteAddress = editorsv_serveraddr;
@@ -364,24 +367,27 @@ namespace Multiplayer
 
         AZ_Printf("MultiplayerEditor", "Editor is sending the editor-server the level data packet.")
 
-        const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
+        const auto& allAssetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
 
         AZStd::vector<uint8_t> buffer;
         AZ::IO::ByteContainerStream byteStream(&buffer);
 
         // Serialize Asset information and AssetData into a potentially large buffer
-        for (const auto& asset : assetData)
+        for (auto& [spawnableName, spawnableAssetData] : allAssetData)
         {
-            AZ::Data::AssetId assetId = asset.GetId();
-            AZStd::string assetHint = asset.GetHint();
-            auto hintSize = aznumeric_cast<uint32_t>(assetHint.size());
-
-            byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast<void*>(&assetId));
-            byteStream.Write(sizeof(uint32_t), reinterpret_cast<void*>(&hintSize));
-            byteStream.Write(assetHint.size(), assetHint.data());
-            AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType());
+            for (auto& asset : spawnableAssetData.m_assets)
+            {
+                AZ::Data::AssetId assetId = asset.GetId();
+                AZStd::string assetHint = asset.GetHint();
+                auto hintSize = aznumeric_cast<uint32_t>(assetHint.size());
+
+                byteStream.Write(sizeof(AZ::Data::AssetId), reinterpret_cast<void*>(&assetId));
+                byteStream.Write(sizeof(uint32_t), reinterpret_cast<void*>(&hintSize));
+                byteStream.Write(assetHint.size(), assetHint.data());
+                AZ::Utils::SaveObjectToStream(byteStream, AZ::DataStream::ST_BINARY, asset.GetData(), asset.GetData()->GetType());
+            }
         }
-
+        
         // Spawnable library needs to be rebuilt since now we have newly registered in-memory spawnable assets
         AZ::Interface<INetworkSpawnableLibrary>::Get()->BuildSpawnablesList();
 

+ 21 - 0
Registry/prefab.test.setreg

@@ -0,0 +1,21 @@
+{
+    "Amazon":
+    {
+        "Tools":
+        {
+            "Prefab":
+            {
+                "Processing":
+                {
+                    "Stack": {
+                        "IntegrationTests": 
+                        [
+                            { "$type": "AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover" },
+                            { "$type": "AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor" }
+                        ]
+                    }
+                }
+            }
+        }
+    }
+}

+ 8 - 2
Tools/LyTestTools/ly_test_tools/o3de/editor_test.py

@@ -43,6 +43,8 @@ import types
 import functools
 import re
 
+from os import path
+
 import ly_test_tools.environment.file_system as file_system
 import ly_test_tools.environment.waiter as waiter
 import ly_test_tools.environment.process_utils as process_utils
@@ -745,7 +747,9 @@ class EditorTestSuite():
         if test_spec.wait_for_debugger:
             test_cmdline_args += ["--wait-for-debugger"]
         if self.enable_prefab_system:
-            test_cmdline_args += ["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
+            test_cmdline_args += [
+                "--regset=/Amazon/Preferences/EnablePrefabSystem=true",
+                f"--regset-file={path.join(workspace.paths.engine_root(), 'Registry', 'prefab.test.setreg')}"]
         else:
             test_cmdline_args += ["--regset=/Amazon/Preferences/EnablePrefabSystem=false"]
 
@@ -812,7 +816,9 @@ class EditorTestSuite():
         if any([t.wait_for_debugger for t in test_spec_list]):
             test_cmdline_args += ["--wait-for-debugger"]
         if self.enable_prefab_system:
-            test_cmdline_args += ["--regset=/Amazon/Preferences/EnablePrefabSystem=true"]
+            test_cmdline_args += [
+                "--regset=/Amazon/Preferences/EnablePrefabSystem=true",
+                f"--regset-file={path.join(workspace.paths.engine_root(), 'Registry', 'prefab.test.setreg')}"]
         else:
             test_cmdline_args += ["--regset=/Amazon/Preferences/EnablePrefabSystem=false"]
 

+ 9 - 0
Tools/LyTestTools/tests/unit/test_o3de_editor_test.py

@@ -594,6 +594,7 @@ class TestRunningTests(unittest.TestCase):
                                                      mock_get_output_results, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_test_spec = mock.MagicMock()
         mock_test_spec.__name__ = 'mock_test_name'
@@ -620,6 +621,7 @@ class TestRunningTests(unittest.TestCase):
                                                      mock_get_output_results, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_test_spec = mock.MagicMock()
         mock_test_spec.__name__ = 'mock_test_name'
@@ -647,6 +649,7 @@ class TestRunningTests(unittest.TestCase):
                                                      mock_get_output_results, mock_retrieve_crash, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_test_spec = mock.MagicMock()
         mock_test_spec.__name__ = 'mock_test_name'
@@ -674,6 +677,7 @@ class TestRunningTests(unittest.TestCase):
                                                      mock_get_output_results, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_test_spec = mock.MagicMock()
         mock_test_spec.__name__ = 'mock_test_name'
@@ -699,6 +703,7 @@ class TestRunningTests(unittest.TestCase):
                                                             mock_retrieve_log, mock_retrieve_editor_log, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_editor.get_returncode.return_value = 0
         mock_test_spec = mock.MagicMock()
@@ -726,6 +731,7 @@ class TestRunningTests(unittest.TestCase):
                                                            mock_retrieve_log, mock_retrieve_editor_log, mock_get_results):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_editor.get_returncode.return_value = 15
         mock_test_spec = mock.MagicMock()
@@ -751,6 +757,7 @@ class TestRunningTests(unittest.TestCase):
                                                                  mock_get_results, mock_retrieve_crash, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_editor.get_returncode.return_value = 1
         mock_test_spec = mock.MagicMock()
@@ -785,6 +792,7 @@ class TestRunningTests(unittest.TestCase):
                                                                    mock_get_results, mock_retrieve_crash, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_editor.get_returncode.return_value = 1
         mock_test_spec = mock.MagicMock()
@@ -821,6 +829,7 @@ class TestRunningTests(unittest.TestCase):
                                                                 mock_get_results, mock_create):
         mock_test_suite = ly_test_tools.o3de.editor_test.EditorTestSuite()
         mock_workspace = mock.MagicMock()
+        mock_workspace.paths.engine_root.return_value = ""
         mock_editor = mock.MagicMock()
         mock_editor.wait.side_effect = ly_test_tools.launchers.exceptions.WaitTimeoutError()
         mock_test_spec = mock.MagicMock()