Merge commit '4ee2f341dc0dc709aedb446b57d5cca61b86160d' into junbo/gitflow_211123_o3de
This commit is contained in:
+3
-2
@@ -20,6 +20,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicRequestBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -244,10 +245,10 @@ namespace AzToolsFramework
|
||||
selectedEntityIds.erase(entityIdIterator--);
|
||||
}
|
||||
}
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
|
||||
|
||||
// Notify Propagation has ended
|
||||
// Notify Propagation has ended, then update selection (which is frozen during propagation, so this order matters)
|
||||
PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotifications::OnPrefabInstancePropagationEnd);
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetSelectedEntities, selectedEntityIds);
|
||||
}
|
||||
|
||||
m_updatingTemplateInstancesInQueue = false;
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace AzToolsFramework
|
||||
inline static const char* EntityIdName = "Id";
|
||||
inline static const char* EntitiesName = "Entities";
|
||||
inline static const char* ContainerEntityName = "ContainerEntity";
|
||||
inline static const char* ComponentsName = "Components";
|
||||
inline static const char* EntityOrderName = "Child Entity Order";
|
||||
inline static const char* TypeName = "$type";
|
||||
|
||||
/**
|
||||
* Find Prefab value from given parent value and target value's name.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/JSON/stringbuffer.h>
|
||||
#include <AzCore/JSON/writer.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Utils/TypeHash.h>
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <AzToolsFramework/Prefab/PrefabUndo.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabUndoHelpers.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
@@ -595,8 +597,41 @@ namespace AzToolsFramework
|
||||
|
||||
Instance& entityOwningInstance = owningInstanceOfParentEntity->get();
|
||||
|
||||
// Get the template for our owning instance from the root prefab DOM and use that to generate our patch
|
||||
AZStd::vector<InstanceOptionalConstReference> pathOfInstances;
|
||||
|
||||
InstanceOptionalReference rootInstance = owningInstanceOfParentEntity;
|
||||
while (rootInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
pathOfInstances.emplace_back(rootInstance);
|
||||
rootInstance = rootInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
AZStd::string aliasPathResult = "";
|
||||
for (auto instanceIter = pathOfInstances.rbegin(); instanceIter != pathOfInstances.rend(); ++instanceIter)
|
||||
{
|
||||
aliasPathResult.append("/Instances/");
|
||||
aliasPathResult.append((*instanceIter)->get().GetInstanceAlias());
|
||||
}
|
||||
|
||||
PrefabDomPath rootPrefabDomPath(aliasPathResult.c_str());
|
||||
|
||||
PrefabDom& rootPrefabTemplateDom = m_prefabSystemComponentInterface->FindTemplateDom(rootInstance->get().GetTemplateId());
|
||||
|
||||
auto instanceDomFromRootValue = rootPrefabDomPath.Get(rootPrefabTemplateDom);
|
||||
if (!instanceDomFromRootValue)
|
||||
{
|
||||
return AZ::Failure<AZStd::string>("Could not load Instance DOM from the top level ancestor's DOM.");
|
||||
}
|
||||
|
||||
PrefabDomValueReference instanceDomFromRoot = *instanceDomFromRootValue;
|
||||
if (!instanceDomFromRoot.has_value())
|
||||
{
|
||||
return AZ::Failure<AZStd::string>("Could not load Instance DOM from the top level ancestor's DOM.");
|
||||
}
|
||||
|
||||
PrefabDom instanceDomBeforeUpdate;
|
||||
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, entityOwningInstance);
|
||||
instanceDomBeforeUpdate.CopyFrom(instanceDomFromRoot.value().get(), instanceDomBeforeUpdate.GetAllocator());
|
||||
|
||||
ScopedUndoBatch undoBatch("Add Entity");
|
||||
|
||||
@@ -674,6 +709,9 @@ namespace AzToolsFramework
|
||||
bool isInstanceContainerEntity = IsInstanceContainerEntity(entityId) && !IsLevelInstanceContainerEntity(entityId);
|
||||
bool isNewParentOwnedByDifferentInstance = false;
|
||||
|
||||
bool isInFocusTree = m_prefabFocusPublicInterface->IsOwningPrefabInFocusHierarchy(entityId);
|
||||
bool isOwnedByFocusedPrefabInstance = m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId);
|
||||
|
||||
if (beforeParentId != afterParentId)
|
||||
{
|
||||
// If the entity parent changed, verify if the owning instance changed too
|
||||
@@ -727,7 +765,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
if (isInstanceContainerEntity)
|
||||
if (isInFocusTree && !isOwnedByFocusedPrefabInstance)
|
||||
{
|
||||
if (isNewParentOwnedByDifferentInstance)
|
||||
{
|
||||
@@ -1648,6 +1686,144 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::AddNewEntityToSortOrder(
|
||||
Instance& owningInstance,
|
||||
PrefabDom& domToAddEntityUnder,
|
||||
const EntityAlias& parentEntityAlias,
|
||||
const EntityAlias& entityToAddAlias)
|
||||
{
|
||||
// Find the parent entity to get its sort order component
|
||||
auto findParentEntity = [&]() -> rapidjson::Value*
|
||||
{
|
||||
if (auto containerEntityIter = domToAddEntityUnder.FindMember(PrefabDomUtils::ContainerEntityName);
|
||||
containerEntityIter != domToAddEntityUnder.MemberEnd())
|
||||
{
|
||||
if (parentEntityAlias == containerEntityIter->value[PrefabDomUtils::EntityIdName].GetString())
|
||||
{
|
||||
return &containerEntityIter->value;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto entitiesIter = domToAddEntityUnder.FindMember(PrefabDomUtils::EntitiesName);
|
||||
entitiesIter != domToAddEntityUnder.MemberEnd())
|
||||
{
|
||||
for (auto entityIter = entitiesIter->value.MemberBegin(); entityIter != entitiesIter->value.MemberEnd(); ++entityIter)
|
||||
{
|
||||
if (parentEntityAlias == entityIter->value[PrefabDomUtils::EntityIdName].GetString())
|
||||
{
|
||||
return &entityIter->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
rapidjson::Value* parentEntityValue = findParentEntity();
|
||||
if (parentEntityValue == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of selected entities, we'll insert our duplicated entities after the last selected
|
||||
// sibling in their parent's list, e.g. for:
|
||||
// - Entity1
|
||||
// - Entity2 (selected)
|
||||
// - Entity3
|
||||
// - Entity4 (selected)
|
||||
// - Entity5
|
||||
// Our duplicate selection command would create duplicate Entity2 and Entity4 and insert them after Entity4:
|
||||
// - Entity1
|
||||
// - Entity2
|
||||
// - Entity3
|
||||
// - Entity4
|
||||
// - Entity2 (new, selected after duplicate)
|
||||
// - Entity4 (new, selected after duplicate)
|
||||
// - Entity5
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
|
||||
|
||||
// Find the EditorEntitySortComponent DOM
|
||||
auto componentsIter = parentEntityValue->FindMember(PrefabDomUtils::ComponentsName);
|
||||
if (componentsIter == parentEntityValue->MemberEnd())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentIter->value.MemberEnd();
|
||||
++componentIter)
|
||||
{
|
||||
// Check the component type
|
||||
auto typeFieldIter = componentIter->value.FindMember(PrefabDomUtils::TypeName);
|
||||
if (typeFieldIter == componentIter->value.MemberEnd())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings jsonDeserializerSettings;
|
||||
AZ::Uuid typeId = AZ::Uuid::CreateNull();
|
||||
AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
|
||||
|
||||
if (typeId != azrtti_typeid<Components::EditorEntitySortComponent>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for the entity order field
|
||||
auto orderMembersIter = componentIter->value.FindMember(PrefabDomUtils::EntityOrderName);
|
||||
if (orderMembersIter == componentIter->value.MemberEnd() || !orderMembersIter->value.IsArray())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Scan for the last selected entity in the list (if any) to determine where to add our entries
|
||||
rapidjson::Value newOrder(rapidjson::kArrayType);
|
||||
auto insertValuesAfter = orderMembersIter->value.End();
|
||||
for (auto orderMemberIter = orderMembersIter->value.Begin(); orderMemberIter != orderMembersIter->value.End();
|
||||
++orderMemberIter)
|
||||
{
|
||||
if (!orderMemberIter->IsString())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const char* value = orderMemberIter->GetString();
|
||||
for (AZ::EntityId selectedEntity : selectedEntities)
|
||||
{
|
||||
auto alias = owningInstance.GetEntityAlias(selectedEntity);
|
||||
if (alias.has_value() && alias.value().get() == value)
|
||||
{
|
||||
insertValuesAfter = orderMemberIter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct our new array with the new order - insertion may happen at end, so check for that in the loop itself
|
||||
for (auto orderMemberIter = orderMembersIter->value.Begin();; ++orderMemberIter)
|
||||
{
|
||||
if (orderMemberIter != orderMembersIter->value.End())
|
||||
{
|
||||
newOrder.PushBack(orderMemberIter->Move(), domToAddEntityUnder.GetAllocator());
|
||||
}
|
||||
if (orderMemberIter == insertValuesAfter)
|
||||
{
|
||||
newOrder.PushBack(
|
||||
rapidjson::Value(entityToAddAlias.c_str(), domToAddEntityUnder.GetAllocator()),
|
||||
domToAddEntityUnder.GetAllocator());
|
||||
}
|
||||
if (orderMemberIter == orderMembersIter->value.End())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the order with our newly constructed one
|
||||
orderMembersIter->value.Swap(newOrder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabPublicHandler::DuplicateNestedEntitiesInInstance(Instance& commonOwningInstance,
|
||||
const AZStd::vector<AZ::Entity*>& entities, PrefabDom& domToAddDuplicatedEntitiesUnder,
|
||||
EntityIdList& duplicatedEntityIds, AZStd::unordered_map<EntityAlias, EntityAlias>& oldAliasToNewAliasMap)
|
||||
@@ -1705,6 +1881,73 @@ namespace AzToolsFramework
|
||||
PrefabDom entityDomAfter(&domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entityDomAfter.Parse(newEntityDomString.toUtf8().constData());
|
||||
|
||||
EntityAlias parentEntityAlias;
|
||||
if (auto componentsIter = entityDomAfter.FindMember(PrefabDomUtils::ComponentsName);
|
||||
componentsIter != entityDomAfter.MemberEnd())
|
||||
{
|
||||
auto checkComponent = [&](const rapidjson::Value& value) -> bool
|
||||
{
|
||||
if (!value.IsObject())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the component type
|
||||
auto typeFieldIter = value.FindMember(PrefabDomUtils::TypeName);
|
||||
if (typeFieldIter == value.MemberEnd())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::JsonDeserializerSettings jsonDeserializerSettings;
|
||||
AZ::Uuid typeId = AZ::Uuid::CreateNull();
|
||||
AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
|
||||
|
||||
// Prefabs get serialized with the Editor transform component type, check for that
|
||||
if (typeId != azrtti_typeid<Components::TransformComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto parentEntityIter = value.FindMember("Parent Entity");
|
||||
parentEntityIter != value.MemberEnd())
|
||||
{
|
||||
parentEntityAlias = parentEntityIter->value.GetString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (componentsIter->value.IsObject())
|
||||
{
|
||||
for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentsIter->value.MemberEnd();
|
||||
++componentIter)
|
||||
{
|
||||
if (checkComponent(componentIter->value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (componentsIter->value.IsArray())
|
||||
{
|
||||
for (auto componentIter = componentsIter->value.Begin(); componentIter != componentsIter->value.End();
|
||||
++componentIter)
|
||||
{
|
||||
if (checkComponent(*componentIter))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert our entity into its parent's sort order
|
||||
if (!parentEntityAlias.empty())
|
||||
{
|
||||
AddNewEntityToSortOrder(commonOwningInstance, domToAddDuplicatedEntitiesUnder, parentEntityAlias, newEntityAlias);
|
||||
}
|
||||
|
||||
// Add the new Entity DOM to the Entities member of the instance
|
||||
rapidjson::Value aliasName(newEntityAlias.c_str(), static_cast<rapidjson::SizeType>(newEntityAlias.length()), domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
entitiesIter->value.AddMember(AZStd::move(aliasName), entityDomAfter, domToAddDuplicatedEntitiesUnder.GetAllocator());
|
||||
|
||||
@@ -78,6 +78,8 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceOptionalReference GetOwnerInstanceByEntityId(AZ::EntityId entityId) const;
|
||||
bool EntitiesBelongToSameInstance(const EntityIdList& entityIds) const;
|
||||
void AddNewEntityToSortOrder(Instance& owningInstance, PrefabDom& domToAddEntityUnder,
|
||||
const EntityAlias& parentEntityAlias, const EntityAlias& entityToAddAlias);
|
||||
|
||||
/**
|
||||
* Duplicate a list of entities owned by a common owning instance by directly
|
||||
|
||||
Reference in New Issue
Block a user