Parcourir la source

Support for templated level creation (#17633)

Signed-off-by: Yaakuro <[email protected]>
Yaakuro il y a 1 an
Parent
commit
7d7fbb43bd

+ 3 - 0
Assets/Editor/Prefabs/Basic.png

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

+ 387 - 0
Assets/Editor/Prefabs/Basic.prefab

@@ -0,0 +1,387 @@
+{
+    "ContainerEntity": {
+        "Id": "ContainerEntity",
+        "Name": "Basic",
+        "Components": {
+            "EditorDisabledCompositionComponent": {
+                "$type": "EditorDisabledCompositionComponent",
+                "Id": 10593268821715458406
+            },
+            "EditorEntityIconComponent": {
+                "$type": "EditorEntityIconComponent",
+                "Id": 7904397583211057659
+            },
+            "EditorEntitySortComponent": {
+                "$type": "EditorEntitySortComponent",
+                "Id": 15097443382011872719,
+                "Child Entity Order": [
+                    "Entity_[1379449802595]"
+                ]
+            },
+            "EditorInspectorComponent": {
+                "$type": "EditorInspectorComponent",
+                "Id": 9028208142210445783
+            },
+            "EditorLockComponent": {
+                "$type": "EditorLockComponent",
+                "Id": 18150128499906753747
+            },
+            "EditorOnlyEntityComponent": {
+                "$type": "EditorOnlyEntityComponent",
+                "Id": 7216229212231741030
+            },
+            "EditorPendingCompositionComponent": {
+                "$type": "EditorPendingCompositionComponent",
+                "Id": 5166675226912254616
+            },
+            "EditorPrefabComponent": {
+                "$type": "EditorPrefabComponent",
+                "Id": 17348824920491302694
+            },
+            "EditorVisibilityComponent": {
+                "$type": "EditorVisibilityComponent",
+                "Id": 3716381488483090297
+            },
+            "TransformComponent": {
+                "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                "Id": 2964698134789647987,
+                "Parent Entity": ""
+            }
+        }
+    },
+    "Entities": {
+        "Entity_[1362269933411]": {
+            "Id": "Entity_[1362269933411]",
+            "Name": "Global Sky",
+            "Components": {
+                "Component_[1428633914413949476]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 1428633914413949476
+                },
+                "Component_[14936200426671614999]": {
+                    "$type": "AZ::Render::EditorImageBasedLightComponent",
+                    "Id": 14936200426671614999,
+                    "Controller": {
+                        "Configuration": {
+                            "diffuseImageAsset": {
+                                "assetId": {
+                                    "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+                                    "subId": 3000
+                                },
+                                "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_ibldiffuse.exr.streamingimage"
+                            },
+                            "specularImageAsset": {
+                                "assetId": {
+                                    "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+                                    "subId": 2000
+                                },
+                                "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_iblspecular.exr.streamingimage"
+                            }
+                        }
+                    },
+                    "diffuseImageAsset": {
+                        "assetId": {
+                            "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+                            "subId": 3000
+                        },
+                        "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_ibldiffuse.exr.streamingimage"
+                    },
+                    "specularImageAsset": {
+                        "assetId": {
+                            "guid": "{3FD09945-D0F2-55C8-B9AF-B2FD421FE3BE}",
+                            "subId": 2000
+                        },
+                        "assetHint": "lightingpresets/highcontrast/goegap_4k_iblglobalcm_iblspecular.exr.streamingimage"
+                    }
+                },
+                "Component_[14994774102579326069]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 14994774102579326069
+                },
+                "Component_[15417479889044493340]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 15417479889044493340
+                },
+                "Component_[15826613364991382688]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 15826613364991382688
+                },
+                "Component_[1665003113283562343]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 1665003113283562343
+                },
+                "Component_[3704934735944502280]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 3704934735944502280
+                },
+                "Component_[5698542331457326479]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 5698542331457326479
+                },
+                "Component_[6644513399057217122]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 6644513399057217122,
+                    "Parent Entity": "Entity_[1379449802595]"
+                },
+                "Component_[931091830724002070]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 931091830724002070
+                }
+            }
+        },
+        "Entity_[1366564900707]": {
+            "Id": "Entity_[1366564900707]",
+            "Name": "Grid",
+            "Components": {
+                "Component_[11443347433215807130]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 11443347433215807130
+                },
+                "Component_[14249419413039427459]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 14249419413039427459
+                },
+                "Component_[15448581635946161318]": {
+                    "$type": "AZ::Render::EditorGridComponent",
+                    "Id": 15448581635946161318,
+                    "Controller": {
+                        "Configuration": {
+                            "primarySpacing": 4.0,
+                            "primaryColor": [
+                                0.501960813999176,
+                                0.501960813999176,
+                                0.501960813999176
+                            ],
+                            "secondarySpacing": 0.5,
+                            "secondaryColor": [
+                                0.250980406999588,
+                                0.250980406999588,
+                                0.250980406999588
+                            ]
+                        }
+                    }
+                },
+                "Component_[1843303322527297409]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 1843303322527297409
+                },
+                "Component_[380249072065273654]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 380249072065273654,
+                    "Parent Entity": "Entity_[1379449802595]"
+                },
+                "Component_[7476660583684339787]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 7476660583684339787
+                },
+                "Component_[7557626501215118375]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 7557626501215118375
+                },
+                "Component_[7984048488947365511]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 7984048488947365511
+                },
+                "Component_[8118181039276487398]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 8118181039276487398
+                },
+                "Component_[9189909764215270515]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 9189909764215270515
+                }
+            }
+        },
+        "Entity_[1370859868003]": {
+            "Id": "Entity_[1370859868003]",
+            "Name": "Camera",
+            "Components": {
+                "Component_[11895140916889160460]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 11895140916889160460
+                },
+                "Component_[16880285896855930892]": {
+                    "$type": "{CA11DA46-29FF-4083-B5F6-E02C3A8C3A3D} EditorCameraComponent",
+                    "Id": 16880285896855930892,
+                    "Controller": {
+                        "Configuration": {
+                            "Field of View": 55.0
+                        }
+                    }
+                },
+                "Component_[17187464423780271193]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 17187464423780271193
+                },
+                "Component_[17495696818315413311]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 17495696818315413311
+                },
+                "Component_[18086214374043522055]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 18086214374043522055,
+                    "Parent Entity": "Entity_[1379449802595]",
+                    "Transform Data": {
+                        "Translate": [
+                            0.0,
+                            -12.883666038513184,
+                            5.73760986328125
+                        ],
+                        "Rotate": [
+                            -25.0,
+                            0.0,
+                            0.0
+                        ]
+                    }
+                },
+                "Component_[2654521436129313160]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 2654521436129313160
+                },
+                "Component_[5265045084611556958]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 5265045084611556958
+                },
+                "Component_[7169798125182238623]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 7169798125182238623
+                },
+                "Component_[7255796294953281766]": {
+                    "$type": "GenericComponentWrapper",
+                    "Id": 7255796294953281766,
+                    "m_template": {
+                        "$type": "FlyCameraInputComponent"
+                    }
+                },
+                "Component_[8866210352157164042]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 8866210352157164042
+                },
+                "Component_[9129253381063760879]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 9129253381063760879
+                }
+            }
+        },
+        "Entity_[1375154835299]": {
+            "Id": "Entity_[1375154835299]",
+            "Name": "Sun",
+            "Components": {
+                "Component_[13620450453324765907]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 13620450453324765907
+                },
+                "Component_[2134313378593666258]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 2134313378593666258
+                },
+                "Component_[234010807770404186]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 234010807770404186
+                },
+                "Component_[2970359110423865725]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 2970359110423865725
+                },
+                "Component_[3722854130373041803]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 3722854130373041803
+                },
+                "Component_[5992533738676323195]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 5992533738676323195
+                },
+                "Component_[7378860763541895402]": {
+                    "$type": "AZ::Render::EditorDirectionalLightComponent",
+                    "Id": 7378860763541895402,
+                    "Controller": {
+                        "Configuration": {
+                            "Intensity": 1.0,
+                            "CameraEntityId": "",
+                            "ShadowFilterMethod": 1
+                        }
+                    }
+                },
+                "Component_[7892834440890947578]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 7892834440890947578,
+                    "Parent Entity": "Entity_[1379449802595]",
+                    "Transform Data": {
+                        "Translate": [
+                            0.0,
+                            0.0,
+                            13.487043380737305
+                        ],
+                        "Rotate": [
+                            -76.13099670410156,
+                            -0.847000002861023,
+                            -15.8100004196167
+                        ]
+                    }
+                },
+                "Component_[8599729549570828259]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 8599729549570828259
+                },
+                "Component_[952797371922080273]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 952797371922080273
+                }
+            }
+        },
+        "Entity_[1379449802595]": {
+            "Id": "Entity_[1379449802595]",
+            "Name": "Basic",
+            "Components": {
+                "Component_[10757302973393310045]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 10757302973393310045,
+                    "Parent Entity": "ContainerEntity"
+                },
+                "Component_[14505817420424255464]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 14505817420424255464,
+                    "ComponentOrderEntryArray": [
+                        {
+                            "ComponentId": 10757302973393310045
+                        }
+                    ]
+                },
+                "Component_[14988041764659020032]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 14988041764659020032
+                },
+                "Component_[15900837685796817138]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 15900837685796817138
+                },
+                "Component_[3298767348226484884]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 3298767348226484884
+                },
+                "Component_[4076975109609220594]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 4076975109609220594
+                },
+                "Component_[5679760548946028854]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 5679760548946028854
+                },
+                "Component_[5855590796136709437]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 5855590796136709437,
+                    "Child Entity Order": [
+                        "Entity_[1375154835299]",
+                        "Entity_[1362269933411]",
+                        "Entity_[1366564900707]",
+                        "Entity_[1370859868003]"
+                    ]
+                },
+                "Component_[9277695270015777859]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 9277695270015777859
+                }
+            }
+        }
+    }
+}

+ 3 - 0
Assets/Editor/Prefabs/Default_Level.png

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

+ 3 - 0
Assets/Editor/Prefabs/Empty.png

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

+ 49 - 0
Assets/Editor/Prefabs/Empty.prefab

@@ -0,0 +1,49 @@
+{
+    "ContainerEntity": {
+        "Id": "ContainerEntity",
+        "Name": "Empty",
+        "Components": {
+            "EditorDisabledCompositionComponent": {
+                "$type": "EditorDisabledCompositionComponent",
+                "Id": 16616931740997949210
+            },
+            "EditorEntityIconComponent": {
+                "$type": "EditorEntityIconComponent",
+                "Id": 2778716686621881360
+            },
+            "EditorEntitySortComponent": {
+                "$type": "EditorEntitySortComponent",
+                "Id": 1069995033707506449
+            },
+            "EditorInspectorComponent": {
+                "$type": "EditorInspectorComponent",
+                "Id": 4185413252280323606
+            },
+            "EditorLockComponent": {
+                "$type": "EditorLockComponent",
+                "Id": 4068240492793213930
+            },
+            "EditorOnlyEntityComponent": {
+                "$type": "EditorOnlyEntityComponent",
+                "Id": 1499221618774487829
+            },
+            "EditorPendingCompositionComponent": {
+                "$type": "EditorPendingCompositionComponent",
+                "Id": 10852705643903064770
+            },
+            "EditorPrefabComponent": {
+                "$type": "EditorPrefabComponent",
+                "Id": 12239744644494076517
+            },
+            "EditorVisibilityComponent": {
+                "$type": "EditorVisibilityComponent",
+                "Id": 17437522806552955660
+            },
+            "TransformComponent": {
+                "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                "Id": 4584354821622104079,
+                "Parent Entity": ""
+            }
+        }
+    }
+}

+ 4 - 3
Code/Editor/CryEdit.cpp

@@ -2596,7 +2596,7 @@ void CCryEditApp::OnUpdatePlayGame(QAction* action)
 }
 
 //////////////////////////////////////////////////////////////////////////
-CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelName, QString& fullyQualifiedLevelName /* ={} */)
+CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& templateName, const QString& levelName, QString& fullyQualifiedLevelName /* ={} */)
 {
     // If we are creating a new level and we're in simulate mode, then switch it off before we do anything else
     if (GetIEditor()->GetGameEngine() && GetIEditor()->GetGameEngine()->GetSimulationMode())
@@ -2672,7 +2672,8 @@ CCryEditApp::ECreateLevelResult CCryEditApp::CreateLevel(const QString& levelNam
     auto* service = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
     if (service)
     {
-        service->CreateNewLevelPrefab(fullyQualifiedLevelName.toUtf8().constData(), DefaultLevelTemplateName);
+        const AZStd::string templateNameString(templateName.toUtf8().constData());
+        service->CreateNewLevelPrefab(fullyQualifiedLevelName.toUtf8().constData(), templateNameString);
     }
 
     if (GetIEditor()->GetDocument()->Save())
@@ -2792,7 +2793,7 @@ bool CCryEditApp::CreateLevel(bool& wasCreateLevelOperationCancelled)
     GetIEditor()->StartLevelErrorReportRecording();
 
     QString fullyQualifiedLevelName;
-    ECreateLevelResult result = CreateLevel(levelNameWithPath, fullyQualifiedLevelName);
+    ECreateLevelResult result = CreateLevel(dlg.GetTemplateName(), levelNameWithPath, fullyQualifiedLevelName);
 
     if (result == ECLR_ALREADY_EXISTS)
     {

+ 1 - 3
Code/Editor/CryEdit.h

@@ -131,7 +131,7 @@ public:
     void SetEditorWindowTitle(QString sTitleStr = QString(), QString sPreTitleStr = QString(), QString sPostTitleStr = QString());
     RecentFileList* GetRecentFileList();
     virtual void AddToRecentFileList(const QString& lpszPathName);
-    ECreateLevelResult CreateLevel(const QString& levelName, QString& fullyQualifiedLevelName);
+    ECreateLevelResult CreateLevel(const QString& templateName, const QString& levelName, QString& fullyQualifiedLevelName);
     bool FirstInstance(bool bForceNewInstance = false);
     void InitFromCommandLine(CEditCommandLineInfo& cmdInfo);
     bool CheckIfAlreadyRunning();
@@ -334,8 +334,6 @@ AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
 AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
 
 private:
-    static inline constexpr const char* DefaultLevelTemplateName = "Prefabs/Default_Level.prefab";
-
     // Optional Uri to start an external lua debugger. If not specified,
     // then the Editor will open LuaIDE.exe.
     // For example, if using The Visual Studio Debugger Extension provided by lumbermixalot

+ 6 - 6
Code/Editor/CryEditPy.cpp

@@ -178,13 +178,13 @@ namespace
         return false;
     }
 
-    int PyCreateLevel(const char* levelName, [[maybe_unused]] int resolution, [[maybe_unused]] int unitSize, [[maybe_unused]] bool bUseTerrain)
+    int PyCreateLevel(const char* templateName, const char* levelName, [[maybe_unused]] int resolution, [[maybe_unused]] int unitSize, [[maybe_unused]] bool bUseTerrain)
     {
         QString qualifiedName;
-        return CCryEditApp::instance()->CreateLevel(levelName, qualifiedName);
+        return CCryEditApp::instance()->CreateLevel(templateName, levelName, qualifiedName);
     }
 
-    int PyCreateLevelNoPrompt(const char* levelName, [[maybe_unused]] int heightmapResolution, [[maybe_unused]] int heightmapUnitSize,
+    int PyCreateLevelNoPrompt(const char* templateName, const char* levelName, [[maybe_unused]] int heightmapResolution, [[maybe_unused]] int heightmapUnitSize,
         [[maybe_unused]] int terrainExportTextureSize, [[maybe_unused]] bool useTerrain)
     {
         // If a level was open, ignore any unsaved changes if it had been modified
@@ -194,7 +194,7 @@ namespace
         }
 
         QString qualifiedName;
-        return CCryEditApp::instance()->CreateLevel(levelName, qualifiedName);
+        return CCryEditApp::instance()->CreateLevel(templateName, levelName, qualifiedName);
     }
 
     const char* PyGetCurrentLevelName()
@@ -407,8 +407,8 @@ namespace AzToolsFramework
             addLegacyGeneral(behaviorContext->Method("open_level", ::PyOpenLevel, nullptr, "Opens a level."));
             addLegacyGeneral(behaviorContext->Method("open_level_no_prompt", ::PyOpenLevelNoPrompt, nullptr, "Opens a level. Doesn't prompt user about saving a modified level."));
             addLegacyGeneral(behaviorContext->Method("reload_current_level", ::PyReloadCurrentLevel, nullptr, "Re-loads the current level. If no level is loaded, then does nothing."));
-            addLegacyGeneral(behaviorContext->Method("create_level", ::PyCreateLevel, nullptr, "Creates a level with the parameters of 'levelName', 'resolution', 'unitSize' and 'bUseTerrain'."));
-            addLegacyGeneral(behaviorContext->Method("create_level_no_prompt", ::PyCreateLevelNoPrompt, nullptr, "Creates a level with the parameters of 'levelName', 'resolution', 'unitSize' and 'bUseTerrain'."));
+            addLegacyGeneral(behaviorContext->Method("create_level", ::PyCreateLevel, nullptr, "Creates a level with the parameters of 'templateName', 'levelName', 'resolution', 'unitSize' and 'bUseTerrain'."));
+            addLegacyGeneral(behaviorContext->Method("create_level_no_prompt", ::PyCreateLevelNoPrompt, nullptr, "Creates a level with the parameters of 'templateName','levelName', 'resolution', 'unitSize' and 'bUseTerrain'."));
             addLegacyGeneral(behaviorContext->Method("get_game_folder", PyGetGameFolderAsString, nullptr, "Gets the path to the Game folder of current project."));
             addLegacyGeneral(behaviorContext->Method("get_build_folder", PyGetBuildFolderAsString, nullptr, "Gets the build folder path of current project."));
             addLegacyGeneral(behaviorContext->Method("get_current_level_name", PyGetCurrentLevelName, nullptr, "Gets the name of the current level."));

+ 8 - 4
Code/Editor/EditorToolsApplication.cpp

@@ -227,14 +227,16 @@ namespace EditorInternal
         return OpenLevel(levelName);
     }
 
-    int EditorToolsApplication::CreateLevel(AZStd::string_view levelName, bool /*bUseTerrain*/)
+    int EditorToolsApplication::CreateLevel(AZStd::string_view templateName, AZStd::string_view levelName, bool /*bUseTerrain*/)
     {
         // Clang warns about a temporary being created in a function's argument list, so fullyQualifiedLevelName before the call
         QString fullyQualifiedLevelName;
-        return CCryEditApp::instance()->CreateLevel(QString::fromUtf8(levelName.data(), static_cast<int>(levelName.size())), fullyQualifiedLevelName);
+        return CCryEditApp::instance()->CreateLevel(QString::fromUtf8(templateName.data(), static_cast<int>(templateName.size())),
+                                                    QString::fromUtf8(levelName.data(), static_cast<int>(levelName.size())),
+                                                    fullyQualifiedLevelName);
     }
 
-    int EditorToolsApplication::CreateLevelNoPrompt(AZStd::string_view levelName, int /*terrainExportTextureSize*/, bool /*useTerrain*/)
+    int EditorToolsApplication::CreateLevelNoPrompt(AZStd::string_view templateName, AZStd::string_view levelName, int /*terrainExportTextureSize*/, bool /*useTerrain*/)
     {
         // If a level was open, ignore any unsaved changes if it had been modified
         if (GetIEditor()->IsLevelLoaded())
@@ -244,7 +246,9 @@ namespace EditorInternal
 
         // Clang warns about a temporary being created in a function's argument list, so fullyQualifiedLevelName before the call
         QString fullyQualifiedLevelName;
-        return CCryEditApp::instance()->CreateLevel(QString::fromUtf8(levelName.data(), static_cast<int>(levelName.size())), fullyQualifiedLevelName);
+        return CCryEditApp::instance()->CreateLevel(QString::fromUtf8(templateName.data(), static_cast<int>(templateName.size())),
+                                                    QString::fromUtf8(levelName.data(), static_cast<int>(levelName.size())),
+                                                    fullyQualifiedLevelName);
     }
 
     AZStd::string EditorToolsApplication::GetCurrentLevelName() const

+ 2 - 2
Code/Editor/EditorToolsApplication.h

@@ -62,8 +62,8 @@ namespace EditorInternal
         bool OpenLevel(AZStd::string_view levelName) override;
         bool OpenLevelNoPrompt(AZStd::string_view levelName) override;
 
-        int CreateLevel(AZStd::string_view levelName, bool bUseTerrain) override;
-        int CreateLevelNoPrompt(AZStd::string_view levelName, int terrainExportTextureSize, bool useTerrain) override;
+        int CreateLevel(AZStd::string_view templateName, AZStd::string_view levelName, bool bUseTerrain) override;
+        int CreateLevelNoPrompt(AZStd::string_view templateName, AZStd::string_view levelName, int terrainExportTextureSize, bool useTerrain) override;
 
         void Exit() override;
         void ExitNoPrompt() override;

+ 2 - 2
Code/Editor/EditorToolsApplicationAPI.h

@@ -30,8 +30,8 @@ namespace EditorInternal
         virtual bool OpenLevel(AZStd::string_view levelName) = 0;
         virtual bool OpenLevelNoPrompt(AZStd::string_view levelName) = 0;
 
-        virtual int CreateLevel(AZStd::string_view levelName, bool bUseTerrain) = 0;
-        virtual int CreateLevelNoPrompt(AZStd::string_view levelName, int terrainExportTextureSize, bool useTerrain) = 0;
+        virtual int CreateLevel(AZStd::string_view templateName, AZStd::string_view levelName, bool bUseTerrain) = 0;
+        virtual int CreateLevelNoPrompt(AZStd::string_view templateName, AZStd::string_view levelName, int terrainExportTextureSize, bool useTerrain) = 0;
 
         virtual AZStd::string GetGameFolder() const = 0;
         virtual AZStd::string GetCurrentLevelName() const = 0;

+ 79 - 19
Code/Editor/NewLevelDialog.cpp

@@ -8,7 +8,8 @@
 
 
 #include "EditorDefs.h"
-
+#include <AzCore/Utils/Utils.h>
+#include <AzCore/Settings/SettingsRegistryVisitorUtils.h>
 #include "NewLevelDialog.h"
 
 // Qt
@@ -17,6 +18,7 @@
 #include <QMessageBox>
 #include <QTimer>
 #include <QToolButton>
+#include <QListWidgetItem>
 
 AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
 #include <ui_NewLevelDialog.h>
@@ -25,6 +27,7 @@ AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
 
 // Folder in which levels are stored
 static const char kNewLevelDialog_LevelsFolder[] = "Levels";
+static constexpr const char* RegistryKey_CustomTemplatePaths = "/O3DE/Preferences/Prefab/CustomTemplatePaths";
 
 class LevelFolderValidator : public QValidator
 {
@@ -49,11 +52,17 @@ private:
     CNewLevelDialog* m_parentDialog;
 };
 
+static QString ChangeFileExtension(const QString& filePath, const QString& newExtension)
+{
+    QFileInfo fileInfo(filePath);
+    QString newFilePath = fileInfo.absolutePath() + QDir::separator() + fileInfo.baseName() + "." + newExtension;
+    return newFilePath;
+}
+
 // CNewLevelDialog dialog
 
 CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/)
     : QDialog(pParent)
-    , m_bUpdate(false)
     , ui(new Ui::CNewLevelDialog)
     , m_initialized(false)
 {
@@ -61,15 +70,8 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/)
 
     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
     setWindowTitle(tr("New Level"));
-    setMaximumSize(QSize(320, 180));
-    adjustSize();
-
-    m_bIsResize = false;
-
-
-    ui->TITLE->setText(tr("Assign a name and location to the new level."));
-    ui->STATIC1->setText(tr("Location:"));
-    ui->STATIC2->setText(tr("Name:"));
+    setStyleSheet("QListWidget::item {height: 148px; padding-left: 0px; padding-right: 0px; background-color: transparent;}");
+    InitTemplateListWidget();
 
     // Level name only supports ASCII characters
     QRegExp rx("[_a-zA-Z0-9-]+");
@@ -90,7 +92,6 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/)
 
     connect(ui->LEVEL_FOLDERS->lineEdit(), &QLineEdit::textEdited, this, &CNewLevelDialog::OnLevelNameChange);
     connect(ui->LEVEL_FOLDERS, &AzQtComponents::BrowseEdit::attachedButtonTriggered, this, &CNewLevelDialog::PopupAssetPicker);
-
     connect(ui->LEVEL, &QLineEdit::textChanged, this, &CNewLevelDialog::OnLevelNameChange);
 
     m_levelFolders = GetLevelsFolder();
@@ -105,10 +106,76 @@ CNewLevelDialog::CNewLevelDialog(QWidget* pParent /*=nullptr*/)
     ReloadLevelFolder();
 }
 
+
+
 CNewLevelDialog::~CNewLevelDialog()
 {
 }
 
+void CNewLevelDialog::InitTemplateListWidget() const
+{
+    ui->listTemplates->clear();
+
+    QStringList templatePaths;
+    if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
+    {
+        auto AppendCustomTemplatePath = [&templatePaths](const AZ::SettingsRegistryInterface::VisitArgs& visitArgs)
+        {
+            AZ::IO::FixedMaxPath customTemplatePath;
+            if (visitArgs.m_registry.Get(customTemplatePath.Native(), visitArgs.m_jsonKeyPath))
+            {
+                if (AZ::IO::FileIOBase::GetInstance()->ResolvePath(customTemplatePath, customTemplatePath))
+                {
+                    templatePaths.push_back(QString::fromUtf8(customTemplatePath.c_str(), int(customTemplatePath.Native().size())));
+                }
+            }
+
+            return AZ::SettingsRegistryInterface::VisitResponse::Skip;
+        };
+
+        AZ::SettingsRegistryVisitorUtils::VisitObject(*settingsRegistry, AppendCustomTemplatePath, RegistryKey_CustomTemplatePaths);
+    }
+
+    // Get all prefab files.
+    const QStringList fileFilter = {"*.prefab"};
+    QStringList allTemplateFiles;
+    for(const QString& path: templatePaths)
+    {
+        QDir projectTemplateDirectory(path);
+        projectTemplateDirectory.setNameFilters(fileFilter);
+
+        const QStringList projectTemplateFiles = projectTemplateDirectory.entryList(QDir::Files);
+        for (const QString& fileName: projectTemplateFiles)
+        {
+            allTemplateFiles.push_back(projectTemplateDirectory.filePath(fileName));
+        }
+    }
+
+    // Create the item with its icons to the QListWidget.
+    const QIcon defaultIcon(":/NewLevel/res/Prefab_80.svg");
+    for (const QString& fileName: allTemplateFiles)
+    {
+        QFileInfo info(fileName);
+        auto* item = new QListWidgetItem(info.baseName());
+        const QString iconPath = ChangeFileExtension(fileName, "png");
+        const QIcon itemIcon = QFile::exists(iconPath) ? QIcon(iconPath) : defaultIcon;
+        item->setIcon(itemIcon);
+        item->setData(Qt::UserRole, fileName);
+        ui->listTemplates->addItem(item);
+    }
+
+    const QSize iconSize(128, 128);
+    ui->listTemplates->setViewMode(QListWidget::IconMode);
+    ui->listTemplates->setIconSize(iconSize);
+    ui->listTemplates->setDragDropMode(QAbstractItemView::NoDragDrop);
+}
+
+QString CNewLevelDialog::GetTemplateName() const
+{
+    const QString name = ui->listTemplates->currentItem()->data(Qt::UserRole).toString();
+    return name;
+}
+
 void CNewLevelDialog::OnStartup()
 {
     UpdateData(false);
@@ -148,7 +215,6 @@ void CNewLevelDialog::OnInitDialog()
 //////////////////////////////////////////////////////////////////////////
 void CNewLevelDialog::ReloadLevelFolder()
 {
-    m_itemFolders.clear();
     ui->LEVEL_FOLDERS->lineEdit()->clear();
     ui->LEVEL_FOLDERS->setText(QString(kNewLevelDialog_LevelsFolder) + '/');
 }
@@ -233,12 +299,6 @@ void CNewLevelDialog::PopupAssetPicker()
     }
 }
 
-//////////////////////////////////////////////////////////////////////////
-void CNewLevelDialog::IsResize(bool bIsResize)
-{
-    m_bIsResize = bIsResize;
-}
-
 //////////////////////////////////////////////////////////////////////////
 void CNewLevelDialog::showEvent(QShowEvent* event)
 {

+ 4 - 17
Code/Editor/NewLevelDialog.h

@@ -8,14 +8,9 @@
 
 
 #pragma once
-#ifndef CRYINCLUDE_EDITOR_NEWLEVELDIALOG_H
-#define CRYINCLUDE_EDITOR_NEWLEVELDIALOG_H
 
 #if !defined(Q_MOC_RUN)
 #include <QScopedPointer>
-
-#include <vector>
-
 #include <QAbstractButton>
 #include <QDialog>
 #endif
@@ -30,22 +25,20 @@ class CNewLevelDialog
     Q_OBJECT
 
 public:
-    CNewLevelDialog(QWidget* pParent = nullptr);   // standard constructor
+    CNewLevelDialog(QWidget* pParent = nullptr);
     ~CNewLevelDialog();
 
     QString GetLevel() const;
-    void IsResize(bool bIsResize);
     bool ValidateLevel();
+    QString GetTemplateName() const;
 
 protected:
     void UpdateData(bool fromUi = true);
     void OnInitDialog();
-
     void ReloadLevelFolder();
-
-    void showEvent(QShowEvent* event);
-
+    void showEvent(QShowEvent* event) override;
     QString GetLevelsFolder() const;
+    void InitTemplateListWidget() const;
 
 protected slots:
     void OnLevelNameChange();
@@ -56,12 +49,6 @@ protected slots:
 public:
     QString         m_level;
     QString         m_levelFolders;
-    bool                m_bIsResize;
-    bool                m_bUpdate;
-
-    std::vector<QString>    m_itemFolders;
-
     QScopedPointer<Ui::CNewLevelDialog> ui;
     bool m_initialized;
 };
-#endif // CRYINCLUDE_EDITOR_NEWLEVELDIALOG_H

+ 6 - 0
Code/Editor/NewLevelDialog.qrc

@@ -0,0 +1,6 @@
+<RCC>
+  <qresource prefix="/NewLevel">
+    <file>res/folder.png</file>
+    <file>res/Prefab_80.svg</file>
+  </qresource>
+</RCC>

+ 89 - 115
Code/Editor/NewLevelDialog.ui

@@ -6,124 +6,96 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>430</width>
-    <height>180</height>
+    <width>471</width>
+    <height>371</height>
    </rect>
   </property>
-   <layout class="QVBoxLayout" name="verticalLayout">
-     <item>
-       <spacer name="topVerticalSpacer">
-         <property name="orientation">
-           <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-           <size>
-             <width>20</width>
-             <height>10</height>
-           </size>
-         </property>
-       </spacer>
-     </item>
-     <item>
-       <widget class="QLabel" name="TITLE">
-         <property name="text">
-           <string>Assign a name and location to the new level.</string>
-         </property>
-         <property name="alignment">
-           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
-         </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QListWidget" name="listTemplates">
+     <property name="movement">
+      <enum>QListView::Static</enum>
+     </property>
+     <property name="resizeMode">
+      <enum>QListView::Adjust</enum>
+     </property>
+     <property name="spacing">
+      <number>4</number>
+     </property>
+     <property name="viewMode">
+      <enum>QListView::IconMode</enum>
+     </property>
+     <property name="uniformItemSizes">
+      <bool>true</bool>
+     </property>
+     <property name="selectionRectVisible">
+      <bool>false</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="STATIC_GROUP1">
+     <layout class="QFormLayout" name="formLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="STATIC2">
+        <property name="text">
+         <string>Name</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+        </property>
+        <property name="buddy">
+         <cstring>LEVEL</cstring>
+        </property>
        </widget>
-     </item>
-     <item>
-       <spacer name="verticalSpacer">
-         <property name="orientation">
-           <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-           <size>
-             <width>20</width>
-             <height>20</height>
-           </size>
-         </property>
-       </spacer>
-     </item>
-     <item>
-       <layout class="QGridLayout" name="gridLayout">
-         <item row="0" column="0" colspan="2">
-           <widget class="QGroupBox" name="STATIC_GROUP1">
-             <property name="styleSheet">
-               <string notr="true">border: 0px;</string>
-             </property>
-             <layout class="QFormLayout" name="formLayout">
-               <item row="0" column="0">
-                 <widget class="QLabel" name="STATIC2">
-                   <property name="text">
-                     <string>Name</string>
-                   </property>
-                   <property name="alignment">
-                     <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
-                   </property>
-                   <property name="buddy">
-                     <cstring>LEVEL</cstring>
-                   </property>
-                 </widget>
-               </item>
-               <item row="0" column="1">
-                 <widget class="QLineEdit" name="LEVEL">
-                   <property name="alignment">
-                     <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-                   </property>
-                 </widget>
-               </item>
-               <item row="1" column="0">
-                 <widget class="QLabel" name="STATIC1">
-                   <property name="minimumSize">
-                     <size>
-                       <width>100</width>
-                       <height>0</height>
-                     </size>
-                   </property>
-                   <property name="text">
-                     <string>Location</string>
-                   </property>
-                   <property name="alignment">
-                     <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
-                   </property>
-                   <property name="buddy">
-                     <cstring>LEVEL_FOLDERS</cstring>
-                   </property>
-                 </widget>
-               </item>
-               <item row="1" column="1">
-                 <widget class="AzQtComponents::BrowseEdit" name="LEVEL_FOLDERS" native="true">
-                   <property name="sizePolicy">
-                     <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
-                       <horstretch>0</horstretch>
-                       <verstretch>0</verstretch>
-                     </sizepolicy>
-                   </property>
-                 </widget>
-               </item>
-             </layout>
-           </widget>
-         </item>
-       </layout>
-     </item>
-     <item>
-       <spacer name="verticalSpacer2">
-         <property name="orientation">
-           <enum>Qt::Vertical</enum>
-         </property>
-       </spacer>
-     </item>
-     <item row="1" column="0" colspan="2">
-       <widget class="QDialogButtonBox" name="buttonBox">
-         <property name="standardButtons">
-           <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
-         </property>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLineEdit" name="LEVEL">
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+        </property>
        </widget>
-     </item>
-   </layout>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="STATIC1">
+        <property name="minimumSize">
+         <size>
+          <width>100</width>
+          <height>0</height>
+         </size>
+        </property>
+        <property name="text">
+         <string>Location</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+        </property>
+        <property name="buddy">
+         <cstring>LEVEL_FOLDERS</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="AzQtComponents::BrowseEdit" name="LEVEL_FOLDERS" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
  </widget>
  <customwidgets>
   <customwidget>
@@ -136,7 +108,9 @@
  <tabstops>
   <tabstop>LEVEL</tabstop>
  </tabstops>
- <resources/>
+ <resources>
+  <include location="NewLevelDialog.qrc"/>
+ </resources>
  <connections>
   <connection>
    <sender>buttonBox</sender>

+ 2 - 0
Code/Editor/editor_lib_files.cmake

@@ -222,6 +222,7 @@ set(FILES
     res/veed_tree.bmp
     res/vegetati.bmp
     res/vegtree.bmp
+    res/Prefab_80.svg
     res/video_record.ico
     res/warning16x16.ico
     res/water.bmp
@@ -382,6 +383,7 @@ set(FILES
     NewLevelDialog.cpp
     NewLevelDialog.h
     NewLevelDialog.ui
+    NewLevelDialog.qrc
     Dialogs/PythonScriptsDialog.cpp
     Dialogs/PythonScriptsDialog.h
     Dialogs/PythonScriptsDialog.ui

+ 3 - 0
Code/Editor/res/Prefab_80.svg

@@ -0,0 +1,3 @@
+<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M66.5521 56.4173C67.6477 55.8373 68.3333 54.7018 68.3333 53.4673V26.7354C68.3333 25.5349 67.6845 24.4298 66.6354 23.8436L43.3726 10.8459C41.3553 9.71877 38.8921 9.71625 36.864 10.8393L13.3922 23.8368C12.3277 24.4263 11.6667 25.5448 11.6667 26.7566L11.6667 53.4761C11.6667 54.7059 12.3472 55.833 13.4365 56.4069L38.5039 69.6154C39.4808 70.1301 40.6518 70.128 41.6307 69.6099L66.5521 56.4173ZM41.6732 24.9691V15.5861L63.1725 27.4054V50.7711L54.6523 46.3625V33.5544L41.6732 40.9791V54.6525L53.2689 48.8595L62.0294 53.1073L40.06 64.8887L17.9082 53.1073L26.7317 48.8595L38.7801 54.6525V40.9791L25.3812 33.5544V46.3625L16.9475 50.7711V27.4054L38.7801 15.5861V24.9691L26.7317 31.6915L40.4377 38.4731L53.2689 31.6915L41.6732 24.9691Z" fill="white"/>
+</svg>

+ 3 - 1
Code/Framework/AzQtComponents/AzQtComponents/Components/Style.cpp

@@ -75,6 +75,7 @@ AZ_POP_DISABLE_WARNING
 #include <QtWidgets/private/qstylehelper_p.h>
 
 #include <limits>
+#include <QListWidget>
 
 namespace AzQtComponents
 {
@@ -789,7 +790,8 @@ namespace AzQtComponents
 
     QPixmap Style::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap& pixmap, const QStyleOption* option) const
     {
-        if (qobject_cast<const TableView*>(m_drawControlWidget) && iconMode == QIcon::Mode::Selected)
+        if ((qobject_cast<const TableView*>(m_drawControlWidget) ||
+            qobject_cast<const QListWidget*>(m_drawControlWidget)) && iconMode == QIcon::Mode::Selected)
         {
             return QProxyStyle::generatedIconPixmap(QIcon::Mode::Active, pixmap, option);
         }

+ 11 - 0
Registry/level_template.setreg

@@ -0,0 +1,11 @@
+{
+  "O3DE": {
+    "Preferences": {
+      "Prefab": {
+        "CustomTemplatePaths": {
+          "EngineTemplatePathKey1": "@engroot@/Assets/Editor/Prefabs"
+        }
+      }
+    }
+  }
+}