Merge branch 'main' into cpack_installer
This commit is contained in:
@@ -613,6 +613,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<AZ::Entity> Instance::DetachContainerEntity()
|
||||
{
|
||||
m_instanceEntityMapper->UnregisterEntity(m_containerEntity->GetId());
|
||||
return AZStd::move(m_containerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace AzToolsFramework
|
||||
using EntityAliasOptionalReference = AZStd::optional<AZStd::reference_wrapper<EntityAlias>>;
|
||||
using InstanceOptionalReference = AZStd::optional<AZStd::reference_wrapper<Instance>>;
|
||||
using InstanceOptionalConstReference = AZStd::optional<AZStd::reference_wrapper<const Instance>>;
|
||||
|
||||
using InstanceSet = AZStd::unordered_set<Instance*>;
|
||||
using InstanceSetConstReference = AZStd::optional<AZStd::reference_wrapper<const InstanceSet>>;
|
||||
using EntityOptionalReference = AZStd::optional<AZStd::reference_wrapper<AZ::Entity>>;
|
||||
@@ -85,6 +86,7 @@ namespace AzToolsFramework
|
||||
bool AddEntity(AZ::Entity& entity);
|
||||
bool AddEntity(AZ::Entity& entity, EntityAlias entityAlias);
|
||||
AZStd::unique_ptr<AZ::Entity> DetachEntity(const AZ::EntityId& entityId);
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void DetachNestedEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveNestedEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
@@ -182,7 +184,6 @@ namespace AzToolsFramework
|
||||
|
||||
void ClearEntities();
|
||||
|
||||
void DetachEntities(const AZStd::function<void(AZStd::unique_ptr<AZ::Entity>)>& callback);
|
||||
void RemoveEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
bool RegisterEntity(const AZ::EntityId& entityId, const EntityAlias& entityAlias);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
@@ -191,24 +192,7 @@ namespace AzToolsFramework
|
||||
if (nestedInstanceLinkPatchesMap.contains(nestedInstance.get()))
|
||||
{
|
||||
previousPatch = AZStd::move(nestedInstanceLinkPatchesMap[nestedInstance.get()]);
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> 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<AZ::u64>(entityId));
|
||||
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
|
||||
}
|
||||
|
||||
previousPatch.Parse(previousPatchString.toUtf8().constData());
|
||||
UpdateLinkPatchesWithNewEntityAliases(previousPatch, oldEntityAliases, instanceToCreate->get());
|
||||
}
|
||||
|
||||
// These link creations shouldn't be undone because that would put the template in a non-usable state if a user
|
||||
@@ -407,8 +391,8 @@ namespace AzToolsFramework
|
||||
|
||||
CreateLink(instanceToCreate->get(), instanceToParentUnder->get().GetTemplateId(), undoBatch.GetUndoBatch(), AZStd::move(patch));
|
||||
|
||||
// Update the cache - this prevents these changes from being stored in the regular undo/redo nodes
|
||||
m_prefabUndoCache.Store(containerEntityId, AZStd::move(containerEntityDomAfter));
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
@@ -1011,6 +995,123 @@ namespace AzToolsFramework
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
PrefabOperationResult PrefabPublicHandler::DetachPrefab(const AZ::EntityId& containerEntityId)
|
||||
{
|
||||
if (!containerEntityId.IsValid())
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach Prefab Instance with invalid container entity."));
|
||||
}
|
||||
|
||||
if (IsLevelInstanceContainerEntity(containerEntityId))
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Cannot detach level Prefab Instance."));
|
||||
}
|
||||
|
||||
InstanceOptionalReference owningInstance = GetOwnerInstanceByEntityId(containerEntityId);
|
||||
if (owningInstance->get().GetContainerEntityId() != containerEntityId)
|
||||
{
|
||||
return AZ::Failure(AZStd::string("Input entity should be its owning Instance's container entity."));
|
||||
}
|
||||
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DetachPrefab:UndoCapture");
|
||||
|
||||
ScopedUndoBatch undoBatch("Detach Prefab");
|
||||
|
||||
InstanceOptionalReference getParentInstanceResult = owningInstance->get().GetParentInstance();
|
||||
AZ_Assert(getParentInstanceResult.has_value(), "Can't get parent Instance from Instance of given container entity.");
|
||||
|
||||
auto& parentInstance = getParentInstanceResult->get();
|
||||
const auto parentTemplateId = parentInstance.GetTemplateId();
|
||||
|
||||
{
|
||||
auto instancePtr = parentInstance.DetachNestedInstance(owningInstance->get().GetInstanceAlias());
|
||||
AZ_Assert(instancePtr, "Can't detach selected Instance from its parent Instance.");
|
||||
|
||||
RemoveLink(instancePtr, parentTemplateId, undoBatch.GetUndoBatch());
|
||||
|
||||
Prefab::PrefabDom instanceDomBefore;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBefore, parentInstance);
|
||||
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::string> oldEntityAliases;
|
||||
oldEntityAliases.emplace(containerEntityId, instancePtr->GetEntityAlias(containerEntityId)->get());
|
||||
|
||||
auto containerEntityPtr = instancePtr->DetachContainerEntity();
|
||||
auto& containerEntity = *containerEntityPtr.release();
|
||||
auto editorPrefabComponent = containerEntity.FindComponent<EditorPrefabComponent>();
|
||||
containerEntity.Deactivate();
|
||||
const bool editorPrefabComponentRemoved = containerEntity.RemoveComponent(editorPrefabComponent);
|
||||
AZ_Assert(editorPrefabComponentRemoved, "Remove EditorPrefabComponent failed.");
|
||||
delete editorPrefabComponent;
|
||||
containerEntity.Activate();
|
||||
|
||||
const bool containerEntityAdded = parentInstance.AddEntity(containerEntity);
|
||||
AZ_Assert(containerEntityAdded, "Add target Instance's container entity to its parent Instance failed.");
|
||||
|
||||
EntityIdList entityIds;
|
||||
entityIds.emplace_back(containerEntity.GetId());
|
||||
|
||||
instancePtr->GetEntities(
|
||||
[&](AZStd::unique_ptr<AZ::Entity>& entityPtr)
|
||||
{
|
||||
oldEntityAliases.emplace(entityPtr->GetId(), instancePtr->GetEntityAlias(entityPtr->GetId())->get());
|
||||
return true;
|
||||
});
|
||||
|
||||
instancePtr->DetachEntities(
|
||||
[&](AZStd::unique_ptr<AZ::Entity> entityPtr)
|
||||
{
|
||||
auto& entity = *entityPtr.release();
|
||||
const bool entityAdded = parentInstance.AddEntity(entity);
|
||||
AZ_Assert(entityAdded, "Add target Instance's entity to its parent Instance failed.");
|
||||
|
||||
entityIds.emplace_back(entity.GetId());
|
||||
});
|
||||
|
||||
Prefab::PrefabDom instanceDomAfter;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfter, parentInstance);
|
||||
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Instance detachment");
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, parentTemplateId);
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
{
|
||||
AZ_PROFILE_SCOPE(AZ::Debug::ProfileCategory::AzToolsFramework, "Internal::DetachPrefab:RunRedo");
|
||||
command->RunRedo();
|
||||
}
|
||||
|
||||
const auto instanceTemplateId = instancePtr->GetTemplateId();
|
||||
auto parentContainerEntityId = parentInstance.GetContainerEntityId();
|
||||
instancePtr->GetNestedInstances(
|
||||
[&](AZStd::unique_ptr<Instance>& nestedInstancePtr)
|
||||
{
|
||||
//get previous link patch
|
||||
auto linkRef = m_prefabSystemComponentInterface->FindLink(nestedInstancePtr->GetLinkId());
|
||||
PrefabDomValueReference linkPatches = linkRef->get().GetLinkPatches();
|
||||
AZ_Assert(
|
||||
linkPatches.has_value(), "Unable to get patches on link with id '%llu' during prefab creation.",
|
||||
nestedInstancePtr->GetLinkId());
|
||||
|
||||
PrefabDom linkPatchesCopy;
|
||||
linkPatchesCopy.CopyFrom(linkPatches->get(), linkPatchesCopy.GetAllocator());
|
||||
|
||||
RemoveLink(nestedInstancePtr, instanceTemplateId, undoBatch.GetUndoBatch());
|
||||
|
||||
UpdateLinkPatchesWithNewEntityAliases(linkPatchesCopy, oldEntityAliases, parentInstance);
|
||||
|
||||
CreateLink(*nestedInstancePtr, parentTemplateId, undoBatch.GetUndoBatch(),
|
||||
AZStd::move(linkPatchesCopy), true);
|
||||
});
|
||||
}
|
||||
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::ClearDirtyEntities);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::GenerateContainerEntityTransform(const EntityList& topLevelEntities,
|
||||
AZ::Vector3& translation, AZ::Quaternion& rotation)
|
||||
{
|
||||
@@ -1274,5 +1375,30 @@ namespace AzToolsFramework
|
||||
|
||||
stringToReplace.replace(oldAliasPathRef, newAliasPathRef);
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
Instance& newParent)
|
||||
{
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
linkPatch.Accept(writer);
|
||||
QString previousPatchString(buffer.GetString());
|
||||
|
||||
for (const auto& [entityId, oldEntityAlias] : oldEntityAliases)
|
||||
{
|
||||
EntityAliasOptionalReference newEntityAlias = newParent.GetEntityAlias(entityId);
|
||||
AZ_Assert(
|
||||
newEntityAlias.has_value(),
|
||||
"Could not fetch entity alias for entity with id '%llu' during prefab creation.",
|
||||
static_cast<AZ::u64>(entityId));
|
||||
|
||||
ReplaceOldAliases(previousPatchString, oldEntityAlias, newEntityAlias->get());
|
||||
}
|
||||
|
||||
linkPatch.Parse(previousPatchString.toUtf8().constData());
|
||||
}
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -64,6 +64,8 @@ namespace AzToolsFramework
|
||||
PrefabOperationResult DeleteEntitiesAndAllDescendantsInInstance(const EntityIdList& entityIds) override;
|
||||
PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) override;
|
||||
|
||||
PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) override;
|
||||
|
||||
private:
|
||||
PrefabOperationResult DeleteFromInstance(const EntityIdList& entityIds, bool deleteDescendants);
|
||||
bool RetrieveAndSortPrefabEntitiesAndInstances(const EntityList& inputEntities, Instance& commonRootEntityOwningInstance,
|
||||
@@ -132,7 +134,12 @@ namespace AzToolsFramework
|
||||
bool IsCyclicalDependencyFound(
|
||||
InstanceOptionalConstReference instance, const AZStd::unordered_set<AZ::IO::Path>& templateSourcePaths);
|
||||
|
||||
void ReplaceOldAliases(QString& stringToReplace, AZStd::string_view oldAlias, AZStd::string_view newAlias);
|
||||
void UpdateLinkPatchesWithNewEntityAliases(
|
||||
PrefabDom& linkPatch,
|
||||
const AZStd::unordered_map<AZ::EntityId, AZStd::string>& oldEntityAliases,
|
||||
Instance& newParent);
|
||||
|
||||
static 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);
|
||||
|
||||
@@ -150,6 +150,17 @@ namespace AzToolsFramework
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DuplicateEntitiesInInstance(const EntityIdList& entityIds) = 0;
|
||||
|
||||
/**
|
||||
* If the entity id is a container entity id, detaches the prefab instance corresponding to it. This includes converting
|
||||
* the container entity into a regular entity and putting it under the parent prefab, removing the link between this
|
||||
* instance and the parent, removing links between this instance and it's nested instances, adding entities directly
|
||||
* owned by this instance under the parent instance.
|
||||
* Bails if the entity is not a container entity or belongs to the level prefab instance.
|
||||
* @param containerEntityId The container entity id of the instance to detach.
|
||||
* @return An outcome object; on failure, it comes with an error message detailing the cause of the error.
|
||||
*/
|
||||
virtual PrefabOperationResult DetachPrefab(const AZ::EntityId& containerEntityId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
|
||||
@@ -652,7 +652,8 @@ namespace AzToolsFramework
|
||||
if (instancesValue->get().FindMember(rapidjson::StringRef(instanceAlias.c_str())) == instancesValue->get().MemberEnd())
|
||||
{
|
||||
instancesValue->get().AddMember(
|
||||
rapidjson::StringRef(instanceAlias.c_str()), PrefabDomValue(), targetTemplateDom.GetAllocator());
|
||||
rapidjson::Value(instanceAlias.c_str(), targetTemplateDom.GetAllocator()), PrefabDomValue(),
|
||||
targetTemplateDom.GetAllocator());
|
||||
}
|
||||
|
||||
Template& sourceTemplate = sourceTemplateRef->get();
|
||||
|
||||
+29
@@ -237,6 +237,24 @@ namespace AzToolsFramework
|
||||
{
|
||||
deleteAction->setDisabled(true);
|
||||
}
|
||||
|
||||
// Detach Prefab
|
||||
if (selectedEntities.size() == 1)
|
||||
{
|
||||
AZ::EntityId selectedEntity = selectedEntities[0];
|
||||
|
||||
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity) &&
|
||||
!s_prefabPublicInterface->IsLevelInstanceContainerEntity(selectedEntity))
|
||||
{
|
||||
QAction* detachPrefabAction = menu->addAction(QObject::tr("Detach Prefab..."));
|
||||
QObject::connect(
|
||||
detachPrefabAction, &QAction::triggered, detachPrefabAction,
|
||||
[this, selectedEntity]
|
||||
{
|
||||
ContextMenu_DetachPrefab(selectedEntity);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
|
||||
@@ -392,6 +410,17 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::ContextMenu_DetachPrefab(AZ::EntityId containerEntity)
|
||||
{
|
||||
PrefabOperationResult detachPrefabResult =
|
||||
s_prefabPublicInterface->DetachPrefab(containerEntity);
|
||||
|
||||
if (!detachPrefabResult.IsSuccess())
|
||||
{
|
||||
WarnUserOfError("Detach Prefab error", detachPrefabResult.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabIntegrationManager::GenerateSuggestedFilenameFromEntities(const EntityIdList& entityIds, AZStd::string& outName)
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework);
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace AzToolsFramework
|
||||
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
|
||||
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
|
||||
static void ContextMenu_DeleteSelected();
|
||||
static void ContextMenu_DetachPrefab(AZ::EntityId containerEntity);
|
||||
|
||||
// Prompt and resolve dialogs
|
||||
static bool QueryUserForPrefabSaveLocation(
|
||||
|
||||
@@ -15,7 +15,11 @@ ly_set(LY_DEFAULT_INSTALL_COMPONENT Core)
|
||||
|
||||
file(RELATIVE_PATH runtime_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
file(RELATIVE_PATH library_output_directory ${CMAKE_BINARY_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
|
||||
set(install_output_folder "${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>")
|
||||
# Anywhere CMAKE_INSTALL_PREFIX is used, it has to be escaped so it is baked into the cmake_install.cmake script instead
|
||||
# of baking the path. This is needed so `cmake --install --prefix <someprefix>` works regardless of the CMAKE_INSTALL_PREFIX
|
||||
# used to generate the solution.
|
||||
# CMAKE_INSTALL_PREFIX is still used when building the INSTALL target
|
||||
set(install_output_folder "\${CMAKE_INSTALL_PREFIX}/${runtime_output_directory}/${PAL_PLATFORM_NAME}/$<CONFIG>")
|
||||
|
||||
|
||||
#! ly_setup_target: Setup the data needed to re-create the cmake target commands for a single target
|
||||
|
||||
Reference in New Issue
Block a user