Browse Source

Squashed commit of Procedural Prefab work (#4481)

* Squashed commit of the following:

commit 964a45ead662f502ff0d63ae3528a9aa18a760f4
Merge: 8d4c1dee78 799ab8585b
Author: amzn-mike <[email protected]>
Date:   Fri Oct 1 16:16:47 2021 -0500

    Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h

commit 8d4c1dee782a1b82ded14d11f7fe879c865980a7
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 15:49:22 2021 -0500

    fixing non-unity build

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

commit e83431b3be58f36a875b5187c03cd67368d91726
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 12:42:38 2021 -0500

    fixing Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp:172:28: error: member access into incomplete type 'AZ::BehaviorContext'

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

commit b0523867d9605aff67710f4ab6030f327cd5558f
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 10:23:56 2021 -0500

    fix for error: unused variable 'targetInstanceRef'

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

commit 387c42ac1a4268ff8b2701c0c914e384b355e629
Merge: d87b41997e 0fb821a44b
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 10:00:46 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' of https://github.com/aws-lumberyard-dev/o3de into Feature_LY-5384_ProceduralPrefabs

commit d87b41997eec9a6b0d03c1040901904d68b873fb
Author: jackalbe <[email protected]>
Date:   Thu Sep 30 18:03:38 2021 -0500

    fixing non-unity build

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

commit 0fb821a44b788ab1cca61dce7c1fbdbedc2f37c0
Author: Jackson <[email protected]>
Date:   Thu Sep 30 15:43:50 2021 -0500

    adding header for validation

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

commit 30f5135f63286ce8f752df5787937f9543589cb5
Merge: 2d9e1b9f16 103dc6cfcf
Author: Jackson <[email protected]>
Date:   Thu Sep 30 11:20:15 2021 -0500

    Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs
    added a few headers as well

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp

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

commit 2d9e1b9f16f8861df92c58f0f83974859e615b1f
Merge: 39ee7a8a80 af84e71638
Author: Allen Jackson <[email protected]>
Date:   Wed Sep 29 14:22:39 2021 -0500

    Merge pull request #244 from aws-lumberyard-dev/feature_lyn5880_procprefab_tooling_updates

    {lyn5880} adding Instantiate Procedural Prefab to the Editor

commit af84e716384de048c8555fe5ccdc293e885896f9
Author: jackalbe <[email protected]>
Date:   Wed Sep 29 14:21:09 2021 -0500

    updated based on feeback

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

commit 9c83f6086203e14becb60af5ae937e8e609eb9ed
Author: jackalbe <[email protected]>
Date:   Wed Sep 29 11:35:30 2021 -0500

    small include tweak

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

commit 11ac99a87097621796af79329bf9d9344155049e
Author: jackalbe <[email protected]>
Date:   Wed Sep 29 10:08:53 2021 -0500

    moved the seg reg key to the CPP file
    removed the Queue Load

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

commit 39ee7a8a803a032652b122b73fba7007abbdbf88
Merge: 0fc7d5f361 8b4f5ded51
Author: amzn-mike <[email protected]>
Date:   Wed Sep 29 08:24:57 2021 -0500

    Merge pull request #241 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonExample

    Add example prefab script and FBX with 2 meshes to test it

commit 941f6a00d1a6222f10acfcd55a2017be6352f723
Author: jackalbe <[email protected]>
Date:   Tue Sep 28 16:07:47 2021 -0500

    make sure the AZ::IO::SystemFile::Exists() before returning fullPath

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

commit 8b4f5ded510d8c6ef47a2d2380fa49c7f6e1fd4e
Author: amzn-mike <[email protected]>
Date:   Tue Sep 28 14:03:11 2021 -0500

    Move sceneJobHandler reset out of exception block.  Add more info to error messages

    Signed-off-by: amzn-mike <[email protected]>

commit 0c82937fcd90d0c606c330f6d3e4cec8eca7edb3
Author: jackalbe <[email protected]>
Date:   Tue Sep 28 13:04:37 2021 -0500

    {lyn5880} adding Instantiate Procedural Prefab to the Editor

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

commit 0fc7d5f3610f95dcdd97614a282b9f4eabfc93dc
Merge: ea90e321d7 8ca6acc67d
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 28 09:23:54 2021 -0500

    Merge pull request #235 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset

     {ly5395} adding asset loading logic for procedural prefabs

commit 8ca6acc67dcbd375df9813acbf0062a8a5c7809a
Author: jackalbe <[email protected]>
Date:   Mon Sep 27 13:52:21 2021 -0500

    added AZ::Prefab::PrefabGroupAssetHandler::s_Extension
    optimized headers

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

commit e446aaa4e9951e474f832299f8149142dbf6e85f
Author: amzn-mike <[email protected]>
Date:   Mon Sep 27 09:12:40 2021 -0500

    Remove some whitespace

    Signed-off-by: amzn-mike <[email protected]>

commit ea90e321d737cd7bafbe617f9b5fbbeae3c4a7e9
Merge: f4c9fc50c3 5ae3c67cc7
Author: amzn-mike <[email protected]>
Date:   Mon Sep 27 09:04:58 2021 -0500

    Merge pull request #238 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonScript

    Fixes to support writing a python script to generate a prefab

commit b69ebbae17826b59f3f4fb675c57be5582acf628
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 17:51:17 2021 -0500

    Use raise_error instead of print

    Signed-off-by: amzn-mike <[email protected]>

commit 407b8d804841f0ba87a0c82405c9d1319435e2da
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 17:47:20 2021 -0500

    Add scene_mesh_to_prefab.py example ProceduralPrefab script and multiple_mesh_one_material FBX which uses the script

    Signed-off-by: amzn-mike <[email protected]>

commit 5ae3c67cc70603ec70f53c03ee716982b55b759a
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 14:50:47 2021 -0500

    Test entity cleanup

    Signed-off-by: amzn-mike <[email protected]>

commit 55da78dda5ef9bc558b65bf99d551ffebd38acef
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 14:50:28 2021 -0500

    Make CastWithTypeName only return true if the object can be successfully cast

    Signed-off-by: amzn-mike <[email protected]>

commit 9f2e85bb691a86910d14477f94b9e631ee343e0b
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 14:49:28 2021 -0500

    Remove RemoveAllTemplates API from scripting API and use prefab system interface version instead

    Signed-off-by: amzn-mike <[email protected]>

commit 41d46d1f00a16243d3fffda32186fcd7964db78a
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 13:19:51 2021 -0500

    Store watch folder in scene so source relative path can be calculated

    Signed-off-by: amzn-mike <[email protected]>

commit f4c9fc50c3ac4c3fc68e98d78b72c4f1f571b516
Merge: de2612b3b9 8bd3c0acdd
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 23 14:10:31 2021 -0500

    Merge pull request #239 from aws-lumberyard-dev/fix_LY5384_script_processing_rule

    {ly5384} script processing rule behavior more stateless

commit 8bd3c0acdd874d6421d25cc77c80da9906afefc2
Author: Jackson <[email protected]>
Date:   Wed Sep 22 15:19:30 2021 -0500

    {ly5384} script processing rule beahvior more stateless

    Made the script processing rule beahvior more stateless so that the
    script name needs to be discovered each time.

    Disconnet from the bus after each scene script builder usage.

    Before it would be possible that the same script can be run more than
    once for each asset.

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

commit 7afd9d4a9911adb1dc665361a09486e6852ba4f8
Author: amzn-mike <[email protected]>
Date:   Wed Sep 22 13:27:12 2021 -0500

    Update scene_data.py to latest PrefabGroup format

    Signed-off-by: amzn-mike <[email protected]>

commit 74d1ba8853d62b75786d68a6bdeb1bfb2ca52346
Author: amzn-mike <[email protected]>
Date:   Wed Sep 22 13:26:27 2021 -0500

    Fix GetNodeContent to return a GraphObjectProxy wrapping a nullptr instead of just returning a nullptr which causes issues for python

    Signed-off-by: amzn-mike <[email protected]>

commit ca4127353e139f9d853784ca6a74e5deeb82d6f9
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 17:48:15 2021 -0500

    AZ::JsonSerializationUtils update

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

commit 30a76be51c37c1718e9b215f33b172a10bd74f08
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 17:21:52 2021 -0500

    revert odd README.md merge issue
    added alias for PrefabBuilder.Tools

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

commit 6c83d47d51898bcdc17c10578f0198bccd09c834
Merge: 46cb4c2a87 de2612b3b9
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 16:52:58 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset

    # Conflicts:
    #	Gems/Prefab/PrefabBuilder/CMakeLists.txt

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

commit 46cb4c2a8711f1adad22be24420922365707c409
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 16:43:19 2021 -0500

    added ProceduralPrefabAssetTest to cover basics for ProceduralPrefabAsset

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

commit de2612b3b9a28ef130fc92d1c9d68c90790cf132
Merge: f03bbb236e 3117c54657
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 16:28:59 2021 -0500

    Merge pull request #232 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_linux_compile

    {ly5384} Fixing Linux build issues.

commit 3117c54657cb21ae2ef200dbfd1cd046c617089d
Merge: 15fddd1795 f03bbb236e
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 16:28:48 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into fix_ly5384_ProceduralPrefabs_linux_compile

commit f03bbb236eab3458fc433d35f9fb84dae88922d6
Merge: f297aa232a fccf900982
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 15:52:07 2021 -0500

    Merge pull request #233 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_merge_fix

    fixing an API merge compile error

commit fccf9009829b182254064ba17ab3b6e7d44919fa
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 15:50:58 2021 -0500

    fixing an API merge compile error

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

commit 762743b54744258007d8f124be95654ee6f18533
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:10:46 2021 -0500

    Make sure EntityUtilityComponent is loaded in AssetBuilderApplication

    Signed-off-by: amzn-mike <[email protected]>

commit 8c4ab65598e3ebb3a0ae621feb22ef5b57e7de27
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:09:03 2021 -0500

    Clean up entities and templates after python script is done

    Signed-off-by: amzn-mike <[email protected]>

commit 50a1f2a1a4f09cbd3e6256a210cd14a0fdb5b815
Author: amzn-mike <[email protected]>
Date:   Mon Sep 20 14:05:12 2021 -0500

    ScriptProcessorRuleBehavior resets entity context

    Signed-off-by: amzn-mike <[email protected]>

commit 51a6af053d95e90e582a25ae51c5730a7e2b0973
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:40:37 2021 -0500

    Add add_prefab_group

    Signed-off-by: amzn-mike <[email protected]>

commit f297aa232a2cd9ad5583b3e2b4aa1ce793c07d92
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 11:53:25 2021 -0500

    Fix merge compile issue

    Signed-off-by: amzn-mike <[email protected]>

commit fc40f5e75efbb87382bf6227966f2f905ccf6d75
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 11:18:44 2021 -0500

    {ly5395} adding asset loading logic for procedural prefabs

    * enabling the Prefab gem for tool work
    * enabling prefab gem for AutomatedTesting
    * AssetTypeInfoHandler for procedural prefab
    * EnableCatalogForAsset for procedural prefab
    * RegisterHandler for AssetManager

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

commit 7a2250db337fbfc693e3cf57458eb56f419b32d6
Merge: c1f3e14304 751a0fab4f
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:32:37 2021 -0500

    Merge remote-tracking branch 'origin/Feature_LY-5384_ProceduralPrefabs' into origin_Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

commit 12440233ccd987d41c248df57cee913ebb2ae2f6
Merge: 751a0fab4f f8d39e2671
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 10:25:50 2021 -0500

    Merge remote-tracking branch 'origin' into feature_lyn5394_procprefab_asset

commit c1f3e143048a914ab2d89146685ca5fe409dda27
Author: amzn-mike <[email protected]>
Date:   Mon Sep 20 14:03:53 2021 -0500

    Fix merge issue

    Signed-off-by: amzn-mike <[email protected]>

commit 1990ec370df81d4b77b4646553c2b13dce18c638
Merge: 23d02ed416 fc8697edd5
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:21:10 2021 -0500

    Merge branch 'development' into origin_Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp

commit 751a0fab4f932ec3d7683e2d46a187eefc7addbf
Merge: 23d02ed416 7b8d5629dd
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 10:10:06 2021 -0500

    Merge pull request #229 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset

    {lyn5394} adding ProceduralPrefabAsset to AZ Tools Framework

commit 15fddd1795ff07e8d6ce8840a2d675713b63655c
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 10:04:06 2021 -0500

    {ly5384} Fixing Linux build issues.

    * symbols "struct FindComponent" and "AZ::Component* FindComponent()" defined in the same scope, renamed function to FindComponentHelper
    * wrapped the AZ::ComponentId return for both cases

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

commit 23d02ed4165a1db9e738057aad0b7613be8105da
Merge: 0a31e39a25 0f3680a996
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 09:44:30 2021 -0500

    Merge pull request #228 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_Misc

    Reflect Prefab/Entity constants and add failure unit tests

commit 7b8d5629dd04ca1ed75c9828dbb0949f12eb2ca3
Merge: 78fe2cec6f 0a31e39a25
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 16 17:37:29 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset

commit 78fe2cec6fbcea9217d0b23ec056e19f144fc9a9
Author: Jackson <[email protected]>
Date:   Thu Sep 16 17:27:57 2021 -0500

    Updated PrefabBuilder to point to new asset type for the procedural prefab

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

commit 4da4e026582c8e7bf0fe9c7c7b39f15189c67b78
Author: Jackson <[email protected]>
Date:   Thu Sep 16 16:56:13 2021 -0500

    {lyn5394} adding Prefab/Procedural/ProceduralPrefabAsset to AZ Tools Framework

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

commit c2fb7b9ccb4b7d4e8080b8830d4ceeb66cd3972c
Merge: 30de326dfb a56daadc45
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 16 16:45:03 2021 -0500

    Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior

    {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab

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

commit 0a31e39a25d36c63827424adc883e873aee20b71
Merge: 30de326dfb a56daadc45
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 16 16:45:03 2021 -0500

    Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior

    {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab

commit 0f3680a9968cbbbcb9558811f8dbd287a97447ae
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 13:53:22 2021 -0500

    Add failure tests and some test cleanup

    Signed-off-by: amzn-mike <[email protected]>

commit 834eab4c4bf151dfb9e49eb0a5e1f20486b6c05c
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 13:34:03 2021 -0500

    Reflect InvalidTemplateId, fix reflection for InvalidComponentId

    Signed-off-by: amzn-mike <[email protected]>

commit 30de326dfbc28bbb9481bf282eb35c427469c847
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:55:16 2021 -0500

    Fix merge issues

    Signed-off-by: amzn-mike <[email protected]>

commit 4bcf6b7b4f2d6285f244226b065bff65ba565094
Merge: 20c5cd7259 28fec42242
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:28:26 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs_EntityManagement' into Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

commit 20c5cd7259235d26f1aa11e2d61ac6b8a57b8638
Merge: 050e26d609 6845942fa4
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:24:44 2021 -0500

    Merge pull request #200 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_CreatePrefab

    API Update: Wrap PrefabSystemComponentInterface behavior

commit 6845942fa419b2b6d5a0103f8d30e6515948310e
Merge: 58a51c738e 65da78dcc2
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:07:33 2021 -0500

    Merge pull request #208 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_SavePrefabToString

    API Update: AzToolsFramework::Prefab::PrefabLoaderInterface

commit 65da78dcc22c4251696ebb75680928ecf83d6733
Merge: e0c5e060ab 58a51c738e
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:07:06 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs_CreatePrefab' into Feature_LY-5384_ProceduralPrefabs_SavePrefabToString

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp
    #	Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp

commit 28fec422426640540bd21b2275c7b7a90e4f8e71
Merge: c1b8b5190f d825305202
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 09:58:36 2021 -0500

    Merge pull request #212 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentManagement

    API Update: Editor Entity Component Functions

commit d825305202d5a02d60dd20fb5d0cc0ecb73f562a
Merge: c280964b98 88fa6983d1
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 09:58:09 2021 -0500

    Merge pull request #218 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentExplore

    API Update: Editor Component JSON Helper Functions

commit a56daadc45e5ad8511aa5160c3afc245ca10fb83
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 16:48:41 2021 -0500

    m_prefabGroupBehavior.release() -> m_prefabGroupBehavior.reset()
    adding error messages for the prefab processing
    fixed typo

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

commit c1a03351f55f9e8f5161c0ef0288c8e64050e399
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 16:32:42 2021 -0500

    reduced the JSON for the testing framework to tree types only

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

commit 0008866baa7882ec106e134049e2bd7b95113796
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 16:25:00 2021 -0500

    enable ProceduralPrefabAsset JsonRegistrationContext

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

commit 58a51c738e1bbd5bcad27cf1c0add4760bb8cffa
Author: amzn-mike <[email protected]>
Date:   Wed Sep 15 14:31:59 2021 -0500

    Remove unneeded include

    Signed-off-by: amzn-mike <[email protected]>

commit e0c5e060ab8a9cb2cfce4c083f276ab064aa08ea
Author: amzn-mike <[email protected]>
Date:   Wed Sep 15 14:20:04 2021 -0500

    Cleanup whitespace

    Signed-off-by: amzn-mike <[email protected]>

commit 88fa6983d19ac3a0d7c4073ea390c36794d2603b
Author: amzn-mike <[email protected]>
Date:   Wed Sep 15 13:49:20 2021 -0500

    FindMatchingComponents now returns a vector of ComponentDetails which includes base class info (non-recursive)

    Fixed a memory leak
    Moved const to header

    Signed-off-by: amzn-mike <[email protected]>

commit b57a9d4261aea85a8ddb1036307d8ad296cac859
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 10:49:32 2021 -0500

    updated the DOM logic for the asset loading sake

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

commit f55ee9f5eabbeac998028f027e1c8adf55419a9d
Author: amzn-mike <[email protected]>
Date:   Tue Sep 14 13:23:13 2021 -0500

    Update CreateStringPermissive to stop when enough data has been collected.

    Update unit tests.
    Fixed out of bounds behavior in EntityUtilityComponent usage of CreateStringPermissive.
    Updated AssetTreeFilterModel to cap the string length

    Signed-off-by: amzn-mike <[email protected]>

commit 6d6707dea8cd072261d65e0a508d1179df3892d4
Author: amzn-mike <[email protected]>
Date:   Tue Sep 14 10:55:38 2021 -0500

    Rename GetComponentJson to GetComponentDefaultJson.

    Clean up GetComponentTypeIdFromName

    Signed-off-by: amzn-mike <[email protected]>

commit f4380d37a40ada353f7156b0d06b7ab60b72a151
Author: amzn-mike <[email protected]>
Date:   Tue Sep 14 10:29:04 2021 -0500

    Move scripting ebus and handler into separate files

    Signed-off-by: amzn-mike <[email protected]>

commit 572cb58f854aa2368b18f8cf70154d471dfac047
Author: amzn-mike <[email protected]>
Date:   Fri Sep 10 20:27:41 2021 -0400

    Renamed SearchComponents to FindMatchingComponents

    Added missing printf formatting

    Signed-off-by: amzn-mike <[email protected]>

commit 050e26d60931700e226a4d1a5d02d6a1d4c69915
Merge: 542bdfc5d7 c1b8b5190f
Author: amzn-mike <[email protected]>
Date:   Fri Sep 10 09:31:08 2021 -0400

    Merge pull request #201 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement

    API Update: Editor Entity Management - Add some comments and error handling

commit 875d5dc466b65d893a44e11aa9467c45cf7994e9
Author: amzn-mike <[email protected]>
Date:   Fri Sep 10 09:13:43 2021 -0400

    Move bus and handler code into separate files

    Signed-off-by: amzn-mike <[email protected]>

commit a2d94e24fb2cd1993835f17e7129000c00926cf7
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 17:25:35 2021 -0400

    Add comments

    Signed-off-by: amzn-mike <[email protected]>

commit 16f507377f4f04c88028833f69327bd11b401b87
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 17:16:33 2021 -0400

    Add error messages

    Signed-off-by: amzn-mike <[email protected]>

commit 2e6d7d40dc731989128fbe829141057b3558c88b
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 16:54:20 2021 -0400

    Cleanup code

    Signed-off-by: amzn-mike <[email protected]>

commit 963891eaeb8d616bb071f2a7b50cdabd994241a2
Author: amzn-mike <[email protected]>
Date:   Fri Sep 3 10:48:40 2021 -0500

    Add and update unit tests

    Reflect APIs to behavior context

    Signed-off-by: amzn-mike <[email protected]>

commit 664be8eca54293407e5ce2d631375d907c1bb2f6
Author: amzn-mike <[email protected]>
Date:   Thu Sep 2 13:00:02 2021 -0500

    Add Search and Component json output APIs

    Signed-off-by: amzn-mike <[email protected]>

commit e94fe64f366009d06b889954d292c37ea4f96fe2
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 13:37:44 2021 -0400

    Make bus handler private

    Signed-off-by: amzn-mike <[email protected]>

commit 345a1b0d5edef99d5a8ea2bce2f4f3478652a4f1
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 12:30:13 2021 -0400

    Address feedback

    Signed-off-by: amzn-mike <[email protected]>

commit c280964b983635bed9e8bbfb94b2188403287d4c
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 10:12:49 2021 -0500

    Address feedback

    Signed-off-by: amzn-mike <[email protected]>

commit 3b819ee827fc4c19312f2d2bc5788e2cc36de4f4
Author: amzn-mike <[email protected]>
Date:   Fri Sep 3 16:06:10 2021 -0500

    Update unit test to verify components on saved prefab

    Signed-off-by: amzn-mike <[email protected]>

commit 0b63c0e316ec621ed17edf36109b7cd1d50ef606
Author: amzn-mike <[email protected]>
Date:   Fri Sep 3 12:52:46 2021 -0500

    Expand test

    Signed-off-by: amzn-mike <[email protected]>

commit ea59416626d9b43599cf8847585e599f8fa5fd22
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 17:32:14 2021 -0500

    Add error handling

    Add component creation
    Add error testing

    Signed-off-by: amzn-mike <[email protected]>

commit 9d0f2ae33eadba0216bd245157b94692792b1a86
Author: Jackson <[email protected]>
Date:   Wed Sep 1 17:31:28 2021 -0500

    enabling ProductDependencies test again

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

commit 15d1e4730df31e71e7e3ee6d4914f19b233c1b7f
Author: Jackson <[email protected]>
Date:   Wed Sep 1 17:23:26 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior

    # Conflicts:
    #	Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake

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

commit ce2e4602bde26fbb34b43c73f820a97b9e229b5a
Merge: 5ad0aac747 542bdfc5d7
Author: Jackson <[email protected]>
Date:   Wed Sep 1 17:14:07 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior

    # Conflicts:
    #	Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake

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

commit 542bdfc5d762c07938f9a0f8e729070f1df9edfc
Merge: 3e7564c944 4899f67986
Author: Allen Jackson <[email protected]>
Date:   Wed Sep 1 17:09:10 2021 -0500

    Merge pull request #207 from aws-lumberyard-dev/feature_lyn5392_proc_prefab_group

    {lyn5392} Adding PrefabGroup scene manifest rule

commit 4899f67986992b7eeb5cc3b750493ed21ec7b4f3
Author: Jackson <[email protected]>
Date:   Wed Sep 1 16:33:24 2021 -0500

    removing unneeded AZStd::move() calls

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

commit 5ad0aac7472d24bb85990d36399a466288ecfdbc
Author: Jackson <[email protected]>
Date:   Wed Sep 1 16:17:52 2021 -0500

    cleaned up the code
    finalized the unit tests
    fixed the code based on the tests... FTW!

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

commit 24f289dd31292c4b9f5e5848ddc94efd986b4341
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 13:06:20 2021 -0500

    Add FindComponentByTypeName and UpdateComponentForEntity APIs

    Add unit tests

    Signed-off-by: amzn-mike <[email protected]>

commit c1b8b5190f973d2429731308f50c7c962d64301e
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 14:02:17 2021 -0500

    Fix bus connect that should have been a bus disconnect

    Signed-off-by: amzn-mike <[email protected]>

commit fde870d6c92e131875ec6aadda2365203a538379
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 12:57:04 2021 -0500

    Fix up includes

    Signed-off-by: amzn-mike <[email protected]>

commit 8ceddd0efc90d33bc34b5287176497356e57abf9
Author: jackalbe <[email protected]>
Date:   Wed Sep 1 08:43:23 2021 -0500

    WIP

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

commit 3cc9135d87efde24430dbf1cd112bcf9b95ecaa0
Author: amzn-mike <[email protected]>
Date:   Tue Aug 31 14:44:37 2021 -0500

    Rename files

    Signed-off-by: amzn-mike <[email protected]>

commit f056b3d9578ae2f49dbc172a5929306047620be6
Author: amzn-mike <[email protected]>
Date:   Tue Aug 31 14:14:34 2021 -0500

    Remove 'editor' from bus/component name

    Signed-off-by: amzn-mike <[email protected]>

commit 0fb7a0788e879eb9dc241291f05f0acffc0b0156
Author: jackalbe <[email protected]>
Date:   Tue Aug 31 09:18:37 2021 -0500

    {lyn5392} Adding PrefabGroup scene manifest rule

    * Adding PrefabGroup abstraction and concrete classes to fill out in a scene manifest
    * has reflections for serialization & behavior
    * testing the behavior using Lua
    * testing the serialization using JSON

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

commit b1756307bff9f86ae0ae1354bae8f470d19b4487
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 17:52:18 2021 -0500

    Reflect SaveTemplateToString

    Add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit ddd2bb89041c5fb8b6add2cfc02c454baab9a7d6
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 12:46:17 2021 -0500

    Add warning/error messages, update module

    Signed-off-by: amzn-mike <[email protected]>

commit e839c1cbcc340859fc6ee5cff2d802660944194b
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 11:17:25 2021 -0500

    Add some comments and error handling

    Signed-off-by: amzn-mike <[email protected]>

commit 347f787cc88405541889e842151de81a70598dd1
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 17:16:45 2021 -0500

    Fix line endings

    Signed-off-by: amzn-mike <[email protected]>

commit be26ab1cb16221ba879c10652b430ea31547d868
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 15:28:00 2021 -0500

    Add CreatePrefabTemplate

    Add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit 3e7564c944497b7b6cce94dbce486ed8c4561f33
Merge: 5a3c289fac 07841ee749
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 10:20:20 2021 -0500

    Merge pull request #199 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement

    Add editor entity creation and unit test

commit 07841ee749d2a1bc22352cbec3076ac087348676
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 16:44:57 2021 -0500

    Fix line endings

    Signed-off-by: amzn-mike <[email protected]>

commit 35bc3b89cd1fcc126114ea7d61cdd21e88699080
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 15:03:35 2021 -0500

    Setup CreateEditorReadyEntity to use a custom entity context

    Add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit 82519b15510a4ec1b2daf4d883a2c4c3d9c0a347
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 09:29:20 2021 -0500

    Add EditorEntityUtilityComponent for managing entities from the behavior context

    Signed-off-by: amzn-mike <[email protected]>

commit f8d39e267162db5fb2091982a7e54bc57ed108bb
Merge: 43603cad5e 575faa4443
Author: Chris Galvan <[email protected]>
Date:   Wed Aug 11 15:29:33 2021 -0500

    Merge pull request #3049 from aws-lumberyard-dev/cgalvan/gitflow_210811

    Merged stabilization/2106 to main

commit 575faa4443823ed239d609692847fbd781beebcb
Merge: 43603cad5e 4b817a6483
Author: Chris Galvan <[email protected]>
Date:   Wed Aug 11 14:13:27 2021 -0500

    Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210811

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

commit 43603cad5e715aadef47b76e24867dd41347d163
Merge: d9cce28a53 bb52475ce8
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:55:51 2021 -0500

    Merge pull request #2271 from aws-lumberyard-dev/Foundation/miterenc/ContributingUpdate

    Updating CONTRIBUTING.md

commit bb52475ce8c37c29b35c81b379befe5a75db1de7
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:55:14 2021 -0500

    Updated text to be more descriptive

    Signed-off-by: Terry Michaels <[email protected]>

commit 697dfad486c69beb5ef40cbf6478d6ccd8753cd7
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:27:24 2021 -0500

    Fixed typo

    Signed-off-by: Terry Michaels <[email protected]>

commit 650e1ab44d86664b6deab7f6cebeda40c3e1361e
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:19:46 2021 -0500

    Updating CONTRIBUTING.md

    Signed-off-by: Terry Michaels <[email protected]>

commit d9cce28a5387c7f5b59041869086547905f1e345
Merge: e7f787572e 486ba58628
Author: Chris Galvan <[email protected]>
Date:   Mon Jul 12 14:06:57 2021 -0500

    Merge pull request #2096 from aws-lumberyard-dev/cgalvan/gitflow_210712_main

    Merged stabilization/2106 to main

commit 486ba58628488c211a6140f26caa421c38be3f0f
Merge: e7f787572e 7cfde884d9
Author: Chris Galvan <[email protected]>
Date:   Mon Jul 12 11:12:41 2021 -0500

    Merged stabilization/2106 to development; Resolved merge conflicts

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

commit e7f787572e805c413115265e5873fb2425e2f41b
Author: Nicholas Lawson <[email protected]>
Date:   Tue Jul 6 08:03:35 2021 -0700

    Updates licenses to APACHE-2.0 OR MIT (#1685)

    Not to be committed before 7/6/2021

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

commit 837e1c737059a819d01ef4ebed7fadae42248dd8
Merge: d30de01752 efcbe2c4a1
Author: Chris Galvan <[email protected]>
Date:   Fri Jul 2 12:11:27 2021 -0500

    Merge pull request #1764 from aws-lumberyard-dev/cgalvan/gitflow_210702

    Merged stabilization/2106 to main

commit efcbe2c4a1e909182dfbd5394f9322513ba91115
Merge: d30de01752 0c43493e29
Author: Chris Galvan <[email protected]>
Date:   Fri Jul 2 10:20:42 2021 -0500

    Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210702

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

commit d30de01752b7eaed1f16e2163b1a75c915f061eb
Author: Alex Peterson <[email protected]>
Date:   Mon Jun 28 11:20:36 2021 -0700

    Updating LFS config to new endpoint (#1624)

    Signed-off-by: AMZN-alexpete

Signed-off-by: amzn-mike <[email protected]>

* fix for "warning C4100: 'outputValueTypeId': unreferenced formal"

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

* Material Editor: Added alternate skybox toggle to the toolbar

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

* Squashed commit of the following:

commit 964a45ead662f502ff0d63ae3528a9aa18a760f4
Merge: 8d4c1dee78 799ab8585b
Author: amzn-mike <[email protected]>
Date:   Fri Oct 1 16:16:47 2021 -0500

    Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h

commit 8d4c1dee782a1b82ded14d11f7fe879c865980a7
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 15:49:22 2021 -0500

    fixing non-unity build

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

commit e83431b3be58f36a875b5187c03cd67368d91726
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 12:42:38 2021 -0500

    fixing Gems/Multiplayer/Code/Source/Components/NetworkCharacterComponent.cpp:172:28: error: member access into incomplete type 'AZ::BehaviorContext'

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

commit b0523867d9605aff67710f4ab6030f327cd5558f
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 10:23:56 2021 -0500

    fix for error: unused variable 'targetInstanceRef'

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

commit 387c42ac1a4268ff8b2701c0c914e384b355e629
Merge: d87b41997e 0fb821a44b
Author: jackalbe <[email protected]>
Date:   Fri Oct 1 10:00:46 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' of https://github.com/aws-lumberyard-dev/o3de into Feature_LY-5384_ProceduralPrefabs

commit d87b41997eec9a6b0d03c1040901904d68b873fb
Author: jackalbe <[email protected]>
Date:   Thu Sep 30 18:03:38 2021 -0500

    fixing non-unity build

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

commit 0fb821a44b788ab1cca61dce7c1fbdbedc2f37c0
Author: Jackson <[email protected]>
Date:   Thu Sep 30 15:43:50 2021 -0500

    adding header for validation

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

commit 30f5135f63286ce8f752df5787937f9543589cb5
Merge: 2d9e1b9f16 103dc6cfcf
Author: Jackson <[email protected]>
Date:   Thu Sep 30 11:20:15 2021 -0500

    Merge branch 'development' into Feature_LY-5384_ProceduralPrefabs
    added a few headers as well

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp

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

commit 2d9e1b9f16f8861df92c58f0f83974859e615b1f
Merge: 39ee7a8a80 af84e71638
Author: Allen Jackson <[email protected]>
Date:   Wed Sep 29 14:22:39 2021 -0500

    Merge pull request #244 from aws-lumberyard-dev/feature_lyn5880_procprefab_tooling_updates

    {lyn5880} adding Instantiate Procedural Prefab to the Editor

commit af84e716384de048c8555fe5ccdc293e885896f9
Author: jackalbe <[email protected]>
Date:   Wed Sep 29 14:21:09 2021 -0500

    updated based on feeback

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

commit 9c83f6086203e14becb60af5ae937e8e609eb9ed
Author: jackalbe <[email protected]>
Date:   Wed Sep 29 11:35:30 2021 -0500

    small include tweak

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

commit 11ac99a87097621796af79329bf9d9344155049e
Author: jackalbe <[email protected]>
Date:   Wed Sep 29 10:08:53 2021 -0500

    moved the seg reg key to the CPP file
    removed the Queue Load

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

commit 39ee7a8a803a032652b122b73fba7007abbdbf88
Merge: 0fc7d5f361 8b4f5ded51
Author: amzn-mike <[email protected]>
Date:   Wed Sep 29 08:24:57 2021 -0500

    Merge pull request #241 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonExample

    Add example prefab script and FBX with 2 meshes to test it

commit 941f6a00d1a6222f10acfcd55a2017be6352f723
Author: jackalbe <[email protected]>
Date:   Tue Sep 28 16:07:47 2021 -0500

    make sure the AZ::IO::SystemFile::Exists() before returning fullPath

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

commit 8b4f5ded510d8c6ef47a2d2380fa49c7f6e1fd4e
Author: amzn-mike <[email protected]>
Date:   Tue Sep 28 14:03:11 2021 -0500

    Move sceneJobHandler reset out of exception block.  Add more info to error messages

    Signed-off-by: amzn-mike <[email protected]>

commit 0c82937fcd90d0c606c330f6d3e4cec8eca7edb3
Author: jackalbe <[email protected]>
Date:   Tue Sep 28 13:04:37 2021 -0500

    {lyn5880} adding Instantiate Procedural Prefab to the Editor

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

commit 0fc7d5f3610f95dcdd97614a282b9f4eabfc93dc
Merge: ea90e321d7 8ca6acc67d
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 28 09:23:54 2021 -0500

    Merge pull request #235 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset

     {ly5395} adding asset loading logic for procedural prefabs

commit 8ca6acc67dcbd375df9813acbf0062a8a5c7809a
Author: jackalbe <[email protected]>
Date:   Mon Sep 27 13:52:21 2021 -0500

    added AZ::Prefab::PrefabGroupAssetHandler::s_Extension
    optimized headers

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

commit e446aaa4e9951e474f832299f8149142dbf6e85f
Author: amzn-mike <[email protected]>
Date:   Mon Sep 27 09:12:40 2021 -0500

    Remove some whitespace

    Signed-off-by: amzn-mike <[email protected]>

commit ea90e321d737cd7bafbe617f9b5fbbeae3c4a7e9
Merge: f4c9fc50c3 5ae3c67cc7
Author: amzn-mike <[email protected]>
Date:   Mon Sep 27 09:04:58 2021 -0500

    Merge pull request #238 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_PythonScript

    Fixes to support writing a python script to generate a prefab

commit b69ebbae17826b59f3f4fb675c57be5582acf628
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 17:51:17 2021 -0500

    Use raise_error instead of print

    Signed-off-by: amzn-mike <[email protected]>

commit 407b8d804841f0ba87a0c82405c9d1319435e2da
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 17:47:20 2021 -0500

    Add scene_mesh_to_prefab.py example ProceduralPrefab script and multiple_mesh_one_material FBX which uses the script

    Signed-off-by: amzn-mike <[email protected]>

commit 5ae3c67cc70603ec70f53c03ee716982b55b759a
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 14:50:47 2021 -0500

    Test entity cleanup

    Signed-off-by: amzn-mike <[email protected]>

commit 55da78dda5ef9bc558b65bf99d551ffebd38acef
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 14:50:28 2021 -0500

    Make CastWithTypeName only return true if the object can be successfully cast

    Signed-off-by: amzn-mike <[email protected]>

commit 9f2e85bb691a86910d14477f94b9e631ee343e0b
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 14:49:28 2021 -0500

    Remove RemoveAllTemplates API from scripting API and use prefab system interface version instead

    Signed-off-by: amzn-mike <[email protected]>

commit 41d46d1f00a16243d3fffda32186fcd7964db78a
Author: amzn-mike <[email protected]>
Date:   Fri Sep 24 13:19:51 2021 -0500

    Store watch folder in scene so source relative path can be calculated

    Signed-off-by: amzn-mike <[email protected]>

commit f4c9fc50c3ac4c3fc68e98d78b72c4f1f571b516
Merge: de2612b3b9 8bd3c0acdd
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 23 14:10:31 2021 -0500

    Merge pull request #239 from aws-lumberyard-dev/fix_LY5384_script_processing_rule

    {ly5384} script processing rule behavior more stateless

commit 8bd3c0acdd874d6421d25cc77c80da9906afefc2
Author: Jackson <[email protected]>
Date:   Wed Sep 22 15:19:30 2021 -0500

    {ly5384} script processing rule beahvior more stateless

    Made the script processing rule beahvior more stateless so that the
    script name needs to be discovered each time.

    Disconnet from the bus after each scene script builder usage.

    Before it would be possible that the same script can be run more than
    once for each asset.

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

commit 7afd9d4a9911adb1dc665361a09486e6852ba4f8
Author: amzn-mike <[email protected]>
Date:   Wed Sep 22 13:27:12 2021 -0500

    Update scene_data.py to latest PrefabGroup format

    Signed-off-by: amzn-mike <[email protected]>

commit 74d1ba8853d62b75786d68a6bdeb1bfb2ca52346
Author: amzn-mike <[email protected]>
Date:   Wed Sep 22 13:26:27 2021 -0500

    Fix GetNodeContent to return a GraphObjectProxy wrapping a nullptr instead of just returning a nullptr which causes issues for python

    Signed-off-by: amzn-mike <[email protected]>

commit ca4127353e139f9d853784ca6a74e5deeb82d6f9
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 17:48:15 2021 -0500

    AZ::JsonSerializationUtils update

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

commit 30a76be51c37c1718e9b215f33b172a10bd74f08
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 17:21:52 2021 -0500

    revert odd README.md merge issue
    added alias for PrefabBuilder.Tools

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

commit 6c83d47d51898bcdc17c10578f0198bccd09c834
Merge: 46cb4c2a87 de2612b3b9
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 16:52:58 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset

    # Conflicts:
    #	Gems/Prefab/PrefabBuilder/CMakeLists.txt

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

commit 46cb4c2a8711f1adad22be24420922365707c409
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 16:43:19 2021 -0500

    added ProceduralPrefabAssetTest to cover basics for ProceduralPrefabAsset

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

commit de2612b3b9a28ef130fc92d1c9d68c90790cf132
Merge: f03bbb236e 3117c54657
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 16:28:59 2021 -0500

    Merge pull request #232 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_linux_compile

    {ly5384} Fixing Linux build issues.

commit 3117c54657cb21ae2ef200dbfd1cd046c617089d
Merge: 15fddd1795 f03bbb236e
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 16:28:48 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into fix_ly5384_ProceduralPrefabs_linux_compile

commit f03bbb236eab3458fc433d35f9fb84dae88922d6
Merge: f297aa232a fccf900982
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 15:52:07 2021 -0500

    Merge pull request #233 from aws-lumberyard-dev/fix_ly5384_ProceduralPrefabs_merge_fix

    fixing an API merge compile error

commit fccf9009829b182254064ba17ab3b6e7d44919fa
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 15:50:58 2021 -0500

    fixing an API merge compile error

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

commit 762743b54744258007d8f124be95654ee6f18533
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:10:46 2021 -0500

    Make sure EntityUtilityComponent is loaded in AssetBuilderApplication

    Signed-off-by: amzn-mike <[email protected]>

commit 8c4ab65598e3ebb3a0ae621feb22ef5b57e7de27
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:09:03 2021 -0500

    Clean up entities and templates after python script is done

    Signed-off-by: amzn-mike <[email protected]>

commit 50a1f2a1a4f09cbd3e6256a210cd14a0fdb5b815
Author: amzn-mike <[email protected]>
Date:   Mon Sep 20 14:05:12 2021 -0500

    ScriptProcessorRuleBehavior resets entity context

    Signed-off-by: amzn-mike <[email protected]>

commit 51a6af053d95e90e582a25ae51c5730a7e2b0973
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:40:37 2021 -0500

    Add add_prefab_group

    Signed-off-by: amzn-mike <[email protected]>

commit f297aa232a2cd9ad5583b3e2b4aa1ce793c07d92
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 11:53:25 2021 -0500

    Fix merge compile issue

    Signed-off-by: amzn-mike <[email protected]>

commit fc40f5e75efbb87382bf6227966f2f905ccf6d75
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 11:18:44 2021 -0500

    {ly5395} adding asset loading logic for procedural prefabs

    * enabling the Prefab gem for tool work
    * enabling prefab gem for AutomatedTesting
    * AssetTypeInfoHandler for procedural prefab
    * EnableCatalogForAsset for procedural prefab
    * RegisterHandler for AssetManager

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

commit 7a2250db337fbfc693e3cf57458eb56f419b32d6
Merge: c1f3e14304 751a0fab4f
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:32:37 2021 -0500

    Merge remote-tracking branch 'origin/Feature_LY-5384_ProceduralPrefabs' into origin_Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

commit 12440233ccd987d41c248df57cee913ebb2ae2f6
Merge: 751a0fab4f f8d39e2671
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 10:25:50 2021 -0500

    Merge remote-tracking branch 'origin' into feature_lyn5394_procprefab_asset

commit c1f3e143048a914ab2d89146685ca5fe409dda27
Author: amzn-mike <[email protected]>
Date:   Mon Sep 20 14:03:53 2021 -0500

    Fix merge issue

    Signed-off-by: amzn-mike <[email protected]>

commit 1990ec370df81d4b77b4646553c2b13dce18c638
Merge: 23d02ed416 fc8697edd5
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 10:21:10 2021 -0500

    Merge branch 'development' into origin_Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp

commit 751a0fab4f932ec3d7683e2d46a187eefc7addbf
Merge: 23d02ed416 7b8d5629dd
Author: Allen Jackson <[email protected]>
Date:   Tue Sep 21 10:10:06 2021 -0500

    Merge pull request #229 from aws-lumberyard-dev/feature_lyn5394_procprefab_asset

    {lyn5394} adding ProceduralPrefabAsset to AZ Tools Framework

commit 15fddd1795ff07e8d6ce8840a2d675713b63655c
Author: jackalbe <[email protected]>
Date:   Tue Sep 21 10:04:06 2021 -0500

    {ly5384} Fixing Linux build issues.

    * symbols "struct FindComponent" and "AZ::Component* FindComponent()" defined in the same scope, renamed function to FindComponentHelper
    * wrapped the AZ::ComponentId return for both cases

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

commit 23d02ed4165a1db9e738057aad0b7613be8105da
Merge: 0a31e39a25 0f3680a996
Author: amzn-mike <[email protected]>
Date:   Tue Sep 21 09:44:30 2021 -0500

    Merge pull request #228 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_Misc

    Reflect Prefab/Entity constants and add failure unit tests

commit 7b8d5629dd04ca1ed75c9828dbb0949f12eb2ca3
Merge: 78fe2cec6f 0a31e39a25
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 16 17:37:29 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5394_procprefab_asset

commit 78fe2cec6fbcea9217d0b23ec056e19f144fc9a9
Author: Jackson <[email protected]>
Date:   Thu Sep 16 17:27:57 2021 -0500

    Updated PrefabBuilder to point to new asset type for the procedural prefab

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

commit 4da4e026582c8e7bf0fe9c7c7b39f15189c67b78
Author: Jackson <[email protected]>
Date:   Thu Sep 16 16:56:13 2021 -0500

    {lyn5394} adding Prefab/Procedural/ProceduralPrefabAsset to AZ Tools Framework

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

commit c2fb7b9ccb4b7d4e8080b8830d4ceeb66cd3972c
Merge: 30de326dfb a56daadc45
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 16 16:45:03 2021 -0500

    Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior

    {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab

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

commit 0a31e39a25d36c63827424adc883e873aee20b71
Merge: 30de326dfb a56daadc45
Author: Allen Jackson <[email protected]>
Date:   Thu Sep 16 16:45:03 2021 -0500

    Merge pull request #210 from aws-lumberyard-dev/feature_lyn5393_proc_prefab_behavior

    {LYN5393} Adding Prefab Group Behavior to output Procedural Prefab

commit 0f3680a9968cbbbcb9558811f8dbd287a97447ae
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 13:53:22 2021 -0500

    Add failure tests and some test cleanup

    Signed-off-by: amzn-mike <[email protected]>

commit 834eab4c4bf151dfb9e49eb0a5e1f20486b6c05c
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 13:34:03 2021 -0500

    Reflect InvalidTemplateId, fix reflection for InvalidComponentId

    Signed-off-by: amzn-mike <[email protected]>

commit 30de326dfbc28bbb9481bf282eb35c427469c847
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:55:16 2021 -0500

    Fix merge issues

    Signed-off-by: amzn-mike <[email protected]>

commit 4bcf6b7b4f2d6285f244226b065bff65ba565094
Merge: 20c5cd7259 28fec42242
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:28:26 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs_EntityManagement' into Feature_LY-5384_ProceduralPrefabs

    Signed-off-by: amzn-mike <[email protected]>

commit 20c5cd7259235d26f1aa11e2d61ac6b8a57b8638
Merge: 050e26d609 6845942fa4
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:24:44 2021 -0500

    Merge pull request #200 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_CreatePrefab

    API Update: Wrap PrefabSystemComponentInterface behavior

commit 6845942fa419b2b6d5a0103f8d30e6515948310e
Merge: 58a51c738e 65da78dcc2
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:07:33 2021 -0500

    Merge pull request #208 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_SavePrefabToString

    API Update: AzToolsFramework::Prefab::PrefabLoaderInterface

commit 65da78dcc22c4251696ebb75680928ecf83d6733
Merge: e0c5e060ab 58a51c738e
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 10:07:06 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs_CreatePrefab' into Feature_LY-5384_ProceduralPrefabs_SavePrefabToString

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp
    #	Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp

commit 28fec422426640540bd21b2275c7b7a90e4f8e71
Merge: c1b8b5190f d825305202
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 09:58:36 2021 -0500

    Merge pull request #212 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentManagement

    API Update: Editor Entity Component Functions

commit d825305202d5a02d60dd20fb5d0cc0ecb73f562a
Merge: c280964b98 88fa6983d1
Author: amzn-mike <[email protected]>
Date:   Thu Sep 16 09:58:09 2021 -0500

    Merge pull request #218 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_ComponentExplore

    API Update: Editor Component JSON Helper Functions

commit a56daadc45e5ad8511aa5160c3afc245ca10fb83
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 16:48:41 2021 -0500

    m_prefabGroupBehavior.release() -> m_prefabGroupBehavior.reset()
    adding error messages for the prefab processing
    fixed typo

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

commit c1a03351f55f9e8f5161c0ef0288c8e64050e399
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 16:32:42 2021 -0500

    reduced the JSON for the testing framework to tree types only

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

commit 0008866baa7882ec106e134049e2bd7b95113796
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 16:25:00 2021 -0500

    enable ProceduralPrefabAsset JsonRegistrationContext

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

commit 58a51c738e1bbd5bcad27cf1c0add4760bb8cffa
Author: amzn-mike <[email protected]>
Date:   Wed Sep 15 14:31:59 2021 -0500

    Remove unneeded include

    Signed-off-by: amzn-mike <[email protected]>

commit e0c5e060ab8a9cb2cfce4c083f276ab064aa08ea
Author: amzn-mike <[email protected]>
Date:   Wed Sep 15 14:20:04 2021 -0500

    Cleanup whitespace

    Signed-off-by: amzn-mike <[email protected]>

commit 88fa6983d19ac3a0d7c4073ea390c36794d2603b
Author: amzn-mike <[email protected]>
Date:   Wed Sep 15 13:49:20 2021 -0500

    FindMatchingComponents now returns a vector of ComponentDetails which includes base class info (non-recursive)

    Fixed a memory leak
    Moved const to header

    Signed-off-by: amzn-mike <[email protected]>

commit b57a9d4261aea85a8ddb1036307d8ad296cac859
Author: jackalbe <[email protected]>
Date:   Wed Sep 15 10:49:32 2021 -0500

    updated the DOM logic for the asset loading sake

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

commit f55ee9f5eabbeac998028f027e1c8adf55419a9d
Author: amzn-mike <[email protected]>
Date:   Tue Sep 14 13:23:13 2021 -0500

    Update CreateStringPermissive to stop when enough data has been collected.

    Update unit tests.
    Fixed out of bounds behavior in EntityUtilityComponent usage of CreateStringPermissive.
    Updated AssetTreeFilterModel to cap the string length

    Signed-off-by: amzn-mike <[email protected]>

commit 6d6707dea8cd072261d65e0a508d1179df3892d4
Author: amzn-mike <[email protected]>
Date:   Tue Sep 14 10:55:38 2021 -0500

    Rename GetComponentJson to GetComponentDefaultJson.

    Clean up GetComponentTypeIdFromName

    Signed-off-by: amzn-mike <[email protected]>

commit f4380d37a40ada353f7156b0d06b7ab60b72a151
Author: amzn-mike <[email protected]>
Date:   Tue Sep 14 10:29:04 2021 -0500

    Move scripting ebus and handler into separate files

    Signed-off-by: amzn-mike <[email protected]>

commit 572cb58f854aa2368b18f8cf70154d471dfac047
Author: amzn-mike <[email protected]>
Date:   Fri Sep 10 20:27:41 2021 -0400

    Renamed SearchComponents to FindMatchingComponents

    Added missing printf formatting

    Signed-off-by: amzn-mike <[email protected]>

commit 050e26d60931700e226a4d1a5d02d6a1d4c69915
Merge: 542bdfc5d7 c1b8b5190f
Author: amzn-mike <[email protected]>
Date:   Fri Sep 10 09:31:08 2021 -0400

    Merge pull request #201 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement

    API Update: Editor Entity Management - Add some comments and error handling

commit 875d5dc466b65d893a44e11aa9467c45cf7994e9
Author: amzn-mike <[email protected]>
Date:   Fri Sep 10 09:13:43 2021 -0400

    Move bus and handler code into separate files

    Signed-off-by: amzn-mike <[email protected]>

commit a2d94e24fb2cd1993835f17e7129000c00926cf7
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 17:25:35 2021 -0400

    Add comments

    Signed-off-by: amzn-mike <[email protected]>

commit 16f507377f4f04c88028833f69327bd11b401b87
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 17:16:33 2021 -0400

    Add error messages

    Signed-off-by: amzn-mike <[email protected]>

commit 2e6d7d40dc731989128fbe829141057b3558c88b
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 16:54:20 2021 -0400

    Cleanup code

    Signed-off-by: amzn-mike <[email protected]>

commit 963891eaeb8d616bb071f2a7b50cdabd994241a2
Author: amzn-mike <[email protected]>
Date:   Fri Sep 3 10:48:40 2021 -0500

    Add and update unit tests

    Reflect APIs to behavior context

    Signed-off-by: amzn-mike <[email protected]>

commit 664be8eca54293407e5ce2d631375d907c1bb2f6
Author: amzn-mike <[email protected]>
Date:   Thu Sep 2 13:00:02 2021 -0500

    Add Search and Component json output APIs

    Signed-off-by: amzn-mike <[email protected]>

commit e94fe64f366009d06b889954d292c37ea4f96fe2
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 13:37:44 2021 -0400

    Make bus handler private

    Signed-off-by: amzn-mike <[email protected]>

commit 345a1b0d5edef99d5a8ea2bce2f4f3478652a4f1
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 12:30:13 2021 -0400

    Address feedback

    Signed-off-by: amzn-mike <[email protected]>

commit c280964b983635bed9e8bbfb94b2188403287d4c
Author: amzn-mike <[email protected]>
Date:   Thu Sep 9 10:12:49 2021 -0500

    Address feedback

    Signed-off-by: amzn-mike <[email protected]>

commit 3b819ee827fc4c19312f2d2bc5788e2cc36de4f4
Author: amzn-mike <[email protected]>
Date:   Fri Sep 3 16:06:10 2021 -0500

    Update unit test to verify components on saved prefab

    Signed-off-by: amzn-mike <[email protected]>

commit 0b63c0e316ec621ed17edf36109b7cd1d50ef606
Author: amzn-mike <[email protected]>
Date:   Fri Sep 3 12:52:46 2021 -0500

    Expand test

    Signed-off-by: amzn-mike <[email protected]>

commit ea59416626d9b43599cf8847585e599f8fa5fd22
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 17:32:14 2021 -0500

    Add error handling

    Add component creation
    Add error testing

    Signed-off-by: amzn-mike <[email protected]>

commit 9d0f2ae33eadba0216bd245157b94692792b1a86
Author: Jackson <[email protected]>
Date:   Wed Sep 1 17:31:28 2021 -0500

    enabling ProductDependencies test again

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

commit 15d1e4730df31e71e7e3ee6d4914f19b233c1b7f
Author: Jackson <[email protected]>
Date:   Wed Sep 1 17:23:26 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior

    # Conflicts:
    #	Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake

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

commit ce2e4602bde26fbb34b43c73f820a97b9e229b5a
Merge: 5ad0aac747 542bdfc5d7
Author: Jackson <[email protected]>
Date:   Wed Sep 1 17:14:07 2021 -0500

    Merge branch 'Feature_LY-5384_ProceduralPrefabs' into feature_lyn5393_proc_prefab_behavior

    # Conflicts:
    #	Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake
    #	Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake

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

commit 542bdfc5d762c07938f9a0f8e729070f1df9edfc
Merge: 3e7564c944 4899f67986
Author: Allen Jackson <[email protected]>
Date:   Wed Sep 1 17:09:10 2021 -0500

    Merge pull request #207 from aws-lumberyard-dev/feature_lyn5392_proc_prefab_group

    {lyn5392} Adding PrefabGroup scene manifest rule

commit 4899f67986992b7eeb5cc3b750493ed21ec7b4f3
Author: Jackson <[email protected]>
Date:   Wed Sep 1 16:33:24 2021 -0500

    removing unneeded AZStd::move() calls

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

commit 5ad0aac7472d24bb85990d36399a466288ecfdbc
Author: Jackson <[email protected]>
Date:   Wed Sep 1 16:17:52 2021 -0500

    cleaned up the code
    finalized the unit tests
    fixed the code based on the tests... FTW!

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

commit 24f289dd31292c4b9f5e5848ddc94efd986b4341
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 13:06:20 2021 -0500

    Add FindComponentByTypeName and UpdateComponentForEntity APIs

    Add unit tests

    Signed-off-by: amzn-mike <[email protected]>

commit c1b8b5190f973d2429731308f50c7c962d64301e
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 14:02:17 2021 -0500

    Fix bus connect that should have been a bus disconnect

    Signed-off-by: amzn-mike <[email protected]>

commit fde870d6c92e131875ec6aadda2365203a538379
Author: amzn-mike <[email protected]>
Date:   Wed Sep 1 12:57:04 2021 -0500

    Fix up includes

    Signed-off-by: amzn-mike <[email protected]>

commit 8ceddd0efc90d33bc34b5287176497356e57abf9
Author: jackalbe <[email protected]>
Date:   Wed Sep 1 08:43:23 2021 -0500

    WIP

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

commit 3cc9135d87efde24430dbf1cd112bcf9b95ecaa0
Author: amzn-mike <[email protected]>
Date:   Tue Aug 31 14:44:37 2021 -0500

    Rename files

    Signed-off-by: amzn-mike <[email protected]>

commit f056b3d9578ae2f49dbc172a5929306047620be6
Author: amzn-mike <[email protected]>
Date:   Tue Aug 31 14:14:34 2021 -0500

    Remove 'editor' from bus/component name

    Signed-off-by: amzn-mike <[email protected]>

commit 0fb7a0788e879eb9dc241291f05f0acffc0b0156
Author: jackalbe <[email protected]>
Date:   Tue Aug 31 09:18:37 2021 -0500

    {lyn5392} Adding PrefabGroup scene manifest rule

    * Adding PrefabGroup abstraction and concrete classes to fill out in a scene manifest
    * has reflections for serialization & behavior
    * testing the behavior using Lua
    * testing the serialization using JSON

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

commit b1756307bff9f86ae0ae1354bae8f470d19b4487
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 17:52:18 2021 -0500

    Reflect SaveTemplateToString

    Add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit ddd2bb89041c5fb8b6add2cfc02c454baab9a7d6
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 12:46:17 2021 -0500

    Add warning/error messages, update module

    Signed-off-by: amzn-mike <[email protected]>

commit e839c1cbcc340859fc6ee5cff2d802660944194b
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 11:17:25 2021 -0500

    Add some comments and error handling

    Signed-off-by: amzn-mike <[email protected]>

commit 347f787cc88405541889e842151de81a70598dd1
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 17:16:45 2021 -0500

    Fix line endings

    Signed-off-by: amzn-mike <[email protected]>

commit be26ab1cb16221ba879c10652b430ea31547d868
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 15:28:00 2021 -0500

    Add CreatePrefabTemplate

    Add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit 3e7564c944497b7b6cce94dbce486ed8c4561f33
Merge: 5a3c289fac 07841ee749
Author: amzn-mike <[email protected]>
Date:   Mon Aug 30 10:20:20 2021 -0500

    Merge pull request #199 from aws-lumberyard-dev/Feature_LY-5384_ProceduralPrefabs_EntityManagement

    Add editor entity creation and unit test

commit 07841ee749d2a1bc22352cbec3076ac087348676
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 16:44:57 2021 -0500

    Fix line endings

    Signed-off-by: amzn-mike <[email protected]>

commit 35bc3b89cd1fcc126114ea7d61cdd21e88699080
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 15:03:35 2021 -0500

    Setup CreateEditorReadyEntity to use a custom entity context

    Add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit 82519b15510a4ec1b2daf4d883a2c4c3d9c0a347
Author: amzn-mike <[email protected]>
Date:   Fri Aug 27 09:29:20 2021 -0500

    Add EditorEntityUtilityComponent for managing entities from the behavior context

    Signed-off-by: amzn-mike <[email protected]>

commit f8d39e267162db5fb2091982a7e54bc57ed108bb
Merge: 43603cad5e 575faa4443
Author: Chris Galvan <[email protected]>
Date:   Wed Aug 11 15:29:33 2021 -0500

    Merge pull request #3049 from aws-lumberyard-dev/cgalvan/gitflow_210811

    Merged stabilization/2106 to main

commit 575faa4443823ed239d609692847fbd781beebcb
Merge: 43603cad5e 4b817a6483
Author: Chris Galvan <[email protected]>
Date:   Wed Aug 11 14:13:27 2021 -0500

    Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210811

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

commit 43603cad5e715aadef47b76e24867dd41347d163
Merge: d9cce28a53 bb52475ce8
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:55:51 2021 -0500

    Merge pull request #2271 from aws-lumberyard-dev/Foundation/miterenc/ContributingUpdate

    Updating CONTRIBUTING.md

commit bb52475ce8c37c29b35c81b379befe5a75db1de7
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:55:14 2021 -0500

    Updated text to be more descriptive

    Signed-off-by: Terry Michaels <[email protected]>

commit 697dfad486c69beb5ef40cbf6478d6ccd8753cd7
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:27:24 2021 -0500

    Fixed typo

    Signed-off-by: Terry Michaels <[email protected]>

commit 650e1ab44d86664b6deab7f6cebeda40c3e1361e
Author: Terry Michaels <[email protected]>
Date:   Mon Jul 19 14:19:46 2021 -0500

    Updating CONTRIBUTING.md

    Signed-off-by: Terry Michaels <[email protected]>

commit d9cce28a5387c7f5b59041869086547905f1e345
Merge: e7f787572e 486ba58628
Author: Chris Galvan <[email protected]>
Date:   Mon Jul 12 14:06:57 2021 -0500

    Merge pull request #2096 from aws-lumberyard-dev/cgalvan/gitflow_210712_main

    Merged stabilization/2106 to main

commit 486ba58628488c211a6140f26caa421c38be3f0f
Merge: e7f787572e 7cfde884d9
Author: Chris Galvan <[email protected]>
Date:   Mon Jul 12 11:12:41 2021 -0500

    Merged stabilization/2106 to development; Resolved merge conflicts

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

commit e7f787572e805c413115265e5873fb2425e2f41b
Author: Nicholas Lawson <[email protected]>
Date:   Tue Jul 6 08:03:35 2021 -0700

    Updates licenses to APACHE-2.0 OR MIT (#1685)

    Not to be committed before 7/6/2021

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

commit 837e1c737059a819d01ef4ebed7fadae42248dd8
Merge: d30de01752 efcbe2c4a1
Author: Chris Galvan <[email protected]>
Date:   Fri Jul 2 12:11:27 2021 -0500

    Merge pull request #1764 from aws-lumberyard-dev/cgalvan/gitflow_210702

    Merged stabilization/2106 to main

commit efcbe2c4a1e909182dfbd5394f9322513ba91115
Merge: d30de01752 0c43493e29
Author: Chris Galvan <[email protected]>
Date:   Fri Jul 2 10:20:42 2021 -0500

    Merge remote-tracking branch 'upstream/stabilization/2106' into cgalvan/gitflow_210702

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

commit d30de01752b7eaed1f16e2163b1a75c915f061eb
Author: Alex Peterson <[email protected]>
Date:   Mon Jun 28 11:20:36 2021 -0700

    Updating LFS config to new endpoint (#1624)

    Signed-off-by: AMZN-alexpete

Signed-off-by: amzn-mike <[email protected]>
Signed-off-by: jackalbe <[email protected]>

* fix for "warning C4100: 'outputValueTypeId': unreferenced formal"

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

* Squashed commit of the following:

commit dbd3526517bcb6553402cbc0af1f02e1f68e0707
Author: amzn-mike <[email protected]>
Date:   Mon Oct 4 14:30:56 2021 -0500

    Increased scene manifest max size to size_t::max to match default json size limit

    Signed-off-by: amzn-mike <[email protected]>

commit ea4a9ffd23feb3beeadc9bf6ca765c96980e9e6d
Author: amzn-mike <[email protected]>
Date:   Fri Oct 1 19:15:25 2021 -0500

    Switch to querying cache location from Settings Registry

    Signed-off-by: amzn-mike <[email protected]>

commit 1c197996132625b8e26856fc92a30f9862d9dfdb
Author: amzn-mike <[email protected]>
Date:   Fri Oct 1 14:42:32 2021 -0500

    Update to look in cache for generated manifest instead of source folder

    Signed-off-by: amzn-mike <[email protected]>

commit cf3c32791fd71dc48066f9783c8859d386370b95
Author: amzn-mike <[email protected]>
Date:   Thu Sep 30 11:09:40 2021 -0500

    Added generated manifest to dependency tracking.  Updated unit tests

    Signed-off-by: amzn-mike <[email protected]>

commit 56cb0d27982e61c2cf123b765def8b5c9e21b021
Author: amzn-mike <[email protected]>
Date:   Wed Sep 29 15:51:52 2021 -0500

    Moved manifest size const to header and used that in scene builder

    Signed-off-by: amzn-mike <[email protected]>

commit 7c8016a0ba6000090b29371f253b7906c9f3d141
Author: amzn-mike <[email protected]>
Date:   Wed Sep 29 15:21:06 2021 -0500

    Add doc comment

    Signed-off-by: amzn-mike <[email protected]>

commit 45fd5473f5ed6fe4fe93f8d629857852bcbe5e03
Author: amzn-mike <[email protected]>
Date:   Wed Sep 29 15:11:36 2021 -0500

    Clean up code and add unit test

    Signed-off-by: amzn-mike <[email protected]>

commit d0e610cad380e2278bc18552a4d1c71c64a5d339
Author: amzn-mike <[email protected]>
Date:   Wed Sep 29 13:21:58 2021 -0500

    Source dependency reporting WIP

    Signed-off-by: amzn-mike <[email protected]>

    # Conflicts:
    #	Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp

(cherry picked from commit d6464dcee20ea4e6f2d40f0d3301f24be5f4dee6)
Signed-off-by: amzn-mike <[email protected]>

* Add comment on unit test globals, fix indentation

Signed-off-by: amzn-mike <[email protected]>

* Cleaned up scene_mesh_to_prefab.py

Added comments
Removed raise_error function
Made mesh_group_name cleanup more comprehensive

Signed-off-by: amzn-mike <[email protected]>

* fix for "warning C4100: 'message': unreferenced formal"

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

Co-authored-by: jackalbe <[email protected]>
Co-authored-by: Guthrie Adams <[email protected]>
amzn-mike 3 years ago
parent
commit
5cee9b43b7
69 changed files with 3696 additions and 95 deletions
  1. 162 0
      AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py
  2. 1 2
      AutomatedTesting/Gem/Code/enabled_gems.cmake
  3. 3 0
      AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png
  4. 3 0
      AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx
  5. 8 0
      AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo
  6. 3 9
      Code/Framework/AzCore/AzCore/Math/Uuid.cpp
  7. 2 1
      Code/Framework/AzCore/AzCore/Math/Uuid.h
  8. 23 0
      Code/Framework/AzCore/Tests/UUIDTests.cpp
  9. 2 0
      Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp
  10. 3 1
      Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp
  11. 2 0
      Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp
  12. 351 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp
  13. 90 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h
  14. 1 0
      Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h
  15. 29 15
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp
  16. 3 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h
  17. 0 1
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h
  18. 45 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h
  19. 17 3
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp
  20. 5 2
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h
  21. 1 2
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h
  22. 38 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h
  23. 75 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp
  24. 39 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h
  25. 144 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp
  26. 90 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h
  27. 37 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp
  28. 42 0
      Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h
  29. 73 0
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp
  30. 2 0
      Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h
  31. 10 0
      Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake
  32. 252 0
      Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp
  33. 173 0
      Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp
  34. 135 0
      Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp
  35. 3 0
      Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake
  36. 2 0
      Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp
  37. 4 1
      Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp
  38. 1 1
      Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp
  39. 11 0
      Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp
  40. 4 0
      Code/Tools/SceneAPI/SceneCore/Containers/Scene.h
  41. 1 1
      Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp
  42. 1 2
      Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp
  43. 2 0
      Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h
  44. 26 1
      Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp
  45. 19 0
      Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h
  46. 7 1
      Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp
  47. 1 0
      Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h
  48. 71 46
      Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp
  49. 4 4
      Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h
  50. 8 1
      Gems/Prefab/PrefabBuilder/CMakeLists.txt
  51. 3 1
      Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp
  52. 1 0
      Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h
  53. 25 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h
  54. 161 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp
  55. 104 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl
  56. 136 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp
  57. 64 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h
  58. 251 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp
  59. 55 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h
  60. 161 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp
  61. 185 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp
  62. 39 0
      Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h
  63. 162 0
      Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp
  64. 7 0
      Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake
  65. 3 0
      Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake
  66. 9 0
      Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py
  67. 96 0
      Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp
  68. 5 0
      Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h
  69. 200 0
      Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp

+ 162 - 0
AutomatedTesting/Editor/Scripts/scene_mesh_to_prefab.py

@@ -0,0 +1,162 @@
+#
+# Copyright (c) Contributors to the Open 3D Engine Project.
+# For complete copyright and license terms please see the LICENSE at the root of this distribution.
+#
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+#
+#
+import os, traceback, binascii, sys, json, pathlib
+import azlmbr.math
+import azlmbr.bus
+
+#
+# SceneAPI Processor
+#
+
+
+def log_exception_traceback():
+    exc_type, exc_value, exc_tb = sys.exc_info()
+    data = traceback.format_exception(exc_type, exc_value, exc_tb)
+    print(str(data))
+
+def get_mesh_node_names(sceneGraph):
+    import azlmbr.scene as sceneApi
+    import azlmbr.scene.graph
+    from scene_api import scene_data as sceneData
+
+    meshDataList = []
+    node = sceneGraph.get_root()
+    children = []
+    paths = []
+
+    while node.IsValid():
+        # store children to process after siblings
+        if sceneGraph.has_node_child(node):
+            children.append(sceneGraph.get_node_child(node))
+
+        nodeName = sceneData.SceneGraphName(sceneGraph.get_node_name(node))
+        paths.append(nodeName.get_path())
+
+        # store any node that has mesh data content
+        nodeContent = sceneGraph.get_node_content(node)
+        if nodeContent.CastWithTypeName('MeshData'):
+            if sceneGraph.is_node_end_point(node) is False:
+                if (len(nodeName.get_path())):
+                    meshDataList.append(sceneData.SceneGraphName(sceneGraph.get_node_name(node)))
+
+        # advance to next node
+        if sceneGraph.has_node_sibling(node):
+            node = sceneGraph.get_node_sibling(node)
+        elif children:
+            node = children.pop()
+        else:
+            node = azlmbr.scene.graph.NodeIndex()
+
+    return meshDataList, paths
+
+def update_manifest(scene):
+    import json
+    import uuid, os
+    import azlmbr.scene as sceneApi
+    import azlmbr.scene.graph
+    from scene_api import scene_data as sceneData
+
+    graph = sceneData.SceneGraph(scene.graph)
+    # Get a list of all the mesh nodes, as well as all the nodes
+    mesh_name_list, all_node_paths = get_mesh_node_names(graph)
+    scene_manifest = sceneData.SceneManifest()
+    
+    clean_filename = scene.sourceFilename.replace('.', '_')
+    
+    # Compute the filename of the scene file
+    source_basepath = scene.watchFolder
+    source_relative_path = os.path.dirname(os.path.relpath(clean_filename, source_basepath))
+    source_filename_only = os.path.basename(clean_filename)
+
+    created_entities = []
+
+    # Loop every mesh node in the scene
+    for activeMeshIndex in range(len(mesh_name_list)):
+        mesh_name = mesh_name_list[activeMeshIndex]
+        mesh_path = mesh_name.get_path()
+        # Create a unique mesh group name using the filename + node name
+        mesh_group_name = '{}_{}'.format(source_filename_only, mesh_name.get_name())
+        # Remove forbidden filename characters from the name since this will become a file on disk later
+        mesh_group_name = "".join(char for char in mesh_group_name if char not in "|<>:\"/?*\\")
+        # Add the MeshGroup to the manifest and give it a unique ID
+        mesh_group = scene_manifest.add_mesh_group(mesh_group_name)
+        mesh_group['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, source_filename_only + mesh_path)) + '}'
+        # Set our current node as the only node that is included in this MeshGroup
+        scene_manifest.mesh_group_select_node(mesh_group, mesh_path)
+
+        # Explicitly remove all other nodes to prevent implicit inclusions
+        for node in all_node_paths:
+            if node != mesh_path:
+                scene_manifest.mesh_group_unselect_node(mesh_group, node)
+
+        # Create an editor entity
+        entity_id = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "CreateEditorReadyEntity", mesh_group_name)
+        # Add an EditorMeshComponent to the entity
+        editor_mesh_component = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "GetOrAddComponentByTypeName", entity_id, "AZ::Render::EditorMeshComponent")
+        # Set the ModelAsset assetHint to the relative path of the input asset + the name of the MeshGroup we just created + the azmodel extension
+        # The MeshGroup we created will be output as a product in the asset's path named mesh_group_name.azmodel
+        # The assetHint will be converted to an AssetId later during prefab loading
+        json_update = json.dumps({
+                            "Controller": { "Configuration": { "ModelAsset": {
+                                "assetHint": os.path.join(source_relative_path, mesh_group_name) + ".azmodel" }}}
+                            });
+        # Apply the JSON above to the component we created
+        result = azlmbr.entity.EntityUtilityBus(azlmbr.bus.Broadcast, "UpdateComponentForEntity", entity_id, editor_mesh_component, json_update)
+
+        if not result:
+            raise RuntimeError("UpdateComponentForEntity failed")
+
+        # Keep track of the entity we set up, we'll add them all to the prefab we're creating later
+        created_entities.append(entity_id)
+
+    # Create a prefab with all our entities
+    prefab_filename = source_filename_only + ".prefab"
+    created_template_id = azlmbr.prefab.PrefabSystemScriptingBus(azlmbr.bus.Broadcast, "CreatePrefab", created_entities, prefab_filename)
+
+    if created_template_id == azlmbr.prefab.InvalidTemplateId:
+        raise RuntimeError("CreatePrefab {} failed".format(prefab_filename))
+
+    # Convert the prefab to a JSON string
+    output = azlmbr.prefab.PrefabLoaderScriptingBus(azlmbr.bus.Broadcast, "SaveTemplateToString", created_template_id)
+
+    if output.IsSuccess():
+        jsonString = output.GetValue()
+        uuid = azlmbr.math.Uuid_CreateRandom().ToString()
+        jsonResult = json.loads(jsonString)
+        # Add a PrefabGroup to the manifest and store the JSON on it
+        scene_manifest.add_prefab_group(source_filename_only, uuid, jsonResult)
+    else:
+        raise RuntimeError("SaveTemplateToString failed for template id {}, prefab {}".format(created_template_id, prefab_filename))
+
+    # Convert the manifest to a JSON string and return it
+    new_manifest = scene_manifest.export()
+
+    return new_manifest
+
+sceneJobHandler = None
+
+def on_update_manifest(args):
+    try:
+        scene = args[0]
+        return update_manifest(scene)
+    except RuntimeError as err:
+        print (f'ERROR - {err}')
+        log_exception_traceback()
+
+    global sceneJobHandler
+    sceneJobHandler = None
+
+# try to create SceneAPI handler for processing
+try:
+    import azlmbr.scene as sceneApi
+    if (sceneJobHandler == None):
+        sceneJobHandler = sceneApi.ScriptBuildingNotificationBusHandler()
+        sceneJobHandler.connect()
+        sceneJobHandler.add_callback('OnUpdateManifest', on_update_manifest)
+except:
+    sceneJobHandler = None

+ 1 - 2
AutomatedTesting/Gem/Code/enabled_gems.cmake

@@ -21,7 +21,6 @@ set(ENABLED_GEMS
     QtForPython
     PythonAssetBuilder
     Metastream
-
     Camera
     EMotionFX
     AtomTressFX
@@ -53,6 +52,6 @@ set(ENABLED_GEMS
     AWSCore
     AWSClientAuth
     AWSMetrics
-    
+    PrefabBuilder
     AudioSystem
 )

+ 3 - 0
AutomatedTesting/multiple_mesh_one_material/FBXTestTexture.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:827a63985273229050bf4f63030bcc666f045091fe81cf8157a9ca23b40074b6
+size 3214

+ 3 - 0
AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d8d24963e6e8765205bc79cbe2304fc39f1245ee75249e2834a71c96c3cab824
+size 22700

+ 8 - 0
AutomatedTesting/multiple_mesh_one_material/multiple_mesh_one_material.fbx.assetinfo

@@ -0,0 +1,8 @@
+{
+    "values": [
+        {
+            "$type": "ScriptProcessorRule",
+            "scriptFilename": "Editor/Scripts/scene_mesh_to_prefab.py"
+        }
+    ]
+}

+ 3 - 9
Code/Framework/AzCore/AzCore/Math/Uuid.cpp

@@ -135,18 +135,12 @@ namespace AZ
         {
             stringLength = strlen(uuidString);
         }
-        if (stringLength > MaxPermissiveStringSize)
-        {
-            if (!skipWarnings)
-            {
-                AZ_Warning("Math", false, "Can't create UUID from string length %zu over maximum %zu", stringLength, MaxPermissiveStringSize);
-            }
-            return Uuid::CreateNull();
-        }
+
         size_t newLength{ 0 };
         char createString[MaxPermissiveStringSize];
 
-        for (size_t curPos = 0; curPos < stringLength; ++curPos)
+        // Loop until we get to the end of the string OR stop once we've accumulated a full UUID string worth of data
+        for (size_t curPos = 0; curPos < stringLength && newLength < ValidUuidStringLength; ++curPos)
         {
             char curChar = uuidString[curPos];
             switch (curChar)

+ 2 - 1
Code/Framework/AzCore/AzCore/Math/Uuid.h

@@ -42,8 +42,9 @@ namespace AZ
             //VER_AZ_RANDOM_CRC32 = 6, // 0 1 1 0
         };
 
+        static constexpr int ValidUuidStringLength = 32; /// Number of characters (data only, no extra formatting) in a valid UUID string
         static const size_t MaxStringBuffer = 39; /// 32 Uuid + 4 dashes + 2 brackets + 1 terminate
-
+        
         Uuid()  {}
         Uuid(const char* string, size_t stringLength = 0) { *this = CreateString(string, stringLength); }
 

+ 23 - 0
Code/Framework/AzCore/Tests/UUIDTests.cpp

@@ -247,4 +247,27 @@ namespace UnitTest
         Uuid right = Uuid::CreateStringPermissive(permissiveStr1);
         EXPECT_EQ(left, right);
     }
+
+    TEST_F(UuidTests, CreateStringPermissive_StringWithExtraData_Succeeds)
+    {
+        const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}";
+        Uuid left = Uuid::CreateString(uuidStr);
+
+        const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World";
+        Uuid right = Uuid::CreateStringPermissive(permissiveStr);
+        EXPECT_EQ(left, right);
+
+    }
+
+    TEST_F(UuidTests, CreateStringPermissive_StringWithLotsOfExtraData_Succeeds)
+    {
+        const char uuidStr[] = "{34D44249-E599-4B30-811F-4215C2DEA269}";
+        Uuid left = Uuid::CreateString(uuidStr);
+
+        const char permissiveStr[] = "0x34D44249-0xE5994B30-0x811F4215-0xC2DEA269 Hello World this is a really long string "
+        "with lots of extra data to make sure we can parse a long string without failing as long as the uuid is in "
+        "the beginning of the string then we should succeed";
+        Uuid right = Uuid::CreateStringPermissive(permissiveStr);
+        EXPECT_EQ(left, right);
+    }
 }

+ 2 - 0
Code/Framework/AzFramework/AzFramework/Entity/BehaviorEntity.cpp

@@ -133,6 +133,8 @@ namespace AzFramework
             behaviorContext->Class<BehaviorEntity>("Entity")
                 ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
                 ->Attribute(AZ::Script::Attributes::ConstructorOverride, &Internal::BehaviorEntityScriptConstructor)
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Module, "entity")
                 ->Constructor()
                 ->Constructor<AZ::EntityId>()
                 ->Constructor<AZ::Entity*>()

+ 3 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp

@@ -69,6 +69,7 @@
 #include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiSystemComponent.h>
 #include <AzToolsFramework/Undo/UndoCacheInterface.h>
 #include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
+#include <Entity/EntityUtilityComponent.h>
 
 #include <QtWidgets/QMessageBox>
 AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer<QFileInfoPrivate>' needs to have dll-interface to be used by clients of class 'QFileInfo'
@@ -271,7 +272,8 @@ namespace AzToolsFramework
                 azrtti_typeid<AzToolsFramework::EditorInteractionSystemComponent>(),
                 azrtti_typeid<Components::EditorEntitySearchComponent>(),
                 azrtti_typeid<Components::EditorIntersectorComponent>(),
-                azrtti_typeid<AzToolsFramework::SliceRequestComponent>()
+                azrtti_typeid<AzToolsFramework::SliceRequestComponent>(),
+                azrtti_typeid<AzToolsFramework::EntityUtilityComponent>()
             });
 
         return components;

+ 2 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFrameworkModule.cpp

@@ -53,6 +53,7 @@
 #include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
 #include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
 #include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
+#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
 
 AZ_DEFINE_BUDGET(AzToolsFramework);
 
@@ -71,6 +72,7 @@ namespace AzToolsFramework
             Components::EditorSelectionAccentSystemComponent::CreateDescriptor(),
             EditorEntityContextComponent::CreateDescriptor(),
             EditorEntityFixupComponent::CreateDescriptor(),
+            EntityUtilityComponent::CreateDescriptor(),
             ContainerEntitySystemComponent::CreateDescriptor(),
             FocusModeSystemComponent::CreateDescriptor(),
             SliceMetadataEntityContextComponent::CreateDescriptor(),

+ 351 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.cpp

@@ -0,0 +1,351 @@
+/*
+ * 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 <sstream>
+#include <AzCore/JSON/rapidjson.h>
+#include <AzCore/Serialization/Json/JsonSerialization.h>
+#include <AzCore/Serialization/Json/JsonSerializationSettings.h>
+#include <AzCore/Serialization/Json/JsonUtils.h>
+#include <AzFramework/Entity/EntityContext.h>
+#include <AzFramework/FileFunc/FileFunc.h>
+#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
+#include <Entity/EditorEntityContextBus.h>
+#include <rapidjson/document.h>
+
+namespace AzToolsFramework
+{
+    void ComponentDetails::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<ComponentDetails>()
+                ->Field("TypeInfo", &ComponentDetails::m_typeInfo)
+                ->Field("BaseClasses", &ComponentDetails::m_baseClasses);
+
+            serializeContext->RegisterGenericType<AZStd::vector<ComponentDetails>>();
+        }
+
+        if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+        {
+            behaviorContext->Class<ComponentDetails>()
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Module, "entity")
+                ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                ->Property("TypeInfo", BehaviorValueProperty(&ComponentDetails::m_typeInfo))
+                ->Property("BaseClasses", BehaviorValueProperty(&ComponentDetails::m_baseClasses))
+                ->Method("__repr__", [](const ComponentDetails& obj)
+                {
+                    std::ostringstream result;
+                    bool first = true;
+
+                    for (const auto& baseClass : obj.m_baseClasses)
+                    {
+                        if (!first)
+                        {
+                            result << ", ";
+                        }
+
+                        first = false;
+                        result << baseClass.c_str();
+                    }
+                    
+                    return AZStd::string::format("%s, Base Classes: <%s>", obj.m_typeInfo.c_str(), result.str().c_str());
+                })
+                ->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString);
+        }
+    }
+
+    AZ::EntityId EntityUtilityComponent::CreateEditorReadyEntity(const AZStd::string& entityName)
+    {
+        auto* newEntity = m_entityContext->CreateEntity(entityName.c_str());
+
+        if (!newEntity)
+        {
+            AZ_Error("EditorEntityUtility", false, "Failed to create new entity %s", entityName.c_str());
+            return AZ::EntityId();
+        }
+
+        AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
+            &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *newEntity);
+
+        newEntity->Init();
+        auto newEntityId = newEntity->GetId();
+
+        m_createdEntities.emplace_back(newEntityId);
+
+        return newEntityId;
+    }
+
+    AZ::TypeId GetComponentTypeIdFromName(const AZStd::string& typeName)
+    {
+        // Try to create a TypeId first.  We won't show any warnings if this fails as the input might be a class name instead
+        AZ::TypeId typeId = AZ::TypeId::CreateStringPermissive(typeName.data());
+
+        // If the typeId is null, try a lookup by class name
+        if (typeId.IsNull())
+        {
+            AZ::SerializeContext* serializeContext = nullptr;
+            AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
+
+            auto typeNameCrc = AZ::Crc32(typeName.data());
+            auto typeUuidList = serializeContext->FindClassId(typeNameCrc);
+
+            // TypeId is invalid or class name is invalid
+            if (typeUuidList.empty())
+            {
+                AZ_Error("EntityUtilityComponent", false, "Provided type %s is either an invalid TypeId or does not match any class names", typeName.c_str());
+                return AZ::TypeId::CreateNull();
+            }
+
+            typeId = typeUuidList[0];
+        }
+
+        return typeId;
+    }
+
+    AZ::Component* FindComponentHelper(AZ::EntityId entityId, const AZ::TypeId& typeId, AZ::ComponentId componentId, bool createComponent = false)
+    {
+        AZ::Entity* entity = nullptr;
+        AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
+
+        if (!entity)
+        {
+            AZ_Error("EntityUtilityComponent", false, "Invalid entityId %s", entityId.ToString().c_str());
+            return nullptr;
+        }
+
+        AZ::Component* component = nullptr;
+        if (componentId != AZ::InvalidComponentId)
+        {
+            component = entity->FindComponent(componentId);
+        }
+        else
+        {
+            component = entity->FindComponent(typeId);
+        }
+
+        if (!component && createComponent)
+        {
+            component = entity->CreateComponent(typeId);
+        }
+
+        if (!component)
+        {
+            AZ_Error(
+                "EntityUtilityComponent", false, "Failed to find component (%s) on entity %s (%s)",
+                componentId != AZ::InvalidComponentId ? AZStd::to_string(componentId).c_str()
+                                                      : typeId.ToString<AZStd::string>().c_str(),
+                entityId.ToString().c_str(),
+                entity->GetName().c_str());
+            return nullptr;
+        }
+
+        return component;
+    }
+
+    AzFramework::BehaviorComponentId EntityUtilityComponent::GetOrAddComponentByTypeName(AZ::EntityId entityId, const AZStd::string& typeName)
+    {
+        AZ::TypeId typeId = GetComponentTypeIdFromName(typeName);
+
+        if (typeId.IsNull())
+        {
+            return AzFramework::BehaviorComponentId(AZ::InvalidComponentId);
+        }
+
+        AZ::Component* component = FindComponentHelper(entityId, typeId, AZ::InvalidComponentId, true);
+
+        return component ? AzFramework::BehaviorComponentId(component->GetId()) : 
+            AzFramework::BehaviorComponentId(AZ::InvalidComponentId);
+    }
+
+    bool EntityUtilityComponent::UpdateComponentForEntity(AZ::EntityId entityId, AzFramework::BehaviorComponentId componentId, const AZStd::string& json)
+    {
+        if (!componentId.IsValid())
+        {
+            AZ_Error("EntityUtilityComponent", false, "Invalid componentId passed to UpdateComponentForEntity");
+            return false;
+        }
+
+        AZ::Component* component = FindComponentHelper(entityId, AZ::TypeId::CreateNull(), componentId);
+
+        if (!component)
+        {
+            return false;
+        }
+
+        using namespace AZ::JsonSerializationResult;
+
+        AZ::JsonDeserializerSettings settings = AZ::JsonDeserializerSettings{};
+        settings.m_reporting = []([[maybe_unused]] AZStd::string_view message, ResultCode result, AZStd::string_view) -> auto
+        {
+            if (result.GetProcessing() == Processing::Halted)
+            {
+                AZ_Error("EntityUtilityComponent", false, "JSON %s\n", message.data());
+            }
+            else if (result.GetOutcome() > Outcomes::PartialDefaults)
+            {
+                AZ_Warning("EntityUtilityComponent", false, "JSON %s\n", message.data());
+            }
+            return result;
+        };
+        
+        rapidjson::Document doc;
+        doc.Parse<rapidjson::kParseCommentsFlag>(json.data(), json.size());
+        ResultCode resultCode = AZ::JsonSerialization::Load(*component, doc, settings);
+
+        return resultCode.GetProcessing() != Processing::Halted;
+    }
+
+    AZStd::string EntityUtilityComponent::GetComponentDefaultJson(const AZStd::string& typeName)
+    {
+        AZ::TypeId typeId = GetComponentTypeIdFromName(typeName);
+
+        if (typeId.IsNull())
+        {
+            // GetComponentTypeIdFromName already does error handling
+            return "";
+        }
+
+        AZ::SerializeContext* serializeContext = nullptr;
+        AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
+
+        const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId);
+
+        if (!classData)
+        {
+            AZ_Error("EntityUtilityComponent", false, "Failed to find ClassData for typeId %s (%s)", typeId.ToString<AZStd::string>().c_str(), typeName.c_str());
+            return "";
+        }
+
+        void* component = classData->m_factory->Create("Component");
+        rapidjson::Document document;
+        AZ::JsonSerializerSettings settings;
+        settings.m_keepDefaults = true;
+
+        auto resultCode = AZ::JsonSerialization::Store(document, document.GetAllocator(), component, nullptr, typeId, settings);
+
+        // Clean up the allocated component ASAP, we don't need it anymore
+        classData->m_factory->Destroy(component);
+
+        if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
+        {
+            AZ_Error("EntityUtilityComponent", false, "Failed to serialize component to json (%s): %s",
+                typeName.c_str(), resultCode.ToString(typeName).c_str())
+            return "";
+        }
+
+        AZStd::string jsonString;
+        AZ::Outcome<void, AZStd::string> outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString);
+
+        if (!outcome.IsSuccess())
+        {
+            AZ_Error("EntityUtilityComponent", false, "Failed to write component json to string: %s", outcome.GetError().c_str());
+            return "";
+        }
+
+        return jsonString;
+    }
+
+    AZStd::vector<ComponentDetails> EntityUtilityComponent::FindMatchingComponents(const AZStd::string& searchTerm)
+    {
+        AZ::SerializeContext* serializeContext = nullptr;
+        AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
+
+        if (m_typeInfo.empty())
+        {
+            serializeContext->EnumerateDerived<AZ::Component>(
+                [this, serializeContext](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid& /*typeId*/)
+                {
+                    auto& typeInfo = m_typeInfo.emplace_back(classData->m_typeId, classData->m_name, AZStd::vector<AZStd::string>{});
+
+                    serializeContext->EnumerateBase(
+                        [&typeInfo](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&)
+                        {
+                            if (classData)
+                            {
+                                AZStd::get<2>(typeInfo).emplace_back(classData->m_name);
+                            }
+                            return true;
+                        },
+                        classData->m_typeId);
+                    
+                    return true;
+                });
+        }
+        
+        AZStd::vector<ComponentDetails> matches;
+
+        for (const auto& [typeId, typeName, baseClasses] : m_typeInfo)
+        {
+            if (AZStd::wildcard_match(searchTerm, typeName))
+            {
+                ComponentDetails details;
+                details.m_typeInfo = AZStd::string::format("%s %s", typeId.ToString<AZStd::string>().c_str(), typeName.c_str());
+                details.m_baseClasses = baseClasses;
+                
+                matches.emplace_back(AZStd::move(details));
+            }
+        }
+
+        return matches;
+    }
+
+    void EntityUtilityComponent::ResetEntityContext()
+    {
+        for (AZ::EntityId entityId : m_createdEntities)
+        {
+            m_entityContext->DestroyEntityById(entityId);
+        }
+
+        m_createdEntities.clear();
+        m_entityContext->ResetContext();
+    }
+
+    void EntityUtilityComponent::Reflect(AZ::ReflectContext* context)
+    {
+        ComponentDetails::Reflect(context);
+
+        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<EntityUtilityComponent, AZ::Component>();
+        }
+
+        if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+        {
+            behaviorContext->ConstantProperty("InvalidComponentId", BehaviorConstant(AZ::InvalidComponentId))
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Category, "Entity")
+                ->Attribute(AZ::Script::Attributes::Module, "entity");
+
+            behaviorContext->EBus<EntityUtilityBus>("EntityUtilityBus")
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                ->Attribute(AZ::Script::Attributes::Category, "Entity")
+                ->Attribute(AZ::Script::Attributes::Module, "entity")
+                ->Event("CreateEditorReadyEntity", &EntityUtilityBus::Events::CreateEditorReadyEntity)
+                ->Event("GetOrAddComponentByTypeName", &EntityUtilityBus::Events::GetOrAddComponentByTypeName)
+                ->Event("UpdateComponentForEntity", &EntityUtilityBus::Events::UpdateComponentForEntity)
+                ->Event("FindMatchingComponents", &EntityUtilityBus::Events::FindMatchingComponents)
+                ->Event("GetComponentDefaultJson", &EntityUtilityBus::Events::GetComponentDefaultJson)
+            ;
+        }
+    }
+
+    void EntityUtilityComponent::Activate()
+    {
+        m_entityContext = AZStd::make_unique<AzFramework::EntityContext>(UtilityEntityContextId);
+        m_entityContext->InitContext();
+        EntityUtilityBus::Handler::BusConnect();
+    }
+
+    void EntityUtilityComponent::Deactivate()
+    {
+        EntityUtilityBus::Handler::BusDisconnect();
+        m_entityContext = nullptr;
+    }
+}

+ 90 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EntityUtilityComponent.h

@@ -0,0 +1,90 @@
+/*
+ * 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/Component/Component.h>
+#include <AzCore/Component/Entity.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
+#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h>
+#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
+#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
+#include <AzCore/Component/ComponentApplication.h>
+#include <AzFramework/Entity/BehaviorEntity.h>
+
+namespace AzToolsFramework
+{
+    struct ComponentDetails
+    {
+        AZ_TYPE_INFO(AzToolsFramework::ComponentDetails, "{107D8379-4AD4-4547-BEE1-184B120F23E9}");
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        AZStd::string m_typeInfo;
+        AZStd::vector<AZStd::string> m_baseClasses;
+    };
+
+    // This ebus is intended to provide behavior-context friendly APIs to create and manage entities
+    struct EntityUtilityTraits : AZ::EBusTraits
+    {
+        AZ_RTTI(AzToolsFramework::EntityUtilityTraits, "{A6305CAE-C825-43F9-A44D-E503910912AF}");
+
+        virtual ~EntityUtilityTraits() = default;
+
+        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+
+        // Creates an entity with the default editor components attached and initializes the entity
+        virtual AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) = 0;
+
+        virtual AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) = 0;
+
+        virtual bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) = 0;
+
+        // Gets a JSON string containing describing the default serialization state of the specified component
+        virtual AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) = 0;
+
+        // Returns a list of matching component type names.  Supports wildcard search terms
+        virtual AZStd::vector<ComponentDetails> FindMatchingComponents(const AZStd::string& searchTerm) = 0;
+
+        virtual void ResetEntityContext() = 0;
+    };
+
+    using EntityUtilityBus = AZ::EBus<EntityUtilityTraits>;
+
+    struct EntityUtilityComponent : AZ::Component
+        , EntityUtilityBus::Handler
+    {
+        inline const static AZ::Uuid UtilityEntityContextId = AZ::Uuid("{9C277B88-E79E-4F8A-BAFF-A4C175BD565F}");
+
+        AZ_COMPONENT(EntityUtilityComponent, "{47205907-A0EA-4FFF-A620-04D20C04A379}");
+
+        AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) override;
+        AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) override;
+        bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) override;
+        AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) override;
+        AZStd::vector<ComponentDetails> FindMatchingComponents(const AZStd::string& searchTerm) override;
+        void ResetEntityContext() override;
+
+        static void Reflect(AZ::ReflectContext* context);
+
+    protected:
+        void Activate() override;
+        void Deactivate() override;
+
+        // Our own entity context.  This API is intended mostly for use in Asset Builders where there is no editor context
+        // Additionally, an entity context is needed when using the Behavior Entity class
+        AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
+
+        // TypeId, TypeName, Vector<BaseClassName>
+        AZStd::vector<AZStd::tuple<AZ::TypeId, AZStd::string, AZStd::vector<AZStd::string>>> m_typeInfo;
+
+        // Keep track of the entities we create so they can be reset
+        AZStd::vector<AZ::EntityId> m_createdEntities;
+    };
+}; // namespace AzToolsFramework

+ 1 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/FocusMode/FocusModeInterface.h

@@ -10,6 +10,7 @@
 
 #include <AzCore/Component/EntityId.h>
 #include <AzCore/Interface/Interface.h>
+#include <AzCore/Component/Entity.h>
 #include <AzCore/Serialization/SerializeContext.h>
 
 #include <AzFramework/Entity/EntityContextBus.h>

+ 29 - 15
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp

@@ -50,17 +50,19 @@ namespace AzToolsFramework
 
             auto settingsRegistry = AZ::SettingsRegistry::Get();
             AZ_Assert(settingsRegistry, "Settings registry is not set");
-            
+
             [[maybe_unused]] bool result =
                 settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
             AZ_Warning("Prefab", result, "Couldn't retrieve project root path");
             m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred();
 
             AZ::Interface<PrefabLoaderInterface>::Register(this);
+            m_scriptingPrefabLoader.Connect(this);
         }
 
         void PrefabLoader::UnregisterPrefabLoaderInterface()
         {
+            m_scriptingPrefabLoader.Disconnect();
             AZ::Interface<PrefabLoaderInterface>::Unregister(this);
         }
 
@@ -568,7 +570,7 @@ namespace AzToolsFramework
                 (pathStr.find_first_of(AZ_FILESYSTEM_INVALID_CHARACTERS) == AZStd::string::npos) &&
                 (pathStr.back() != '\\' && pathStr.back() != '/');
         }
-
+        
         AZ::IO::Path PrefabLoader::GetFullPath(AZ::IO::PathView path)
         {
             AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred();
@@ -596,26 +598,38 @@ namespace AzToolsFramework
             {
                 // The asset system provided us with a valid root folder and relative path, so return it.
                 fullPath = AZ::IO::Path(rootFolder) / assetInfo.m_relativePath;
+                return fullPath;
             }
             else
             {
-                // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic.
+                // attempt to find the absolute from the Cache folder
+                AZStd::string assetRootFolder;
+                if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
+                {
+                    settingsRegistry->Get(assetRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
+                }
+                fullPath = AZ::IO::Path(assetRootFolder) / path;
+                if (fullPath.IsAbsolute() && AZ::IO::SystemFile::Exists(fullPath.c_str()))
+                {
+                    return fullPath;
+                }
+            }
 
-                // Check to see if the AssetProcessor is ready.  If it *is* and we didn't get a path, print an error then follow
-                // the fallback logic.  If it's *not* ready, we're probably either extremely early in a tool startup flow or inside
-                // a unit test, so just execute the fallback logic without an error.
-                [[maybe_unused]] bool assetProcessorReady = false;
-                AzFramework::AssetSystemRequestBus::BroadcastResult(
-                    assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady);
+            // If for some reason the Asset system couldn't provide a relative path, provide some fallback logic.
 
-                AZ_Error(
-                    "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.",
-                    AZ_STRING_ARG(path.Native()));
+            // Check to see if the AssetProcessor is ready.  If it *is* and we didn't get a path, print an error then follow
+            // the fallback logic.  If it's *not* ready, we're probably either extremely early in a tool startup flow or inside
+            // a unit test, so just execute the fallback logic without an error.
+            [[maybe_unused]] bool assetProcessorReady = false;
+            AzFramework::AssetSystemRequestBus::BroadcastResult(
+                assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady);
 
-                // If a relative path was passed in, make it relative to the project root.
-                fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
-            }
+            AZ_Error(
+                "Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.",
+                AZ_STRING_ARG(path.Native()));
 
+            // If a relative path was passed in, make it relative to the project root.
+            fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
             return fullPath;
         }
 

+ 3 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h

@@ -15,6 +15,7 @@
 #include <AzCore/std/containers/unordered_set.h>
 #include <AzCore/std/string/string.h>
 #include <AzToolsFramework/Prefab/PrefabDomTypes.h>
+#include <Prefab/ScriptingPrefabLoader.h>
 
 namespace AZ
 {
@@ -114,6 +115,7 @@ namespace AzToolsFramework
             void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override;
 
         private:
+
             /**
              * Copies the template dom provided and manipulates it into the proper format to be saved to disk.
              * @param templateRef The template whose dom we want to transform into the proper format to be saved to disk.
@@ -177,6 +179,7 @@ namespace AzToolsFramework
             AZStd::optional<AZStd::pair<PrefabDom, AZ::IO::Path>> StoreTemplateIntoFileFormat(TemplateId templateId);
 
             PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
+            ScriptingPrefabLoader m_scriptingPrefabLoader;
             AZ::IO::Path m_projectPathWithOsSeparator;
             AZ::IO::Path m_projectPathWithSlashSeparator;
         };

+ 0 - 1
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h

@@ -99,7 +99,6 @@ namespace AzToolsFramework
             // Generates a new path
             static AZ::IO::Path GeneratePath();
         };
-
     } // namespace Prefab
 } // namespace AzToolsFramework
 

+ 45 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h

@@ -0,0 +1,45 @@
+/*
+ * 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/Interface/Interface.h>
+#include <AzCore/IO/Path/Path.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
+#include <AzCore/EBus/EBus.h>
+
+namespace AzToolsFramework
+{
+    namespace Prefab
+    {
+        // Ebus for script-friendly APIs for the prefab loader
+        struct PrefabLoaderScriptingTraits : AZ::EBusTraits
+        {
+            AZ_TYPE_INFO(PrefabLoaderScriptingTraits, "{C344B7D8-8299-48C9-8450-26E1332EA011}");
+
+            virtual ~PrefabLoaderScriptingTraits() = default;
+
+            static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+            static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+
+            /**
+             * Saves a Prefab Template into the provided output string.
+             * Converts Prefab Template form into .prefab form by collapsing nested Template info
+             * into a source path and patches.
+             * @param templateId Id of the template to be saved
+             * @return Will contain the serialized template json on success
+             */
+            virtual AZ::Outcome<AZStd::string, void> SaveTemplateToString(TemplateId templateId) = 0;
+        };
+
+        using PrefabLoaderScriptingBus = AZ::EBus<PrefabLoaderScriptingTraits>;
+
+    } // namespace Prefab
+} // namespace AzToolsFramework
+

+ 17 - 3
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp

@@ -10,6 +10,7 @@
 
 #include <AzCore/Component/Entity.h>
 #include <AzCore/IO/Path/Path.h>
+#include <AzCore/RTTI/BehaviorContext.h>
 #include <AzCore/Serialization/Json/JsonSerialization.h>
 #include <AzCore/Serialization/Json/RegistrationContext.h>
 #include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
@@ -36,12 +37,14 @@ namespace AzToolsFramework
             m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface();
             m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface();
             m_prefabPublicRequestHandler.Connect();
+            m_prefabSystemScriptingHandler.Connect(this);
             AZ::SystemTickBus::Handler::BusConnect();
         }
 
         void PrefabSystemComponent::Deactivate()
         {
             AZ::SystemTickBus::Handler::BusDisconnect();
+            m_prefabSystemScriptingHandler.Disconnect();
             m_prefabPublicRequestHandler.Disconnect();
             m_prefabPublicHandler.UnregisterPrefabPublicHandlerInterface();
             m_instanceToTemplatePropagator.UnregisterInstanceToTemplateInterface();
@@ -58,13 +61,24 @@ namespace AzToolsFramework
             AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context);
             PrefabPublicRequestHandler::Reflect(context);
             PrefabLoader::Reflect(context);
+            PrefabSystemScriptingHandler::Reflect(context);
 
-            AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
-            if (serialize)
+            if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
             {
                 serialize->Class<PrefabSystemComponent, AZ::Component>()->Version(1);
             }
 
+            if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+            {
+
+                behaviorContext->EBus<PrefabLoaderScriptingBus>("PrefabLoaderScriptingBus")
+                    ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                    ->Attribute(AZ::Script::Attributes::Module, "prefab")
+                    ->Attribute(AZ::Script::Attributes::Category, "Prefab")
+                    ->Event("SaveTemplateToString", &PrefabLoaderScriptingBus::Events::SaveTemplateToString);
+                ;
+            }
+
             AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast<AZ::JsonRegistrationContext*>(context);
             if (jsonRegistration)
             {
@@ -145,7 +159,7 @@ namespace AzToolsFramework
                 newInstance->SetTemplateId(newTemplateId);
             }
         }
-
+        
         void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude)
         {
             UpdatePrefabInstances(templateId, immediate, instanceToExclude);

+ 5 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h

@@ -27,6 +27,7 @@
 #include <AzToolsFramework/Prefab/PrefabPublicRequestHandler.h>
 #include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
 #include <AzToolsFramework/Prefab/Template/Template.h>
+#include <Prefab/PrefabSystemScriptingHandler.h>
 
 namespace AZ
 {
@@ -219,7 +220,7 @@ namespace AzToolsFramework
                 const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
                 AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr,
                 InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override;
-
+            
             PrefabDom& FindTemplateDom(TemplateId templateId) override;
 
             /**
@@ -244,7 +245,7 @@ namespace AzToolsFramework
 
         private:
             AZ_DISABLE_COPY_MOVE(PrefabSystemComponent);
-
+            
             /**
             * Builds a new Prefab Template out of entities and instances and returns the first instance comprised of
             * these entities and instances.
@@ -412,6 +413,8 @@ namespace AzToolsFramework
 
             // Handler of the public Prefab requests.
             PrefabPublicRequestHandler m_prefabPublicRequestHandler;
+
+            PrefabSystemScriptingHandler m_prefabSystemScriptingHandler;
         };
     } // namespace Prefab
 } // namespace AzToolsFramework

+ 1 - 2
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h

@@ -78,8 +78,7 @@ namespace AzToolsFramework
                 AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt,
                 bool shouldCreateLinks = true) = 0;
         };
-
-
+        
     } // namespace Prefab
 } // namespace AzToolsFramework
 

+ 38 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingBus.h

@@ -0,0 +1,38 @@
+/*
+ * 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/Interface/Interface.h>
+#include <AzToolsFramework/Prefab/Instance/Instance.h>
+#include <AzToolsFramework/Prefab/Link/Link.h>
+#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
+#include <AzToolsFramework/Prefab/Template/Template.h>
+#include <AzCore/std/smart_ptr/unique_ptr.h>
+#include <AzCore/EBus/EBus.h>
+
+namespace AzToolsFramework
+{
+    namespace Prefab
+    {
+        // Bus that exposes a script-friendly interface to the PrefabSystemComponent
+        struct PrefabSystemScriptingEbusTraits : AZ::EBusTraits
+        {
+            using MutexType = AZ::NullMutex;
+            static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
+            static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+
+            virtual TemplateId CreatePrefabTemplate(
+                const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath) = 0;
+        };
+        
+        using PrefabSystemScriptingBus = AZ::EBus<PrefabSystemScriptingEbusTraits>;
+        
+    } // namespace Prefab
+} // namespace AzToolsFramework
+

+ 75 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/RTTI/BehaviorContext.h>
+#include <Prefab/PrefabSystemComponentInterface.h>
+#include <Prefab/PrefabSystemScriptingHandler.h>
+#include <AzCore/Component/Entity.h>
+
+namespace AzToolsFramework::Prefab
+{
+    void PrefabSystemScriptingHandler::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+        {
+            behaviorContext->ConstantProperty("InvalidTemplateId", BehaviorConstant(InvalidTemplateId))
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Module, "prefab")
+                ->Attribute(AZ::Script::Attributes::Category, "Prefab");
+
+            behaviorContext->EBus<PrefabSystemScriptingBus>("PrefabSystemScriptingBus")
+                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
+                ->Attribute(AZ::Script::Attributes::Module, "prefab")
+                ->Attribute(AZ::Script::Attributes::Category, "Prefab")
+                ->Event("CreatePrefab", &PrefabSystemScriptingBus::Events::CreatePrefabTemplate);
+        }
+    }
+
+    void PrefabSystemScriptingHandler::Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface)
+    {
+        AZ_Assert(prefabSystemComponentInterface != nullptr, "prefabSystemComponentInterface must not be null");
+        m_prefabSystemComponentInterface = prefabSystemComponentInterface;
+        PrefabSystemScriptingBus::Handler::BusConnect();
+    }
+
+    void PrefabSystemScriptingHandler::Disconnect()
+    {
+        PrefabSystemScriptingBus::Handler::BusDisconnect();
+    }
+
+    TemplateId PrefabSystemScriptingHandler::CreatePrefabTemplate(const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath)
+    {
+        AZStd::vector<AZ::Entity*> entities;
+
+        for (const auto& entityId : entityIds)
+        {
+            AZ::Entity* entity = nullptr;
+            AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
+
+            AZ_Warning(
+                "PrefabSystemComponent", entity, "EntityId %s was not found and will not be added to the prefab",
+                entityId.ToString().c_str());
+
+            if (entity)
+            {
+                entities.push_back(entity);
+            }
+        }
+        
+        auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)));
+
+        if (!prefab)
+        {
+            AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str());
+            return InvalidTemplateId;
+        }
+
+        return prefab->GetTemplateId();
+    }
+}

+ 39 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+#include <Prefab/PrefabSystemScriptingBus.h>
+
+namespace AzToolsFramework
+{
+    namespace Prefab
+    {
+        class PrefabSystemScriptingHandler
+            : PrefabSystemScriptingBus::Handler
+        {
+        public:
+            static void Reflect(AZ::ReflectContext* context);
+
+            PrefabSystemScriptingHandler() = default;
+
+            void Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface);
+            void Disconnect();
+
+        private:
+            AZ_DISABLE_COPY(PrefabSystemScriptingHandler);
+
+            //////////////////////////////////////////////////////////////////////////
+            // PrefabSystemScriptingBus implementation
+            TemplateId CreatePrefabTemplate(const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath) override;
+            //////////////////////////////////////////////////////////////////////////
+
+            PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
+        };
+    } // namespace Prefab
+} // namespace AzToolsFramework
+

+ 144 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.cpp

@@ -0,0 +1,144 @@
+/*
+ * 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 <Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Serialization/Json/RegistrationContext.h>
+#include <AzCore/Settings/SettingsRegistry.h>
+#include <AzFramework/FileFunc/FileFunc.h>
+
+namespace AZ::Prefab
+{
+    static constexpr const char s_useProceduralPrefabsKey[] = "/O3DE/Preferences/Prefabs/UseProceduralPrefabs";
+
+    // ProceduralPrefabAsset
+
+    ProceduralPrefabAsset::ProceduralPrefabAsset(const AZ::Data::AssetId& assetId)
+        : AZ::Data::AssetData(assetId)
+        , m_templateId(AzToolsFramework::Prefab::InvalidTemplateId)
+    {
+    }
+
+    void ProceduralPrefabAsset::Reflect(AZ::ReflectContext* context)
+    {
+        PrefabDomData::Reflect(context);
+
+        if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
+        {
+            serializeContext->Class<ProceduralPrefabAsset, AZ::Data::AssetData>()
+                ->Version(1)
+                ->Field("Template Name", &ProceduralPrefabAsset::m_templateName)
+                ->Field("Template ID", &ProceduralPrefabAsset::m_templateId);
+        }
+    }
+
+    const AZStd::string& ProceduralPrefabAsset::GetTemplateName() const
+    {
+        return m_templateName;
+    }
+
+    void ProceduralPrefabAsset::SetTemplateName(AZStd::string templateName)
+    {
+        m_templateName = AZStd::move(templateName);
+    }
+
+    AzToolsFramework::Prefab::TemplateId ProceduralPrefabAsset::GetTemplateId() const
+    {
+        return m_templateId;
+    }
+
+    void ProceduralPrefabAsset::SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId)
+    {
+        m_templateId = templateId;
+    }
+
+    bool ProceduralPrefabAsset::UseProceduralPrefabs()
+    {
+        bool useProceduralPrefabs = false;
+        bool result = AZ::SettingsRegistry::Get()->GetObject(useProceduralPrefabs, s_useProceduralPrefabsKey);
+        return result && useProceduralPrefabs;
+    }
+
+    // PrefabDomData
+
+    void PrefabDomData::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto* jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
+        {
+            jsonContext->Serializer<PrefabDomDataJsonSerializer>()->HandlesType<PrefabDomData>();
+        }
+
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
+        if (serializeContext)
+        {
+            serializeContext->Class<PrefabDomData>()
+                ->Version(1);
+        }
+    }
+
+    void PrefabDomData::CopyValue(const rapidjson::Value& inputValue)
+    {
+        m_prefabDom.CopyFrom(inputValue, m_prefabDom.GetAllocator());
+    }
+
+    const AzToolsFramework::Prefab::PrefabDom& PrefabDomData::GetValue() const
+    {
+        return m_prefabDom;
+    }
+
+    // PrefabDomDataJsonSerializer
+
+    AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Load(
+        void* outputValue,
+        [[maybe_unused]] const AZ::Uuid& outputValueTypeId,
+        const rapidjson::Value& inputValue,
+        AZ::JsonDeserializerContext& context)
+    {
+        AZ_Assert(outputValueTypeId == azrtti_typeid<PrefabDomData>(),
+            "PrefabDomDataJsonSerializer Load against output typeID that was not PrefabDomData");
+        AZ_Assert(outputValue, "PrefabDomDataJsonSerializer Load against null output");
+
+        namespace JSR = AZ::JsonSerializationResult;
+        JSR::ResultCode result(JSR::Tasks::ReadField);
+
+        if (inputValue.IsObject() == false)
+        {
+            result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing object"));
+            return context.Report(result, "Prefab should be an object.");
+        }
+
+        if (inputValue.MemberCount() < 1)
+        {
+            result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing members"));
+            return context.Report(result, "Prefab should have multiple members.");
+        }
+
+        auto* outputVariable = reinterpret_cast<PrefabDomData*>(outputValue);
+        outputVariable->CopyValue(inputValue);
+        return context.Report(result, "Loaded procedural prefab");
+    }
+
+    AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Store(
+        rapidjson::Value& outputValue,
+        const void* inputValue,
+        [[maybe_unused]] const void* defaultValue,
+        [[maybe_unused]] const AZ::Uuid& valueTypeId,
+        AZ::JsonSerializerContext& context)
+    {
+        AZ_Assert(inputValue, "Input value for PrefabDomDataJsonSerializer can't be null.");
+        AZ_Assert(azrtti_typeid<PrefabDomData>() == valueTypeId,
+            "Unable to Serialize because the provided type is not PrefabGroup::PrefabDomData.");
+
+        const PrefabDomData* prefabDomData = reinterpret_cast<const PrefabDomData*>(inputValue);
+
+        namespace JSR = AZ::JsonSerializationResult;
+        JSR::ResultCode result(JSR::Tasks::WriteValue);
+        outputValue.SetObject();
+        outputValue.CopyFrom(prefabDomData->GetValue(), context.GetJsonAllocator());
+        return context.Report(result, "Stored procedural prefab");
+    }
+}

+ 90 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h

@@ -0,0 +1,90 @@
+/*
+ * 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/Asset/AssetCommon.h>
+#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
+#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
+#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
+
+namespace AZ::Prefab
+{
+    //! A wrapper around the JSON DOM type so that the assets can read in and write out
+    //! JSON directly since Prefabs are JSON serialized entity-component data
+    class PrefabDomData final
+    {
+    public:
+        AZ_RTTI(PrefabDomData, "{C73A3360-D772-4D41-9118-A039BF9340C1}");
+        AZ_CLASS_ALLOCATOR(PrefabDomData, AZ::SystemAllocator, 0);
+
+        PrefabDomData() = default;
+        ~PrefabDomData() = default;
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        void CopyValue(const rapidjson::Value& inputValue);
+        const AzToolsFramework::Prefab::PrefabDom& GetValue() const;
+
+    private:
+        AzToolsFramework::Prefab::PrefabDom m_prefabDom;
+    };
+
+    //! Registered to help read/write JSON for the PrefabDomData::m_prefabDom
+    class PrefabDomDataJsonSerializer final
+        : public AZ::BaseJsonSerializer
+    {
+    public:
+        AZ_RTTI(PrefabDomDataJsonSerializer, "{9FC48652-A00B-4EFA-8FD9-345A8E625439}", BaseJsonSerializer);
+        AZ_CLASS_ALLOCATOR(PrefabDomDataJsonSerializer, AZ::SystemAllocator, 0);
+
+        ~PrefabDomDataJsonSerializer() override = default;
+
+        AZ::JsonSerializationResult::Result Load(
+            void* outputValue,
+            const AZ::Uuid& outputValueTypeId,
+            const rapidjson::Value& inputValue,
+            AZ::JsonDeserializerContext& context) override;
+
+        AZ::JsonSerializationResult::Result Store(
+            rapidjson::Value& outputValue,
+            const void* inputValue,
+            const void* defaultValue,
+            const AZ::Uuid& valueTypeId,
+            AZ::JsonSerializerContext& context) override;
+    };
+
+    //! An asset type to register templates into the Prefab system so that they
+    //! can instantiate like Authored Prefabs
+    class ProceduralPrefabAsset
+        : public AZ::Data::AssetData
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(ProceduralPrefabAsset, AZ::SystemAllocator, 0);
+        AZ_RTTI(ProceduralPrefabAsset, "{9B7C8459-471E-4EAD-A363-7990CC4065A9}", AZ::Data::AssetData);
+
+        static bool UseProceduralPrefabs();
+
+        ProceduralPrefabAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId());
+        ~ProceduralPrefabAsset() override = default;
+        ProceduralPrefabAsset(const ProceduralPrefabAsset& rhs) = delete;
+        ProceduralPrefabAsset& operator=(const ProceduralPrefabAsset& rhs) = delete;
+
+        const AZStd::string& GetTemplateName() const;
+        void SetTemplateName(AZStd::string templateName);
+
+        AzToolsFramework::Prefab::TemplateId GetTemplateId() const;
+        void SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+    private:
+        AZStd::string m_templateName;
+        AzToolsFramework::Prefab::TemplateId m_templateId;
+    };
+
+}

+ 37 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.cpp

@@ -0,0 +1,37 @@
+/*
+ * 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 <Prefab/ScriptingPrefabLoader.h>
+
+namespace AzToolsFramework::Prefab
+{
+    void ScriptingPrefabLoader::Connect(PrefabLoaderInterface* prefabLoaderInterface)
+    {
+        AZ_Assert(prefabLoaderInterface, "prefabLoaderInterface must not be null");
+
+        m_prefabLoaderInterface = prefabLoaderInterface;
+        PrefabLoaderScriptingBus::Handler::BusConnect();
+    }
+
+    void ScriptingPrefabLoader::Disconnect()
+    {
+        PrefabLoaderScriptingBus::Handler::BusDisconnect();
+    }
+
+    AZ::Outcome<AZStd::string, void> ScriptingPrefabLoader::SaveTemplateToString(TemplateId templateId)
+    {
+        AZStd::string json;
+
+        if (m_prefabLoaderInterface->SaveTemplateToString(templateId, json))
+        {
+            return AZ::Success(json);
+        }
+
+        return AZ::Failure();
+    }
+} // namespace AzToolsFramework::Prefab

+ 42 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/ScriptingPrefabLoader.h

@@ -0,0 +1,42 @@
+/*
+ * 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 <Prefab/PrefabLoaderInterface.h>
+#include <Prefab/PrefabLoaderScriptingBus.h>
+
+namespace AzToolsFramework
+{
+    namespace Prefab
+    {
+        /**
+        * The Scripting Prefab Loader handles scripting-friendly API requests for the prefab loader
+        */
+        class ScriptingPrefabLoader
+            : private PrefabLoaderScriptingBus::Handler
+        {
+        public:
+            AZ_CLASS_ALLOCATOR(ScriptingPrefabLoader, AZ::SystemAllocator, 0);
+            AZ_RTTI(ScriptingPrefabLoader, "{ABC3C989-4D4F-41E7-B25B-B0FEF97177E6}");
+
+            void Connect(PrefabLoaderInterface* prefabLoaderInterface);
+            void Disconnect();
+            
+        private:
+
+            //////////////////////////////////////////////////////////////////////////
+            // PrefabLoaderRequestBus implementation
+            AZ::Outcome<AZStd::string, void> SaveTemplateToString(TemplateId templateId) override;
+            //////////////////////////////////////////////////////////////////////////
+
+            PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
+        };
+    } // namespace Prefab
+} // namespace AzToolsFramework
+

+ 73 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp

@@ -13,6 +13,7 @@
 #include <AzCore/StringFunc/StringFunc.h>
 #include <AzCore/Component/TransformBus.h>
 #include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzCore/Asset/AssetManager.h>
 
 #include <AzFramework/API/ApplicationAPI.h>
 #include <AzFramework/Asset/AssetSystemBus.h>
@@ -25,6 +26,9 @@
 #include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
 #include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
 #include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
+#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
+#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
 #include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
 #include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
 #include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
@@ -218,6 +222,16 @@ namespace AzToolsFramework
                     instantiateAction, &QAction::triggered, instantiateAction, [] { ContextMenu_InstantiatePrefab(); });
             }
 
+            // Instantiate Procedural Prefab
+            if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs())
+            {
+                QAction* action = menu->addAction(QObject::tr("Instantiate Procedural Prefab..."));
+                action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab."));
+
+                QObject::connect(
+                    action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); });
+            }
+
             menu->addSeparator();
 
             bool itemWasShown = false;
@@ -435,6 +449,38 @@ namespace AzToolsFramework
             }
         }
 
+        void PrefabIntegrationManager::ContextMenu_InstantiateProceduralPrefab()
+        {
+            AZStd::string prefabAssetPath;
+            bool hasUserForProceduralPrefabAsset = QueryUserForProceduralPrefabAsset(prefabAssetPath);
+
+            if (hasUserForProceduralPrefabAsset)
+            {
+                AZ::EntityId parentId;
+                AZ::Vector3 position = AZ::Vector3::CreateZero();
+
+                EntityIdList selectedEntities;
+                ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
+                if (selectedEntities.size() == 1)
+                {
+                    parentId = selectedEntities.front();
+                    AZ::TransformBus::EventResult(position, parentId, &AZ::TransformInterface::GetWorldTranslation);
+                }
+                else
+                {
+                    // otherwise return since it needs to be inside an authored prefab
+                    return;
+                }
+
+                // Instantiating from context menu always puts the instance at the root level
+                auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabAssetPath, parentId, position);
+                if (!createPrefabOutcome.IsSuccess())
+                {
+                    WarnUserOfError("Prefab Instantiation Error", createPrefabOutcome.GetError());
+                }
+            }
+        }
+
         void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
         {
             s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
@@ -690,6 +736,33 @@ namespace AzToolsFramework
             return true;
         }
 
+        bool PrefabIntegrationManager::QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath)
+        {
+            using namespace AzToolsFramework;
+            auto selection = AssetBrowser::AssetSelectionModel::AssetTypeSelection(azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>());
+            EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
+
+            if (!selection.IsValid())
+            {
+                return false;
+            }
+
+            auto product = azrtti_cast<const ProductAssetBrowserEntry*>(selection.GetResult());
+            if (product == nullptr)
+            {
+                return false;
+            }
+
+            outPrefabAssetPath = product->GetRelativePath();
+
+            auto asset = AZ::Data::AssetManager::Instance().GetAsset(
+                product->GetAssetId(),
+                azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>(),
+                AZ::Data::AssetLoadBehavior::Default);
+
+            return asset.BlockUntilLoadComplete() != AZ::Data::AssetData::AssetStatus::Error;
+        }
+
         void PrefabIntegrationManager::WarnUserOfError(AZStd::string_view title, AZStd::string_view message)
         {
             QWidget* activeWindow = QApplication::activeWindow();

+ 2 - 0
Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h

@@ -91,6 +91,7 @@ namespace AzToolsFramework
             // Context menu item handlers
             static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities);
             static void ContextMenu_InstantiatePrefab();
+            static void ContextMenu_InstantiateProceduralPrefab();
             static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
             static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
             static void ContextMenu_DeleteSelected();
@@ -101,6 +102,7 @@ namespace AzToolsFramework
                 const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow,
                 AZStd::string& outPrefabName, AZStd::string& outPrefabFilePath);
             static bool QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath);
+            static bool QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath);
             static void WarnUserOfError(AZStd::string_view title, AZStd::string_view message);
 
             // Path and filename generation

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

@@ -152,6 +152,8 @@ set(FILES
     Entity/SliceEditorEntityOwnershipService.h
     Entity/SliceEditorEntityOwnershipService.cpp
     Entity/SliceEditorEntityOwnershipServiceBus.h
+    Entity/EntityUtilityComponent.h
+    Entity/EntityUtilityComponent.cpp
     Fingerprinting/TypeFingerprinter.h
     Fingerprinting/TypeFingerprinter.cpp
     FocusMode/FocusModeInterface.h
@@ -646,9 +648,15 @@ set(FILES
     Prefab/PrefabLoader.h
     Prefab/PrefabLoader.cpp
     Prefab/PrefabLoaderInterface.h
+    Prefab/PrefabLoaderScriptingBus.h
+    Prefab/ScriptingPrefabLoader.h
+    Prefab/ScriptingPrefabLoader.cpp
     Prefab/PrefabSystemComponent.h
     Prefab/PrefabSystemComponent.cpp
     Prefab/PrefabSystemComponentInterface.h
+    Prefab/PrefabSystemScriptingBus.h
+    Prefab/PrefabSystemScriptingHandler.h
+    Prefab/PrefabSystemScriptingHandler.cpp
     Prefab/Instance/Instance.h
     Prefab/Instance/Instance.cpp
     Prefab/Instance/InstanceSerializer.h
@@ -671,6 +679,8 @@ set(FILES
     Prefab/Instance/TemplateInstanceMapperInterface.h
     Prefab/Link/Link.h
     Prefab/Link/Link.cpp
+    Prefab/Procedural/ProceduralPrefabAsset.h
+    Prefab/Procedural/ProceduralPrefabAsset.cpp
     Prefab/PrefabPublicHandler.h
     Prefab/PrefabPublicHandler.cpp
     Prefab/PrefabPublicInterface.h

+ 252 - 0
Code/Framework/AzToolsFramework/Tests/Entity/EntityUtilityComponentTests.cpp

@@ -0,0 +1,252 @@
+/*
+ * 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 <AzFramework/Entity/BehaviorEntity.h>
+#include <AzTest/AzTest.h>
+
+#include <AzToolsFramework/Application/ToolsApplication.h>
+#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
+#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
+#include <Entity/EntityUtilityComponent.h>
+#include <ToolsComponents/TransformComponent.h>
+
+namespace UnitTest
+{
+    // Global variables for communicating between Lua test code and C++
+    AZ::EntityId g_globalEntityId = AZ::EntityId{};
+    AZStd::string g_globalString = "";
+    AzFramework::BehaviorComponentId g_globalComponentId = {};
+    AZStd::vector<AzToolsFramework::ComponentDetails> g_globalComponentDetails = {};
+    bool g_globalBool = false;
+
+    class EntityUtilityComponentTests
+        : public ToolsApplicationFixture
+    {
+        void InitProperties()
+        {
+            AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+
+            ASSERT_NE(componentApplicationRequests, nullptr);
+
+            auto behaviorContext = componentApplicationRequests->GetBehaviorContext();
+
+            ASSERT_NE(behaviorContext, nullptr);
+
+            behaviorContext->Property("g_globalEntityId", BehaviorValueProperty(&g_globalEntityId));
+            behaviorContext->Property("g_globalString", BehaviorValueProperty(&g_globalString));
+            behaviorContext->Property("g_globalComponentId", BehaviorValueProperty(&g_globalComponentId));
+            behaviorContext->Property("g_globalBool", BehaviorValueProperty(&g_globalBool));
+            behaviorContext->Property("g_globalComponentDetails", BehaviorValueProperty(&g_globalComponentDetails));
+
+            g_globalEntityId = AZ::EntityId{};
+            g_globalString = AZStd::string{};
+            g_globalComponentId = AzFramework::BehaviorComponentId{};
+            g_globalBool = false;
+            g_globalComponentDetails = AZStd::vector<AzToolsFramework::ComponentDetails>{};
+        }
+
+        void SetUpEditorFixtureImpl() override
+        {
+            InitProperties();
+        }
+        
+        void TearDownEditorFixtureImpl() override
+        {
+            g_globalString.set_capacity(0); // Free all memory
+            g_globalComponentDetails.set_capacity(0);
+        }
+    };
+
+    TEST_F(EntityUtilityComponentTests, CreateEntity)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            my_entity = Entity(g_globalEntityId)
+            g_globalString = my_entity:GetName()
+            )LUA");
+
+        EXPECT_NE(g_globalEntityId, AZ::EntityId{});
+        EXPECT_STREQ(g_globalString.c_str(), "test");
+
+        AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
+
+        ASSERT_NE(entity, nullptr);
+
+        // Test cleaning up, make sure the entity is destroyed
+        AzToolsFramework::EntityUtilityBus::Broadcast(&AzToolsFramework::EntityUtilityBus::Events::ResetEntityContext);
+
+        entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
+
+        ASSERT_EQ(entity, nullptr);
+    }
+
+    TEST_F(EntityUtilityComponentTests, CreateEntityEmptyName)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("")
+            )LUA");
+
+        EXPECT_NE(g_globalEntityId, AZ::EntityId{});
+
+        AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
+
+        ASSERT_NE(entity, nullptr);
+    }
+
+    TEST_F(EntityUtilityComponentTests, FindComponent)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0 TransformComponent")
+            )LUA");
+        
+        EXPECT_TRUE(g_globalComponentId.IsValid());
+    }
+
+    TEST_F(EntityUtilityComponentTests, InvalidComponentName)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        AZ_TEST_START_TRACE_SUPPRESSION;
+        sc.Execute(R"LUA(
+            ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ThisIsNotAComponent-Error")
+            )LUA");
+        AZ_TEST_STOP_TRACE_SUPPRESSION(1);
+        EXPECT_FALSE(g_globalComponentId.IsValid());
+    }
+
+    TEST_F(EntityUtilityComponentTests, InvalidComponentId)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        AZ_TEST_START_TRACE_SUPPRESSION;
+        sc.Execute(R"LUA(
+            ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "{1234-hello-world-this-is-not-an-id}")
+            )LUA");
+        AZ_TEST_STOP_TRACE_SUPPRESSION(1); // Should get 1 error stating the type id is not valid
+        EXPECT_FALSE(g_globalComponentId.IsValid());
+    }
+
+    TEST_F(EntityUtilityComponentTests, CreateComponent)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ScriptEditorComponent")
+            )LUA");
+
+        EXPECT_TRUE(g_globalComponentId.IsValid());
+    }
+
+    TEST_F(EntityUtilityComponentTests, UpdateComponent)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            comp_id = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(g_globalEntityId, "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent")
+            json_update = [[
+            {
+                "Transform Data": { "Rotate": [0.0, 0.1, 180.0] }
+            }
+            ]]
+            g_globalBool = EntityUtilityBus.Broadcast.UpdateComponentForEntity(g_globalEntityId, comp_id, json_update);
+            )LUA");
+
+        EXPECT_TRUE(g_globalBool);
+        EXPECT_NE(g_globalEntityId, AZ::EntityId(AZ::EntityId::InvalidEntityId));
+
+        AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
+
+        auto* transformComponent = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
+
+        ASSERT_NE(transformComponent, nullptr);
+
+        AZ::Vector3 localRotation = transformComponent->GetLocalRotationQuaternion().GetEulerDegrees();
+
+        EXPECT_EQ(localRotation, AZ::Vector3(.0f, 0.1f, 180.0f));
+    }
+
+    TEST_F(EntityUtilityComponentTests, GetComponentJson)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("ScriptEditorComponent")
+            )LUA");
+
+        EXPECT_STRNE(g_globalString.c_str(), "");
+    }
+
+    TEST_F(EntityUtilityComponentTests, GetComponentJsonDoesNotExist)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+
+        sc.BindTo(behaviorContext);
+        AZ_TEST_START_TRACE_SUPPRESSION;
+        sc.Execute(R"LUA(
+            g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("404")
+            )LUA");
+        AZ_TEST_STOP_TRACE_SUPPRESSION(1); // 1 error: Failed to find component id for type name 404
+
+        EXPECT_STREQ(g_globalString.c_str(), "");
+    }
+
+    TEST_F(EntityUtilityComponentTests, SearchComponents)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("Transform*")
+            )LUA");
+
+        // There should be 2 transform components
+        EXPECT_EQ(g_globalComponentDetails.size(), 2);
+    }
+
+    TEST_F(EntityUtilityComponentTests, SearchComponentsNotFound)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("404")
+            )LUA");
+        
+        EXPECT_EQ(g_globalComponentDetails.size(), 0);
+    }
+}

+ 173 - 0
Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp

@@ -0,0 +1,173 @@
+/*
+ * 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/ToolsComponents/TransformComponent.h>
+#include <Entity/EntityUtilityComponent.h>
+
+#include <Prefab/PrefabTestComponent.h>
+#include <Prefab/PrefabTestDomUtils.h>
+#include <Prefab/PrefabTestFixture.h>
+
+namespace UnitTest
+{
+    TemplateId g_globalTemplateId = {};
+    AZStd::string g_globalPrefabString = "";
+
+    class PrefabScriptingTest : public PrefabTestFixture
+    {
+        void InitProperties() const
+        {
+            AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+
+            ASSERT_NE(componentApplicationRequests, nullptr);
+
+            auto behaviorContext = componentApplicationRequests->GetBehaviorContext();
+
+            ASSERT_NE(behaviorContext, nullptr);
+
+            behaviorContext->Property("g_globalTemplateId", BehaviorValueProperty(&g_globalTemplateId));
+            behaviorContext->Property("g_globalPrefabString", BehaviorValueProperty(&g_globalPrefabString));
+
+            g_globalTemplateId = TemplateId{};
+            g_globalPrefabString = AZStd::string{};
+        }
+
+        void SetUpEditorFixtureImpl() override
+        {
+            InitProperties();
+        }
+
+        void TearDownEditorFixtureImpl() override
+        {
+            g_globalPrefabString.set_capacity(0); // Free all memory
+        }
+    };
+
+    TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            entities = vector_EntityId()
+            entities:push_back(my_id)
+            g_globalTemplateId = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab")
+            )LUA");
+
+        EXPECT_NE(g_globalTemplateId, TemplateId{});
+
+        auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
+
+        ASSERT_NE(prefabSystemComponentInterface, nullptr);
+
+        TemplateReference templateRef = prefabSystemComponentInterface->FindTemplate(g_globalTemplateId);
+
+        EXPECT_TRUE(templateRef);
+    }
+
+    TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab_NoEntities)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            entities = vector_EntityId()
+            g_globalTemplateId = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab")
+            )LUA");
+
+        EXPECT_NE(g_globalTemplateId, TemplateId{});
+
+        auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
+
+        ASSERT_NE(prefabSystemComponentInterface, nullptr);
+
+        TemplateReference templateRef = prefabSystemComponentInterface->FindTemplate(g_globalTemplateId);
+
+        EXPECT_TRUE(templateRef);
+    }
+
+    TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab_NoPath)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        AZ_TEST_START_TRACE_SUPPRESSION;
+        sc.Execute(R"LUA(
+            my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            entities = vector_EntityId()
+            template_id = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "")
+            )LUA");
+        /*
+        error: PrefabSystemComponent::CreateTemplateFromInstance - Attempted to create a prefab template from an instance without a source file path. Unable to proceed.
+        error: Failed to create a Template associated with file path  during CreatePrefab.
+        error: Failed to create prefab
+         */
+        AZ_TEST_STOP_TRACE_SUPPRESSION(3);
+    }
+
+    TEST_F(PrefabScriptingTest, PrefabScripting_SaveToString)
+    {
+        AZ::ScriptContext sc;
+        auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
+        
+        sc.BindTo(behaviorContext);
+        sc.Execute(R"LUA(
+            my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
+            entities = vector_EntityId()
+            entities:push_back(my_id)
+            template_id = PrefabSystemScriptingBus.Broadcast.CreatePrefab(entities, "test.prefab")
+            my_result = PrefabLoaderScriptingBus.Broadcast.SaveTemplateToString(template_id)
+
+            if my_result:IsSuccess() then
+                g_globalPrefabString = my_result:GetValue()
+            end
+            )LUA");
+        
+        auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
+        prefabSystemComponentInterface->RemoveAllTemplates();
+
+        EXPECT_STRNE(g_globalPrefabString.c_str(), "");
+        TemplateId templateFromString = AZ::Interface<PrefabLoaderInterface>::Get()->LoadTemplateFromString(g_globalPrefabString);
+
+        EXPECT_NE(templateFromString, InvalidTemplateId);
+
+        // Create another entity for comparison purposes
+        AZ::EntityId entityId;
+        AzToolsFramework::EntityUtilityBus::BroadcastResult(
+            entityId, &AzToolsFramework::EntityUtilityBus::Events::CreateEditorReadyEntity, "test");
+
+        AZ::Entity* testEntity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityId);
+
+        // Instantiate the prefab we saved
+        AZStd::unique_ptr<Instance> instance = prefabSystemComponentInterface->InstantiatePrefab(templateFromString);
+
+        EXPECT_NE(instance, nullptr);
+
+        AZStd::vector<const AZ::Entity*> loadedEntities;
+
+        // Get the entities from the instance
+        instance->GetConstEntities(
+            [&loadedEntities](const AZ::Entity& entity)
+            {
+                loadedEntities.push_back(&entity);
+                return true;
+            });
+
+        // Make sure the instance has an entity with the same number of components as our test entity
+        EXPECT_EQ(loadedEntities.size(), 1);
+        EXPECT_EQ(loadedEntities[0]->GetComponents().size(), testEntity->GetComponents().size());
+
+        g_globalPrefabString.set_capacity(0); // Free all memory
+    }
+    
+}

+ 135 - 0
Code/Framework/AzToolsFramework/Tests/Prefab/ProceduralPrefabAssetTests.cpp

@@ -0,0 +1,135 @@
+/*
+ * 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/ToolsComponents/TransformComponent.h>
+#include <Entity/EntityUtilityComponent.h>
+
+#include <Prefab/PrefabTestComponent.h>
+#include <Prefab/PrefabTestDomUtils.h>
+#include <Prefab/PrefabTestFixture.h>
+#include <Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <AzCore/Serialization/Json/RegistrationContext.h>
+
+namespace UnitTest
+{
+    class ProceduralPrefabAssetTest
+        : public PrefabTestFixture
+    {
+        void SetUpEditorFixtureImpl() override
+        {
+            AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+            ASSERT_NE(componentApplicationRequests, nullptr);
+
+            auto* behaviorContext = componentApplicationRequests->GetBehaviorContext();
+            ASSERT_NE(behaviorContext, nullptr);
+
+            auto* jsonRegistrationContext = componentApplicationRequests->GetJsonRegistrationContext();
+            ASSERT_NE(jsonRegistrationContext, nullptr);
+
+            auto* serializeContext = componentApplicationRequests->GetSerializeContext();
+            ASSERT_NE(serializeContext, nullptr);
+
+            AZ::Prefab::ProceduralPrefabAsset::Reflect(serializeContext);
+            AZ::Prefab::ProceduralPrefabAsset::Reflect(behaviorContext);
+            AZ::Prefab::ProceduralPrefabAsset::Reflect(jsonRegistrationContext);
+        }
+
+        void TearDownEditorFixtureImpl() override
+        {
+            AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+            componentApplicationRequests->GetJsonRegistrationContext()->EnableRemoveReflection();
+            AZ::Prefab::ProceduralPrefabAsset::Reflect(componentApplicationRequests->GetJsonRegistrationContext());
+        }
+    };
+
+    TEST_F(ProceduralPrefabAssetTest, ReflectContext_AccessMethods_Works)
+    {
+        AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
+
+        auto* serializeContext = componentApplicationRequests->GetSerializeContext();
+        EXPECT_TRUE(!serializeContext->CreateAny(azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>()).empty());
+        EXPECT_TRUE(!serializeContext->CreateAny(azrtti_typeid<AZ::Prefab::PrefabDomData>()).empty());
+
+        auto* jsonRegistrationContext = componentApplicationRequests->GetJsonRegistrationContext();
+        EXPECT_TRUE(jsonRegistrationContext->GetSerializerForSerializerType(azrtti_typeid<AZ::Prefab::PrefabDomDataJsonSerializer>()));
+    }
+
+    TEST_F(ProceduralPrefabAssetTest, ProceduralPrefabAsset_AccessMethods_Works)
+    {
+        const auto templateId = TemplateId(1);
+        const auto prefabString = "fake.prefab";
+
+        AZ::Prefab::ProceduralPrefabAsset asset{};
+        asset.SetTemplateId(templateId);
+        EXPECT_EQ(asset.GetTemplateId(), templateId);
+
+        asset.SetTemplateName(prefabString);
+        EXPECT_EQ(asset.GetTemplateName(), prefabString);
+    }
+
+    TEST_F(ProceduralPrefabAssetTest, PrefabDomData_AccessMethods_Works)
+    {
+        AzToolsFramework::Prefab::PrefabDom dom;
+        dom.SetObject();
+        dom.AddMember("boolValue", true, dom.GetAllocator());
+
+        AZ::Prefab::PrefabDomData prefabDomData;
+        prefabDomData.CopyValue(dom);
+
+        const AzToolsFramework::Prefab::PrefabDom& result = prefabDomData.GetValue();
+        EXPECT_TRUE(result.HasMember("boolValue"));
+        EXPECT_TRUE(result.FindMember("boolValue")->value.GetBool());
+    }
+
+    TEST_F(ProceduralPrefabAssetTest, PrefabDomDataJsonSerializer_Load_Works)
+    {
+        AZ::Prefab::PrefabDomData prefabDomData;
+
+        AzToolsFramework::Prefab::PrefabDom dom;
+        dom.SetObject();
+        dom.AddMember("member", "value", dom.GetAllocator());
+
+        AZ::Prefab::PrefabDomDataJsonSerializer prefabDomDataJsonSerializer;
+        AZ::JsonDeserializerSettings settings;
+        settings.m_reporting = [](auto, auto, auto)
+        {
+            AZ::JsonSerializationResult::ResultCode result(AZ::JsonSerializationResult::Tasks::ReadField);
+            return result;
+        };
+        AZ::JsonDeserializerContext context{ settings };
+
+        auto result = prefabDomDataJsonSerializer.Load(&prefabDomData, azrtti_typeid(prefabDomData), dom, context);
+        EXPECT_EQ(result.GetResultCode().GetOutcome(), AZ::JsonSerializationResult::Outcomes::DefaultsUsed);
+        EXPECT_TRUE(prefabDomData.GetValue().HasMember("member"));
+        EXPECT_STREQ(prefabDomData.GetValue().FindMember("member")->value.GetString(), "value");
+    }
+
+    TEST_F(ProceduralPrefabAssetTest, PrefabDomDataJsonSerializer_Store_Works)
+    {
+        AzToolsFramework::Prefab::PrefabDom dom;
+        dom.SetObject();
+        dom.AddMember("member", "value", dom.GetAllocator());
+
+        AZ::Prefab::PrefabDomData prefabDomData;
+        prefabDomData.CopyValue(dom);
+
+        AZ::Prefab::PrefabDomDataJsonSerializer prefabDomDataJsonSerializer;
+        AzToolsFramework::Prefab::PrefabDom outputValue;
+        AZ::JsonSerializerSettings settings;
+        settings.m_reporting = [](auto, auto, auto)
+        {
+            AZ::JsonSerializationResult::ResultCode result(AZ::JsonSerializationResult::Tasks::WriteValue);
+            return result;
+        };
+        AZ::JsonSerializerContext context{ settings, outputValue.GetAllocator() };
+        auto result = prefabDomDataJsonSerializer.Store(outputValue, &prefabDomData, nullptr, azrtti_typeid(prefabDomData), context);
+        EXPECT_EQ(result.GetResultCode().GetOutcome(), AZ::JsonSerializationResult::Outcomes::DefaultsUsed);
+        EXPECT_TRUE(outputValue.HasMember("member"));
+        EXPECT_STREQ(outputValue.FindMember("member")->value.GetString(), "value");
+    }
+}

+ 3 - 0
Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake

@@ -27,6 +27,7 @@ set(FILES
     Entity/EditorEntityHelpersTests.cpp
     Entity/EditorEntitySearchComponentTests.cpp
     Entity/EditorEntitySelectionTests.cpp
+    Entity/EntityUtilityComponentTests.cpp
     EntityIdQLabelTests.cpp
     EntityInspectorTests.cpp
     EntityOwnershipService/EntityOwnershipServiceTestFixture.cpp
@@ -91,6 +92,8 @@ set(FILES
     Prefab/SpawnableSortEntitiesTestFixture.cpp
     Prefab/SpawnableSortEntitiesTestFixture.h
     Prefab/SpawnableSortEntitiesTests.cpp
+    Prefab/PrefabScriptingTests.cpp
+    Prefab/ProceduralPrefabAssetTests.cpp
     PropertyIntCtrlCommonTests.h
     PropertyIntSliderCtrlTests.cpp
     PropertyIntSpinCtrlTests.cpp

+ 2 - 0
Code/Tools/AssetProcessor/AssetBuilder/AssetBuilderApplication.cpp

@@ -36,6 +36,7 @@
 #include <AssetBuilderComponent.h>
 #include <AssetBuilderInfo.h>
 #include <AzCore/Interface/Interface.h>
+#include <Entity/EntityUtilityComponent.h>
 
 namespace AssetBuilder
 {
@@ -80,6 +81,7 @@ AZ::ComponentTypeList AssetBuilderApplication::GetRequiredSystemComponents() con
         azrtti_typeid<AzToolsFramework::Components::EditorEntityModelComponent>(),
         azrtti_typeid<AzToolsFramework::EditorEntityContextComponent>(),
         azrtti_typeid<AzToolsFramework::Prefab::PrefabSystemComponent>(),
+        azrtti_typeid<AzToolsFramework::EntityUtilityComponent>(),
         });
 
     return components;

+ 4 - 1
Code/Tools/AssetProcessor/native/ui/AssetTreeFilterModel.cpp

@@ -62,7 +62,10 @@ namespace AssetProcessor
         {
             searchStr = searchStr.mid(0, subidPos);
         }
-        AZ::Uuid filterAsUuid = AZ::Uuid::CreateStringPermissive(searchStr.toUtf8(), 0);
+
+         // Cap the string to some reasonable length, we don't want to try parsing an entire book
+        size_t cappedStringLength = searchStr.length() > 60 ? 60 : searchStr.length();
+        AZ::Uuid filterAsUuid = AZ::Uuid::CreateStringPermissive(searchStr.toUtf8(), cappedStringLength);
 
         return DescendantMatchesFilter(*assetTreeItem, filter, filterAsUuid);
     }

+ 1 - 1
Code/Tools/SceneAPI/SceneCore/Containers/GraphObjectProxy.cpp

@@ -194,8 +194,8 @@ namespace AZ
                     if (baseClass)
                     {
                         m_behaviorClass = behaviorClass;
+                        return true;
                     }
-                    return true;
                 }
                 return false;
             }

+ 11 - 0
Code/Tools/SceneAPI/SceneCore/Containers/Scene.cpp

@@ -47,6 +47,16 @@ namespace AZ
                 return m_sourceGuid;
             }
 
+            void Scene::SetWatchFolder(const AZStd::string& watchFolder)
+            {
+                m_watchFolder = watchFolder;
+            }
+
+            const AZStd::string& Scene::GetWatchFolder() const
+            {
+                return m_watchFolder;
+            }
+
             void Scene::SetManifestFilename(const AZStd::string& name)
             {
                 m_manifestFilename = name;
@@ -111,6 +121,7 @@ namespace AZ
                         ->Property("sourceGuid", BehaviorValueGetter(&Scene::m_sourceGuid), nullptr)
                         ->Property("graph", BehaviorValueGetter(&Scene::m_graph), nullptr)
                         ->Property("manifest", BehaviorValueGetter(&Scene::m_manifest), nullptr)
+                        ->Property("watchFolder", BehaviorValueGetter(&Scene::m_watchFolder), nullptr)
                         ->Constant("SceneOrientation_YUp", BehaviorConstant(SceneOrientation::YUp))
                         ->Constant("SceneOrientation_ZUp", BehaviorConstant(SceneOrientation::ZUp))
                         ->Constant("SceneOrientation_XUp", BehaviorConstant(SceneOrientation::XUp))

+ 4 - 0
Code/Tools/SceneAPI/SceneCore/Containers/Scene.h

@@ -34,6 +34,9 @@ namespace AZ
                 const AZStd::string& GetSourceFilename() const;
                 const Uuid& GetSourceGuid() const;
 
+                void SetWatchFolder(const AZStd::string& watchFolder);
+                const AZStd::string& GetWatchFolder() const;
+
                 void SetManifestFilename(const AZStd::string& name);
                 void SetManifestFilename(AZStd::string&& name);
                 const AZStd::string& GetManifestFilename() const;
@@ -59,6 +62,7 @@ namespace AZ
                 AZStd::string m_name;
                 AZStd::string m_manifestFilename;
                 AZStd::string m_sourceFilename;
+                AZStd::string m_watchFolder;
                 Uuid m_sourceGuid;
                 SceneGraph m_graph;
                 SceneManifest m_manifest;

+ 1 - 1
Code/Tools/SceneAPI/SceneCore/Containers/SceneGraph.cpp

@@ -97,7 +97,7 @@ namespace AZ
                                 GraphObjectProxy* proxy = aznew GraphObjectProxy(graphObject);
                                 return proxy;
                             }
-                            return nullptr;
+                            return aznew GraphObjectProxy(nullptr);
                         });
                 }
             }

+ 1 - 2
Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.cpp

@@ -36,8 +36,7 @@ namespace AZ
     {
         namespace Containers
         {
-            //! Protects from allocating too much memory. The choice of a 5MB threshold is arbitrary.
-            const size_t MaxSceneManifestFileSizeInBytes = 5 * 1024 * 1024;
+            
 
             const char ErrorWindowName[] = "SceneManifest";
 

+ 2 - 0
Code/Tools/SceneAPI/SceneCore/Containers/SceneManifest.h

@@ -41,6 +41,8 @@ namespace AZ
 
                 AZ_RTTI(SceneManifest, "{9274AD17-3212-4651-9F3B-7DCCB080E467}");
                 
+                static constexpr size_t MaxSceneManifestFileSizeInBytes = AZStd::numeric_limits<size_t>::max();
+
                 virtual ~SceneManifest();
                 
                 static AZStd::shared_ptr<const DataTypes::IManifestObject> SceneManifestConstDataConverter(

+ 26 - 1
Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.cpp

@@ -6,6 +6,7 @@
  *
  */
 
+#include <API/EditorAssetSystemAPI.h>
 #include <AzCore/IO/SystemFile.h>
 #include <AzCore/std/smart_ptr/make_shared.h>
 #include <AzFramework/StringFunc/StringFunc.h>
@@ -73,6 +74,10 @@ namespace AZ
             {
             }
 
+            void AssetImportRequest::GetGeneratedManifestExtension(AZStd::string& /*result*/)
+            {
+            }
+
             void AssetImportRequest::GetSupportedFileExtensions(AZStd::unordered_set<AZStd::string>& /*extensions*/)
             {
             }
@@ -103,14 +108,34 @@ namespace AZ
                 AZ_UNUSED(value);
             }
 
+            void AssetImportRequest::GetManifestDependencyPaths(AZStd::vector<AZStd::string>&)
+            {
+            }
+
             AZStd::shared_ptr<Containers::Scene> AssetImportRequest::LoadSceneFromVerifiedPath(const AZStd::string& assetFilePath, const Uuid& sourceGuid,
-                RequestingApplication requester, const Uuid& loadingComponentUuid)
+                                                                                               RequestingApplication requester, const Uuid& loadingComponentUuid)
             {
                 AZStd::string sceneName;
                 AzFramework::StringFunc::Path::GetFileName(assetFilePath.c_str(), sceneName);
                 AZStd::shared_ptr<Containers::Scene> scene = AZStd::make_shared<Containers::Scene>(AZStd::move(sceneName));
                 AZ_Assert(scene, "Unable to create new scene for asset importing.");
 
+                Data::AssetInfo assetInfo;
+                AZStd::string watchFolder;
+                bool result = false;
+                AzToolsFramework::AssetSystemRequestBus::BroadcastResult(result, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, sourceGuid, assetInfo, watchFolder);
+
+                if (result)
+                {
+                    scene->SetWatchFolder(watchFolder);
+                }
+                else
+                {
+                    AZ_Error(
+                        "AssetImportRequest", false, "Failed to get watch folder for source %s",
+                        sourceGuid.ToString<AZStd::string>().c_str());
+                }
+
                 // Unique pointer, will deactivate and clean up once going out of scope.
                 SceneCore::EntityConstructor::EntityPointer loaders = 
                     SceneCore::EntityConstructor::BuildEntity("Scene Loading", loadingComponentUuid);

+ 19 - 0
Code/Tools/SceneAPI/SceneCore/Events/AssetImportRequest.h

@@ -78,6 +78,8 @@ namespace AZ
                 virtual void GetSupportedFileExtensions(AZStd::unordered_set<AZStd::string>& extensions);
                 //! Gets the file extension for the manifest.
                 virtual void GetManifestExtension(AZStd::string& result);
+                //! Gets the file extension for the generated manifest.
+                virtual void GetGeneratedManifestExtension(AZStd::string& result);
 
                 //! Before asset loading starts this is called to allow for any required initialization.
                 virtual ProcessingResult PrepareForAssetLoading(Containers::Scene& scene, RequestingApplication requester);
@@ -97,6 +99,23 @@ namespace AZ
                 // Get scene processing project setting: UseCustomNormal 
                 virtual void AreCustomNormalsUsed(bool & value);
 
+                /*!
+                    Optional method for reporting source file dependencies that may exist in the scene manifest
+                    Paths is a vector of JSON Path strings, relative to the IRule object
+                    For example, the following path: /scriptFilename
+                    Would match with this manifest:
+                
+                    {
+                        "values": [
+                            {
+                                "$type": "Test",
+                                "scriptFilename": "file.py"
+                            }
+                        ]
+                    }
+                 */
+                virtual void GetManifestDependencyPaths(AZStd::vector<AZStd::string>& paths);
+
                 //! Utility function to load an asset and manifest from file by using the EBus functions above.
                 //! @param assetFilePath The absolute path to the source file (not the manifest).
                 //! @param sourceGuid The guid assigned to the source file (not the manifest).

+ 7 - 1
Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.cpp

@@ -56,8 +56,14 @@ namespace AZ
                 result = s_extension;
             }
 
+            void ManifestImportRequestHandler::GetGeneratedManifestExtension(AZStd::string& result)
+            {
+                result = s_extension;
+                result.append(s_generated);
+            }
+
             Events::LoadingResult ManifestImportRequestHandler::LoadAsset(Containers::Scene& scene, const AZStd::string& path, 
-                const Uuid& /*guid*/, RequestingApplication /*requester*/)
+                                                                          const Uuid& /*guid*/, RequestingApplication /*requester*/)
             {
                 AZStd::string manifestPath = path + s_extension;
 

+ 1 - 0
Code/Tools/SceneAPI/SceneCore/Import/ManifestImportRequestHandler.h

@@ -31,6 +31,7 @@ namespace AZ
                 static void Reflect(ReflectContext* context);
 
                 void GetManifestExtension(AZStd::string& result) override;
+                void GetGeneratedManifestExtension(AZStd::string& result) override;
                 Events::LoadingResult LoadAsset(Containers::Scene& scene, const AZStd::string& path, const Uuid& guid,
                     RequestingApplication requester) override;
                 

+ 71 - 46
Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.cpp

@@ -19,6 +19,9 @@
 #include <AzToolsFramework/API/EditorPythonConsoleBus.h>
 #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
 #include <AzToolsFramework/Debug/TraceContext.h>
+#include <Entity/EntityUtilityComponent.h>
+#include <Prefab/PrefabSystemComponentInterface.h>
+#include <Prefab/PrefabSystemScriptingBus.h>
 #include <SceneAPI/SceneCore/Containers/Scene.h>
 #include <SceneAPI/SceneCore/Containers/SceneGraph.h>
 #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
@@ -99,6 +102,7 @@ namespace AZ::SceneAPI::Behaviors
         {
             AZStd::string result;
             CallResult(result, FN_OnUpdateManifest, scene);
+            ScriptBuildingNotificationBusHandler::BusDisconnect();
             return result;
         }
 
@@ -110,6 +114,7 @@ namespace AZ::SceneAPI::Behaviors
         {
             ExportProductList result;
             CallResult(result, FN_OnPrepareForExport, scene, outputDirectory, platformIdentifier, productList);
+            ScriptBuildingNotificationBusHandler::BusDisconnect();
             return result;
         }
 
@@ -168,21 +173,9 @@ namespace AZ::SceneAPI::Behaviors
         UnloadPython();
     }
 
-    bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene)
+    bool ScriptProcessorRuleBehavior::LoadPython(const AZ::SceneAPI::Containers::Scene& scene, AZStd::string& scriptPath)
     {
-        if (m_editorPythonEventsInterface && !m_scriptFilename.empty())
-        {
-            return true;
-        }
-
-        // get project folder
-        auto settingsRegistry = AZ::SettingsRegistry::Get();
-        AZ::IO::FixedMaxPath projectPath;
-        if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
-        {
-            return false;
-        }
-
+        int scriptDiscoveryAttempts = 0;
         const AZ::SceneAPI::Containers::SceneManifest& manifest = scene.GetManifest();
         auto view = Containers::MakeDerivedFilterView<DataTypes::IScriptProcessorRule>(manifest.GetValueStorage());
         for (const auto& scriptItem : view)
@@ -194,9 +187,21 @@ namespace AZ::SceneAPI::Behaviors
                 continue;
             }
 
+            ++scriptDiscoveryAttempts;
+
             // check for file exist via absolute path
             if (!IO::FileIOBase::GetInstance()->Exists(scriptFilename.c_str()))
             {
+                // get project folder
+                auto settingsRegistry = AZ::SettingsRegistry::Get();
+                AZ::IO::FixedMaxPath projectPath;
+                if (!settingsRegistry->Get(projectPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath))
+                {
+                    AZ_Error("scene", false, "With (%s) could not find Project Path during script discovery.",
+                        scene.GetManifestFilename().c_str());
+                    return false;
+                }
+
                 // check for script in the project folder
                 AZ::IO::FixedMaxPath projectScriptPath = projectPath / scriptFilename;
                 if (!IO::FileIOBase::GetInstance()->Exists(projectScriptPath.c_str()))
@@ -209,32 +214,47 @@ namespace AZ::SceneAPI::Behaviors
                 scriptFilename = AZStd::move(projectScriptPath);
             }
 
-            // lazy load the Python interface
-            auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
-            if (editorPythonEventsInterface->IsPythonActive() == false)
-            {
-                const bool silenceWarnings = false;
-                if (editorPythonEventsInterface->StartPython(silenceWarnings) == false)
-                {
-                    editorPythonEventsInterface = nullptr;
-                }
-            }
+            scriptPath = scriptFilename.c_str();
+            break;
+        }
 
-            // both Python and the script need to be ready
-            if (editorPythonEventsInterface == nullptr || scriptFilename.empty())
-            {
-                AZ_Warning("scene", false,"The scene manifest (%s) attempted to use script(%s) but Python is not enabled;"
-                    "please add the EditorPythonBinding gem & PythonAssetBuilder gem to your project.",
-                    scene.GetManifestFilename().c_str(), scriptFilename.c_str());
+        if (scriptPath.empty())
+        {
+            AZ_Warning("scene", scriptDiscoveryAttempts == 0,
+                "The scene manifest (%s) attempted to use script rule, but no script file path could be found.",
+                scene.GetManifestFilename().c_str());
+            return false;
+        }
+
+        // already prepared the Python VM?
+        if (m_editorPythonEventsInterface)
+        {
+            return true;
+        }
 
-                return false;
+        // lazy load the Python interface
+        auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
+        if (editorPythonEventsInterface->IsPythonActive() == false)
+        {
+            const bool silenceWarnings = false;
+            if (editorPythonEventsInterface->StartPython(silenceWarnings) == false)
+            {
+                editorPythonEventsInterface = nullptr;
             }
+        }
 
-            m_editorPythonEventsInterface = editorPythonEventsInterface;
-            m_scriptFilename = scriptFilename.c_str();
-            return true;
+        // both Python and the script need to be ready
+        if (editorPythonEventsInterface == nullptr)
+        {
+            AZ_Warning("scene", false,
+                "The scene manifest (%s) attempted to prepare Python but Python can not start",
+                scene.GetManifestFilename().c_str());
+
+            return false;
         }
-        return false;
+
+        m_editorPythonEventsInterface = editorPythonEventsInterface;
+        return true;
     }
 
     void ScriptProcessorRuleBehavior::UnloadPython()
@@ -251,11 +271,13 @@ namespace AZ::SceneAPI::Behaviors
     {
         using namespace AzToolsFramework;
 
-        auto executeCallback = [this, &context]()
+        AZStd::string scriptPath;
+
+        auto executeCallback = [&context, &scriptPath]()
         {
             // set up script's hook callback
             EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
-                m_scriptFilename.c_str());
+                scriptPath.c_str());
 
             // call script's callback to allow extra products
             ExportProductList extraProducts;
@@ -279,7 +301,7 @@ namespace AZ::SceneAPI::Behaviors
             }
         };
 
-        if (LoadPython(context.GetScene()))
+        if (LoadPython(context.GetScene(), scriptPath))
         {
             EditorPythonConsoleNotificationHandler logger;
             m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
@@ -306,23 +328,19 @@ namespace AZ::SceneAPI::Behaviors
     {
         using namespace AzToolsFramework;
 
-        // This behavior persists on the same AssetBuilder. Clear the script file name so that if
-        // this builder processes a scene file with a script file name, and then later processes
-        // a scene without a script file name, it won't run the old script on the new scene.
-        m_scriptFilename.clear();
-
         if (action != ManifestAction::Update)
         {
             return Events::ProcessingResult::Ignored;
         }
 
-        if (LoadPython(scene))
+        AZStd::string scriptPath;
+        if (LoadPython(scene, scriptPath))
         {
             AZStd::string manifestUpdate;
-            auto executeCallback = [this, &scene, &manifestUpdate]()
+            auto executeCallback = [&scene, &manifestUpdate, &scriptPath]()
             {
                 EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilename,
-                    m_scriptFilename.c_str());
+                    scriptPath.c_str());
 
                 ScriptBuildingNotificationBus::BroadcastResult(manifestUpdate, &ScriptBuildingNotificationBus::Events::OnUpdateManifest,
                     scene);
@@ -331,6 +349,9 @@ namespace AZ::SceneAPI::Behaviors
             EditorPythonConsoleNotificationHandler logger;
             m_editorPythonEventsInterface->ExecuteWithLock(executeCallback);
 
+            EntityUtilityBus::Broadcast(&EntityUtilityBus::Events::ResetEntityContext);
+            AZ::Interface<Prefab::PrefabSystemComponentInterface>::Get()->RemoveAllTemplates();
+
             // attempt to load the manifest string back to a JSON-scene-manifest
             auto sceneManifestLoader = AZStd::make_unique<AZ::SceneAPI::Containers::SceneManifest>();
             auto loadOutcome = sceneManifestLoader->LoadFromString(manifestUpdate);
@@ -347,4 +368,8 @@ namespace AZ::SceneAPI::Behaviors
         return Events::ProcessingResult::Ignored;
     }
 
+    void ScriptProcessorRuleBehavior::GetManifestDependencyPaths(AZStd::vector<AZStd::string>& paths)
+    {
+        paths.emplace_back("/scriptFilename");
+    }
 } // namespace AZ

+ 4 - 4
Code/Tools/SceneAPI/SceneData/Behaviors/ScriptProcessorRuleBehavior.h

@@ -37,6 +37,7 @@ namespace AZ::SceneAPI::Behaviors
         , public Events::AssetImportRequestBus::Handler
     {
     public:
+        
         AZ_COMPONENT(ScriptProcessorRuleBehavior, "{24054E73-1B92-43B0-AC13-174B2F0E3F66}", SceneCore::BehaviorComponent);
 
         ~ScriptProcessorRuleBehavior() override = default;
@@ -44,22 +45,21 @@ namespace AZ::SceneAPI::Behaviors
         SCENE_DATA_API void Activate() override;
         SCENE_DATA_API void Deactivate() override;
         static void Reflect(ReflectContext* context);
-
+        
         // AssetImportRequestBus::Handler
         SCENE_DATA_API Events::ProcessingResult UpdateManifest(
             Containers::Scene& scene,
             ManifestAction action,
             RequestingApplication requester) override;
 
-
+        SCENE_DATA_API void GetManifestDependencyPaths(AZStd::vector<AZStd::string>& paths) override;
     protected:
-        bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene);
+        bool LoadPython(const AZ::SceneAPI::Containers::Scene& scene, AZStd::string& scriptPath);
         void UnloadPython();
         bool DoPrepareForExport(Events::PreExportEventContext& context);
 
     private:
         AzToolsFramework::EditorPythonEventsInterface* m_editorPythonEventsInterface = nullptr;
-        AZStd::string m_scriptFilename;
 
         struct ExportEventHandler;
         AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;

+ 8 - 1
Gems/Prefab/PrefabBuilder/CMakeLists.txt

@@ -13,6 +13,9 @@ endif()
 ly_add_target(
     NAME PrefabBuilder.Static STATIC
     NAMESPACE Gem
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            .
     FILES_CMAKE
         prefabbuilder_files.cmake
     BUILD_DEPENDENCIES
@@ -20,6 +23,9 @@ ly_add_target(
             AZ::AzCore
             AZ::AzToolsFramework
             AZ::AssetBuilderSDK
+            AZ::SceneCore
+            AZ::SceneData
+            3rdParty::RapidJSON
 )
 
 ly_add_target(
@@ -35,7 +41,8 @@ ly_add_target(
             Gem::PrefabBuilder.Static
 )
 
-# the prefab builder only needs to be active in builders
+# create an alias for the tool version
+ly_create_alias(NAME PrefabBuilder.Tools NAMESPACE Gem TARGETS Gem::PrefabBuilder.Builders)
 
 # we automatically add this gem, if it is present, to all our known set of builder applications:
 ly_enable_gems(GEMS PrefabBuilder)

+ 3 - 1
Gems/Prefab/PrefabBuilder/PrefabBuilderModule.cpp

@@ -8,6 +8,7 @@
 
 #include <AzCore/Module/Module.h>
 #include <PrefabBuilderComponent.h>
+#include <PrefabGroup/PrefabGroupBehavior.h>
 
 namespace AZ::Prefab
 {
@@ -22,7 +23,8 @@ namespace AZ::Prefab
             : Module()
         {
             m_descriptors.insert(m_descriptors.end(), {
-                PrefabBuilderComponent::CreateDescriptor()
+                PrefabBuilderComponent::CreateDescriptor(),
+                AZ::SceneAPI::Behaviors::PrefabGroupBehavior::CreateDescriptor()
             });
         }
     };

+ 1 - 0
Gems/Prefab/PrefabBuilder/PrefabBuilderTests.h

@@ -13,6 +13,7 @@
 #include <Application/ToolsApplication.h>
 #include <AzCore/Asset/AssetManager.h>
 #include <AzCore/Component/ComponentApplication.h>
+#include <AzCore/Asset/AssetSerializer.h>
 
 namespace UnitTest
 {

+ 25 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/IPrefabGroup.h

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzCore/RTTI/RTTI.h>
+#include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
+#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
+
+namespace AZ::SceneAPI::DataTypes
+{
+    class IPrefabGroup
+        : public ISceneNodeGroup
+    {
+    public:
+        AZ_RTTI(IPrefabGroup, "{7E50FAEF-3379-4521-99C5-B428FDEE3B7B}", ISceneNodeGroup);
+
+        ~IPrefabGroup() override = default;
+        virtual AzToolsFramework::Prefab::PrefabDomConstReference GetPrefabDomRef() const = 0;
+    };
+}

+ 161 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.cpp

@@ -0,0 +1,161 @@
+/*
+ * 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 <PrefabBuilderTests.h>
+#include <PrefabGroup/IPrefabGroup.h>
+#include <PrefabGroup/PrefabGroup.h>
+#include <PrefabGroup/PrefabGroupBehavior.h>
+#include <AzTest/Utils.h>
+#include <Tests/AssetSystemMocks.h>
+#include <AzCore/Serialization/Json/JsonSystemComponent.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzToolsFramework/Asset/AssetSystemComponent.h>
+#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <SceneAPI/SceneCore/Containers/Scene.h>
+#include <SceneAPI/SceneCore/Events/CallProcessorBus.h>
+#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
+#include <SceneAPI/SceneCore/Events/ExportProductList.h>
+#include <SceneAPI/SceneCore/SceneCoreStandaloneAllocator.h>
+
+#include <PrefabGroup/PrefabBehaviorTests.inl>
+
+namespace UnitTest
+{
+    class PrefabBehaviorTests
+        : public PrefabBuilderTests
+    {
+    public:
+        static void SetUpTestCase()
+        {
+            // Allocator needed by SceneCore
+            if (!AZ::AllocatorInstance<AZ::SystemAllocator>().IsReady())
+            {
+                AZ::AllocatorInstance<AZ::SystemAllocator>().Create();
+            }
+            AZ::SceneAPI::SceneCoreStandaloneAllocator::Initialize(AZ::Environment::GetInstance());
+        }
+
+        static void TearDownTestCase()
+        {
+            AZ::SceneAPI::SceneCoreStandaloneAllocator::TearDown();
+            AZ::AllocatorInstance<AZ::SystemAllocator>().Destroy();
+        }
+
+        void SetUp() override
+        {
+            PrefabBuilderTests::SetUp();
+            m_prefabGroupBehavior = AZStd::make_unique<AZ::SceneAPI::Behaviors::PrefabGroupBehavior>();
+            m_prefabGroupBehavior->Activate();
+
+            // Mocking the asset system replacing the AssetSystem::AssetSystemComponent
+            AZ::Entity* systemEntity = m_app.FindEntity(AZ::SystemEntityId);
+            systemEntity->FindComponent<AzToolsFramework::AssetSystem::AssetSystemComponent>()->Deactivate();
+            using namespace testing;
+            ON_CALL(m_assetSystemRequestMock, GetSourceInfoBySourcePath(_, _, _)).WillByDefault([](auto* path, auto& info, auto&)
+            {
+                return PrefabBehaviorTests::OnGetSourceInfoBySourcePath(path, info);
+            });
+            m_assetSystemRequestMock.BusConnect();
+        }
+
+        void TearDown() override
+        {
+            m_assetSystemRequestMock.BusDisconnect();
+
+            m_prefabGroupBehavior->Deactivate();
+            m_prefabGroupBehavior.reset();
+
+            PrefabBuilderTests::TearDown();
+        }
+
+        static bool OnGetSourceInfoBySourcePath(AZStd::string_view sourcePath, AZ::Data::AssetInfo& assetInfo)
+        {
+            if (sourcePath == AZStd::string_view("mock"))
+            {
+                assetInfo.m_assetId = AZ::Uuid::CreateRandom();
+                assetInfo.m_assetType = azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>();
+                assetInfo.m_relativePath = "mock/path";
+                assetInfo.m_sizeBytes = 0;
+            }
+            return true;
+        }
+
+        struct TestPreExportEventContext
+        {
+            TestPreExportEventContext()
+                : m_scene("test_context")
+            {
+                using namespace AZ::SceneAPI::Events;
+                m_preExportEventContext = AZStd::make_unique<PreExportEventContext>(m_productList, m_outputDirectory, m_scene, "mock");
+            }
+
+            void SetOutputDirectory(AZStd::string outputDirectory)
+            {
+                using namespace AZ::SceneAPI::Events;
+                m_outputDirectory = AZStd::move(outputDirectory);
+                m_preExportEventContext = AZStd::make_unique<PreExportEventContext>(m_productList, m_outputDirectory, m_scene, "mock");
+            }
+
+            AZStd::unique_ptr<AZ::SceneAPI::Events::PreExportEventContext> m_preExportEventContext;
+            AZ::SceneAPI::Events::ExportProductList m_productList;
+            AZStd::string m_outputDirectory;
+            AZ::SceneAPI::Containers::Scene m_scene;
+        };
+
+        AZStd::unique_ptr<AZ::SceneAPI::Behaviors::PrefabGroupBehavior> m_prefabGroupBehavior;
+        testing::NiceMock<UnitTests::MockAssetSystemRequest> m_assetSystemRequestMock;
+    };
+
+    TEST_F(PrefabBehaviorTests, PrefabBehavior_EmptyContextIgnored_Works)
+    {
+        auto context = TestPreExportEventContext{};
+
+        auto result = AZ::SceneAPI::Events::ProcessingResult::Failure;
+        AZ::SceneAPI::Events::CallProcessorBus::BroadcastResult(
+            result,
+            &AZ::SceneAPI::Events::CallProcessorBus::Events::Process,
+            context.m_preExportEventContext.get());
+
+        EXPECT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Ignored);
+    }
+
+    TEST_F(PrefabBehaviorTests, PrefabBehavior_SimplePrefab_Works)
+    {
+        auto context = TestPreExportEventContext{};
+
+        // check for the file at <temp_directory>/mock/fake_prefab.procprefab
+        AZ::Test::ScopedAutoTempDirectory tempDir;
+        context.SetOutputDirectory(tempDir.GetDirectory());
+
+        auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(Data::jsonPrefab);
+        ASSERT_TRUE(jsonOutcome);
+
+        auto prefabGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::PrefabGroup>();
+        prefabGroup.get()->SetId(AZ::Uuid::CreateRandom());
+        prefabGroup.get()->SetName("fake_prefab");
+        prefabGroup.get()->SetPrefabDom(AZStd::move(jsonOutcome.GetValue()));
+        context.m_scene.GetManifest().AddEntry(prefabGroup);
+        context.m_scene.SetSource("mock", AZ::Uuid::CreateRandom());
+
+        auto result = AZ::SceneAPI::Events::ProcessingResult::Failure;
+        AZ::SceneAPI::Events::CallProcessorBus::BroadcastResult(
+            result,
+            &AZ::SceneAPI::Events::CallProcessorBus::Events::Process,
+            context.m_preExportEventContext.get());
+
+        EXPECT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Success);
+
+        AZStd::string pathStr;
+        AzFramework::StringFunc::Path::ConstructFull(tempDir.GetDirectory(), "mock/fake_prefab.procprefab", pathStr, true);
+        if (!AZ::IO::SystemFile::Exists(pathStr.c_str()))
+        {
+            AZ_Warning("testing", false, "The product asset (%s) is missing", pathStr.c_str());
+        }
+    }
+}

+ 104 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabBehaviorTests.inl

@@ -0,0 +1,104 @@
+/*
+ * 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
+ *
+ */
+
+namespace UnitTest
+{
+    namespace Data
+    {
+        const char* jsonPrefab = R"JSON(
+        {
+            "ContainerEntity": {
+                "Id": "ContainerEntity",
+                "Name": "test_template_1",
+                "Components": {
+                    "Component_[12122553907433030840]": {
+                        "$type": "EditorVisibilityComponent",
+                        "Id": 12122553907433030840
+                    },
+                    "Component_[5666150279650800686]": {
+                        "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                        "Id": 5666150279650800686,
+                        "Parent Entity": ""
+                    },
+                    "Component_[8790726658974076423]": {
+                        "$type": "EditorOnlyEntityComponent",
+                        "Id": 8790726658974076423
+                    }
+                }
+            },
+            "Entities": {
+                "Entity_[1588652751483]": {
+                    "Id": "Entity_[1588652751483]",
+                    "Name": "root",
+                    "Components": {
+                        "Component_[11872748096995986607]": {
+                            "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                            "Id": 11872748096995986607,
+                            "Parent Entity": "ContainerEntity",
+                            "Transform Data": {
+                                "Rotate": [
+                                    0.0,
+                                    0.10000000149011612,
+                                    180.0
+                                ]
+                            }
+                        },
+                        "Component_[12138841758570858610]": {
+                            "$type": "EditorVisibilityComponent",
+                            "Id": 12138841758570858610
+                        },
+                        "Component_[15735658354806796004]": {
+                            "$type": "EditorOnlyEntityComponent",
+                            "Id": 15735658354806796004
+                        }
+                    }
+                },
+                "Entity_[1592947718779]": {
+                    "Id": "Entity_[1592947718779]",
+                    "Name": "cube",
+                    "Components": {
+                        "Component_[2505301170249328189]": {
+                            "$type": "EditorOnlyEntityComponent",
+                            "Id": 2505301170249328189
+                        },
+                        "Component_[3716170894544198343]": {
+                            "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                            "Id": 3716170894544198343,
+                            "Parent Entity": "Entity_[1588652751483]"
+                        },
+                        "Component_[5862175558847453681]": {
+                            "$type": "EditorVisibilityComponent",
+                            "Id": 5862175558847453681
+                        }
+                    }
+                },
+                "Entity_[1597242686075]": {
+                    "Id": "Entity_[1597242686075]",
+                    "Name": "cubeKid",
+                    "Components": {
+                        "Component_[10128771992421174485]": {
+                            "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                            "Id": 10128771992421174485,
+                            "Parent Entity": "Entity_[1592947718779]"
+                        },
+                        "Component_[14936165953779771344]": {
+                            "$type": "EditorVisibilityComponent",
+                            "Id": 14936165953779771344
+                        },
+                        "Component_[403416213715997356]": {
+                            "$type": "EditorOnlyEntityComponent",
+                            "Id": 403416213715997356
+                        }
+                    }
+                }
+            }
+        }
+        )JSON";
+
+    }
+}

+ 136 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.cpp

@@ -0,0 +1,136 @@
+/*
+ * 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 <PrefabGroup/PrefabGroup.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/RTTI/BehaviorContext.h>
+#include <AzCore/JSON/error/error.h>
+#include <AzCore/JSON/error/en.h>
+#include <AzCore/Serialization/Json/JsonUtils.h>
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzCore/std/optional.h>
+#include <AzFramework/FileFunc/FileFunc.h>
+
+namespace AZ::SceneAPI::SceneData
+{
+     // PrefabGroup
+
+     PrefabGroup::PrefabGroup()
+        : m_id(Uuid::CreateNull())
+        , m_name()
+    {
+    }
+
+    const AZStd::string& PrefabGroup::GetName() const
+    {
+        return m_name;
+    }
+
+    void PrefabGroup::SetName(AZStd::string name)
+    {
+        m_name = AZStd::move(name);
+    }
+
+    const Uuid& PrefabGroup::GetId() const
+    {
+        return m_id;
+    }
+
+    void PrefabGroup::SetId(Uuid id)
+    {
+        m_id = AZStd::move(id);
+    }
+
+    Containers::RuleContainer& PrefabGroup::GetRuleContainer()
+    {
+        return m_rules;
+    }
+
+    const Containers::RuleContainer& PrefabGroup::GetRuleContainerConst() const
+    {
+        return m_rules;
+    }
+            
+    DataTypes::ISceneNodeSelectionList& PrefabGroup::GetSceneNodeSelectionList()
+    {
+        return m_nodeSelectionList;
+    }
+
+    const DataTypes::ISceneNodeSelectionList& PrefabGroup::GetSceneNodeSelectionList() const
+    {
+        return m_nodeSelectionList;
+    }
+
+    void PrefabGroup::SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom)
+    {
+        m_prefabDomData = AZStd::make_shared<Prefab::PrefabDomData>();
+        m_prefabDomData->CopyValue(prefabDom);
+    }
+
+    AzToolsFramework::Prefab::PrefabDomConstReference PrefabGroup::GetPrefabDomRef() const
+    {
+        if (m_prefabDomData)
+        {
+            return m_prefabDomData->GetValue();
+        }
+        return {};
+    }
+
+    void PrefabGroup::Reflect(ReflectContext* context)
+    {
+        SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
+        if (serializeContext)
+        {
+            serializeContext->Class<DataTypes::IPrefabGroup, DataTypes::ISceneNodeGroup>()
+                ->Version(1);
+
+            serializeContext->Class<PrefabGroup, DataTypes::IPrefabGroup>()
+                ->Version(1)
+                ->Field("name", &PrefabGroup::m_name)
+                ->Field("nodeSelectionList", &PrefabGroup::m_nodeSelectionList)
+                ->Field("rules", &PrefabGroup::m_rules)
+                ->Field("id", &PrefabGroup::m_id)
+                ->Field("prefabDomData", &PrefabGroup::m_prefabDomData);
+        }
+
+        BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
+        if (behaviorContext)
+        {
+            auto setPrefabDomData = [](PrefabGroup& self, const AZStd::string& json)
+            {
+                auto jsonOutcome = JsonSerializationUtils::ReadJsonString(json);
+                if (jsonOutcome.IsSuccess())
+                {
+                    self.SetPrefabDom(AZStd::move(jsonOutcome.GetValue()));
+                    return true;
+                }
+                AZ_Error("prefab", false, "Set PrefabDom failed (%s)", jsonOutcome.GetError().c_str());
+                return false;
+            };
+
+            auto getPrefabDomData = [](const PrefabGroup& self) -> AZStd::string
+            {
+                if (self.GetPrefabDomRef().has_value() == false)
+                {
+                    return {};
+                }
+                AZStd::string buffer;
+                JsonSerializationUtils::WriteJsonString(self.GetPrefabDomRef().value(), buffer);
+                return buffer;
+            };
+
+            behaviorContext->Class<PrefabGroup>()
+                ->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
+                ->Attribute(Script::Attributes::Scope, Script::Attributes::ScopeFlags::Common)
+                ->Attribute(Script::Attributes::Module, "prefab")
+                ->Property("name", BehaviorValueProperty(&PrefabGroup::m_name))
+                ->Property("id", BehaviorValueProperty(&PrefabGroup::m_id))
+                ->Property("prefabDomData", getPrefabDomData, setPrefabDomData);
+        }
+    }
+}

+ 64 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroup.h

@@ -0,0 +1,64 @@
+/*
+ * 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 <PrefabGroup/IPrefabGroup.h>
+#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <AzCore/Memory/Memory.h>
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/std/containers/vector.h>
+#include <AzCore/std/smart_ptr/shared_ptr.h>
+#include <SceneAPI/SceneCore/Containers/RuleContainer.h>
+#include <SceneAPI/SceneData/ManifestBase/SceneNodeSelectionList.h>
+
+namespace AZ
+{
+    class ReflectContext;
+}
+
+namespace AZ::SceneAPI::Containers
+{
+    class Scene;
+}
+
+namespace AZ::SceneAPI::SceneData
+{
+    class PrefabGroup final
+        : public DataTypes::IPrefabGroup
+    {
+    public:
+        AZ_RTTI(PrefabGroup, "{99FE3C6F-5B55-4D8B-8013-2708010EC715}", DataTypes::IPrefabGroup);
+        AZ_CLASS_ALLOCATOR(PrefabGroup, SystemAllocator, 0);
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        PrefabGroup();
+        ~PrefabGroup() override = default;
+
+        // DataTypes::IPrefabGroup
+        AzToolsFramework::Prefab::PrefabDomConstReference GetPrefabDomRef() const override;
+        const AZStd::string& GetName() const override;
+        const Uuid& GetId() const override;
+        Containers::RuleContainer& GetRuleContainer() override;
+        const Containers::RuleContainer& GetRuleContainerConst() const override;
+        DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() override;
+        const DataTypes::ISceneNodeSelectionList& GetSceneNodeSelectionList() const override;
+
+        // Concrete API
+        void SetId(Uuid id);
+        void SetName(AZStd::string name);
+        void SetPrefabDom(AzToolsFramework::Prefab::PrefabDom prefabDom);
+
+    private:
+        SceneNodeSelectionList m_nodeSelectionList;
+        Containers::RuleContainer m_rules;
+        AZStd::string m_name;
+        Uuid m_id;
+        AZStd::shared_ptr<Prefab::PrefabDomData> m_prefabDomData;
+    };
+}

+ 251 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.cpp

@@ -0,0 +1,251 @@
+/*
+ * 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 <PrefabGroup/PrefabGroupBehavior.h>
+#include <PrefabGroup/PrefabGroup.h>
+#include <PrefabGroup/ProceduralAssetHandler.h>
+#include <AzCore/Asset/AssetManagerBus.h>
+#include <AzCore/IO/FileIO.h>
+#include <AzCore/IO/Path/Path.h>
+#include <AzCore/JSON/document.h>
+#include <AzCore/JSON/error/en.h>
+#include <AzCore/JSON/error/error.h>
+#include <AzCore/JSON/prettywriter.h>
+#include <AzCore/JSON/stringbuffer.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
+#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
+#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
+#include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
+#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <SceneAPI/SceneCore/Components/ExportingComponent.h>
+#include <SceneAPI/SceneCore/Containers/Scene.h>
+#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
+#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
+#include <SceneAPI/SceneCore/Events/ExportProductList.h>
+#include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
+
+namespace AZ::SceneAPI::Behaviors
+{
+    //
+    // ExportEventHandler
+    //
+
+    struct PrefabGroupBehavior::ExportEventHandler final
+        : public AZ::SceneAPI::SceneCore::ExportingComponent
+    {
+        using PreExportEventContextFunction = AZStd::function<Events::ProcessingResult(Events::PreExportEventContext&)>;
+        PreExportEventContextFunction m_preExportEventContextFunction;
+        AZ::Prefab::PrefabGroupAssetHandler m_prefabGroupAssetHandler;
+
+        ExportEventHandler() = delete;
+
+        ExportEventHandler(PreExportEventContextFunction function)
+            : m_preExportEventContextFunction(AZStd::move(function))
+        {
+            BindToCall(&ExportEventHandler::PrepareForExport);
+            AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
+        }
+
+        ~ExportEventHandler()
+        {
+            AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
+        }
+
+        Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
+        {
+            return m_preExportEventContextFunction(context);
+        }
+    };
+
+    //
+    // PrefabGroupBehavior
+    //
+
+    void PrefabGroupBehavior::Activate()
+    {
+        m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](auto& context)
+        {
+            return this->OnPrepareForExport(context);
+        });
+    }
+
+    void PrefabGroupBehavior::Deactivate()
+    {
+        m_exportEventHandler.reset();
+    }
+
+    AZStd::unique_ptr<rapidjson::Document> PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const
+    {
+        using namespace AzToolsFramework::Prefab;
+
+        auto* prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
+        if (!prefabLoaderInterface)
+        {
+            AZ_Error("prefab", false, "Could not get PrefabLoaderInterface");
+            return {};
+        }
+
+        // write to a UTF-8 string buffer
+        auto prefabDomRef = prefabGroup->GetPrefabDomRef();
+        if (!prefabDomRef)
+        {
+            AZ_Error("prefab", false, "PrefabGroup(%s) missing PrefabDom", prefabGroup->GetName().c_str());
+            return {};
+        }
+
+        const AzToolsFramework::Prefab::PrefabDom& prefabDom = prefabDomRef.value();
+        rapidjson::StringBuffer sb;
+        rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>> writer(sb);
+        if (prefabDom.Accept(writer) == false)
+        {
+            AZ_Error("prefab", false, "Could not write PrefabGroup(%s) to JSON", prefabGroup->GetName().c_str());
+            return {};
+        }
+
+        // validate the PrefabDom will make a valid Prefab template instance
+        auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), prefabGroup->GetName().c_str());
+        if (templateId == InvalidTemplateId)
+        {
+            AZ_Error("prefab", false, "PrefabGroup(%s) Could not write load template", prefabGroup->GetName().c_str());
+            return {};
+        }
+
+        auto* prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
+        if (!prefabSystemComponentInterface)
+        {
+            AZ_Error("prefab", false, "Could not get PrefabSystemComponentInterface");
+            return {};
+        }
+
+        // create instance to update the asset hints
+        auto instance = prefabSystemComponentInterface->InstantiatePrefab(templateId);
+        if (!instance)
+        {
+            AZ_Error("prefab", false, "PrefabGroup(%s) Could not instantiate prefab", prefabGroup->GetName().c_str());
+            return {};
+        }
+
+        auto* instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
+        if (!instanceToTemplateInterface)
+        {
+            AZ_Error("prefab", false, "Could not get InstanceToTemplateInterface");
+            return {};
+        }
+
+        // fill out a JSON DOM
+        auto proceduralPrefab = AZStd::make_unique<rapidjson::Document>(rapidjson::kObjectType);
+        instanceToTemplateInterface->GenerateDomForInstance(*proceduralPrefab.get(), *instance.get());
+        return proceduralPrefab;
+    }
+
+    bool PrefabGroupBehavior::WriteOutProductAsset(
+        Events::PreExportEventContext& context,
+        const SceneData::PrefabGroup* prefabGroup,
+        const rapidjson::Document& doc) const
+    {
+        // Retrieve source asset info so we can get a string with the relative path to the asset
+        bool assetInfoResult;
+        Data::AssetInfo info;
+        AZStd::string watchFolder;
+        AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
+            assetInfoResult,
+            &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
+            context.GetScene().GetSourceFilename().c_str(),
+            info,
+            watchFolder);
+
+        AZ::IO::FixedMaxPath assetPath(info.m_relativePath);
+        assetPath.ReplaceFilename(prefabGroup->GetName().c_str());
+
+        AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName(
+            assetPath.c_str(),
+            context.GetOutputDirectory(),
+            AZ::Prefab::PrefabGroupAssetHandler::s_Extension);
+
+        AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite);
+        if (fileStream.IsOpen() == false)
+        {
+            AZ_Error("prefab", false, "File path(%s) could not open for write", filePath.c_str());
+            return false;
+        }
+
+        // write to a UTF-8 string buffer
+        rapidjson::StringBuffer sb;
+        rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>> writer(sb);
+        if (doc.Accept(writer) == false)
+        {
+            AZ_Error("prefab", false, "PrefabGroup(%s) Could not buffer JSON", prefabGroup->GetName().c_str());
+            return false;
+        }
+
+        const auto bytesWritten = fileStream.Write(sb.GetSize(), sb.GetString());
+        if (bytesWritten > 1)
+        {
+            AZ::u32 subId = AZ::Crc32(filePath.c_str());
+            context.GetProductList().AddProduct(
+                filePath,
+                context.GetScene().GetSourceGuid(),
+                azrtti_typeid<Prefab::ProceduralPrefabAsset>(),
+                {},
+                AZStd::make_optional(subId));
+
+            return true;
+        }
+        return false;
+    }
+
+    Events::ProcessingResult PrefabGroupBehavior::OnPrepareForExport(Events::PreExportEventContext& context) const
+    {
+        AZStd::vector<const SceneData::PrefabGroup*> prefabGroupCollection;
+        const Containers::SceneManifest& manifest = context.GetScene().GetManifest();
+
+        for (size_t i = 0; i < manifest.GetEntryCount(); ++i)
+        {
+            const auto* group = azrtti_cast<const SceneData::PrefabGroup*>(manifest.GetValue(i).get());
+            if (group)
+            {
+                prefabGroupCollection.push_back(group);
+            }
+        }
+
+        if (prefabGroupCollection.empty())
+        {
+            return AZ::SceneAPI::Events::ProcessingResult::Ignored;
+        }
+
+        for (const auto* prefabGroup : prefabGroupCollection)
+        {
+            auto result = CreateProductAssetData(prefabGroup);
+            if (!result)
+            {
+                return Events::ProcessingResult::Failure;
+            }
+
+            if (WriteOutProductAsset(context, prefabGroup, *result.get()) == false)
+            {
+                return Events::ProcessingResult::Failure;
+            }
+        }
+
+        return Events::ProcessingResult::Success;
+    }
+
+    void PrefabGroupBehavior::Reflect(ReflectContext* context)
+    {
+        AZ::SceneAPI::SceneData::PrefabGroup::Reflect(context);
+        Prefab::ProceduralPrefabAsset::Reflect(context);
+
+        SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
+        if (serializeContext)
+        {
+            serializeContext->Class<PrefabGroupBehavior, BehaviorComponent>()->Version(1);
+        }
+    }
+}

+ 55 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupBehavior.h

@@ -0,0 +1,55 @@
+/*
+ * 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 <PrefabGroup/PrefabGroup.h>
+#include <AzCore/Memory/Memory.h>
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/std/containers/vector.h>
+#include <AzCore/std/smart_ptr/shared_ptr.h>
+#include <SceneAPI/SceneCore/Events/ProcessingResult.h>
+#include <SceneAPI/SceneCore/Components/BehaviorComponent.h>
+#include <AzCore/JSON/rapidjson.h>
+
+namespace AZ
+{
+    class ReflectContext;
+}
+
+namespace AZ::SceneAPI::Events
+{
+    class PreExportEventContext;
+}
+
+namespace AZ::SceneAPI::Behaviors
+{
+    class PrefabGroupBehavior
+        : public SceneCore::BehaviorComponent
+    {
+    public:
+        AZ_COMPONENT(PrefabGroupBehavior, "{13DC2819-CAC2-4977-91D7-C870087072AB}", SceneCore::BehaviorComponent);
+
+        ~PrefabGroupBehavior() override = default;
+
+        void Activate() override;
+        void Deactivate() override;
+        static void Reflect(ReflectContext* context);
+
+    private:
+        Events::ProcessingResult OnPrepareForExport(Events::PreExportEventContext& context) const;
+        AZStd::unique_ptr<rapidjson::Document> CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const;
+
+        bool WriteOutProductAsset(
+            Events::PreExportEventContext& context,
+            const SceneData::PrefabGroup* prefabGroup,
+            const rapidjson::Document& doc) const;
+
+        struct ExportEventHandler;
+        AZStd::shared_ptr<ExportEventHandler> m_exportEventHandler;
+    };
+}

+ 161 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/PrefabGroupTests.cpp

@@ -0,0 +1,161 @@
+/*
+ * 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 <PrefabBuilderTests.h>
+#include <PrefabGroup/IPrefabGroup.h>
+#include <PrefabGroup/PrefabGroup.h>
+#include <AzCore/Serialization/Json/JsonSystemComponent.h>
+#include <AzCore/Serialization/Json/RegistrationContext.h>
+#include <AzFramework/FileFunc/FileFunc.h>
+
+namespace UnitTest
+{
+    TEST_F(PrefabBuilderTests, PrefabGroup_FindsRequiredReflection_True)
+    {
+        using namespace AZ::SceneAPI;
+        auto* serializeContext = m_app.GetSerializeContext();
+        ASSERT_NE(nullptr, serializeContext);
+        SceneData::PrefabGroup::Reflect(serializeContext);
+        SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext());
+        ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid<DataTypes::IPrefabGroup>()));
+        ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid<SceneData::PrefabGroup>()));
+
+        auto findElementWithName = [](const AZ::SerializeContext::ClassData* classData, const char* name)
+        {
+            auto it = AZStd::find_if(classData->m_elements.begin(), classData->m_elements.end(), [name](const auto& element)
+            {
+                return strcmp(element.m_name, name) == 0;
+            });
+            return it != classData->m_elements.end();
+        };
+
+        auto* prefabGroupClassData = serializeContext->FindClassData(azrtti_typeid<SceneData::PrefabGroup>());
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "name"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "nodeSelectionList"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "rules"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "id"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "prefabDomData"));
+
+        m_app.GetJsonRegistrationContext()->EnableRemoveReflection();
+        SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext());
+    }
+
+    TEST_F(PrefabBuilderTests, PrefabGroup_JsonWithPrefabArbitraryPrefab_Works)
+    {
+        namespace JSR = AZ::JsonSerializationResult;
+        using namespace AZ::SceneAPI;
+        auto* serializeContext = m_app.GetSerializeContext();
+        ASSERT_NE(nullptr, serializeContext);
+        SceneData::PrefabGroup::Reflect(serializeContext);
+        AZ::Prefab::ProceduralPrefabAsset::Reflect(serializeContext);
+        SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext());
+        AZ::Prefab::ProceduralPrefabAsset::Reflect(m_app.GetJsonRegistrationContext());
+
+        // fill out a PrefabGroup using JSON
+        AZStd::string_view input = R"JSON(
+        {
+            "name" : "tester",
+            "id" : "{49698DBC-B447-49EF-9B56-25BB29342AFB}",
+            "prefabDomData" : {"foo": "bar"}
+        })JSON";
+
+        rapidjson::Document document;
+        document.Parse<rapidjson::kParseCommentsFlag>(input.data(), input.size());
+        ASSERT_FALSE(document.HasParseError());
+
+        SceneData::PrefabGroup instancePrefabGroup;
+        EXPECT_EQ(AZ::JsonSerialization::Load(instancePrefabGroup, document).GetOutcome(), JSR::Outcomes::PartialDefaults);
+
+        ASSERT_TRUE(instancePrefabGroup.GetPrefabDomRef().has_value());
+        const AzToolsFramework::Prefab::PrefabDom& dom = instancePrefabGroup.GetPrefabDomRef().value();
+        EXPECT_TRUE(dom.IsObject());
+        EXPECT_TRUE(dom.GetObject().HasMember("foo"));
+        EXPECT_STREQ(dom.GetObject().FindMember("foo")->value.GetString(), "bar");
+        EXPECT_STREQ(instancePrefabGroup.GetName().c_str(), "tester");
+        EXPECT_STREQ(instancePrefabGroup.GetId().ToString<AZStd::string>().c_str(), "{49698DBC-B447-49EF-9B56-25BB29342AFB}");
+
+        m_app.GetJsonRegistrationContext()->EnableRemoveReflection();
+        SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext());
+        AZ::Prefab::ProceduralPrefabAsset::Reflect(m_app.GetJsonRegistrationContext());
+    }
+
+    TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJson_Detected)
+    {
+        using namespace AZ::SceneAPI;
+
+        AZStd::string_view input = R"JSON(
+        {
+            bad json that will not parse
+        })JSON";
+
+        rapidjson::Document document;
+        document.Parse<rapidjson::kParseCommentsFlag>(input.data(), input.size());
+
+        SceneData::PrefabGroup prefabGroup;
+        prefabGroup.SetId(AZ::Uuid::CreateRandom());
+        prefabGroup.SetName("tester");
+        prefabGroup.SetPrefabDom(AZStd::move(document));
+
+        const AzToolsFramework::Prefab::PrefabDom& dom = prefabGroup.GetPrefabDomRef().value();
+        EXPECT_TRUE(dom.IsNull());
+        EXPECT_STREQ("tester", prefabGroup.GetName().c_str());
+    }
+
+    struct PrefabBuilderBehaviorTests
+        : public PrefabBuilderTests
+    {
+        void SetUp() override
+        {
+            using namespace AZ::SceneAPI;
+
+            PrefabBuilderTests::SetUp();
+            SceneData::PrefabGroup::Reflect(m_app.GetSerializeContext());
+            SceneData::PrefabGroup::Reflect(m_app.GetBehaviorContext());
+            SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext());
+            m_scriptContext = AZStd::make_unique<AZ::ScriptContext>();
+            m_scriptContext->BindTo(m_app.GetBehaviorContext());
+        }
+
+        void TearDown() override
+        {
+            using namespace AZ::SceneAPI;
+            m_app.GetJsonRegistrationContext()->EnableRemoveReflection();
+            SceneData::PrefabGroup::Reflect(m_app.GetJsonRegistrationContext());
+
+            m_scriptContext.reset();
+            PrefabBuilderTests::TearDown();
+        }
+
+        void ExpectExecute(AZStd::string_view script)
+        {
+            EXPECT_TRUE(m_scriptContext->Execute(script.data()));
+        }
+
+        AZStd::unique_ptr<AZ::ScriptContext> m_scriptContext;
+    };
+
+    TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupClass_Exists)
+    {
+        ExpectExecute("group = PrefabGroup()");
+        ExpectExecute("assert(group)");
+        ExpectExecute("assert(group.name)");
+        ExpectExecute("assert(group.id)");
+        ExpectExecute("assert(group.prefabDomData)");
+    }
+
+    TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupAssignment_Works)
+    {
+        ExpectExecute("group = PrefabGroup()");
+        ExpectExecute("group.name = 'tester'");
+        ExpectExecute("group.id = Uuid.CreateString('{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}', 0)");
+        ExpectExecute("group.prefabDomData = '{\"foo\": \"bar\"}'");
+        ExpectExecute("assert(group.name == 'tester')");
+        ExpectExecute("assert(tostring(group.id) == '{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}')");
+        ExpectExecute("assert(group.prefabDomData == '{\\n    \"foo\": \"bar\"\\n}')");
+    }
+}

+ 185 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.cpp

@@ -0,0 +1,185 @@
+/*
+ * 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 <PrefabGroup/ProceduralAssetHandler.h>
+#include <AzCore/Asset/AssetTypeInfoBus.h>
+#include <AzCore/std/smart_ptr/make_shared.h>
+#include <AzFramework/FileFunc/FileFunc.h>
+#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
+#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
+#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
+#include <AzCore/Serialization/Json/JsonUtils.h>
+
+namespace AZ::Prefab
+{
+    // AssetTypeInfoHandler
+
+    class PrefabGroupAssetHandler::AssetTypeInfoHandler final
+        : public AZ::AssetTypeInfoBus::Handler
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(AssetTypeInfoHandler, AZ::SystemAllocator, 0);
+        AssetTypeInfoHandler();
+        ~AssetTypeInfoHandler() override;
+        AZ::Data::AssetType GetAssetType() const override;
+        const char* GetAssetTypeDisplayName() const override;
+        const char* GetGroup() const override;
+        const char* GetBrowserIcon() const override;
+        void GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions) override;
+    };
+
+    PrefabGroupAssetHandler::AssetTypeInfoHandler::AssetTypeInfoHandler()
+    {
+        AZ::AssetTypeInfoBus::Handler::BusConnect(azrtti_typeid<ProceduralPrefabAsset>());
+    }
+
+    PrefabGroupAssetHandler::AssetTypeInfoHandler::~AssetTypeInfoHandler()
+    {
+        AZ::AssetTypeInfoBus::Handler::BusDisconnect(azrtti_typeid<ProceduralPrefabAsset>());
+    }
+
+    AZ::Data::AssetType PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetType() const
+    {
+        return azrtti_typeid<ProceduralPrefabAsset>();
+    }
+
+    const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetTypeDisplayName() const
+    {
+        return "Procedural Prefab";
+    }
+
+    const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetGroup() const
+    {
+        return "Prefab";
+    }
+
+    const char* PrefabGroupAssetHandler::AssetTypeInfoHandler::GetBrowserIcon() const
+    {
+        return "Icons/Components/Box.png";
+    }
+
+    void PrefabGroupAssetHandler::AssetTypeInfoHandler::GetAssetTypeExtensions(AZStd::vector<AZStd::string>& extensions)
+    {
+        extensions.push_back(PrefabGroupAssetHandler::s_Extension);
+    }
+
+    // PrefabGroupAssetHandler
+
+    AZStd::string_view PrefabGroupAssetHandler::s_Extension{ "procprefab" };
+
+    PrefabGroupAssetHandler::PrefabGroupAssetHandler()
+    {
+        auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
+        if (assetCatalog)
+        {
+            assetCatalog->EnableCatalogForAsset(azrtti_typeid<ProceduralPrefabAsset>());
+            assetCatalog->AddExtension(s_Extension.data());
+        }
+        if (AZ::Data::AssetManager::IsReady())
+        {
+            AZ::Data::AssetManager::Instance().RegisterHandler(this, azrtti_typeid<ProceduralPrefabAsset>());
+        }
+        m_assetTypeInfoHandler = AZStd::make_shared<AssetTypeInfoHandler>();
+    }
+
+    PrefabGroupAssetHandler::~PrefabGroupAssetHandler()
+    {
+        m_assetTypeInfoHandler.reset();
+        if (AZ::Data::AssetManager::IsReady())
+        {
+            AZ::Data::AssetManager::Instance().UnregisterHandler(this);
+        }
+    }
+
+    AZ::Data::AssetData* PrefabGroupAssetHandler::CreateAsset([[maybe_unused]] const AZ::Data::AssetId& id, const AZ::Data::AssetType& type)
+    {
+        if (type != azrtti_typeid<ProceduralPrefabAsset>())
+        {
+            AZ_Error("prefab", false, "Invalid asset type! Only handle 'ProceduralPrefabAsset'");
+            return nullptr;
+        }
+        return aznew ProceduralPrefabAsset{};
+    }
+
+    void PrefabGroupAssetHandler::DestroyAsset(AZ::Data::AssetData* ptr)
+    {
+        // Note: the PrefabLoaderInterface will handle the lifetime of the Prefab Template
+        delete ptr;
+    }
+
+    void PrefabGroupAssetHandler::GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes)
+    {
+        assetTypes.push_back(azrtti_typeid<ProceduralPrefabAsset>());
+    }
+
+    AZ::Data::AssetHandler::LoadResult PrefabGroupAssetHandler::LoadAssetData(
+        const AZ::Data::Asset<AZ::Data::AssetData>& asset,
+        AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
+        [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB)
+    {
+        using namespace AzToolsFramework::Prefab;
+
+        auto* proceduralPrefabAsset = asset.GetAs<ProceduralPrefabAsset>();
+        if (!proceduralPrefabAsset)
+        {
+            AZ_Error("prefab", false, "This should be a ProceduralPrefabAsset type, as this is the only type we process!");
+            return LoadResult::Error;
+        }
+
+        AZStd::string buffer;
+        buffer.resize(stream->GetLoadedSize());
+        stream->Read(stream->GetLoadedSize(), buffer.data());
+
+        auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(buffer);
+        if (jsonOutcome.IsSuccess() == false)
+        {
+            AZ_Error("prefab", false, "Asset JSON failed to compile %s", jsonOutcome.GetError().c_str());
+            return LoadResult::Error;
+        }
+        const auto& jsonDoc = jsonOutcome.GetValue();
+
+        if (jsonDoc.IsObject() == false)
+        {
+            return LoadResult::Error;
+        }
+
+        if (jsonDoc.FindMember("Source") == jsonDoc.MemberEnd())
+        {
+            return LoadResult::Error;
+        }
+        const auto& templateName = jsonDoc["Source"];
+
+        AZStd::string stringJson;
+        auto stringOutcome = AZ::JsonSerializationUtils::WriteJsonString(jsonDoc, stringJson);
+        if (stringOutcome.IsSuccess() == false)
+        {
+            AZ_Error("prefab", false, "Could not write to JSON string %s", stringOutcome.GetError().c_str());
+            return LoadResult::Error;
+        }
+
+        // prepare the template
+        auto* prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
+        if (!prefabLoaderInterface)
+        {
+            return LoadResult::Error;
+        }
+
+        auto templateId = prefabLoaderInterface->LoadTemplateFromString(stringJson.data(), templateName.GetString());
+        if (templateId == InvalidTemplateId)
+        {
+            return LoadResult::Error;
+        }
+
+        proceduralPrefabAsset->SetTemplateId(templateId);
+        proceduralPrefabAsset->SetTemplateName(templateName.GetString());
+        return LoadResult::LoadComplete;
+    }
+
+    AZStd::unique_ptr<PrefabGroupAssetHandler> s_PrefabGroupAssetHandler;
+}
+

+ 39 - 0
Gems/Prefab/PrefabBuilder/PrefabGroup/ProceduralAssetHandler.h

@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzCore/Asset/AssetManager.h>
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/std/smart_ptr/shared_ptr.h>
+#include <AzCore/std/containers/vector.h>
+
+namespace AZ::Prefab
+{
+    class PrefabGroupAssetHandler final
+        : public AZ::Data::AssetHandler
+    {
+    public:
+        AZ_CLASS_ALLOCATOR(PrefabGroupAssetHandler, AZ::SystemAllocator, 0);
+        PrefabGroupAssetHandler();
+        ~PrefabGroupAssetHandler() override;
+
+        static AZStd::string_view s_Extension;
+
+    protected:
+        AZ::Data::AssetData* CreateAsset(const AZ::Data::AssetId& id, const AZ::Data::AssetType& type) override;
+        void DestroyAsset(AZ::Data::AssetData* ptr) override;
+        void GetHandledAssetTypes(AZStd::vector<AZ::Data::AssetType>& assetTypes) override;
+        AZ::Data::AssetHandler::LoadResult LoadAssetData(
+            const AZ::Data::Asset<AZ::Data::AssetData>& asset,
+            AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
+            const AZ::Data::AssetFilterCB& assetLoadFilterCB) override;
+
+        class AssetTypeInfoHandler;
+        AZStd::shared_ptr<AssetTypeInfoHandler> m_assetTypeInfoHandler;
+    };
+}

+ 162 - 0
Gems/Prefab/PrefabBuilder/PrefabGroupTests.cpp

@@ -0,0 +1,162 @@
+/*
+ * 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 <PrefabGroup/PrefabBuilderTests.h>
+#include <PrefabGroup/IPrefabGroup.h>
+#include <PrefabGroup/PrefabGroup.h>
+#include <AzCore/Serialization/Json/JsonSystemComponent.h>
+
+namespace UnitTest
+{
+    TEST_F(PrefabBuilderTests, PrefabGroup_FindsRequiredReflection_True)
+    {
+        using namespace AZ::SceneAPI;
+        auto* serializeContext = m_app.GetSerializeContext();
+        ASSERT_NE(nullptr, serializeContext);
+        SceneData::PrefabGroup::Reflect(serializeContext);
+        ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid<DataTypes::IPrefabGroup>()));
+        ASSERT_NE(nullptr, serializeContext->FindClassData(azrtti_typeid<SceneData::PrefabGroup>()));
+
+        auto findElementWithName = [](const AZ::SerializeContext::ClassData* classData, const char* name)
+        {
+            auto it = AZStd::find_if(classData->m_elements.begin(), classData->m_elements.end(), [name](const auto& element)
+            {
+                return strcmp(element.m_name, name) == 0;
+            });
+            return it != classData->m_elements.end();
+        };
+
+        auto* prefabGroupClassData = serializeContext->FindClassData(azrtti_typeid<SceneData::PrefabGroup>());
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "name"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "nodeSelectionList"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "rules"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "id"));
+        EXPECT_TRUE(findElementWithName(prefabGroupClassData, "prefabDomBuffer"));
+    }
+
+    TEST_F(PrefabBuilderTests, PrefabGroup_JsonWithPrefabArbitraryPrefab_Works)
+    {
+        using namespace AZ::SceneAPI;
+        auto* serializeContext = m_app.GetSerializeContext();
+        ASSERT_NE(nullptr, serializeContext);
+        SceneData::PrefabGroup::Reflect(serializeContext);
+
+        // fill out a PrefabGroup using JSON
+        AZStd::string_view input = R"JSON(
+        {
+            "name" : "tester",
+            "id" : "{49698DBC-B447-49EF-9B56-25BB29342AFB}",
+            "prefabDomBuffer" : "{\"foo\":\"bar\"}"
+        })JSON";
+
+        rapidjson::Document document;
+        document.Parse<rapidjson::kParseCommentsFlag>(input.data(), input.size());
+        ASSERT_FALSE(document.HasParseError());
+
+        SceneData::PrefabGroup instancePrefabGroup;
+        AZ::JsonSerialization::Load(instancePrefabGroup, document);
+
+        const auto& dom = instancePrefabGroup.GetPrefabDom();
+        EXPECT_TRUE(dom.GetObject().HasMember("foo"));
+        EXPECT_STREQ(dom.GetObject().FindMember("foo")->value.GetString(), "bar");
+        EXPECT_STREQ(instancePrefabGroup.GetName().c_str(), "tester");
+        EXPECT_STREQ(
+            instancePrefabGroup.GetId().ToString<AZStd::string>().c_str(),
+            "{49698DBC-B447-49EF-9B56-25BB29342AFB}");
+        EXPECT_TRUE(instancePrefabGroup.GetPrefabDom().IsObject());
+    }
+
+    TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJson_Detected)
+    {
+        using namespace AZ::SceneAPI;
+
+        AZStd::string_view input = R"JSON(
+        {
+            bad json that will not parse
+        })JSON";
+
+        rapidjson::Document document;
+        document.Parse<rapidjson::kParseCommentsFlag>(input.data(), input.size());
+
+        SceneData::PrefabGroup prefabGroup;
+        prefabGroup.SetId(AZStd::move(AZ::Uuid::CreateRandom()));
+        prefabGroup.SetName(AZStd::move("tester"));
+        prefabGroup.SetPrefabDom(AZStd::move(document));
+
+        const auto& dom = prefabGroup.GetPrefabDom();
+        EXPECT_TRUE(dom.IsNull());
+        EXPECT_STREQ("tester", prefabGroup.GetName().c_str());
+    }
+
+    TEST_F(PrefabBuilderTests, PrefabGroup_InvalidPrefabJsonBuffer_Detected)
+    {
+        using namespace AZ::SceneAPI;
+
+        AZStd::string_view inputJson = R"JSON(
+        {
+            bad json that will not parse
+        })JSON";
+
+        SceneData::PrefabGroup prefabGroup;
+        prefabGroup.SetId(AZStd::move(AZ::Uuid::CreateRandom()));
+        prefabGroup.SetName(AZStd::move("tester"));
+        prefabGroup.SetPrefabDomBuffer(std::move(inputJson));
+
+        const auto& dom = prefabGroup.GetPrefabDom();
+        EXPECT_TRUE(dom.IsNull());
+        EXPECT_STREQ("tester", prefabGroup.GetName().c_str());
+    }
+
+    struct PrefabBuilderBehaviorTests
+        : public PrefabBuilderTests
+    {
+        void SetUp() override
+        {
+            using namespace AZ::SceneAPI;
+
+            PrefabBuilderTests::SetUp();
+            SceneData::PrefabGroup::Reflect(m_app.GetSerializeContext());
+            SceneData::PrefabGroup::Reflect(m_app.GetBehaviorContext());
+            m_scriptContext = AZStd::make_unique<AZ::ScriptContext>();
+            m_scriptContext->BindTo(m_app.GetBehaviorContext());
+        }
+
+        void TearDown() override
+        {
+            m_scriptContext.reset();
+            PrefabBuilderTests::TearDown();
+        }
+
+        void ExpectExecute(AZStd::string_view script)
+        {
+            EXPECT_TRUE(m_scriptContext->Execute(script.data()));
+        }
+
+        AZStd::unique_ptr<AZ::ScriptContext> m_scriptContext;
+    };
+
+    TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupClass_Exists)
+    {
+        ExpectExecute("group = PrefabGroup()");
+        ExpectExecute("assert(group)");
+        ExpectExecute("assert(group.name)");
+        ExpectExecute("assert(group.id)");
+        ExpectExecute("assert(group.prefabDomBuffer)");
+    }
+
+    TEST_F(PrefabBuilderBehaviorTests, PrefabGroup_PrefabGroupAssignment_Works)
+    {
+        ExpectExecute("group = PrefabGroup()");
+        ExpectExecute("group.name = 'tester'");
+        ExpectExecute("group.id = Uuid.CreateString('{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}', 0)");
+        ExpectExecute("group.prefabDomBuffer = '{}'");
+        ExpectExecute("assert(group.name == 'tester')");
+        ExpectExecute("assert(tostring(group.id) == '{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}')");
+        ExpectExecute("assert(group.prefabDomBuffer == '{}')");
+    }
+}

+ 7 - 0
Gems/Prefab/PrefabBuilder/prefabbuilder_files.cmake

@@ -9,4 +9,11 @@
 set(FILES
     PrefabBuilderComponent.h
     PrefabBuilderComponent.cpp
+    PrefabGroup/IPrefabGroup.h
+    PrefabGroup/PrefabGroup.cpp
+    PrefabGroup/PrefabGroup.h
+    PrefabGroup/PrefabGroupBehavior.cpp
+    PrefabGroup/PrefabGroupBehavior.h
+    PrefabGroup/ProceduralAssetHandler.cpp
+    PrefabGroup/ProceduralAssetHandler.h
 )

+ 3 - 0
Gems/Prefab/PrefabBuilder/prefabbuilder_tests_files.cmake

@@ -9,4 +9,7 @@
 set(FILES
     PrefabBuilderTests.h
     PrefabBuilderTests.cpp
+    PrefabGroup/PrefabGroupTests.cpp
+    PrefabGroup/PrefabBehaviorTests.cpp
+    PrefabGroup/PrefabBehaviorTests.inl
 )

+ 9 - 0
Gems/PythonAssetBuilder/Editor/Scripts/scene_api/scene_data.py

@@ -104,6 +104,15 @@ class SceneManifest():
         self.manifest['values'].append(meshGroup)
         return meshGroup
 
+    def add_prefab_group(self, name, id, json) -> dict:
+        prefabGroup = {}
+        prefabGroup['$type'] = '{99FE3C6F-5B55-4D8B-8013-2708010EC715} PrefabGroup'
+        prefabGroup['name'] = name
+        prefabGroup['id'] = id
+        prefabGroup['prefabDomData'] = json
+        self.manifest['values'].append(prefabGroup)
+        return prefabGroup
+
     def mesh_group_select_node(self, meshGroup, nodeName):
         meshGroup['nodeSelectionList']['selectedNodes'].append(nodeName)
 

+ 96 - 0
Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.cpp

@@ -13,6 +13,7 @@
 #include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/std/containers/set.h>
+#include <AzCore/Utils/Utils.h>
 #include <AzFramework/Application/Application.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzToolsFramework/Debug/TraceContext.h>
@@ -34,6 +35,9 @@
 #include <SceneAPI/SceneCore/DataTypes/GraphData/IAnimationData.h>
 
 #include <AssetBuilderSDK/AssetBuilderSDK.h>
+#include <AzCore/Serialization/Json/JsonUtils.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <rapidjson/pointer.h>
 #include <SceneBuilder/SceneBuilderWorker.h>
 #include <SceneBuilder/TraceMessageHook.h>
 
@@ -81,6 +85,93 @@ namespace SceneBuilder
         return m_cachedFingerprint.c_str();
     }
 
+    void SceneBuilderWorker::PopulateSourceDependencies(const AZStd::string& manifestJson, AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceFileDependencies)
+    {
+        auto readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(manifestJson);
+        AZStd::string errorMsg;
+        if (!readJsonOutcome.IsSuccess())
+        {
+            // This may be an old format xml file.  We don't have any dependencies in the old format so there's no point trying to parse an xml
+            return;
+        }
+
+        rapidjson::Document document = readJsonOutcome.TakeValue();
+
+        auto manifestObject = document.GetObject();
+        auto valuesIterator = manifestObject.FindMember("values");
+        auto valuesArray = valuesIterator->value.GetArray();
+
+        AZStd::vector<AZStd::string> paths;
+        AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(
+            &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestDependencyPaths, paths);
+
+        for (const auto& value : valuesArray)
+        {
+            auto object = value.GetObject();
+
+            for (const auto& path : paths)
+            {
+                rapidjson::Pointer pointer(path.c_str());
+
+                auto dependencyValue = pointer.Get(object);
+
+                if (dependencyValue && dependencyValue->IsString())
+                {
+                    AZStd::string dependency = dependencyValue->GetString();
+
+                    sourceFileDependencies.emplace_back(AssetBuilderSDK::SourceFileDependency(
+                        dependency, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute));
+                }
+            }
+        }
+    }
+
+    bool SceneBuilderWorker::ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
+    {
+        AZStd::string manifestExtension;
+        AZStd::string generatedManifestExtension;
+
+        AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(
+            &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestExtension, manifestExtension);
+        AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(
+            &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetGeneratedManifestExtension, generatedManifestExtension);
+
+        if (manifestExtension.empty() || generatedManifestExtension.empty())
+        {
+            AZ_Error("SceneBuilderWorker", false, "Failed to get scene manifest extension");
+            return false;
+        }
+
+        AZ::SettingsRegistryInterface::FixedValueString assetCacheRoot;
+        AZ::SettingsRegistry::Get()->Get(assetCacheRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
+
+        auto manifestPath = (AZ::IO::Path(request.m_watchFolder) / (request.m_sourceFile + manifestExtension));
+        auto generatedManifestPath = (AZ::IO::Path(assetCacheRoot) / (request.m_sourceFile + generatedManifestExtension));
+
+        auto populateDependenciesFunc = [&response](const AZStd::string& path)
+        {
+            auto readFileOutcome = AZ::Utils::ReadFile(path, AZ::SceneAPI::Containers::SceneManifest::MaxSceneManifestFileSizeInBytes);
+            if (!readFileOutcome.IsSuccess())
+            {
+                AZ_Error("SceneBuilderWorker", false, "%s", readFileOutcome.GetError().c_str());
+                return;
+            }
+
+            PopulateSourceDependencies(readFileOutcome.TakeValue(), response.m_sourceFileDependencyList);
+        };
+
+        if (AZ::IO::FileIOBase::GetInstance()->Exists(manifestPath.Native().c_str()))
+        {
+            populateDependenciesFunc(manifestPath.Native());
+        }
+        else if (AZ::IO::FileIOBase::GetInstance()->Exists(generatedManifestPath.Native().c_str()))
+        {
+            populateDependenciesFunc(generatedManifestPath.Native());
+        }
+
+        return true;
+    }
+
     void SceneBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
     {
         // Check for shutdown
@@ -118,6 +209,11 @@ namespace SceneBuilder
         sourceFileDependencyInfo.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards;
         response.m_sourceFileDependencyList.push_back(sourceFileDependencyInfo);
 
+        if (!ManifestDependencyCheck(request, response))
+        {
+            return;
+        }
+
         response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
     }
 

+ 5 - 0
Gems/SceneProcessing/Code/Source/SceneBuilder/SceneBuilderWorker.h

@@ -11,6 +11,7 @@
 #include <AzCore/Component/Component.h>
 #include <AzCore/std/smart_ptr/shared_ptr.h>
 #include <AssetBuilderSDK/AssetBuilderBusses.h>
+#include <AssetBuilderSDK/AssetBuilderSDK.h>
 
 namespace AssetBuilderSDK
 {
@@ -45,11 +46,15 @@ namespace SceneBuilder
     public:
         ~SceneBuilderWorker() override = default;
 
+        
         void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
         void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response);
 
         void ShutDown() override;
         const char* GetFingerprint() const;
+        static void PopulateSourceDependencies(
+            const AZStd::string& manifestJson, AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceFileDependencies);
+        static bool ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response);
         static AZ::Uuid GetUUID();
 
         void PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const;

+ 200 - 0
Gems/SceneProcessing/Code/Tests/SceneBuilder/SceneBuilderTests.cpp

@@ -11,11 +11,14 @@
 #include <AzCore/IO/Path/Path.h>
 #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
 #include <AzCore/UnitTest/TestTypes.h>
+#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
 #include <AzCore/UserSettings/UserSettingsComponent.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzToolsFramework/Application/ToolsApplication.h>
+#include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
 #include <SceneBuilder/SceneBuilderWorker.h>
 #include <SceneAPI/SceneCore/Events/ExportProductList.h>
+#include <Tests/FileIOBaseTestTypes.h>
 
 using namespace AZ;
 using namespace SceneBuilder;
@@ -193,3 +196,200 @@ TEST_F(SceneBuilderTests, SceneBuilderWorker_ExportProductDependencies_ProductAn
 
     TestSuccessCase(exportProduct, expectedPathDependencies, { dependencyId });
 }
+
+struct ImportHandler
+    : SceneAPI::Events::AssetImportRequestBus::Handler
+{
+    ImportHandler()
+    {
+        BusConnect();
+    }
+
+    ~ImportHandler() override
+    {
+        BusDisconnect();
+    }
+
+    void GetManifestDependencyPaths(AZStd::vector<AZStd::string>& paths) override
+    {
+        paths.emplace_back("/scriptFilename");
+        paths.emplace_back("/layer1/layer2/0/target");
+    }
+
+    void GetManifestExtension(AZStd::string& result) override
+    {
+        result = ".test";
+    }
+
+    void GetGeneratedManifestExtension(AZStd::string& result) override
+    {
+        result = ".test.gen";
+    }
+};
+
+using SourceDependencyTests = UnitTest::ScopedAllocatorSetupFixture;
+
+namespace SourceDependencyJson
+{
+    constexpr const char* TestJson = R"JSON(
+{
+    "values": [
+        {
+            "$type": "Test1",
+            "scriptFilename": "a/test/path.png"
+        },
+        {
+            "$type": "Test2",
+            "layer1" : {
+                "layer2" : [
+                    {
+                        "target": "value.png",
+                        "otherData": "value2.png"
+                    },
+                    {
+                        "target" : "wrong.png"
+                    }
+                ]
+            }
+        }
+    ]
+}
+    )JSON";
+}
+
+TEST_F(SourceDependencyTests, SourceDependencyTest)
+{
+    ImportHandler handler;
+    AZStd::vector<AssetBuilderSDK::SourceFileDependency> dependencies;
+
+    SceneBuilderWorker::PopulateSourceDependencies(SourceDependencyJson::TestJson, dependencies);
+
+    ASSERT_EQ(dependencies.size(), 2);
+    ASSERT_STREQ(dependencies[0].m_sourceFileDependencyPath.c_str(), "a/test/path.png");
+    ASSERT_STREQ(dependencies[1].m_sourceFileDependencyPath.c_str(), "value.png");
+}
+
+struct SettingsRegistryMock : AZ::Interface<SettingsRegistryInterface>::Registrar
+{
+    bool Get(FixedValueString& result, AZStd::string_view) const override
+    {
+        result = "cache";
+        return true;
+    }
+
+    void SetApplyPatchSettings(const AZ::JsonApplyPatchSettings& /*applyPatchSettings*/) override{}
+    void GetApplyPatchSettings(AZ::JsonApplyPatchSettings& /*applyPatchSettings*/) override{}
+
+    MOCK_CONST_METHOD1(GetType, Type (AZStd::string_view));
+    MOCK_CONST_METHOD2(Visit, bool (Visitor&, AZStd::string_view));
+    MOCK_CONST_METHOD2(Visit, bool (const VisitorCallback&, AZStd::string_view));
+    MOCK_METHOD1(RegisterNotifier, NotifyEventHandler (const NotifyCallback&));
+    MOCK_METHOD1(RegisterNotifier, NotifyEventHandler (NotifyCallback&&));
+    MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler (const PreMergeEventCallback&));
+    MOCK_METHOD1(RegisterPreMergeEvent, PreMergeEventHandler (PreMergeEventCallback&&));
+    MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler (const PostMergeEventCallback&));
+    MOCK_METHOD1(RegisterPostMergeEvent, PostMergeEventHandler (PostMergeEventCallback&&));
+    MOCK_CONST_METHOD2(Get, bool (bool&, AZStd::string_view));
+    MOCK_CONST_METHOD2(Get, bool (s64&, AZStd::string_view));
+    MOCK_CONST_METHOD2(Get, bool (u64&, AZStd::string_view));
+    MOCK_CONST_METHOD2(Get, bool (double&, AZStd::string_view));
+    MOCK_CONST_METHOD2(Get, bool (AZStd::string&, AZStd::string_view));
+    MOCK_CONST_METHOD3(GetObject, bool (void*, Uuid, AZStd::string_view));
+    MOCK_METHOD2(Set, bool (AZStd::string_view, bool));
+    MOCK_METHOD2(Set, bool (AZStd::string_view, s64));
+    MOCK_METHOD2(Set, bool (AZStd::string_view, u64));
+    MOCK_METHOD2(Set, bool (AZStd::string_view, double));
+    MOCK_METHOD2(Set, bool (AZStd::string_view, AZStd::string_view));
+    MOCK_METHOD2(Set, bool (AZStd::string_view, const char*));
+    MOCK_METHOD3(SetObject, bool (AZStd::string_view, const void*, Uuid));
+    MOCK_METHOD1(Remove, bool (AZStd::string_view));
+    MOCK_METHOD3(MergeCommandLineArgument, bool (AZStd::string_view, AZStd::string_view, const CommandLineArgumentSettings&));
+    MOCK_METHOD2(MergeSettings, bool (AZStd::string_view, Format));
+    MOCK_METHOD4(MergeSettingsFile, bool (AZStd::string_view, Format, AZStd::string_view, AZStd::vector<char>*));
+    MOCK_METHOD5(MergeSettingsFolder, bool (AZStd::string_view, const Specializations&, AZStd::string_view, AZStd::string_view, AZStd::vector<char>*));
+    MOCK_METHOD1(SetUseFileIO, void (bool));
+};
+
+struct SourceDependencyMockedIOTests : UnitTest::ScopedAllocatorSetupFixture
+    , UnitTest::SetRestoreFileIOBaseRAII
+{
+    SourceDependencyMockedIOTests()
+        : UnitTest::SetRestoreFileIOBaseRAII(m_ioMock)
+    {
+        
+    }
+
+    void SetUp() override
+    {
+        using namespace ::testing;
+
+        ON_CALL(m_ioMock, Open(_, _, _))
+            .WillByDefault(Invoke(
+                [](auto, auto, IO::HandleType& handle)
+                {
+                    handle = 1234;
+                    return AZ::IO::Result(AZ::IO::ResultCode::Success);
+                }));
+
+        ON_CALL(m_ioMock, Size(An<AZ::IO::HandleType>(), _)).WillByDefault(Invoke([](auto, AZ::u64& size)
+        {
+            size = strlen(SourceDependencyJson::TestJson);
+            return AZ::IO::ResultCode::Success;
+        }));
+
+        EXPECT_CALL(m_ioMock, Read(_, _, _, _, _))
+            .WillRepeatedly(Invoke(
+                [](auto, void* buffer, auto, auto, AZ::u64* bytesRead)
+                {
+                    memcpy(buffer, SourceDependencyJson::TestJson, strlen(SourceDependencyJson::TestJson));
+                    *bytesRead = strlen(SourceDependencyJson::TestJson);
+                    return AZ::IO::ResultCode::Success;
+                }));
+
+        EXPECT_CALL(m_ioMock, Close(_)).WillRepeatedly(Return(AZ::IO::ResultCode::Success));
+    }
+
+    IO::NiceFileIOBaseMock m_ioMock;
+};
+
+TEST_F(SourceDependencyMockedIOTests, RegularManifestHasPriority)
+{
+    ImportHandler handler;
+    SettingsRegistryMock settingsRegistry;
+
+    AssetBuilderSDK::CreateJobsRequest request;
+    AssetBuilderSDK::CreateJobsResponse response;
+
+    request.m_sourceFile = "file.fbx";
+
+    using namespace ::testing;
+
+    AZStd::string genPath = AZStd::string("cache").append(1, AZ_TRAIT_OS_PATH_SEPARATOR).append("file.fbx.test.gen");
+
+    EXPECT_CALL(m_ioMock, Exists(StrEq("file.fbx.test"))).WillRepeatedly(Return(true));
+    EXPECT_CALL(m_ioMock, Exists(StrEq(genPath.c_str()))).Times(Exactly(0));
+    
+    ASSERT_TRUE(SceneBuilderWorker::ManifestDependencyCheck(request, response));
+    ASSERT_EQ(response.m_sourceFileDependencyList.size(), 2);
+}
+
+TEST_F(SourceDependencyMockedIOTests, GeneratedManifestTest)
+{
+    ImportHandler handler;
+    SettingsRegistryMock settingsRegistry;
+
+    AssetBuilderSDK::CreateJobsRequest request;
+    AssetBuilderSDK::CreateJobsResponse response;
+
+    request.m_sourceFile = "file.fbx";
+
+    using namespace ::testing;
+
+    AZStd::string genPath = AZStd::string("cache").append(1, AZ_TRAIT_OS_PATH_SEPARATOR).append("file.fbx.test.gen");
+
+    EXPECT_CALL(m_ioMock, Exists(StrEq("file.fbx.test"))).WillRepeatedly(Return(false));
+    EXPECT_CALL(m_ioMock, Exists(StrEq(genPath.c_str()))).WillRepeatedly(Return(true));
+
+    ASSERT_TRUE(SceneBuilderWorker::ManifestDependencyCheck(request, response));
+    ASSERT_EQ(response.m_sourceFileDependencyList.size(), 2);
+}