PrefabPublicHandler.cpp 114 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Component/TransformBus.h>
  9. #include <AzCore/JSON/stringbuffer.h>
  10. #include <AzCore/JSON/writer.h>
  11. #include <AzCore/Serialization/Json/JsonSerialization.h>
  12. #include <AzCore/Utils/TypeHash.h>
  13. #include <AzCore/std/sort.h>
  14. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  15. #include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
  16. #include <AzToolsFramework/Entity/EditorEntityContextBus.h>
  17. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  18. #include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
  19. #include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
  20. #include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
  21. #include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
  22. #include <AzToolsFramework/Prefab/Instance/Instance.h>
  23. #include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
  24. #include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
  25. #include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
  26. #include <AzToolsFramework/Prefab/Instance/InstanceDomGeneratorInterface.h>
  27. #include <AzToolsFramework/Prefab/PrefabDomUtils.h>
  28. #include <AzToolsFramework/Prefab/PrefabEditorPreferences.h>
  29. #include <AzToolsFramework/Prefab/PrefabInstanceUtils.h>
  30. #include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
  31. #include <AzToolsFramework/Prefab/PrefabPublicHandler.h>
  32. #include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
  33. #include <AzToolsFramework/Prefab/Undo/PrefabUndo.h>
  34. #include <AzToolsFramework/Prefab/Undo/PrefabUndoUpdateLink.h>
  35. #include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
  36. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  37. #include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
  38. #include <QString>
  39. namespace AzToolsFramework
  40. {
  41. namespace Prefab
  42. {
  43. void PrefabPublicHandler::RegisterPrefabPublicHandlerInterface()
  44. {
  45. m_prefabFocusHandler.RegisterPrefabFocusInterface();
  46. m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
  47. AZ_Assert(m_instanceEntityMapperInterface, "PrefabPublicHandler - Could not get InstanceEntityMapperInterface.");
  48. m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
  49. AZ_Assert(m_instanceToTemplateInterface, "PrefabPublicHandler - Could not get InstanceToTemplateInterface.");
  50. m_instanceDomGeneratorInterface = AZ::Interface<InstanceDomGeneratorInterface>::Get();
  51. AZ_Assert(m_instanceDomGeneratorInterface, "PrefabPublicHandler - Could not get InstanceDomGeneratorInterface.");
  52. m_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
  53. AZ_Assert(m_prefabLoaderInterface, "PrefabPublicHandler - Could not get PrefabLoaderInterface.");
  54. m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
  55. AZ_Assert(m_prefabFocusInterface, "PrefabPublicHandler - Could not get PrefabFocusInterface.");
  56. m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
  57. AZ_Assert(m_prefabFocusPublicInterface, "PrefabPublicHandler - Could not get PrefabFocusPublicInterface.");
  58. m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
  59. AZ_Assert(m_prefabSystemComponentInterface, "PrefabPublicHandler - Could not get PrefabSystemComponentInterface.");
  60. m_prefabUndoCache.Initialize();
  61. AZ::Interface<PrefabPublicInterface>::Register(this);
  62. }
  63. void PrefabPublicHandler::UnregisterPrefabPublicHandlerInterface()
  64. {
  65. AZ::Interface<PrefabPublicInterface>::Unregister(this);
  66. m_prefabUndoCache.Destroy();
  67. m_prefabSystemComponentInterface = nullptr;
  68. m_prefabFocusPublicInterface = nullptr;
  69. m_prefabFocusInterface = nullptr;
  70. m_prefabLoaderInterface = nullptr;
  71. m_instanceDomGeneratorInterface = nullptr;
  72. m_instanceToTemplateInterface = nullptr;
  73. m_instanceEntityMapperInterface = nullptr;
  74. m_prefabFocusHandler.UnregisterPrefabFocusInterface();
  75. }
  76. CreatePrefabResult PrefabPublicHandler::CreatePrefabInMemory(const EntityIdList& entityIds, AZ::IO::PathView filePath)
  77. {
  78. EntityList inputEntityList, topLevelEntities;
  79. AZ::EntityId commonRootEntityId;
  80. InstanceOptionalReference commonRootEntityOwningInstance;
  81. // Find common root entity.
  82. PrefabOperationResult findCommonRootOutcome = FindCommonRootOwningInstance(
  83. entityIds, inputEntityList, topLevelEntities, commonRootEntityId, commonRootEntityOwningInstance);
  84. if (!findCommonRootOutcome.IsSuccess())
  85. {
  86. return AZ::Failure(findCommonRootOutcome.TakeError());
  87. }
  88. const TemplateId commonRootInstanceTemplateId = commonRootEntityOwningInstance->get().GetTemplateId();
  89. // Order entities by their respective positions within hierarchy.
  90. EditorEntitySortRequestBus::Event(
  91. commonRootEntityId,
  92. [&topLevelEntities](EditorEntitySortRequestBus::Events* sortRequests)
  93. {
  94. AZStd::sort(
  95. topLevelEntities.begin(), topLevelEntities.end(),
  96. [&sortRequests](AZ::Entity* entity1, AZ::Entity* entity2)
  97. {
  98. return sortRequests->GetChildEntityIndex(entity1->GetId()) <
  99. sortRequests->GetChildEntityIndex(entity2->GetId());
  100. });
  101. });
  102. AZ::EntityId newContainerEntityId;
  103. InstanceOptionalReference instanceToCreate;
  104. {
  105. // Initialize Undo Batch object
  106. ScopedUndoBatch undoBatch("Create Prefab");
  107. AZStd::vector<AZ::Entity*> detachedEntities;
  108. AZStd::vector<Instance*> detachedInstances;
  109. AZStd::vector<AZStd::unique_ptr<Instance>> detachedInstancePtrs;
  110. AZStd::unordered_map<Instance*, PrefabDom> nestedInstanceLinkPatchesMap;
  111. // Retrieve all entities affected and identify Instances
  112. PrefabOperationResult retrieveEntitiesAndInstancesOutcome = RetrieveAndSortPrefabEntitiesAndInstances(
  113. inputEntityList, commonRootEntityOwningInstance->get(), detachedEntities, detachedInstances);
  114. if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
  115. {
  116. return AZ::Failure(retrieveEntitiesAndInstancesOutcome.TakeError());
  117. }
  118. // Capture parent DOM state before detaching.
  119. PrefabDom commonRootEntityDomBeforeDetaching;
  120. if (commonRootEntityId.IsValid())
  121. {
  122. m_instanceToTemplateInterface->GenerateDomForEntity(commonRootEntityDomBeforeDetaching,
  123. *GetEntityById(commonRootEntityId));
  124. }
  125. // Calculate new transform data before updating the direct top level entities.
  126. AZ::Vector3 containerEntityTranslation(AZ::Vector3::CreateZero());
  127. AZ::Quaternion containerEntityRotation(AZ::Quaternion::CreateZero());
  128. GenerateContainerEntityTransform(topLevelEntities, containerEntityTranslation, containerEntityRotation);
  129. // Set the parent of top level entities to null, so the parent entity's child entity order array does not include the entities.
  130. // This is needed before doing serialization on the parent entity to avoid referring to the detached entities.
  131. for (AZ::Entity* topLevelEntity : topLevelEntities)
  132. {
  133. AZ::TransformBus::Event(topLevelEntity->GetId(), &AZ::TransformBus::Events::SetParent, AZ::EntityId());
  134. }
  135. // Detach the retrieved entities. This includes the top level entities.
  136. AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
  137. {
  138. // DOM value pointers can't be relied upon if the original DOM gets modified after pointer creation.
  139. // This scope is added to limit their usage and ensure DOM is not modified when it is being used.
  140. AZStd::vector<AZStd::pair<const PrefabDomValue*, AZStd::string>> detachedEntityDomAndPathList;
  141. const PrefabDomValue& commonRootInstanceTemplateDom = m_prefabSystemComponentInterface->FindTemplateDom(
  142. commonRootInstanceTemplateId);
  143. for (AZ::Entity* entity : detachedEntities)
  144. {
  145. const AZ::EntityId entityId = entity->GetId();
  146. const AZStd::string entityAliasPath = m_instanceToTemplateInterface->GenerateEntityAliasPath(entityId);
  147. PrefabDomPath entityDomPathInOwningTemplate(entityAliasPath.c_str());
  148. const PrefabDomValue* detachedEntityDom = entityDomPathInOwningTemplate.Get(commonRootInstanceTemplateDom);
  149. detachedEntityDomAndPathList.emplace_back(detachedEntityDom, entityAliasPath);
  150. oldEntityAliases.emplace(entityId, commonRootEntityOwningInstance->get().GetEntityAlias(entityId)->get());
  151. commonRootEntityOwningInstance->get().DetachEntity(entityId).release();
  152. }
  153. PrefabUndoHelpers::RemoveEntityDoms(detachedEntityDomAndPathList, commonRootInstanceTemplateId, undoBatch.GetUndoBatch());
  154. }
  155. // Detach the retrieved nested instances.
  156. // When we create a prefab with other prefab instances, we have to remove the existing links between the source and
  157. // target templates of the other instances.
  158. for (auto& instance : detachedInstances)
  159. {
  160. const InstanceAlias instanceAlias = instance->GetInstanceAlias();
  161. AZStd::unique_ptr<Instance> detachedInstance =
  162. commonRootEntityOwningInstance->get().DetachNestedInstance(instanceAlias);
  163. LinkId detachingInstanceLinkId = detachedInstance->GetLinkId();
  164. auto linkRef = m_prefabSystemComponentInterface->FindLink(detachingInstanceLinkId);
  165. AZ_Assert(linkRef.has_value(), "Unable to find link with id '%llu' during prefab creation.", detachingInstanceLinkId);
  166. PrefabDom linkPatches;
  167. linkRef->get().GetLinkPatches(linkPatches, linkPatches.GetAllocator());
  168. nestedInstanceLinkPatchesMap.emplace(detachedInstance.get(), AZStd::move(linkPatches));
  169. RemoveLink(detachedInstance, commonRootInstanceTemplateId, undoBatch.GetUndoBatch());
  170. detachedInstancePtrs.emplace_back(AZStd::move(detachedInstance));
  171. }
  172. // Create new prefab instance.
  173. auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
  174. if (!prefabEditorEntityOwnershipInterface)
  175. {
  176. return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error "
  177. "(PrefabEditorEntityOwnershipInterface unavailable)."));
  178. }
  179. instanceToCreate = prefabEditorEntityOwnershipInterface->CreatePrefab(
  180. detachedEntities, AZStd::move(detachedInstancePtrs), m_prefabLoaderInterface->GenerateRelativePath(filePath),
  181. commonRootEntityOwningInstance);
  182. if (!instanceToCreate)
  183. {
  184. return AZ::Failure(AZStd::string("Could not create a new prefab out of the entities provided - internal error "
  185. "(A null instance is returned)."));
  186. }
  187. // Note that changes to the new template DOM do not require undo/redo support. In other words, an undo after this operation
  188. // will not change the newly created template DOM. This helps keep in-memory template in sync with in-disk prefab.
  189. newContainerEntityId = instanceToCreate->get().GetContainerEntityId();
  190. AZ::Entity* newContainerEntity = GetEntityById(newContainerEntityId);
  191. const TemplateId newInstanceTemplateId = instanceToCreate->get().GetTemplateId();
  192. PrefabDom& newInstanceTemplateDom = m_prefabSystemComponentInterface->FindTemplateDom(newInstanceTemplateId);
  193. // Update and patch the container entity DOM in template with new components.
  194. PrefabDom newContainerEntityDomWithComponents(&(newInstanceTemplateDom.GetAllocator()));
  195. m_instanceToTemplateInterface->GenerateDomForEntity(newContainerEntityDomWithComponents, *newContainerEntity);
  196. PrefabDomPath newContainerEntityAliasDomPath(m_instanceToTemplateInterface->GenerateEntityAliasPath(newContainerEntityId).c_str());
  197. newContainerEntityAliasDomPath.Set(newInstanceTemplateDom, newContainerEntityDomWithComponents.Move());
  198. // Apply the correct transform to the container for the new instance, and store the patch for use when creating the link.
  199. PrefabDom linkPatches = ApplyContainerTransformAndGeneratePatch(newContainerEntityId, commonRootEntityId,
  200. containerEntityTranslation, containerEntityRotation);
  201. // Capture the container entity DOM with parent data in transform.
  202. // This DOM will be used later to generate patches for child sort update and they should not contain transform data differences.
  203. PrefabDom newContainerEntityDomInitialStateWithTransform;
  204. m_instanceToTemplateInterface->GenerateDomForEntity(newContainerEntityDomInitialStateWithTransform, *newContainerEntity);
  205. // Parent the non-container top level entities to the new container entity.
  206. // It also patches the entity DOMs to the new template without undo/redo support.
  207. EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::SetForceAddEntitiesToBackFlag, true);
  208. for (AZ::Entity* topLevelEntity : topLevelEntities)
  209. {
  210. const AZ::EntityId topLevelEntityId = topLevelEntity->GetId();
  211. // Note: Parenting the top level container entities will be done during the creation of links.
  212. if (!IsInstanceContainerEntity(topLevelEntityId))
  213. {
  214. AZ::TransformBus::Event(topLevelEntityId, &AZ::TransformBus::Events::SetParent, newContainerEntityId);
  215. // Update the entity DOM with the latest transform data.
  216. PrefabDom topLevelContainerEntityDomWithParentInTransform(&(newInstanceTemplateDom.GetAllocator()));
  217. m_instanceToTemplateInterface->GenerateDomForEntity(
  218. topLevelContainerEntityDomWithParentInTransform, *topLevelEntity);
  219. PrefabDomPath entityAliasDomPath(m_instanceToTemplateInterface->GenerateEntityAliasPath(topLevelEntityId).c_str());
  220. entityAliasDomPath.Set(newInstanceTemplateDom, topLevelContainerEntityDomWithParentInTransform.Move());
  221. }
  222. }
  223. EditorEntityContextNotificationBus::Broadcast(&EditorEntityContextNotification::SetForceAddEntitiesToBackFlag, false);
  224. // Set up links between the created prefab instance and its nested instances without undo/redo support.
  225. instanceToCreate->get().GetNestedInstances([&](AZStd::unique_ptr<Instance>& nestedInstance) {
  226. AZ_Assert(nestedInstance, "Invalid nested instance found in the new prefab created.");
  227. EntityOptionalReference nestedInstanceContainerEntity = nestedInstance->GetContainerEntity();
  228. AZ_Assert(
  229. nestedInstanceContainerEntity, "Invalid container entity found for the nested instance used in prefab creation.");
  230. AZ::EntityId nestedInstanceContainerEntityId = nestedInstanceContainerEntity->get().GetId();
  231. PrefabDom previousPatch;
  232. // Retrieve the previous patch if it exists
  233. if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
  234. {
  235. previousPatch.Swap(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
  236. UpdateLinkPatchesWithNewEntityAliases(previousPatch, oldEntityAliases, instanceToCreate->get());
  237. }
  238. // These link creations shouldn't be undone because that would put the template in a non-usable state if a user
  239. // chooses to instantiate the template after undoing the creation.
  240. CreateLink(*nestedInstance, newInstanceTemplateId, undoBatch.GetUndoBatch(), AZStd::move(previousPatch), false);
  241. // If this nested instance's container is a top level entity in the new prefab, re-parent it and apply the change.
  242. if (AZStd::find(topLevelEntities.begin(), topLevelEntities.end(), &nestedInstanceContainerEntity->get()) != topLevelEntities.end())
  243. {
  244. PrefabDom nestedContainerEntityDomBefore;
  245. m_instanceToTemplateInterface->GenerateDomForEntity(nestedContainerEntityDomBefore, *nestedInstanceContainerEntity);
  246. AZ::TransformBus::Event(nestedInstanceContainerEntityId, &AZ::TransformBus::Events::SetParent, newContainerEntityId);
  247. PrefabDom nestedContainerEntityDomAfter;
  248. m_instanceToTemplateInterface->GenerateDomForEntity(nestedContainerEntityDomAfter, *nestedInstanceContainerEntity);
  249. PrefabDom reparentPatch;
  250. m_instanceToTemplateInterface->GeneratePatch(reparentPatch, nestedContainerEntityDomBefore, nestedContainerEntityDomAfter);
  251. m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId);
  252. // We won't parent this undo node to the undo batch so that the newly created template and link will remain
  253. // unaffected by undo actions. This is needed so that any future instantiations of the template will work.
  254. PrefabUndoUpdateLink linkUpdate = PrefabUndoUpdateLink(AZStd::to_string(static_cast<AZ::u64>(nestedInstanceContainerEntityId)));
  255. linkUpdate.Capture(reparentPatch, nestedInstance->GetLinkId());
  256. linkUpdate.Redo();
  257. }
  258. });
  259. // Patch the new template with child sort array update without undo/redo support.
  260. PrefabDom newContainerEntityDomWithTransformAndChildSortOrder;
  261. m_instanceToTemplateInterface->GenerateDomForEntity(newContainerEntityDomWithTransformAndChildSortOrder, *newContainerEntity);
  262. PrefabDom childSortOrderUpdatePatches;
  263. m_instanceToTemplateInterface->GeneratePatch(childSortOrderUpdatePatches,
  264. newContainerEntityDomInitialStateWithTransform, newContainerEntityDomWithTransformAndChildSortOrder);
  265. m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(childSortOrderUpdatePatches, newContainerEntityId);
  266. m_instanceToTemplateInterface->PatchTemplate(childSortOrderUpdatePatches, newInstanceTemplateId);
  267. // Create a link between the new prefab template and its parent template with undo/redo support.
  268. CreateLink(instanceToCreate->get(), commonRootInstanceTemplateId, undoBatch.GetUndoBatch(), AZStd::move(linkPatches));
  269. // Update common parent entity DOM with the new prefab instance as child with undo/redo support.
  270. if (commonRootEntityId.IsValid())
  271. {
  272. PrefabDom commonRootEntityDomAfterAddingNewPrefab;
  273. m_instanceToTemplateInterface->GenerateDomForEntity(commonRootEntityDomAfterAddingNewPrefab,
  274. *GetEntityById(commonRootEntityId));
  275. PrefabUndoHelpers::UpdateEntity(
  276. commonRootEntityDomBeforeDetaching,
  277. commonRootEntityDomAfterAddingNewPrefab,
  278. commonRootEntityId,
  279. undoBatch.GetUndoBatch());
  280. }
  281. // Update undo cache.
  282. m_prefabUndoCache.UpdateCache(newContainerEntityId);
  283. for (AZ::Entity* topLevelEntity : topLevelEntities)
  284. {
  285. m_prefabUndoCache.UpdateCache(topLevelEntity->GetId());
  286. }
  287. // This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab.
  288. // We are doing this so that the changes in those entities are not queued up twice for propagation.
  289. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  290. &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
  291. // Select Container Entity
  292. {
  293. const EntityIdList selectedEntities = EntityIdList{ newContainerEntityId };
  294. auto selectionUndo = aznew SelectionCommand(selectedEntities, "Select Prefab Container Entity");
  295. selectionUndo->SetParent(undoBatch.GetUndoBatch());
  296. ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
  297. }
  298. }
  299. return AZ::Success(newContainerEntityId);
  300. }
  301. CreatePrefabResult PrefabPublicHandler::CreatePrefabInDisk(const EntityIdList& entityIds, AZ::IO::PathView filePath)
  302. {
  303. AZ_Assert(filePath.IsAbsolute(), "CreatePrefabInDisk requires an absolute file path.");
  304. auto result = CreatePrefabInMemory(entityIds, filePath);
  305. if (result.IsSuccess())
  306. {
  307. // Save Template to file
  308. auto relativePath = m_prefabLoaderInterface->GenerateRelativePath(filePath);
  309. Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
  310. if (!m_prefabLoaderInterface->SaveTemplateToFile(templateId, filePath))
  311. {
  312. AZStd::string_view filePathString(filePath);
  313. return AZ::Failure(AZStd::string::format("CreatePrefabInDisk - "
  314. "Could not save the newly created prefab to file path %.*s - internal error ",
  315. AZ_STRING_ARG(filePathString)));
  316. }
  317. }
  318. return result;
  319. }
  320. PrefabDom PrefabPublicHandler::ApplyContainerTransformAndGeneratePatch(
  321. AZ::EntityId containerEntityId, AZ::EntityId parentEntityId, const AZ::Vector3& translation, const AZ::Quaternion& rotation)
  322. {
  323. AZ::Entity* containerEntity = GetEntityById(containerEntityId);
  324. AZ_Assert(containerEntity, "Invalid container entity passed to ApplyContainerTransformAndGeneratePatch.");
  325. // Generate the transform for the container entity out of the top level entities, and set it
  326. // This step needs to be done before anything is parented to the container, else children position will be wrong
  327. Prefab::PrefabDom containerEntityDomBefore;
  328. m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
  329. // Set container entity to be child of common root
  330. AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parentEntityId);
  331. // Set the transform (translation, rotation) of the container entity
  332. AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalTranslation, translation);
  333. AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, rotation);
  334. PrefabDom containerEntityDomAfter;
  335. m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
  336. PrefabDom patch;
  337. m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
  338. m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
  339. return AZStd::move(patch);
  340. }
  341. InstantiatePrefabResult PrefabPublicHandler::InstantiatePrefab(
  342. AZStd::string_view filePath, AZ::EntityId parentId, const AZ::Vector3& position)
  343. {
  344. auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
  345. if (!prefabEditorEntityOwnershipInterface)
  346. {
  347. return AZ::Failure(AZStd::string("Could not instantiate prefab - internal error "
  348. "(PrefabEditorEntityOwnershipInterface unavailable)."));
  349. }
  350. if (!prefabEditorEntityOwnershipInterface->IsRootPrefabAssigned())
  351. {
  352. return AZ::Failure(AZStd::string("Could not instantiate prefab - no root prefab assigned. "
  353. "Currently, prefabs can only be instantiated inside a level"));
  354. }
  355. InstanceOptionalReference instanceToParentUnder;
  356. // Get parent entity's owning instance
  357. if (parentId.IsValid())
  358. {
  359. instanceToParentUnder = m_instanceEntityMapperInterface->FindOwningInstance(parentId);
  360. }
  361. if (!instanceToParentUnder.has_value())
  362. {
  363. AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
  364. instanceToParentUnder = m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId);
  365. parentId = instanceToParentUnder->get().GetContainerEntityId();
  366. }
  367. //Detect whether this instantiation would produce a cyclical dependency
  368. auto relativePath = m_prefabLoaderInterface->GenerateRelativePath(filePath);
  369. Prefab::TemplateId templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(relativePath);
  370. if (templateId == InvalidTemplateId)
  371. {
  372. // Load the template from the file
  373. templateId = m_prefabLoaderInterface->LoadTemplateFromFile(filePath);
  374. AZ_Assert(templateId != InvalidTemplateId, "Template with source path %.*s couldn't be loaded correctly.", AZ_STRING_ARG(filePath));
  375. }
  376. const PrefabDom& templateDom = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
  377. AZStd::unordered_set<AZ::IO::Path> templatePaths;
  378. PrefabDomUtils::GetTemplateSourcePaths(templateDom, templatePaths);
  379. if (IsCyclicalDependencyFound(instanceToParentUnder->get(), templatePaths))
  380. {
  381. return AZ::Failure(AZStd::string::format(
  382. "Instantiate Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).",
  383. relativePath.Native().c_str(), instanceToParentUnder->get().GetTemplateSourcePath().Native().c_str()));
  384. }
  385. AZ::EntityId containerEntityId;
  386. {
  387. // Initialize Undo Batch object
  388. ScopedUndoBatch undoBatch("Instantiate Prefab");
  389. // Instantiate the Prefab
  390. PrefabDom instanceToParentUnderDomBeforeCreate;
  391. m_instanceToTemplateInterface->GenerateDomForInstance(instanceToParentUnderDomBeforeCreate, instanceToParentUnder->get());
  392. auto instanceToCreate = prefabEditorEntityOwnershipInterface->InstantiatePrefab(relativePath, instanceToParentUnder);
  393. if (!instanceToCreate)
  394. {
  395. return AZ::Failure(AZStd::string("Could not instantiate the prefab provided - internal error "
  396. "(A null instance is returned)."));
  397. }
  398. // Create Link with correct container patches
  399. containerEntityId = instanceToCreate->get().GetContainerEntityId();
  400. AZ::Entity* containerEntity = GetEntityById(containerEntityId);
  401. AZ_Assert(containerEntity, "Invalid container entity detected in InstantiatePrefab.");
  402. Prefab::PrefabDom containerEntityDomBefore;
  403. m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomBefore, *containerEntity);
  404. // Capture parent entity DOM before adding the nested prefab instance
  405. AZ::Entity* parentEntity = GetEntityById(parentId);
  406. if (!parentEntity)
  407. {
  408. return AZ::Failure<AZStd::string>("Parent entity cannot be found while instantiating a prefab.");
  409. }
  410. PrefabDom parentEntityDomBeforeAdding;
  411. m_instanceToTemplateInterface->GenerateDomForEntity(parentEntityDomBeforeAdding, *parentEntity);
  412. // Set container entity's parent
  413. AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetParent, parentId);
  414. // Set the position of the container entity
  415. AZ::TransformBus::Event(containerEntityId, &AZ::TransformBus::Events::SetWorldTranslation, position);
  416. PrefabDom containerEntityDomAfter;
  417. m_instanceToTemplateInterface->GenerateDomForEntity(containerEntityDomAfter, *containerEntity);
  418. // Generate patch to be stored in the link
  419. PrefabDom patch;
  420. m_instanceToTemplateInterface->GeneratePatch(patch, containerEntityDomBefore, containerEntityDomAfter);
  421. m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, containerEntityId);
  422. CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch));
  423. // Update parent entity DOM
  424. PrefabDom parentEntityDomAfterAdding;
  425. m_instanceToTemplateInterface->GenerateDomForEntity(parentEntityDomAfterAdding, *parentEntity);
  426. PrefabUndoHelpers::UpdateEntity(parentEntityDomBeforeAdding, parentEntityDomAfterAdding, parentId, undoBatch.GetUndoBatch());
  427. m_prefabUndoCache.UpdateCache(containerEntityId);
  428. m_prefabUndoCache.UpdateCache(parentId);
  429. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  430. &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
  431. }
  432. return AZ::Success(containerEntityId);
  433. }
  434. PrefabOperationResult PrefabPublicHandler::FindCommonRootOwningInstance(
  435. const AZStd::vector<AZ::EntityId>& entityIds, EntityList& inputEntityList, EntityList& topLevelEntities,
  436. AZ::EntityId& commonRootEntityId, InstanceOptionalReference& commonRootEntityOwningInstance)
  437. {
  438. // Retrieve entityList from entityIds
  439. inputEntityList = EntityIdListToEntityList(entityIds);
  440. // Remove Level Container Entity if it's part of the list
  441. AZ::EntityId levelEntityId = GetLevelInstanceContainerEntityId();
  442. if (levelEntityId.IsValid())
  443. {
  444. AZ::Entity* levelEntity = GetEntityById(levelEntityId);
  445. if (levelEntity)
  446. {
  447. auto levelEntityIter = AZStd::find(inputEntityList.begin(), inputEntityList.end(), levelEntity);
  448. if (levelEntityIter != inputEntityList.end())
  449. {
  450. inputEntityList.erase(levelEntityIter);
  451. }
  452. }
  453. }
  454. // Find common root and top level entities
  455. bool entitiesHaveCommonRoot = false;
  456. AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
  457. entitiesHaveCommonRoot, &AzToolsFramework::ToolsApplicationRequests::FindCommonRootInactive, inputEntityList,
  458. commonRootEntityId, &topLevelEntities);
  459. // Bail if entities don't share a common root
  460. if (!entitiesHaveCommonRoot)
  461. {
  462. return AZ::Failure(AZStd::string("Failed to create a prefab: Provided entities do not share a common root."));
  463. }
  464. // Retrieve the owning instance of the common root entity, which will be our new instance's parent instance.
  465. commonRootEntityOwningInstance = GetOwnerInstanceByEntityId(commonRootEntityId);
  466. AZ_Assert(
  467. commonRootEntityOwningInstance.has_value(),
  468. "Failed to create prefab : Couldn't get a valid owning instance for the common root entity of the enities provided");
  469. return AZ::Success();
  470. }
  471. bool PrefabPublicHandler::IsCyclicalDependencyFound(
  472. InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths)
  473. {
  474. InstanceOptionalConstReference currentInstance = instance;
  475. while (currentInstance.has_value())
  476. {
  477. if (templateSourcePaths.contains(currentInstance->get().GetTemplateSourcePath()))
  478. {
  479. return true;
  480. }
  481. currentInstance = currentInstance->get().GetParentInstance();
  482. }
  483. return false;
  484. }
  485. void PrefabPublicHandler::CreateLink(
  486. Instance& sourceInstance, TemplateId targetTemplateId,
  487. UndoSystem::URSequencePoint* undoBatch, PrefabDom patch, const bool isUndoRedoSupportNeeded)
  488. {
  489. LinkId linkId;
  490. if (isUndoRedoSupportNeeded)
  491. {
  492. linkId = PrefabUndoHelpers::CreateLink(
  493. sourceInstance.GetTemplateId(), targetTemplateId, AZStd::move(patch), sourceInstance.GetInstanceAlias(), undoBatch);
  494. }
  495. else
  496. {
  497. linkId = m_prefabSystemComponentInterface->CreateLink(
  498. targetTemplateId, sourceInstance.GetTemplateId(), sourceInstance.GetInstanceAlias(), patch,
  499. InvalidLinkId);
  500. m_prefabSystemComponentInterface->PropagateTemplateChanges(targetTemplateId);
  501. }
  502. sourceInstance.SetLinkId(linkId);
  503. }
  504. void PrefabPublicHandler::RemoveLink(
  505. AZStd::unique_ptr<Instance>& sourceInstance, TemplateId targetTemplateId, UndoSystem::URSequencePoint* undoBatch)
  506. {
  507. LinkReference nestedInstanceLink = m_prefabSystemComponentInterface->FindLink(sourceInstance->GetLinkId());
  508. AZ_Assert(
  509. nestedInstanceLink.has_value(),
  510. "A valid link was not found for one of the instances provided as input for the CreatePrefab operation.");
  511. PrefabDom patchesCopyForUndoSupport;
  512. PrefabDom nestedInstanceLinkDom;
  513. nestedInstanceLink->get().GetLinkDom(nestedInstanceLinkDom, nestedInstanceLinkDom.GetAllocator());
  514. PrefabDomValueReference nestedInstanceLinkPatches =
  515. PrefabDomUtils::FindPrefabDomValue(nestedInstanceLinkDom, PrefabDomUtils::PatchesName);
  516. if (nestedInstanceLinkPatches.has_value())
  517. {
  518. patchesCopyForUndoSupport.CopyFrom(nestedInstanceLinkPatches->get(), patchesCopyForUndoSupport.GetAllocator());
  519. }
  520. PrefabUndoHelpers::RemoveLink(
  521. sourceInstance->GetTemplateId(), targetTemplateId, sourceInstance->GetInstanceAlias(), sourceInstance->GetLinkId(),
  522. AZStd::move(patchesCopyForUndoSupport), undoBatch);
  523. }
  524. PrefabOperationResult PrefabPublicHandler::SavePrefab(AZ::IO::Path filePath)
  525. {
  526. auto templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(filePath.c_str());
  527. if (templateId == InvalidTemplateId)
  528. {
  529. return AZ::Failure(
  530. AZStd::string("SavePrefab - Path error. Path could be invalid, or the prefab may not be loaded in this level."));
  531. }
  532. if (!m_prefabLoaderInterface->SaveTemplate(templateId))
  533. {
  534. return AZ::Failure(AZStd::string("Could not save prefab - internal error (Json write operation failure)."));
  535. }
  536. return AZ::Success();
  537. }
  538. PrefabEntityResult PrefabPublicHandler::CreateEntity(AZ::EntityId parentId, const AZ::Vector3& position)
  539. {
  540. AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
  541. // If the parent is invalid, parent to the container of the currently focused prefab.
  542. if (!parentId.IsValid())
  543. {
  544. parentId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
  545. }
  546. // If the parent entity isn't owned by a prefab instance, bail.
  547. InstanceOptionalReference owningInstanceOfParentEntity = GetOwnerInstanceByEntityId(parentId);
  548. if (!owningInstanceOfParentEntity)
  549. {
  550. return AZ::Failure(AZStd::string::format(
  551. "Cannot add entity because the owning instance of parent entity with id '%llu' could not be found.",
  552. static_cast<AZ::u64>(parentId)));
  553. }
  554. // If the parent entity is a closed container, bail.
  555. if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get(); !containerEntityInterface->IsContainerOpen(parentId))
  556. {
  557. return AZ::Failure(AZStd::string::format(
  558. "Cannot add entity because the parent entity (id '%llu') is a closed container entity.",
  559. static_cast<AZ::u64>(parentId)));
  560. }
  561. // If the parent entity is marked as read only, bail.
  562. if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get(); readOnlyEntityPublicInterface->IsReadOnly(parentId))
  563. {
  564. return AZ::Failure(AZStd::string::format(
  565. "Cannot add entity because the parent entity (id '%llu') is marked as read only.",
  566. static_cast<AZ::u64>(parentId)));
  567. }
  568. EntityAlias entityAlias = Instance::GenerateEntityAlias();
  569. AliasPath absoluteEntityPath = owningInstanceOfParentEntity->get().GetAbsoluteInstanceAliasPath();
  570. absoluteEntityPath.Append(entityAlias);
  571. AZ::EntityId entityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
  572. AZStd::string entityName = AZStd::string::format("Entity%llu", static_cast<AZ::u64>(m_newEntityCounter++));
  573. AZ::Entity* entity = aznew AZ::Entity(entityId, entityName.c_str());
  574. Instance& entityOwningInstance = owningInstanceOfParentEntity->get();
  575. ScopedUndoBatch undoBatch("Add Entity");
  576. entityOwningInstance.AddEntity(*entity, entityAlias);
  577. EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::HandleEntitiesAdded, EntityList{entity});
  578. EditorEntityContextRequestBus::Broadcast(&EditorEntityContextRequestBus::Events::FinalizeEditorEntity, entity);
  579. AZ::Transform transform = AZ::Transform::CreateIdentity();
  580. transform.SetTranslation(position);
  581. EntityOptionalReference owningInstanceContainerEntity = entityOwningInstance.GetContainerEntity();
  582. if (owningInstanceContainerEntity && !parentId.IsValid())
  583. {
  584. parentId = owningInstanceContainerEntity->get().GetId();
  585. }
  586. if (parentId.IsValid())
  587. {
  588. AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetParent, parentId);
  589. AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetLocalTM, transform);
  590. }
  591. else
  592. {
  593. AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetWorldTM, transform);
  594. }
  595. m_prefabUndoCache.UpdateCache(entityId);
  596. m_prefabUndoCache.UpdateCache(parentId);
  597. AZ::Entity* parentEntity = GetEntityById(parentId);
  598. if (!parentEntity)
  599. {
  600. return AZ::Failure<AZStd::string>("Parent entity cannot be found while adding an entity.");
  601. }
  602. InstanceOptionalReference focusedInstance =
  603. m_prefabFocusHandler.GetFocusedPrefabInstance(editorEntityContextId);
  604. if (!focusedInstance.has_value())
  605. {
  606. return AZ::Failure<AZStd::string>("Can't find current focused prefab instance.");
  607. }
  608. PrefabUndoHelpers::AddEntity(*parentEntity, *entity,
  609. entityOwningInstance, focusedInstance->get(), undoBatch.GetUndoBatch());
  610. // Select the new entity (and deselect others).
  611. AzToolsFramework::EntityIdList selection = { entityId };
  612. SelectionCommand* selectionCommand = aznew SelectionCommand(selection, "");
  613. selectionCommand->SetParent(undoBatch.GetUndoBatch());
  614. ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selection);
  615. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  616. &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
  617. return AZ::Success(entityId);
  618. }
  619. PrefabOperationResult PrefabPublicHandler::GenerateUndoNodesForEntityChangeAndUpdateCache(
  620. AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch)
  621. {
  622. // Create Undo node on entities if they belong to an instance
  623. InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  624. if (!owningInstance.has_value())
  625. {
  626. AZ_Warning("Prefab", false, "GenerateUndoNodesForEntityChangeAndUpdateCache - "
  627. "The dirty entity has no owning instance.");
  628. return AZ::Success();
  629. }
  630. AZ::Entity* entity = GetEntityById(entityId);
  631. if (!entity)
  632. {
  633. m_prefabUndoCache.PurgeCache(entityId);
  634. AZ_Warning("Prefab", false, "GenerateUndoNodesForEntityChangeAndUpdateCache - "
  635. "The dirty entity is invalid.");
  636. return AZ::Success();
  637. }
  638. PrefabDom beforeState;
  639. m_instanceDomGeneratorInterface->GenerateEntityDom(beforeState, *entity);
  640. AZ::EntityId beforeParentId;
  641. m_prefabUndoCache.Retrieve(entityId, beforeParentId);
  642. PrefabDom afterState;
  643. m_instanceToTemplateInterface->GenerateDomForEntity(afterState, *entity);
  644. AZ::EntityId afterParentId;
  645. AZ::TransformBus::EventResult(afterParentId, entityId, &AZ::TransformBus::Events::GetParentId);
  646. // Skip further processing if either state is not a valid JSON object.
  647. if (!beforeState.IsObject() || !afterState.IsObject())
  648. {
  649. AZ_Warning("Prefab", false, "GenerateUndoNodesForEntityChangeAndUpdateCache - "
  650. "The before or after DOM state of the dirty entity is not a valid JSON object.");
  651. return AZ::Success();
  652. }
  653. PrefabDom patch;
  654. m_instanceToTemplateInterface->GeneratePatch(patch, beforeState, afterState);
  655. m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(patch, entityId);
  656. if (patch.IsArray() && !patch.Empty())
  657. {
  658. bool isNewParentOwnedByDifferentInstance = false;
  659. // Reparenting of entities happens before they are associated with their owning instances. So the owning instance
  660. // of the entity can be stale. Therefore, check whether the parent entity is in the focus tree instead.
  661. bool isInFocusTree = m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(afterParentId);
  662. bool isOwnedByFocusedPrefabInstance = m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId);
  663. if (beforeParentId != afterParentId)
  664. {
  665. // If the entity parent changed, verify if the owning instance changed too
  666. InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
  667. InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
  668. if (beforeOwningInstance.has_value() && afterOwningInstance.has_value() &&
  669. (&beforeOwningInstance->get() != &afterOwningInstance->get()))
  670. {
  671. isNewParentOwnedByDifferentInstance = true;
  672. // Detect loops. Assert if an instance has been reparented in such a way to generate circular dependencies.
  673. AZStd::vector<Instance*> instancesInvolved;
  674. if (isInFocusTree && !isOwnedByFocusedPrefabInstance)
  675. {
  676. instancesInvolved.push_back(&owningInstance->get());
  677. }
  678. else
  679. {
  680. // Retrieve all nested instances that are part of the subtree under the current entity.
  681. EntityList entities;
  682. PrefabOperationResult retrieveEntitiesAndInstancesOutcome = RetrieveAndSortPrefabEntitiesAndInstances(
  683. { entity }, beforeOwningInstance->get(), entities, instancesInvolved);
  684. if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
  685. {
  686. return retrieveEntitiesAndInstancesOutcome;
  687. }
  688. }
  689. for (Instance* instance : instancesInvolved)
  690. {
  691. const PrefabDom& templateDom =
  692. m_prefabSystemComponentInterface->FindTemplateDom(instance->GetTemplateId());
  693. AZStd::unordered_set<AZ::IO::Path> templatePaths;
  694. PrefabDomUtils::GetTemplateSourcePaths(templateDom, templatePaths);
  695. if (IsCyclicalDependencyFound(afterOwningInstance->get(), templatePaths))
  696. {
  697. // Cancel the operation by restoring the previous parent
  698. AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetParent, beforeParentId);
  699. m_prefabUndoCache.UpdateCache(entityId);
  700. // Skip the creation of an undo node
  701. return AZ::Failure(AZStd::string::format(
  702. "Reparent Prefab operation aborted - Cyclical dependency detected\n(%s depends on %s).",
  703. instance->GetTemplateSourcePath().Native().c_str(),
  704. afterOwningInstance->get().GetTemplateSourcePath().Native().c_str()));
  705. }
  706. }
  707. }
  708. }
  709. if (isInFocusTree && !isOwnedByFocusedPrefabInstance)
  710. {
  711. if (isNewParentOwnedByDifferentInstance)
  712. {
  713. Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
  714. PrefabDom afterStateafterReparenting;
  715. m_instanceToTemplateInterface->GenerateDomForEntity(afterStateafterReparenting, *entity);
  716. PrefabDom newPatch;
  717. m_instanceToTemplateInterface->GeneratePatch(newPatch, afterState, afterStateafterReparenting);
  718. m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(newPatch, entityId);
  719. InstanceOptionalReference owningInstanceAfterReparenting =
  720. m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  721. Internal_HandleContainerOverride(
  722. parentUndoBatch, entityId, newPatch, owningInstanceAfterReparenting->get().GetLinkId());
  723. }
  724. else
  725. {
  726. PrefabDom newPatch;
  727. m_instanceToTemplateInterface->GeneratePatch(newPatch, beforeState, afterState);
  728. LinkId linkId = m_prefabFocusInterface->AppendPathFromFocusedInstanceToPatchPaths(newPatch, entityId);
  729. Internal_HandleContainerOverride(parentUndoBatch, entityId, newPatch, linkId);
  730. }
  731. }
  732. else
  733. {
  734. Internal_HandleEntityChange(parentUndoBatch, entityId, beforeState, afterState);
  735. if (isNewParentOwnedByDifferentInstance)
  736. {
  737. Internal_HandleInstanceChange(parentUndoBatch, entity, beforeParentId, afterParentId);
  738. }
  739. }
  740. }
  741. m_prefabUndoCache.UpdateCache(entityId);
  742. return AZ::Success();
  743. }
  744. void PrefabPublicHandler::Internal_HandleContainerOverride(
  745. UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, const PrefabDom& patch, const LinkId linkId)
  746. {
  747. // Save these changes as patches to the link
  748. PrefabUndoUpdateLink* linkUpdate = aznew PrefabUndoUpdateLink(AZStd::to_string(static_cast<AZ::u64>(entityId)));
  749. linkUpdate->SetParent(undoBatch);
  750. linkUpdate->Capture(patch, linkId);
  751. linkUpdate->Redo();
  752. }
  753. void PrefabPublicHandler::Internal_HandleEntityChange(
  754. UndoSystem::URSequencePoint* undoBatch, AZ::EntityId entityId, PrefabDom& beforeState,
  755. PrefabDom& afterState)
  756. {
  757. // Update the state of the entity
  758. PrefabUndoHelpers::UpdateEntity(beforeState, afterState,
  759. entityId, undoBatch);
  760. }
  761. void PrefabPublicHandler::Internal_HandleInstanceChange(
  762. UndoSystem::URSequencePoint* undoBatch, AZ::Entity* entity, AZ::EntityId beforeParentId, AZ::EntityId afterParentId)
  763. {
  764. // If the entity parent changed, verify if the owning instance changed too
  765. InstanceOptionalReference beforeOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(beforeParentId);
  766. InstanceOptionalReference afterOwningInstance = m_instanceEntityMapperInterface->FindOwningInstance(afterParentId);
  767. EntityList entities;
  768. AZStd::vector<Instance*> instances;
  769. // Retrieve all descendant entities and instances of this entity that belonged to the same owning instance.
  770. PrefabOperationResult retrieveEntitiesAndInstancesOutcome = RetrieveAndSortPrefabEntitiesAndInstances(
  771. { entity }, beforeOwningInstance->get(), entities, instances);
  772. AZ_Error("Prefab", retrieveEntitiesAndInstancesOutcome.IsSuccess(), retrieveEntitiesAndInstancesOutcome.GetError().data());
  773. AZStd::vector<AZStd::unique_ptr<Instance>> instanceUniquePtrs;
  774. AZStd::vector<AZStd::pair<Instance*, PrefabDom>> instancePatches;
  775. // Remove Entities and Instances from the prior instance
  776. {
  777. // Remove Instances
  778. for (Instance* nestedInstance : instances)
  779. {
  780. auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId());
  781. PrefabDom oldLinkPatches;
  782. if (linkRef.has_value())
  783. {
  784. linkRef->get().GetLinkPatches(oldLinkPatches, oldLinkPatches.GetAllocator());
  785. }
  786. auto nestedInstanceUniquePtr = beforeOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias());
  787. RemoveLink(nestedInstanceUniquePtr, beforeOwningInstance->get().GetTemplateId(), undoBatch);
  788. instancePatches.emplace_back(AZStd::make_pair(nestedInstanceUniquePtr.get(), AZStd::move(oldLinkPatches)));
  789. instanceUniquePtrs.emplace_back(AZStd::move(nestedInstanceUniquePtr));
  790. }
  791. // Get the previous state of the prior instance for undo/redo purposes
  792. PrefabDom beforeInstanceDomBeforeRemoval;
  793. m_instanceToTemplateInterface->GenerateDomForInstance(beforeInstanceDomBeforeRemoval, beforeOwningInstance->get());
  794. // Remove Entities
  795. for (AZ::Entity* nestedEntity : entities)
  796. {
  797. beforeOwningInstance->get().DetachEntity(nestedEntity->GetId()).release();
  798. }
  799. // Create the Update node for the prior owning instance
  800. // Instance removal will be taken care of from the RemoveLink function for undo/redo purposes
  801. PrefabUndoHelpers::UpdatePrefabInstance(
  802. beforeOwningInstance->get(), "Update prior prefab instance", beforeInstanceDomBeforeRemoval, undoBatch);
  803. }
  804. // Add Entities and Instances to new instance
  805. {
  806. // Add Instances
  807. for (auto& instanceUniquePtr : instanceUniquePtrs)
  808. {
  809. afterOwningInstance->get().AddInstance(AZStd::move(instanceUniquePtr));
  810. }
  811. // Create Links
  812. for (auto& instanceInfo : instancePatches)
  813. {
  814. // Add a new link with the old dom
  815. CreateLink(
  816. *instanceInfo.first, afterOwningInstance->get().GetTemplateId(), undoBatch,
  817. AZStd::move(instanceInfo.second));
  818. }
  819. // Get the previous state of the new instance for undo/redo purposes
  820. PrefabDom afterInstanceDomBeforeAdd;
  821. m_instanceToTemplateInterface->GenerateDomForInstance(afterInstanceDomBeforeAdd, afterOwningInstance->get());
  822. // Add Entities
  823. for (AZ::Entity* nestedEntity : entities)
  824. {
  825. afterOwningInstance->get().AddEntity(*nestedEntity);
  826. }
  827. // Create the Update node for the new owning instance
  828. PrefabUndoHelpers::UpdatePrefabInstance(
  829. afterOwningInstance->get(), "Update new prefab instance", afterInstanceDomBeforeAdd, undoBatch);
  830. }
  831. }
  832. bool PrefabPublicHandler::IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const
  833. {
  834. if (InstanceOptionalReference instanceReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  835. instanceReference.has_value())
  836. {
  837. TemplateReference templateReference = m_prefabSystemComponentInterface->FindTemplate(instanceReference->get().GetTemplateId());
  838. return (templateReference.has_value()) && (templateReference->get().IsProcedural());
  839. }
  840. return false;
  841. }
  842. bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
  843. {
  844. InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  845. return owningInstance && (owningInstance->get().GetContainerEntityId() == entityId);
  846. }
  847. bool PrefabPublicHandler::IsLevelInstanceContainerEntity(AZ::EntityId entityId) const
  848. {
  849. // Get owning instance
  850. InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  851. // Get level root instance
  852. auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
  853. if (!prefabEditorEntityOwnershipInterface)
  854. {
  855. AZ_Assert(
  856. false,
  857. "Could not get owning instance of common root entity :"
  858. "PrefabEditorEntityOwnershipInterface unavailable.");
  859. }
  860. InstanceOptionalReference levelInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
  861. return owningInstance
  862. && levelInstance
  863. && (&owningInstance->get() == &levelInstance->get())
  864. && (owningInstance->get().GetContainerEntityId() == entityId);
  865. }
  866. AZ::EntityId PrefabPublicHandler::GetInstanceContainerEntityId(AZ::EntityId entityId) const
  867. {
  868. AZ::Entity* entity = GetEntityById(entityId);
  869. if (entity)
  870. {
  871. InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity->GetId());
  872. if (owningInstance)
  873. {
  874. return owningInstance->get().GetContainerEntityId();
  875. }
  876. }
  877. return AZ::EntityId();
  878. }
  879. AZ::EntityId PrefabPublicHandler::GetLevelInstanceContainerEntityId() const
  880. {
  881. auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
  882. if (!prefabEditorEntityOwnershipInterface)
  883. {
  884. AZ_Assert(
  885. false,
  886. "Could not get owning instance of common root entity :"
  887. "PrefabEditorEntityOwnershipInterface unavailable.");
  888. return AZ::EntityId();
  889. }
  890. auto rootInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
  891. if (!rootInstance.has_value())
  892. {
  893. return AZ::EntityId();
  894. }
  895. return rootInstance->get().GetContainerEntityId();
  896. }
  897. AZ::IO::Path PrefabPublicHandler::GetOwningInstancePrefabPath(AZ::EntityId entityId) const
  898. {
  899. AZ::IO::Path path;
  900. InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityId);
  901. if (instance.has_value())
  902. {
  903. path = instance->get().GetTemplateSourcePath();
  904. }
  905. return path;
  906. }
  907. PrefabRequestResult PrefabPublicHandler::HasUnsavedChanges(AZ::IO::Path prefabFilePath) const
  908. {
  909. auto templateId = m_prefabSystemComponentInterface->GetTemplateIdFromFilePath(prefabFilePath.c_str());
  910. if (templateId == InvalidTemplateId)
  911. {
  912. return AZ::Failure(AZStd::string("HasUnsavedChanges - Path error. Path could be invalid, or the prefab may not be loaded in this level."));
  913. }
  914. return AZ::Success(m_prefabSystemComponentInterface->IsTemplateDirty(templateId));
  915. }
  916. PrefabOperationResult PrefabPublicHandler::DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds)
  917. {
  918. return DeleteFromInstance(entityIds);
  919. }
  920. DuplicatePrefabResult PrefabPublicHandler::DuplicateEntitiesInInstance(const EntityIdList& entityIds)
  921. {
  922. if (entityIds.empty())
  923. {
  924. return AZ::Failure(AZStd::string("No entities to duplicate."));
  925. }
  926. const EntityIdList entityIdsNoFocusContainer = SanitizeEntityIdList(entityIds);
  927. if (entityIdsNoFocusContainer.empty())
  928. {
  929. return AZ::Failure(AZStd::string("No entities to duplicate because only instance selected is the container entity of the focused instance."));
  930. }
  931. if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer))
  932. {
  933. return AZ::Failure(AZStd::string("Cannot duplicate multiple entities belonging to different instances with one operation."
  934. "Change your selection to contain entities in the same instance."));
  935. }
  936. // We've already verified the entities are all owned by the same instance,
  937. // so we can just retrieve our instance from the first entity in the list.
  938. AZ::EntityId firstEntityIdToDuplicate = entityIdsNoFocusContainer[0];
  939. InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDuplicate);
  940. if (!commonOwningInstance.has_value())
  941. {
  942. return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
  943. }
  944. // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance
  945. // This is because containers, despite representing the nested instance in the parent, are owned by the child.
  946. if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDuplicate)
  947. {
  948. commonOwningInstance = commonOwningInstance->get().GetParentInstance();
  949. }
  950. if (!commonOwningInstance.has_value())
  951. {
  952. return AZ::Failure(AZStd::string("Failed to duplicate : Couldn't get a valid owning instance for the common root entity of the entities provided."));
  953. }
  954. // This will cull out any entities that have ancestors in the list, since we will end up duplicating
  955. // the full nested hierarchy with what is returned from RetrieveAndSortPrefabEntitiesAndInstances
  956. AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entityIdsNoFocusContainer);
  957. AZ_PROFILE_FUNCTION(AzToolsFramework);
  958. ScopedUndoBatch undoBatch("Duplicate Entities");
  959. EntityIdList duplicatedEntityAndInstanceIds;
  960. {
  961. AZ_PROFILE_SCOPE(AzToolsFramework, "DuplicateEntitiesInInstance::UndoCaptureAndDuplicateEntities");
  962. AZStd::vector<AZ::Entity*> entities;
  963. AZStd::vector<Instance*> instances;
  964. EntityList inputEntityList = EntityIdSetToEntityList(duplicationSet);
  965. PrefabOperationResult retrieveEntitiesAndInstancesOutcome =
  966. RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entities, instances);
  967. if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
  968. {
  969. return AZ::Failure(retrieveEntitiesAndInstancesOutcome.TakeError());
  970. }
  971. // Take a snapshot of the instance DOM before we manipulate it
  972. PrefabDom instanceDomBefore;
  973. m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, commonOwningInstance->get());
  974. // Make a copy of our before instance DOM where we will add our duplicated entities and/or instances
  975. PrefabDom instanceDomAfter;
  976. instanceDomAfter.CopyFrom(instanceDomBefore, instanceDomAfter.GetAllocator());
  977. // Duplicate any nested entities and instances as requested
  978. AZStd::unordered_map<InstanceAlias, Instance*> newInstanceAliasToOldInstanceMap;
  979. AZStd::unordered_map<EntityAlias, EntityAlias> duplicateEntityAliasMap;
  980. DuplicateNestedEntitiesInInstance(commonOwningInstance->get(),
  981. entities, instanceDomAfter, duplicatedEntityAndInstanceIds, duplicateEntityAliasMap);
  982. PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
  983. command->SetParent(undoBatch.GetUndoBatch());
  984. command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
  985. command->Redo();
  986. DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
  987. instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
  988. // Create links for our duplicated instances (if any were duplicated)
  989. for (auto [newInstanceAlias, oldInstance] : newInstanceAliasToOldInstanceMap)
  990. {
  991. LinkId oldLinkId = oldInstance->GetLinkId();
  992. auto linkRef = m_prefabSystemComponentInterface->FindLink(oldLinkId);
  993. AZ_Assert(
  994. linkRef.has_value(), "Unable to find link with id '%llu' during instance duplication.",
  995. oldLinkId);
  996. PrefabDom linkPatches;
  997. linkRef->get().GetLinkPatches(linkPatches, linkPatches.GetAllocator());
  998. // If the instance was duplicated as part of an ancestor's nested hierarchy, the container's parent patch
  999. // will need to be refreshed to point to the new duplicated parent entity
  1000. auto oldInstanceContainerEntityId = oldInstance->GetContainerEntityId();
  1001. AZ_Assert(oldInstanceContainerEntityId.IsValid(), "Instance returned invalid Container Entity Id");
  1002. AZ::EntityId previousParentEntityId;
  1003. AZ::TransformBus::EventResult(previousParentEntityId, oldInstanceContainerEntityId, &AZ::TransformBus::Events::GetParentId);
  1004. if (previousParentEntityId.IsValid() && AZStd::find(duplicatedEntityAndInstanceIds.begin(), duplicatedEntityAndInstanceIds.end(), previousParentEntityId))
  1005. {
  1006. auto oldParentAlias = commonOwningInstance->get().GetEntityAlias(previousParentEntityId);
  1007. if (oldParentAlias.has_value() && duplicateEntityAliasMap.contains(oldParentAlias->get()))
  1008. {
  1009. // Get the dom into a QString for search/replace purposes
  1010. rapidjson::StringBuffer buffer;
  1011. rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  1012. linkPatches.Accept(writer);
  1013. QString linkPatchesString(buffer.GetString());
  1014. ReplaceOldAliases(linkPatchesString, oldParentAlias->get(), duplicateEntityAliasMap[oldParentAlias->get()]);
  1015. linkPatches.Parse(linkPatchesString.toUtf8().constData());
  1016. }
  1017. }
  1018. PrefabUndoHelpers::CreateLink(
  1019. oldInstance->GetTemplateId(), commonOwningInstance->get().GetTemplateId(),
  1020. AZStd::move(linkPatches),
  1021. newInstanceAlias,
  1022. undoBatch.GetUndoBatch());
  1023. }
  1024. // Select the duplicated entities/instances
  1025. auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
  1026. selectionUndo->SetParent(undoBatch.GetUndoBatch());
  1027. ToolsApplicationRequestBus::Broadcast(
  1028. &ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
  1029. }
  1030. return AZ::Success(AZStd::move(duplicatedEntityAndInstanceIds));
  1031. }
  1032. PrefabOperationResult PrefabPublicHandler::DeleteFromInstance(const EntityIdList& entityIds)
  1033. {
  1034. // Remove the container entity of the focused prefab from the list, if it is included.
  1035. const EntityIdList entityIdsNoFocusContainer = SanitizeEntityIdList(entityIds);
  1036. if (entityIdsNoFocusContainer.empty())
  1037. {
  1038. return AZ::Success();
  1039. }
  1040. // All entities in this list need to belong to the same prefab instance for the operation to be valid.
  1041. if (!EntitiesBelongToSameInstance(entityIdsNoFocusContainer))
  1042. {
  1043. return AZ::Failure(AZStd::string("Cannot delete multiple entities belonging to different instances with one operation."));
  1044. }
  1045. AZ::EntityId firstEntityIdToDelete = entityIdsNoFocusContainer[0];
  1046. InstanceOptionalReference commonOwningInstance = GetOwnerInstanceByEntityId(firstEntityIdToDelete);
  1047. if (!commonOwningInstance.has_value())
  1048. {
  1049. return AZ::Failure(AZStd::string("Cannot delete entities belonging to an invalid instance"));
  1050. }
  1051. // If the first entity id is a container entity id, then we need to mark its parent as the common owning instance because you
  1052. // cannot delete an instance from itself.
  1053. if (commonOwningInstance->get().GetContainerEntityId() == firstEntityIdToDelete)
  1054. {
  1055. commonOwningInstance = commonOwningInstance->get().GetParentInstance();
  1056. }
  1057. // We only allow explicit deletions for entities inside the currently focused prefab.
  1058. AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
  1059. InstanceOptionalReference focusedInstance = m_prefabFocusInterface->GetFocusedPrefabInstance(editorEntityContextId);
  1060. if (!focusedInstance.has_value())
  1061. {
  1062. return AZ::Failure(AZStd::string("Cannot get the focused instance."));
  1063. }
  1064. if (!PrefabInstanceUtils::IsDescendantInstance(commonOwningInstance->get(), focusedInstance->get()))
  1065. {
  1066. return AZ::Failure(AZStd::string("The common owning instance is not descendant of currently focused instance."));
  1067. }
  1068. // Retrieve entityList from entityIds
  1069. EntityList inputEntityList = EntityIdListToEntityList(entityIdsNoFocusContainer);
  1070. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1071. ScopedUndoBatch undoBatch("Delete Selected");
  1072. AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DeleteEntities:UndoCaptureAndPurgeEntities");
  1073. AZStd::vector<AZ::Entity*> entityList;
  1074. AZStd::vector<Instance*> instanceList;
  1075. PrefabOperationResult retrieveEntitiesAndInstancesOutcome =
  1076. RetrieveAndSortPrefabEntitiesAndInstances(inputEntityList, commonOwningInstance->get(), entityList, instanceList);
  1077. if (!retrieveEntitiesAndInstancesOutcome.IsSuccess())
  1078. {
  1079. return AZStd::move(retrieveEntitiesAndInstancesOutcome);
  1080. }
  1081. // Gets selected entities.
  1082. EntityIdList selectedEntities;
  1083. ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
  1084. SelectionCommand* selCommand = aznew SelectionCommand(selectedEntities, "Delete Entities");
  1085. selCommand->SetParent(undoBatch.GetUndoBatch());
  1086. AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DeleteEntities:RunRedo");
  1087. selCommand->RunRedo();
  1088. // We insert a "deselect all" command before we delete the entities. This ensures the delete operations aren't changing
  1089. // selection state, which triggers expensive UI updates. By deselecting up front, we are able to do those expensive
  1090. // UI updates once at the start instead of once for each entity.
  1091. EntityIdList deselection;
  1092. SelectionCommand* deselectAllCommand = aznew SelectionCommand(deselection, "Deselect Entities");
  1093. deselectAllCommand->SetParent(undoBatch.GetUndoBatch());
  1094. // Removing instances and entities for one owning instance...
  1095. // - Detach instance objects and entity objects.
  1096. // - Update focused template DOM accordingly with undo/redo support.
  1097. // Set for entity ids that will be removed. It is used for filtering out parents that won't need to be updated.
  1098. // If we know in advance that a parent entity will be removed, we can skip updating this parent entity.
  1099. AZStd::unordered_set<AZ::EntityId> entitiesThatWillBeRemoved;
  1100. AZStd::for_each(entityList.begin(), entityList.end(), [&entitiesThatWillBeRemoved](const AZ::Entity* entity)
  1101. {
  1102. if (entity->GetId().IsValid())
  1103. {
  1104. entitiesThatWillBeRemoved.insert(entity->GetId());
  1105. }
  1106. });
  1107. // Set of parent entities that need to be updated. It should not include entities that will be removed.
  1108. AZStd::unordered_set<const AZ::Entity*> parentEntitiesToUpdate;
  1109. // List of detached entity alias paths to owning instance.
  1110. AZStd::vector<AZStd::string> detachedEntityAliasPaths;
  1111. // List of detached instance alias paths to owning instance. It is only used for override editing.
  1112. [[maybe_unused]] AZStd::vector<AZStd::string> detachedInstanceAliasPaths;
  1113. // Flag that determines if it is source template editing or override editing to focused template.
  1114. const bool isOverrideEditing = &(commonOwningInstance->get()) != &(focusedInstance->get());
  1115. // 1. Detaches instance objects.
  1116. for (const Instance* instance : instanceList)
  1117. {
  1118. const InstanceAlias instanceAlias = instance->GetInstanceAlias();
  1119. // Captures the parent entity that needs to be updated.
  1120. AZ::EntityId parentEntityId;
  1121. AZ::TransformBus::EventResult(parentEntityId, instance->GetContainerEntityId(), &AZ::TransformBus::Events::GetParentId);
  1122. if (parentEntityId.IsValid() && (entitiesThatWillBeRemoved.find(parentEntityId) == entitiesThatWillBeRemoved.end()))
  1123. {
  1124. parentEntitiesToUpdate.insert(GetEntityById(parentEntityId));
  1125. }
  1126. AZStd::unique_ptr<Instance> detachedInstance = commonOwningInstance->get().DetachNestedInstance(instanceAlias);
  1127. if (isOverrideEditing)
  1128. {
  1129. detachedInstanceAliasPaths.push_back(AZStd::move(PrefabDomUtils::PathStartingWithInstances + instanceAlias));
  1130. }
  1131. else
  1132. {
  1133. // Removes the link if it is source template editing.
  1134. RemoveLink(detachedInstance, commonOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch());
  1135. }
  1136. detachedInstance.reset();
  1137. }
  1138. // 2. Detaches entity objects.
  1139. for (const AZ::Entity* entity : entityList)
  1140. {
  1141. const AZ::EntityId entityId = entity->GetId();
  1142. const AZStd::string nestedEntityAliasPath = m_instanceToTemplateInterface->GenerateEntityAliasPath(entityId);
  1143. // Captures the parent entity that needs to be updated.
  1144. AZ::EntityId parentEntityId;
  1145. AZ::TransformBus::EventResult(parentEntityId, entityId, &AZ::TransformBus::Events::GetParentId);
  1146. if (parentEntityId.IsValid() && (entitiesThatWillBeRemoved.find(parentEntityId) == entitiesThatWillBeRemoved.end()))
  1147. {
  1148. parentEntitiesToUpdate.insert(GetEntityById(parentEntityId));
  1149. }
  1150. commonOwningInstance->get().DetachEntity(entityId);
  1151. detachedEntityAliasPaths.push_back(AZStd::move(nestedEntityAliasPath));
  1152. }
  1153. // 3. Updates template DOM with undo/redo support.
  1154. if (isOverrideEditing)
  1155. {
  1156. PrefabUndoHelpers::DeleteEntitiesAndPrefabsAsOverride(
  1157. detachedEntityAliasPaths,
  1158. detachedInstanceAliasPaths,
  1159. { parentEntitiesToUpdate.begin(), parentEntitiesToUpdate.end() }, // convert set to vector
  1160. commonOwningInstance->get(),
  1161. focusedInstance->get(),
  1162. undoBatch.GetUndoBatch());
  1163. }
  1164. else
  1165. {
  1166. // Note: Detached instances have been updated in RemoveLink.
  1167. PrefabUndoHelpers::DeleteEntities(
  1168. detachedEntityAliasPaths,
  1169. { parentEntitiesToUpdate.begin(), parentEntitiesToUpdate.end() }, // convert set to vector
  1170. focusedInstance->get(),
  1171. undoBatch.GetUndoBatch());
  1172. }
  1173. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  1174. &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
  1175. return AZ::Success();
  1176. }
  1177. PrefabOperationResult PrefabPublicHandler::DetachPrefab(const AZ::EntityId& containerEntityId)
  1178. {
  1179. if (!containerEntityId.IsValid())
  1180. {
  1181. return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity."));
  1182. }
  1183. auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
  1184. EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
  1185. if (containerEntityId == m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId))
  1186. {
  1187. return AZ::Failure(AZStd::string("Cannot detach focused Prefab Instance."));
  1188. }
  1189. InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId);
  1190. if (owningInstance->get().GetContainerEntityId() != containerEntityId)
  1191. {
  1192. return AZ::Failure(AZStd::string("Input entity should be its owning Instance's container entity."));
  1193. }
  1194. AZ_PROFILE_FUNCTION(AzToolsFramework);
  1195. {
  1196. AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DetachPrefab:UndoCapture");
  1197. ScopedUndoBatch undoBatch("Detach Prefab");
  1198. InstanceOptionalReference getParentInstanceResult = owningInstance->get().GetParentInstance();
  1199. AZ_Assert(getParentInstanceResult.has_value(), "Can't get parent Instance from Instance of given container entity.");
  1200. auto& parentInstance = getParentInstanceResult->get();
  1201. const auto parentTemplateId = parentInstance.GetTemplateId();
  1202. {
  1203. auto instancePtr = parentInstance.DetachNestedInstance(owningInstance->get().GetInstanceAlias());
  1204. AZ_Assert(instancePtr, "Can't detach selected Instance from its parent Instance.");
  1205. RemoveLink(instancePtr, parentTemplateId, undoBatch.GetUndoBatch());
  1206. Prefab::PrefabDom instanceDomBefore;
  1207. m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, parentInstance);
  1208. AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
  1209. oldEntityAliases.emplace(containerEntityId, instancePtr->GetEntityAlias(containerEntityId)->get());
  1210. auto containerEntityPtr = instancePtr->DetachContainerEntity();
  1211. auto& containerEntity = *containerEntityPtr.release();
  1212. auto editorPrefabComponent = containerEntity.FindComponent<EditorPrefabComponent>();
  1213. containerEntity.Deactivate();
  1214. [[maybe_unused]] const bool editorPrefabComponentRemoved = containerEntity.RemoveComponent(editorPrefabComponent);
  1215. AZ_Assert(editorPrefabComponentRemoved, "Remove EditorPrefabComponent failed.");
  1216. delete editorPrefabComponent;
  1217. containerEntity.Activate();
  1218. [[maybe_unused]] const bool containerEntityAdded = parentInstance.AddEntity(containerEntity);
  1219. AZ_Assert(containerEntityAdded, "Add target Instance's container entity to its parent Instance failed.");
  1220. EntityIdList entityIds;
  1221. entityIds.emplace_back(containerEntity.GetId());
  1222. instancePtr->GetEntities(
  1223. [&](AZStd::unique_ptr<AZ::Entity>& entityPtr)
  1224. {
  1225. oldEntityAliases.emplace(entityPtr->GetId(), instancePtr->GetEntityAlias(entityPtr->GetId())->get());
  1226. return true;
  1227. });
  1228. instancePtr->DetachEntities(
  1229. [&](AZStd::unique_ptr<AZ::Entity> entityPtr)
  1230. {
  1231. auto& entity = *entityPtr.release();
  1232. [[maybe_unused]] const bool entityAdded = parentInstance.AddEntity(entity);
  1233. AZ_Assert(entityAdded, "Add target Instance's entity to its parent Instance failed.");
  1234. entityIds.emplace_back(entity.GetId());
  1235. });
  1236. Prefab::PrefabDom instanceDomAfter;
  1237. m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance);
  1238. PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment");
  1239. command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId);
  1240. command->SetParent(undoBatch.GetUndoBatch());
  1241. {
  1242. AZ_PROFILE_SCOPE(AzToolsFramework, "Internal::DetachPrefab:RunRedo");
  1243. command->Redo();
  1244. }
  1245. instancePtr->DetachNestedInstances(
  1246. [&](AZStd::unique_ptr<Instance> detachedNestedInstance)
  1247. {
  1248. PrefabDom& nestedInstanceTemplateDom =
  1249. m_prefabSystemComponentInterface->FindTemplateDom(detachedNestedInstance->GetTemplateId());
  1250. Instance& nestedInstanceUnderNewParent = parentInstance.AddInstance(AZStd::move(detachedNestedInstance));
  1251. PrefabDom nestedInstanceDomUnderNewParent;
  1252. m_instanceToTemplateInterface->GenerateDomForInstance(
  1253. nestedInstanceDomUnderNewParent, nestedInstanceUnderNewParent);
  1254. PrefabDom reparentPatch;
  1255. m_instanceToTemplateInterface->GeneratePatch(
  1256. reparentPatch, nestedInstanceTemplateDom, nestedInstanceDomUnderNewParent);
  1257. CreateLink(nestedInstanceUnderNewParent, parentTemplateId, undoBatch.GetUndoBatch(), AZStd::move(reparentPatch), true);
  1258. });
  1259. }
  1260. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  1261. &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
  1262. }
  1263. return AZ::Success();
  1264. }
  1265. void PrefabPublicHandler::GenerateContainerEntityTransform(const EntityList& topLevelEntities,
  1266. AZ::Vector3& translation, AZ::Quaternion& rotation)
  1267. {
  1268. // --- Multiple top level entities
  1269. // Translation is the average of all translations, with the minimum Z value.
  1270. // Rotation is set to zero.
  1271. if (topLevelEntities.size() > 1)
  1272. {
  1273. AZ::Vector3 translationSum = AZ::Vector3::CreateZero();
  1274. float minZ = AZStd::numeric_limits<float>::max();
  1275. int transformCount = 0;
  1276. for (AZ::Entity* topLevelEntity : topLevelEntities)
  1277. {
  1278. if (topLevelEntity != nullptr)
  1279. {
  1280. AzToolsFramework::Components::TransformComponent* transformComponent =
  1281. topLevelEntity->FindComponent<AzToolsFramework::Components::TransformComponent>();
  1282. if (transformComponent != nullptr)
  1283. {
  1284. ++transformCount;
  1285. auto currentTranslation = transformComponent->GetLocalTranslation();
  1286. translationSum += currentTranslation;
  1287. minZ = AZ::GetMin<float>(minZ, currentTranslation.GetZ());
  1288. }
  1289. }
  1290. }
  1291. if (transformCount > 0)
  1292. {
  1293. translation = translationSum / aznumeric_cast<float>(transformCount);
  1294. translation.SetZ(minZ);
  1295. rotation = AZ::Quaternion::CreateZero();
  1296. }
  1297. }
  1298. // --- Single top level entity
  1299. // World Translation and Rotation are inherited, unchanged.
  1300. else if (topLevelEntities.size() == 1)
  1301. {
  1302. AZ::Entity* topLevelEntity = topLevelEntities[0];
  1303. if (topLevelEntity)
  1304. {
  1305. AzToolsFramework::Components::TransformComponent* transformComponent =
  1306. topLevelEntity->FindComponent<AzToolsFramework::Components::TransformComponent>();
  1307. if (transformComponent)
  1308. {
  1309. translation = transformComponent->GetLocalTranslation();
  1310. rotation = transformComponent->GetLocalRotationQuaternion();
  1311. }
  1312. }
  1313. }
  1314. }
  1315. InstanceOptionalReference PrefabPublicHandler::GetOwnerInstanceByEntityId(AZ::EntityId entityId) const
  1316. {
  1317. if (entityId.IsValid())
  1318. {
  1319. return m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  1320. }
  1321. // If the entityId is invalid, then the owning instance would be the root prefab instance of the
  1322. // PrefabEditorEntityOwnershipService.
  1323. auto prefabEditorEntityOwnershipInterface = AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
  1324. if (!prefabEditorEntityOwnershipInterface)
  1325. {
  1326. AZ_Assert(false, "Could not get owning instance of common root entity :"
  1327. "PrefabEditorEntityOwnershipInterface unavailable.");
  1328. }
  1329. return prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
  1330. }
  1331. Instance* PrefabPublicHandler::GetParentInstance(Instance* instance)
  1332. {
  1333. auto instanceRef = instance->GetParentInstance();
  1334. if (instanceRef != AZStd::nullopt)
  1335. {
  1336. return &instanceRef->get();
  1337. }
  1338. return nullptr;
  1339. }
  1340. Instance* PrefabPublicHandler::GetAncestorOfInstanceThatIsChildOfRoot(const Instance* root, Instance* instance)
  1341. {
  1342. while (instance != nullptr)
  1343. {
  1344. Instance* parent = GetParentInstance(instance);
  1345. if (parent == root)
  1346. {
  1347. return instance;
  1348. }
  1349. instance = parent;
  1350. }
  1351. return nullptr;
  1352. }
  1353. PrefabOperationResult PrefabPublicHandler::RetrieveAndSortPrefabEntitiesAndInstances(
  1354. const EntityList& inputEntities,
  1355. Instance& commonRootEntityOwningInstance,
  1356. EntityList& outEntities,
  1357. AZStd::vector<Instance*>& outInstances) const
  1358. {
  1359. if (inputEntities.size() == 0)
  1360. {
  1361. return AZ::Failure(
  1362. AZStd::string("An empty list of input entities is provided to retrieve the prefab entities and instances."));
  1363. }
  1364. AZStd::queue<AZ::Entity*> entityQueue;
  1365. auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
  1366. EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
  1367. AZ::EntityId focusedPrefabContainerEntityId =
  1368. m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
  1369. for (auto inputEntity : inputEntities)
  1370. {
  1371. if (inputEntity && inputEntity->GetId() != focusedPrefabContainerEntityId)
  1372. {
  1373. entityQueue.push(inputEntity);
  1374. }
  1375. }
  1376. // Support sets to easily identify if we're processing the same entity multiple times.
  1377. AZStd::unordered_set<AZ::Entity*> entities;
  1378. AZStd::unordered_set<Instance*> instances;
  1379. while (!entityQueue.empty())
  1380. {
  1381. AZ::Entity* entity = entityQueue.front();
  1382. entityQueue.pop();
  1383. // Get this entity's owning instance.
  1384. InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity->GetId());
  1385. AZ_Assert(
  1386. owningInstance.has_value(),
  1387. "An error occurred while retrieving entities and prefab instances : "
  1388. "Owning instance of entity with name '%s' and id '%llu' couldn't be found",
  1389. entity->GetName().c_str(), static_cast<AZ::u64>(entity->GetId()));
  1390. // Check if this entity is owned by the same instance owning the root.
  1391. if (&owningInstance->get() == &commonRootEntityOwningInstance)
  1392. {
  1393. // If it's the same instance, we can add this entity to the new instance entities.
  1394. size_t priorEntitiesSize = entities.size();
  1395. entities.insert(entity);
  1396. // If the size of entities increased, then it wasn't added before.
  1397. // In that case, add the children of this entity to the queue.
  1398. if (entities.size() > priorEntitiesSize)
  1399. {
  1400. EntityIdList childrenIds;
  1401. EditorEntityInfoRequestBus::EventResult(
  1402. childrenIds,
  1403. entity->GetId(),
  1404. &EditorEntityInfoRequests::GetChildren
  1405. );
  1406. for (AZ::EntityId childId : childrenIds)
  1407. {
  1408. AZ::Entity* child = GetEntityById(childId);
  1409. entityQueue.push(child);
  1410. }
  1411. }
  1412. }
  1413. else
  1414. {
  1415. // The instances differ, so we should add the instance to the instances set,
  1416. // but only if it's a direct descendant of the root instance!
  1417. Instance* childInstance = GetAncestorOfInstanceThatIsChildOfRoot(&commonRootEntityOwningInstance, &owningInstance->get());
  1418. if (childInstance != nullptr)
  1419. {
  1420. instances.insert(childInstance);
  1421. }
  1422. else
  1423. {
  1424. // This can only happen if one entity does not share the common root!
  1425. return AZ::Failure(AZStd::string::format(
  1426. "Entity with name '%s' and id '%llu' has an owning instance that doesn't belong to the instance "
  1427. "hierarchy of the selected entities.",
  1428. entity->GetName().c_str(), static_cast<AZ::u64>(entity->GetId())));
  1429. }
  1430. }
  1431. }
  1432. // Store results
  1433. outEntities.clear();
  1434. outEntities.reserve(entities.size());
  1435. for (AZ::Entity* entity : entities)
  1436. {
  1437. outEntities.emplace_back(entity);
  1438. }
  1439. outInstances.clear();
  1440. outInstances.reserve(instances.size());
  1441. for (Instance* instancePtr : instances)
  1442. {
  1443. outInstances.push_back(instancePtr);
  1444. }
  1445. if ((outEntities.size() + outInstances.size()) == 0)
  1446. {
  1447. return AZ::Failure(
  1448. AZStd::string("An empty list of entities and prefab instances were retrieved from the selected entities"));
  1449. }
  1450. return AZ::Success();
  1451. }
  1452. EntityIdList PrefabPublicHandler::SanitizeEntityIdList(const EntityIdList& entityIds) const
  1453. {
  1454. EntityIdList outEntityIds;
  1455. if (auto readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get())
  1456. {
  1457. std::copy_if(
  1458. entityIds.begin(), entityIds.end(), AZStd::back_inserter(outEntityIds),
  1459. [readOnlyEntityPublicInterface](const AZ::EntityId& entityId)
  1460. {
  1461. return !readOnlyEntityPublicInterface->IsReadOnly(entityId);
  1462. }
  1463. );
  1464. }
  1465. else
  1466. {
  1467. outEntityIds = entityIds;
  1468. }
  1469. AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
  1470. AZ::EntityId focusedInstanceContainerEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
  1471. if (auto iter = AZStd::find(outEntityIds.begin(), outEntityIds.end(), focusedInstanceContainerEntityId); iter != outEntityIds.end())
  1472. {
  1473. outEntityIds.erase(iter);
  1474. }
  1475. return outEntityIds;
  1476. }
  1477. bool PrefabPublicHandler::EntitiesBelongToSameInstance(const EntityIdList& entityIds) const
  1478. {
  1479. if (entityIds.size() <= 1)
  1480. {
  1481. return true;
  1482. }
  1483. InstanceOptionalReference sharedInstance = AZStd::nullopt;
  1484. for (AZ::EntityId entityId : entityIds)
  1485. {
  1486. InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
  1487. if (!owningInstance.has_value())
  1488. {
  1489. AZ_Assert(
  1490. false,
  1491. "An error occurred in function EntitiesBelongToSameInstance: "
  1492. "Owning instance of entity with id '%llu' couldn't be found",
  1493. entityId);
  1494. return false;
  1495. }
  1496. // If this is a container entity, it actually represents a child instance so get its owner.
  1497. // The only exception in the level root instance. We leave it as is to streamline operations.
  1498. if (owningInstance->get().GetContainerEntityId() == entityId && !IsLevelInstanceContainerEntity(entityId))
  1499. {
  1500. owningInstance = owningInstance->get().GetParentInstance();
  1501. }
  1502. if (!sharedInstance.has_value())
  1503. {
  1504. sharedInstance = owningInstance;
  1505. }
  1506. else
  1507. {
  1508. if (&sharedInstance->get() != &owningInstance->get())
  1509. {
  1510. return false;
  1511. }
  1512. }
  1513. }
  1514. return true;
  1515. }
  1516. void PrefabPublicHandler::AddNewEntityToSortOrder(
  1517. Instance& owningInstance,
  1518. PrefabDom& domToAddEntityUnder,
  1519. const EntityAlias& parentEntityAlias,
  1520. const EntityAlias& entityToAddAlias)
  1521. {
  1522. // Find the parent entity to get its sort order component
  1523. auto findParentEntity = [&]() -> rapidjson::Value*
  1524. {
  1525. if (auto containerEntityIter = domToAddEntityUnder.FindMember(PrefabDomUtils::ContainerEntityName);
  1526. containerEntityIter != domToAddEntityUnder.MemberEnd())
  1527. {
  1528. if (parentEntityAlias == containerEntityIter->value[PrefabDomUtils::EntityIdName].GetString())
  1529. {
  1530. return &containerEntityIter->value;
  1531. }
  1532. }
  1533. if (auto entitiesIter = domToAddEntityUnder.FindMember(PrefabDomUtils::EntitiesName);
  1534. entitiesIter != domToAddEntityUnder.MemberEnd())
  1535. {
  1536. for (auto entityIter = entitiesIter->value.MemberBegin(); entityIter != entitiesIter->value.MemberEnd(); ++entityIter)
  1537. {
  1538. if (parentEntityAlias == entityIter->value[PrefabDomUtils::EntityIdName].GetString())
  1539. {
  1540. return &entityIter->value;
  1541. }
  1542. }
  1543. }
  1544. return nullptr;
  1545. };
  1546. rapidjson::Value* parentEntityValue = findParentEntity();
  1547. if (parentEntityValue == nullptr)
  1548. {
  1549. return;
  1550. }
  1551. // Get the list of selected entities, we'll insert our duplicated entities after the last selected
  1552. // sibling in their parent's list, e.g. for:
  1553. // - Entity1
  1554. // - Entity2 (selected)
  1555. // - Entity3
  1556. // - Entity4 (selected)
  1557. // - Entity5
  1558. // Our duplicate selection command would create duplicate Entity2 and Entity4 and insert them after Entity4:
  1559. // - Entity1
  1560. // - Entity2
  1561. // - Entity3
  1562. // - Entity4
  1563. // - Entity2 (new, selected after duplicate)
  1564. // - Entity4 (new, selected after duplicate)
  1565. // - Entity5
  1566. AzToolsFramework::EntityIdList selectedEntities;
  1567. AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
  1568. selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
  1569. // Find the EditorEntitySortComponent DOM
  1570. auto componentsIter = parentEntityValue->FindMember(PrefabDomUtils::ComponentsName);
  1571. if (componentsIter == parentEntityValue->MemberEnd())
  1572. {
  1573. return;
  1574. }
  1575. for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentIter->value.MemberEnd();
  1576. ++componentIter)
  1577. {
  1578. // Check the component type
  1579. auto typeFieldIter = componentIter->value.FindMember(PrefabDomUtils::TypeName);
  1580. if (typeFieldIter == componentIter->value.MemberEnd())
  1581. {
  1582. continue;
  1583. }
  1584. AZ::JsonDeserializerSettings jsonDeserializerSettings;
  1585. AZ::Uuid typeId = AZ::Uuid::CreateNull();
  1586. AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
  1587. if (typeId != azrtti_typeid<Components::EditorEntitySortComponent>())
  1588. {
  1589. continue;
  1590. }
  1591. // Check for the entity order field
  1592. auto orderMembersIter = componentIter->value.FindMember(PrefabDomUtils::EntityOrderName);
  1593. if (orderMembersIter == componentIter->value.MemberEnd() || !orderMembersIter->value.IsArray())
  1594. {
  1595. continue;
  1596. }
  1597. // Scan for the last selected entity in the list (if any) to determine where to add our entries
  1598. rapidjson::Value newOrder(rapidjson::kArrayType);
  1599. auto insertValuesAfter = orderMembersIter->value.End();
  1600. for (auto orderMemberIter = orderMembersIter->value.Begin(); orderMemberIter != orderMembersIter->value.End();
  1601. ++orderMemberIter)
  1602. {
  1603. if (!orderMemberIter->IsString())
  1604. {
  1605. continue;
  1606. }
  1607. const char* value = orderMemberIter->GetString();
  1608. for (AZ::EntityId selectedEntity : selectedEntities)
  1609. {
  1610. auto alias = owningInstance.GetEntityAlias(selectedEntity);
  1611. if (alias.has_value() && alias.value().get() == value)
  1612. {
  1613. insertValuesAfter = orderMemberIter;
  1614. break;
  1615. }
  1616. }
  1617. }
  1618. // Construct our new array with the new order - insertion may happen at end, so check for that in the loop itself
  1619. for (auto orderMemberIter = orderMembersIter->value.Begin();; ++orderMemberIter)
  1620. {
  1621. if (orderMemberIter != orderMembersIter->value.End())
  1622. {
  1623. newOrder.PushBack(orderMemberIter->Move(), domToAddEntityUnder.GetAllocator());
  1624. }
  1625. if (orderMemberIter == insertValuesAfter)
  1626. {
  1627. newOrder.PushBack(
  1628. rapidjson::Value(entityToAddAlias.c_str(), domToAddEntityUnder.GetAllocator()),
  1629. domToAddEntityUnder.GetAllocator());
  1630. }
  1631. if (orderMemberIter == orderMembersIter->value.End())
  1632. {
  1633. break;
  1634. }
  1635. }
  1636. // Replace the order with our newly constructed one
  1637. orderMembersIter->value.Swap(newOrder);
  1638. break;
  1639. }
  1640. }
  1641. void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
  1642. const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
  1643. EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap)
  1644. {
  1645. if (entities.empty())
  1646. {
  1647. return;
  1648. }
  1649. AZStd::unordered_map<EntityAlias, QString> aliasToEntityDomMap;
  1650. for (AZ::Entity* entity : entities)
  1651. {
  1652. EntityAliasOptionalReference oldAliasRef = commonOwningInstance.GetEntityAlias(entity->GetId());
  1653. AZ_Assert(oldAliasRef.has_value(), "No alias found for Entity in the DOM");
  1654. EntityAlias oldAlias = oldAliasRef.value();
  1655. // Give this the outer allocator so that the memory reference will be valid when
  1656. // it gets used for AddMember
  1657. PrefabDom entityDomBefore(&domToAddDuplicatedEntitiesUnder.GetAllocator());
  1658. m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *entity);
  1659. // Keep track of the old alias <-> new alias mapping for this duplicated entity
  1660. // so we can fixup references later
  1661. EntityAlias newEntityAlias = Instance::GenerateEntityAlias();
  1662. oldAliasToNewAliasMap.emplace(oldAlias, newEntityAlias);
  1663. rapidjson::StringBuffer buffer;
  1664. rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  1665. entityDomBefore.Accept(writer);
  1666. // Store our duplicated Entity DOM with its new alias as a string
  1667. // so that we can fixup entity alias references before adding it
  1668. // to the Entities member of our instance DOM
  1669. QString entityDomString(buffer.GetString());
  1670. aliasToEntityDomMap.emplace(newEntityAlias, entityDomString);
  1671. }
  1672. auto entitiesIter = domToAddDuplicatedEntitiesUnder.FindMember(PrefabDomUtils::EntitiesName);
  1673. AZ_Assert(entitiesIter != domToAddDuplicatedEntitiesUnder.MemberEnd(), "Instance DOM missing the Entities member.");
  1674. // Now that all the duplicated Entity DOMs have been created, we need to iterate
  1675. // through them and replace any previous EntityAlias references with the new ones.
  1676. // These are more than just parent entity references for nested entities, this will
  1677. // also cover any EntityId references that were made in the components between them.
  1678. for (auto [newEntityAlias, newEntityDomString] : aliasToEntityDomMap)
  1679. {
  1680. // Replace all of the old alias references with the new ones
  1681. for (auto [oldAlias, newAlias] : oldAliasToNewAliasMap)
  1682. {
  1683. ReplaceOldAliases(newEntityDomString, oldAlias, newAlias);
  1684. }
  1685. // Create the new Entity DOM from parsing the JSON string
  1686. PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator());
  1687. entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
  1688. EntityAlias parentEntityAlias;
  1689. if (auto componentsIter = entityDomAfter.FindMember(PrefabDomUtils::ComponentsName);
  1690. componentsIter != entityDomAfter.MemberEnd())
  1691. {
  1692. auto checkComponent = [&](const rapidjson::Value& value) -> bool
  1693. {
  1694. if (!value.IsObject())
  1695. {
  1696. return false;
  1697. }
  1698. // Check the component type
  1699. auto typeFieldIter = value.FindMember(PrefabDomUtils::TypeName);
  1700. if (typeFieldIter == value.MemberEnd())
  1701. {
  1702. return false;
  1703. }
  1704. AZ::JsonDeserializerSettings jsonDeserializerSettings;
  1705. AZ::Uuid typeId = AZ::Uuid::CreateNull();
  1706. AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
  1707. // Prefabs get serialized with the Editor transform component type, check for that
  1708. if (typeId != azrtti_typeid<Components::TransformComponent>())
  1709. {
  1710. return false;
  1711. }
  1712. if (auto parentEntityIter = value.FindMember("Parent Entity");
  1713. parentEntityIter != value.MemberEnd())
  1714. {
  1715. parentEntityAlias = parentEntityIter->value.GetString();
  1716. return true;
  1717. }
  1718. return false;
  1719. };
  1720. if (componentsIter->value.IsObject())
  1721. {
  1722. for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentsIter->value.MemberEnd();
  1723. ++componentIter)
  1724. {
  1725. if (checkComponent(componentIter->value))
  1726. {
  1727. break;
  1728. }
  1729. }
  1730. }
  1731. else if (componentsIter->value.IsArray())
  1732. {
  1733. for (auto componentIter = componentsIter->value.Begin(); componentIter != componentsIter->value.End();
  1734. ++componentIter)
  1735. {
  1736. if (checkComponent(*componentIter))
  1737. {
  1738. break;
  1739. }
  1740. }
  1741. }
  1742. }
  1743. // Insert our entity into its parent's sort order
  1744. if (!parentEntityAlias.empty())
  1745. {
  1746. AddNewEntityToSortOrder(commonOwningInstance, domToAddDuplicatedEntitiesUnder, parentEntityAlias, newEntityAlias);
  1747. }
  1748. // Add the new Entity DOM to the Entities member of the instance
  1749. rapidjson::Value aliasName(newEntityAlias.c_str(), static_cast<rapidjson::SizeType>(newEntityAlias.length()), domToAddDuplicatedEntitiesUnder.GetAllocator());
  1750. entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator());
  1751. }
  1752. for (auto aliasMapIter : oldAliasToNewAliasMap)
  1753. {
  1754. EntityAlias newEntityAlias = aliasMapIter.second;
  1755. AliasPath absoluteEntityPath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
  1756. absoluteEntityPath.Append(newEntityAlias);
  1757. AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteEntityPath);
  1758. duplicatedEntityIds.push_back(newEntityId);
  1759. }
  1760. }
  1761. void PrefabPublicHandler::DuplicateNestedInstancesInInstance(Instance& commonOwningInstance,
  1762. const AZStd::vector<Instance*>& instances, PrefabDom& domToAddDuplicatedInstancesUnder,
  1763. EntityIdList& duplicatedEntityIds, AZStd::unordered_map<InstanceAlias, Instance*>& newInstanceAliasToOldInstanceMap)
  1764. {
  1765. if (instances.empty())
  1766. {
  1767. return;
  1768. }
  1769. AZStd::unordered_map<InstanceAlias, InstanceAlias> oldInstanceAliasToNewInstanceAliasMap;
  1770. AZStd::unordered_map<InstanceAlias, QString> aliasToInstanceDomMap;
  1771. for (auto instance : instances)
  1772. {
  1773. PrefabDom nestedInstanceDomBefore;
  1774. m_instanceToTemplateInterface->GenerateDomForInstance(nestedInstanceDomBefore, *instance);
  1775. // Keep track of the old alias <-> new alias mapping for this duplicated instance
  1776. // so we can fixup references later
  1777. InstanceAlias oldAlias = instance->GetInstanceAlias();
  1778. InstanceAlias newInstanceAlias = Instance::GenerateInstanceAlias();
  1779. oldInstanceAliasToNewInstanceAliasMap.emplace(oldAlias, newInstanceAlias);
  1780. // Keep track of our new instance alias with the Instance it was duplicated from,
  1781. // so that after all instances are duplicated, we can go back and create links for them
  1782. newInstanceAliasToOldInstanceMap.emplace(newInstanceAlias, instance);
  1783. rapidjson::StringBuffer buffer;
  1784. rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  1785. nestedInstanceDomBefore.Accept(writer);
  1786. // Store our duplicated Instance DOM with its new alias as a string
  1787. // so that we can fixup instance alias references before adding it
  1788. // to the Instances member of our instance DOM
  1789. QString instanceDomString(buffer.GetString());
  1790. aliasToInstanceDomMap.emplace(newInstanceAlias, instanceDomString);
  1791. }
  1792. auto instancesIter = domToAddDuplicatedInstancesUnder.FindMember(PrefabDomUtils::InstancesName);
  1793. AZ_Assert(instancesIter != domToAddDuplicatedInstancesUnder.MemberEnd(), "Instance DOM missing the Instances member.");
  1794. // Now that all the duplicated Instance DOMs have been created, we need to iterate
  1795. // through them and replace any previous InstanceAlias references with the new ones.
  1796. for (auto [newInstanceAlias, newInstanceDomString]: aliasToInstanceDomMap)
  1797. {
  1798. // Replace all of the old alias references with the new ones
  1799. for (auto [oldAlias, newAlias] : oldInstanceAliasToNewInstanceAliasMap)
  1800. {
  1801. ReplaceOldAliases(newInstanceDomString, oldAlias, newAlias);
  1802. }
  1803. // Create the new Instance DOM from parsing the JSON string
  1804. PrefabDom nestedInstanceDomAfter(&domToAddDuplicatedInstancesUnder.GetAllocator());
  1805. nestedInstanceDomAfter.Parse(newInstanceDomString.toUtf8().constData());
  1806. // Add the new Instance DOM to the Instances member of the instance
  1807. rapidjson::Value aliasName(newInstanceAlias.c_str(), static_cast<rapidjson::SizeType>(newInstanceAlias.length()), domToAddDuplicatedInstancesUnder.GetAllocator());
  1808. instancesIter->value.AddMember(AZStd::move(aliasName), nestedInstanceDomAfter, domToAddDuplicatedInstancesUnder.GetAllocator());
  1809. }
  1810. for (auto aliasMapIter : oldInstanceAliasToNewInstanceAliasMap)
  1811. {
  1812. InstanceAlias newInstanceAlias = aliasMapIter.second;
  1813. AliasPath absoluteInstancePath = commonOwningInstance.GetAbsoluteInstanceAliasPath();
  1814. absoluteInstancePath.Append(newInstanceAlias);
  1815. absoluteInstancePath.Append(PrefabDomUtils::ContainerEntityName);
  1816. AZ::EntityId newEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteInstancePath);
  1817. duplicatedEntityIds.push_back(newEntityId);
  1818. }
  1819. }
  1820. void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias)
  1821. {
  1822. // Replace all of the old alias references with the new ones
  1823. // We bookend the aliases with \" and also with a / as an extra precaution to prevent
  1824. // inadvertently replacing a matching string vs. where an actual EntityId is expected
  1825. // This will cover both cases where an alias could be used in a normal entity vs. an instance
  1826. QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data());
  1827. QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data());
  1828. stringToReplace.replace(oldAliasQuotes, newAliasQuotes);
  1829. QString oldAliasPathRef = QString("/%1").arg(oldAlias.data());
  1830. QString newAliasPathRef = QString("/%1").arg(newAlias.data());
  1831. stringToReplace.replace(oldAliasPathRef, newAliasPathRef);
  1832. }
  1833. void PrefabPublicHandler::UpdateLinkPatchesWithNewEntityAliases(
  1834. PrefabDom& linkPatch,
  1835. const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
  1836. Instance& newParent)
  1837. {
  1838. rapidjson::StringBuffer buffer;
  1839. rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  1840. linkPatch.Accept(writer);
  1841. QString previousPatchString(buffer.GetString());
  1842. for (const auto& [entityId, oldEntityAlias] : oldEntityAliases)
  1843. {
  1844. EntityAliasOptionalReference newEntityAlias = newParent.GetEntityAlias(entityId);
  1845. AZ_Assert(
  1846. newEntityAlias.has_value(),
  1847. "Could not fetch entity alias for entity with id '%llu' during prefab creation.",
  1848. static_cast<AZ::u64>(entityId));
  1849. ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
  1850. }
  1851. linkPatch.Parse(previousPatchString.toUtf8().constData());
  1852. }
  1853. } // namespace Prefab
  1854. } // namespace AzToolsFramework