From be9c9d9fa3f6b4d07a68f96e586c05e4a1f4f204 Mon Sep 17 00:00:00 2001 From: srikappa Date: Tue, 25 May 2021 12:57:16 -0700 Subject: [PATCH 1/3] Remove prefab undo cache dependency on CreatePrefab use case --- .../API/ToolsApplicationAPI.h | 5 + .../Application/ToolsApplication.cpp | 5 + .../Application/ToolsApplication.h | 1 + .../Instance/InstanceToTemplatePropagator.cpp | 20 ++-- .../AzToolsFramework/Prefab/Link/Link.cpp | 5 + .../AzToolsFramework/Prefab/Link/Link.h | 2 + .../Prefab/PrefabPublicHandler.cpp | 97 +++++++++++-------- .../Prefab/PrefabPublicHandler.h | 5 + 8 files changed, 87 insertions(+), 53 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h index 82fa3f94f5..72150b1b57 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/ToolsApplicationAPI.h @@ -239,6 +239,11 @@ namespace AzToolsFramework */ virtual int RemoveDirtyEntity(AZ::EntityId target) = 0; + /*! + * Clears the dirty entity set. + */ + virtual void ClearDirtyEntities() = 0; + /*! * \return true if an undo/redo operation is in progress. */ diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp index e77704c920..88057787bb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.cpp @@ -1354,6 +1354,11 @@ namespace AzToolsFramework return static_cast(m_dirtyEntities.erase(entityId)); } + void ToolsApplication::ClearDirtyEntities() + { + m_dirtyEntities.clear(); + } + void ToolsApplication::UndoPressed() { if (m_undoStack) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h index 6c836ac888..bafced67bd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Application/ToolsApplication.h @@ -85,6 +85,7 @@ namespace AzToolsFramework void AddDirtyEntity(AZ::EntityId entityId) override; int RemoveDirtyEntity(AZ::EntityId entityId) override; + void ClearDirtyEntities() override; bool IsDuringUndoRedo() override { return m_isDuringUndoRedo; } void UndoPressed() override; void RedoPressed() override; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp index a21c5301aa..6d3ddedd51 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp @@ -276,18 +276,14 @@ namespace AzToolsFramework PrefabDomValueReference linkPatchesReference = PrefabDomUtils::FindPrefabDomValue(linkDom, PrefabDomUtils::PatchesName); - // This logic only covers addition of patches. If patches already exists, the given list of patches must be appended to them. - if (!linkPatchesReference.has_value()) - { - /* - If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the - linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to - associate them with the linkDom's allocator. - */ - PrefabDom patchesCopy; - patchesCopy.CopyFrom(patches, linkDom.GetAllocator()); - linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator()); - } + /* + If the original allocator the patches were created with gets destroyed, then the patches would become garbage in the + linkDom. Since we cannot guarantee the lifecycle of the patch allocators, we are doing a copy of the patches here to + associate them with the linkDom's allocator. + */ + PrefabDom patchesCopy; + patchesCopy.CopyFrom(patches, linkDom.GetAllocator()); + linkDom.AddMember(rapidjson::StringRef(PrefabDomUtils::PatchesName), patchesCopy, linkDom.GetAllocator()); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp index 308749ab28..01a954ebdd 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp @@ -234,5 +234,10 @@ namespace AzToolsFramework } } + PrefabDomValueConstReference Link::GetLinkPatches() + { + return PrefabDomUtils::FindPrefabDomValue(m_linkDom, PrefabDomUtils::PatchesName); + } + } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h index 073e619f20..7d30f9235d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h @@ -79,6 +79,8 @@ namespace AzToolsFramework */ void AddLinkIdToInstanceDom(PrefabDomValue& instanceDomValue); + PrefabDomValueConstReference GetLinkPatches(); + private: /** diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 579f465eb2..7284a9cd95 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -33,8 +33,6 @@ #include #include -#include - namespace AzToolsFramework { namespace Prefab @@ -98,9 +96,13 @@ namespace AzToolsFramework AZStd::string("Could not create a new prefab out of the entities provided - invalid selection.")); } + AZStd::unordered_map oldEntityAliases; + // Detach the retrieved entities for (AZ::Entity* entity : entities) { + AZ::EntityId entityId = entity->GetId(); + oldEntityAliases.emplace(entityId, commonRootEntityOwningInstance->get().GetEntityAlias(entityId)->get()); commonRootEntityOwningInstance->get().DetachEntity(entity->GetId()).release(); } @@ -110,15 +112,18 @@ namespace AzToolsFramework { AZStd::unique_ptr outInstance = commonRootEntityOwningInstance->get().DetachNestedInstance(nestedInstance->GetInstanceAlias()); - auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstance->GetLinkId()); + LinkId detachingInstanceLinkId = nestedInstance->GetLinkId(); + auto linkRef = m_prefabSystemComponentInterface->FindLink(detachingInstanceLinkId); + AZ_Assert(linkRef.has_value(), "Unable to find link with id '%llu' during prefab creation.", detachingInstanceLinkId); - if (linkRef.has_value()) - { - PrefabDom oldLinkPatches; - oldLinkPatches.CopyFrom(linkRef->get().GetLinkDom(), oldLinkPatches.GetAllocator()); + PrefabDomValueConstReference linkPatches = linkRef->get().GetLinkPatches(); + AZ_Assert( + linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.", + detachingInstanceLinkId); - nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(oldLinkPatches)); - } + PrefabDom linkPatchesCopy; + linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator()); + nestedInstanceLinkPatchesMap.emplace(nestedInstance, AZStd::move(linkPatchesCopy)); RemoveLink(outInstance, commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch()); @@ -182,6 +187,24 @@ namespace AzToolsFramework if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get())) { previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + previousPatch.Accept(writer); + QString previousPatchString(buffer.GetString()); + + for (AZ::Entity* entity : entities) + { + AZ::EntityId entityId = entity->GetId(); + AZStd::string oldEntityAlias = oldEntityAliases[entityId]; + EntityAliasOptionalReference newEntityAlias = instanceToCreate->get().GetEntityAlias(entityId); + AZ_Assert( + newEntityAlias.has_value(), + "Could not fetch entity alias for entity with id '%llu' during prefab creation.", + static_cast(entityId)); + ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get()); + } + + previousPatch.Parse(previousPatchString.toUtf8().constData()); } // These link creations shouldn't be undone because that would put the template in a non-usable state if a user @@ -203,36 +226,23 @@ namespace AzToolsFramework m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter); m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId); - // Update the cache - this prevents these changes from being stored in the regular undo/redo nodes as a separate step - m_prefabUndoCache.Store(nestedInstanceContainerEntityId, AZStd::move(containerEntityDomAfter)); - - // Save these changes as patches to the link - PrefabUndoLinkUpdate* linkUpdate = aznew PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); - linkUpdate->SetParent(undoBatch.GetUndoBatch()); - linkUpdate->Capture(reparentPatch, nestedInstance->GetLinkId()); - - linkUpdate->Redo(); + // We ar not parenting this undo node to the undo batch because we don't want the user to undo these changes + // so that the newly created template and link remain unaffected for supporting instantiating the template later. + PrefabUndoLinkUpdate linkUpdate = PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); + linkUpdate.Capture(reparentPatch, nestedInstance->GetLinkId()); + linkUpdate.Redo(); } }); - + // Create a link between the templates of the newly created instance and the instance it's being parented under. CreateLink( instanceToCreate->get(), commonRootEntityOwningInstance->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch)); - for (AZ::Entity* topLevelEntity : topLevelEntities) - { - AZ::EntityId topLevelEntityId = topLevelEntity->GetId(); - if (topLevelEntityId.IsValid()) - { - m_prefabUndoCache.UpdateCache(topLevelEntity->GetId()); - - // Parenting entities would mark entities as dirty. But we want to unmark the top level entities as dirty because - // if we don't, the template created would be updated and cause issues with undo operation followed by instantiation. - ToolsApplicationRequests::Bus::Broadcast( - &ToolsApplicationRequests::Bus::Events::RemoveDirtyEntity, topLevelEntity->GetId()); - } - } + // This clears any entities marked as dirty due to reparenting of entities during the process of creating a prefab. + // We are doing this so that the changes in those enities are not queued up twice for propagation. + AzToolsFramework::ToolsApplicationRequestBus::Broadcast( + &AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities); // Select Container Entity { @@ -824,15 +834,7 @@ namespace AzToolsFramework // This will cover both cases where an alias could be used in a normal entity vs. an instance for (auto aliasMapIter : oldAliasToNewAliasMap) { - QString oldAliasQuotes = QString("\"%1\"").arg(aliasMapIter.first.c_str()); - QString newAliasQuotes = QString("\"%1\"").arg(aliasMapIter.second.c_str()); - - newEntityDomString.replace(oldAliasQuotes, newAliasQuotes); - - QString oldAliasPathRef = QString("/%1").arg(aliasMapIter.first.c_str()); - QString newAliasPathRef = QString("/%1").arg(aliasMapIter.second.c_str()); - - newEntityDomString.replace(oldAliasPathRef, newAliasPathRef); + ReplaceOldAliases(newEntityDomString, aliasMapIter.first, aliasMapIter.second); } // Create the new Entity DOM from parsing the JSON string @@ -1233,5 +1235,18 @@ namespace AzToolsFramework return true; } + + void PrefabPublicHandler::ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias) + { + QString oldAliasQuotes = QString("\"%1\"").arg(oldAlias.data()); + QString newAliasQuotes = QString("\"%1\"").arg(newAlias.data()); + + stringToReplace.replace(oldAliasQuotes, newAliasQuotes); + + QString oldAliasPathRef = QString("/%1").arg(oldAlias.data()); + QString newAliasPathRef = QString("/%1").arg(newAlias.data()); + + stringToReplace.replace(oldAliasPathRef, newAliasPathRef); + } } // namespace Prefab } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 223a725c6c..a3e2632ea4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -14,12 +14,15 @@ #include #include +#include #include #include #include #include +#include + namespace AzToolsFramework { using EntityList = AZStd::vector; @@ -130,6 +133,8 @@ namespace AzToolsFramework bool IsCyclicalDependencyFound( InstanceOptionalConstReference instance, const AZStd::unordered_set& templateSourcePaths); + void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias); + static Instance* GetParentInstance(Instance* instance); static Instance* GetAncestorOfInstanceThatIsChildOfRoot(const Instance* ancestor, Instance* descendant); static void GenerateContainerEntityTransform(const EntityList& topLevelEntities, AZ::Vector3& translation, AZ::Quaternion& rotation); From 5a417466832bc875b50f7070dcae0644e7b15568 Mon Sep 17 00:00:00 2001 From: srikappa Date: Wed, 26 May 2021 10:46:00 -0700 Subject: [PATCH 2/3] Fixed a comment and added a forward declaration --- .../AzToolsFramework/Prefab/PrefabPublicHandler.cpp | 9 +++++---- .../AzToolsFramework/Prefab/PrefabPublicHandler.h | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 7284a9cd95..57e3c87501 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -10,8 +10,6 @@ * */ -#include - #include #include #include @@ -28,11 +26,14 @@ #include #include #include +#include #include #include #include #include +#include + namespace AzToolsFramework { namespace Prefab @@ -226,8 +227,8 @@ namespace AzToolsFramework m_instanceToTemplateInterface->GeneratePatch(reparentPatch, containerEntityDomBefore, containerEntityDomAfter); m_instanceToTemplateInterface->AppendEntityAliasToPatchPaths(reparentPatch, nestedInstanceContainerEntityId); - // We ar not parenting this undo node to the undo batch because we don't want the user to undo these changes - // so that the newly created template and link remain unaffected for supporting instantiating the template later. + // We won't parent this undo node to the undo batch so that the newly created template and link will remain + // unaffected by undo actions. This is needed so that any future instantiations of the template will work. PrefabUndoLinkUpdate linkUpdate = PrefabUndoLinkUpdate(AZStd::to_string(static_cast(nestedInstanceContainerEntityId))); linkUpdate.Capture(reparentPatch, nestedInstance->GetLinkId()); linkUpdate.Redo(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index a3e2632ea4..7e2357dd44 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -21,7 +21,7 @@ #include #include -#include +class QString; namespace AzToolsFramework { @@ -30,7 +30,6 @@ namespace AzToolsFramework namespace Prefab { class Instance; - class InstanceEntityMapperInterface; class InstanceToTemplateInterface; class PrefabLoaderInterface; From 0ffaa5429b9f7d608fdb34da8ecf5b6f3e670d45 Mon Sep 17 00:00:00 2001 From: srikappa Date: Wed, 26 May 2021 11:31:16 -0700 Subject: [PATCH 3/3] Fixed a linux build error where an implicit conversion to const ref is not supported --- .../AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp | 2 +- .../AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h | 2 +- .../AzToolsFramework/Prefab/PrefabPublicHandler.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp index 01a954ebdd..2fb22ea8e8 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp @@ -234,7 +234,7 @@ namespace AzToolsFramework } } - PrefabDomValueConstReference Link::GetLinkPatches() + PrefabDomValueReference Link::GetLinkPatches() { return PrefabDomUtils::FindPrefabDomValue(m_linkDom, PrefabDomUtils::PatchesName); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h index 7d30f9235d..c8f43b291e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.h @@ -79,7 +79,7 @@ namespace AzToolsFramework */ void AddLinkIdToInstanceDom(PrefabDomValue& instanceDomValue); - PrefabDomValueConstReference GetLinkPatches(); + PrefabDomValueReference GetLinkPatches(); private: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 57e3c87501..0181050a32 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -117,7 +117,7 @@ namespace AzToolsFramework auto linkRef = m_prefabSystemComponentInterface->FindLink(detachingInstanceLinkId); AZ_Assert(linkRef.has_value(), "Unable to find link with id '%llu' during prefab creation.", detachingInstanceLinkId); - PrefabDomValueConstReference linkPatches = linkRef->get().GetLinkPatches(); + PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches(); AZ_Assert( linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.", detachingInstanceLinkId);