Merge commit '4ee2f341dc0dc709aedb446b57d5cca61b86160d' into junbo/gitflow_211123_o3de
This commit is contained in:
@@ -449,16 +449,23 @@ namespace AzToolsFramework
|
||||
AZStd::unordered_map<AZ::EntityId, AZStd::pair<AZ::EntityId, AZ::u64>>::const_iterator orderItr = m_savedOrderInfo.find(childId);
|
||||
if (orderItr != m_savedOrderInfo.end() && orderItr->second.first == parentId)
|
||||
{
|
||||
bool sortOrderUpdated = AzToolsFramework::RecoverEntitySortInfo(parentId, childId, orderItr->second.second);
|
||||
m_savedOrderInfo.erase(childId);
|
||||
|
||||
// force notify the child sort order changed on the parent entity info, but only if the restore didn't actually modify
|
||||
// the order internally (and sent ChildEntityOrderArrayUpdated). that may seem heavy handed, and it is, but necessary
|
||||
// to combat scenarios when the initial override detection returns a false positive (see comment about IDH comparisons
|
||||
// in OnChildSortOrderChanged) and the slice instance source-to-live mapping hasn't been fully reconstructed yet.
|
||||
if (!sortOrderUpdated)
|
||||
bool isPrefabEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
// If prefabs are enabled, rely on the component to do a sanity check instead of restoring the order from the model
|
||||
if (!isPrefabEnabled)
|
||||
{
|
||||
parentInfo.OnChildSortOrderChanged();
|
||||
bool sortOrderUpdated = AzToolsFramework::RecoverEntitySortInfo(parentId, childId, orderItr->second.second);
|
||||
m_savedOrderInfo.erase(childId);
|
||||
|
||||
// force notify the child sort order changed on the parent entity info, but only if the restore didn't actually modify
|
||||
// the order internally (and sent ChildEntityOrderArrayUpdated). that may seem heavy handed, and it is, but necessary
|
||||
// to combat scenarios when the initial override detection returns a false positive (see comment about IDH comparisons
|
||||
// in OnChildSortOrderChanged) and the slice instance source-to-live mapping hasn't been fully reconstructed yet.
|
||||
if (!sortOrderUpdated)
|
||||
{
|
||||
parentInfo.OnChildSortOrderChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+93
-17
@@ -8,11 +8,17 @@
|
||||
#include "EditorEntitySortComponent.h"
|
||||
#include "EditorEntityInfoBus.h"
|
||||
#include "EditorEntityHelpers.h"
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzFramework/API/ApplicationAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicRequestBus.h>
|
||||
#include <AzToolsFramework/Undo/UndoSystem.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h>
|
||||
|
||||
static_assert(sizeof(AZ::u64) == sizeof(AZ::EntityId), "We use AZ::EntityId for Persistent ID, which is a u64 under the hood. These must be the same size otherwise the persistent id will have to be rewritten");
|
||||
|
||||
@@ -51,6 +57,12 @@ namespace AzToolsFramework
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast<AZ::JsonRegistrationContext*>(context);
|
||||
if (jsonRegistration)
|
||||
{
|
||||
jsonRegistration->Serializer<JsonEditorEntitySortComponentSerializer>()->HandlesType<EditorEntitySortComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
@@ -167,9 +179,6 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
MarkDirtyAndSendChangedEvent();
|
||||
|
||||
// Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -187,6 +196,10 @@ namespace AzToolsFramework
|
||||
else
|
||||
{
|
||||
EntityOrderArray::iterator insertPosition = GetFirstSelectedEntityPosition();
|
||||
if (insertPosition != m_childEntityOrderArray.end())
|
||||
{
|
||||
++insertPosition;
|
||||
}
|
||||
retval = AddChildEntityInternal(entityId, false, insertPosition);
|
||||
}
|
||||
|
||||
@@ -220,9 +233,6 @@ namespace AzToolsFramework
|
||||
|
||||
MarkDirtyAndSendChangedEvent();
|
||||
|
||||
// Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -272,6 +282,12 @@ namespace AzToolsFramework
|
||||
void EditorEntitySortComponent::OnPrefabInstancePropagationEnd()
|
||||
{
|
||||
m_ignoreIncomingOrderChanges = false;
|
||||
|
||||
if (m_shouldSanityCheckStateAfterPropagation)
|
||||
{
|
||||
SanitizeOrderEntryArray();
|
||||
m_shouldSanityCheckStateAfterPropagation = false;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::MarkDirtyAndSendChangedEvent()
|
||||
@@ -280,14 +296,8 @@ namespace AzToolsFramework
|
||||
// one of the event listeners needs to build the InstanceDataHierarchy
|
||||
m_entityOrderIsDirty = true;
|
||||
|
||||
// Force an immediate update for prefabs, which won't receive PrepareSave
|
||||
bool isPrefabEnabled = false;
|
||||
AzFramework::ApplicationRequests::Bus::BroadcastResult(
|
||||
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
if (isPrefabEnabled)
|
||||
{
|
||||
PrepareSave();
|
||||
}
|
||||
// Use the ToolsApplication to mark the entity dirty, this will only do something if we already have an undo batch
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::AddDirtyEntity, GetEntityId());
|
||||
EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated);
|
||||
}
|
||||
|
||||
@@ -308,9 +318,8 @@ namespace AzToolsFramework
|
||||
isPrefabEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
|
||||
if (isPrefabEnabled)
|
||||
{
|
||||
PostLoad();
|
||||
m_shouldSanityCheckStateAfterPropagation = true;
|
||||
}
|
||||
|
||||
// Send out that the order for our entity is now updated
|
||||
EditorEntitySortNotificationBus::Event(GetEntityId(), &EditorEntitySortNotificationBus::Events::ChildEntityOrderArrayUpdated);
|
||||
}
|
||||
@@ -336,6 +345,73 @@ namespace AzToolsFramework
|
||||
m_entityOrderIsDirty = false;
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::SanitizeOrderEntryArray()
|
||||
{
|
||||
bool shouldEmitDirtyState = false;
|
||||
|
||||
// Remove invalid and duplicate entries that point at non-existent entities
|
||||
AZStd::unordered_set<AZ::EntityId> duplicateIds;
|
||||
for (auto it = m_childEntityOrderArray.begin(); it != m_childEntityOrderArray.end();)
|
||||
{
|
||||
if (!it->IsValid() || GetEntityById(*it) == nullptr || duplicateIds.contains(*it))
|
||||
{
|
||||
it = m_childEntityOrderArray.erase(it);
|
||||
shouldEmitDirtyState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicateIds.insert(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Append any missing children
|
||||
EntityIdList children;
|
||||
AZ::TransformBus::EventResult(children, GetEntityId(), &AZ::TransformBus::Events::GetChildren);
|
||||
for (auto it = m_childEntityOrderArray.begin(); it != m_childEntityOrderArray.end(); ++it)
|
||||
{
|
||||
if (auto removedChildrenIt = AZStd::remove(children.begin(), children.end(), *it); removedChildrenIt != children.end())
|
||||
{
|
||||
children.erase(removedChildrenIt);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::sort(children.begin(), children.end(), [](AZ::EntityId lhs, AZ::EntityId rhs)
|
||||
{
|
||||
return GetEntityById(lhs)->GetName() < GetEntityById(rhs)->GetName();
|
||||
});
|
||||
|
||||
if (!children.empty())
|
||||
{
|
||||
shouldEmitDirtyState = true;
|
||||
EntityOrderArray::iterator insertPosition = GetFirstSelectedEntityPosition();
|
||||
if (insertPosition != m_childEntityOrderArray.end())
|
||||
{
|
||||
++insertPosition;
|
||||
}
|
||||
m_childEntityOrderArray.insert(insertPosition, children.begin(), children.end());
|
||||
}
|
||||
|
||||
// Clear out the vector to be rebuilt from persistent id
|
||||
m_childEntityOrderEntryArray.resize(m_childEntityOrderArray.size());
|
||||
for (size_t i = 0; i < m_childEntityOrderArray.size(); ++i)
|
||||
{
|
||||
m_childEntityOrderEntryArray[i] = {
|
||||
m_childEntityOrderArray[i],
|
||||
static_cast<AZ::u64>(i)
|
||||
};
|
||||
}
|
||||
|
||||
RebuildEntityOrderCache();
|
||||
|
||||
if (shouldEmitDirtyState)
|
||||
{
|
||||
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::Bus::Events::AddDirtyEntity, GetEntityId());
|
||||
}
|
||||
|
||||
m_entityOrderIsDirty = false;
|
||||
}
|
||||
|
||||
void EditorEntitySortComponent::PostLoad()
|
||||
{
|
||||
// Clear out the vector to be rebuilt from persistent id
|
||||
@@ -383,7 +459,7 @@ namespace AzToolsFramework
|
||||
firstSelectedEntityPos = selectedEntityPos < firstSelectedEntityPos ? selectedEntityPos : firstSelectedEntityPos;
|
||||
}
|
||||
|
||||
return firstSelectedEntityPos == m_childEntityOrderArray.end() ? m_childEntityOrderArray.begin() : firstSelectedEntityPos;
|
||||
return firstSelectedEntityPos;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace AzToolsFramework
|
||||
, public EditorEntityContextNotificationBus::Handler
|
||||
, public AzToolsFramework::Prefab::PrefabPublicNotificationBus::Handler
|
||||
{
|
||||
friend class JsonEditorEntitySortComponentSerializer;
|
||||
|
||||
public:
|
||||
AZ_COMPONENT(EditorEntitySortComponent, "{6EA1E03D-68B2-466D-97F7-83998C8C27F0}", EditorComponentBase);
|
||||
|
||||
@@ -64,6 +66,8 @@ namespace AzToolsFramework
|
||||
void PrepareSave();
|
||||
void PostLoad();
|
||||
|
||||
void SanitizeOrderEntryArray();
|
||||
|
||||
class EntitySortSerializationEvents
|
||||
: public AZ::SerializeContext::IEventHandler
|
||||
{
|
||||
@@ -112,6 +116,7 @@ namespace AzToolsFramework
|
||||
|
||||
bool m_entityOrderIsDirty = true; ///< This flag indicates our stored serialization order data is out of date and must be rebuilt before serialization occurs
|
||||
bool m_ignoreIncomingOrderChanges = false; ///< This is set when prefab propagation occurs so that non-authored order changes can be ignored
|
||||
bool m_shouldSanityCheckStateAfterPropagation = false; //< This is set after activation, to queue a cleanup of any invalid state after the next prefab propagation.
|
||||
};
|
||||
}
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntitySortComponentSerializer.h>
|
||||
|
||||
namespace AzToolsFramework::Components
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonEditorEntitySortComponentSerializer, AZ::SystemAllocator, 0);
|
||||
|
||||
AZ::JsonSerializationResult::Result JsonEditorEntitySortComponentSerializer::Load(
|
||||
void* outputValue,
|
||||
[[maybe_unused]] const AZ::Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(
|
||||
azrtti_typeid<EditorEntitySortComponent>() == outputValueTypeId,
|
||||
"Unable to deserialize EditorEntitySortComponent from json because the provided type is %s.",
|
||||
outputValueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
EditorEntitySortComponent* sortComponentInstance = reinterpret_cast<EditorEntitySortComponent*>(outputValue);
|
||||
AZ_Assert(sortComponentInstance, "Output value for JsonEditorEntitySortComponentSerializer can't be null.");
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
{
|
||||
JSR::ResultCode componentIdLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&sortComponentInstance->m_id, azrtti_typeid<decltype(sortComponentInstance->m_id)>(), inputValue,
|
||||
"Id", context);
|
||||
|
||||
result.Combine(componentIdLoadResult);
|
||||
}
|
||||
|
||||
{
|
||||
sortComponentInstance->m_childEntityOrderArray.clear();
|
||||
JSR::ResultCode enryLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&sortComponentInstance->m_childEntityOrderArray,
|
||||
azrtti_typeid<decltype(sortComponentInstance->m_childEntityOrderArray)>(), inputValue, "Child Entity Order",
|
||||
context);
|
||||
|
||||
// Migrate ChildEntityOrderEntryArray -> ChildEntityOrderArray
|
||||
if (sortComponentInstance->m_childEntityOrderArray.empty())
|
||||
{
|
||||
enryLoadResult = ContinueLoadingFromJsonObjectField(
|
||||
&sortComponentInstance->m_childEntityOrderEntryArray,
|
||||
azrtti_typeid<decltype(sortComponentInstance->m_childEntityOrderEntryArray)>(), inputValue,
|
||||
"ChildEntityOrderEntryArray", context);
|
||||
|
||||
AZStd::sort(
|
||||
sortComponentInstance->m_childEntityOrderEntryArray.begin(),
|
||||
sortComponentInstance->m_childEntityOrderEntryArray.end(),
|
||||
[](const EditorEntitySortComponent::EntityOrderEntry& lhs,
|
||||
const EditorEntitySortComponent::EntityOrderEntry& rhs) -> bool
|
||||
{
|
||||
return lhs.m_sortIndex < rhs.m_sortIndex;
|
||||
});
|
||||
|
||||
// Sort by index and copy to the order array, any duplicates or invalid entries will be cleaned up by the sanitization pass
|
||||
sortComponentInstance->m_childEntityOrderArray.resize(sortComponentInstance->m_childEntityOrderEntryArray.size());
|
||||
for (size_t i = 0; i < sortComponentInstance->m_childEntityOrderEntryArray.size(); ++i)
|
||||
{
|
||||
sortComponentInstance->m_childEntityOrderArray[i] = sortComponentInstance->m_childEntityOrderEntryArray[i].m_entityId;
|
||||
}
|
||||
}
|
||||
|
||||
sortComponentInstance->RebuildEntityOrderCache();
|
||||
|
||||
result.Combine(enryLoadResult);
|
||||
}
|
||||
|
||||
return context.Report(
|
||||
result,
|
||||
result.GetProcessing() != JSR::Processing::Halted ? "Successfully loaded EditorEntitySortComponent information."
|
||||
: "Failed to load EditorEntitySortComponent information.");
|
||||
}
|
||||
|
||||
AZ::JsonSerializationResult::Result JsonEditorEntitySortComponentSerializer::Store(
|
||||
rapidjson::Value& outputValue,
|
||||
const void* inputValue,
|
||||
const void* defaultValue,
|
||||
[[maybe_unused]] const AZ::Uuid& valueTypeId,
|
||||
AZ::JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(
|
||||
azrtti_typeid<EditorEntitySortComponent>() == valueTypeId,
|
||||
"Unable to Serialize EditorEntitySortComponent because the provided type is %s.",
|
||||
valueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
const EditorEntitySortComponent* sortComponentInstance = reinterpret_cast<const EditorEntitySortComponent*>(inputValue);
|
||||
AZ_Assert(sortComponentInstance, "Input value for JsonEditorEntitySortComponentSerializer can't be null.");
|
||||
const EditorEntitySortComponent* defaultsortComponentInstance =
|
||||
reinterpret_cast<const EditorEntitySortComponent*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_id");
|
||||
const AZ::ComponentId* componentId = &sortComponentInstance->m_id;
|
||||
const AZ::ComponentId* defaultComponentId =
|
||||
defaultsortComponentInstance ? &defaultsortComponentInstance->m_id : nullptr;
|
||||
|
||||
JSR::ResultCode resultComponentId = ContinueStoringToJsonObjectField(
|
||||
outputValue, "Id", componentId, defaultComponentId, azrtti_typeid<decltype(sortComponentInstance->m_id)>(),
|
||||
context);
|
||||
|
||||
result.Combine(resultComponentId);
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_childEntityOrderArray");
|
||||
const EntityOrderArray* childEntityOrderArray = &sortComponentInstance->m_childEntityOrderArray;
|
||||
const EntityOrderArray* defaultChildEntityOrderArray =
|
||||
defaultsortComponentInstance ? &defaultsortComponentInstance->m_childEntityOrderArray : nullptr;
|
||||
|
||||
JSR::ResultCode resultParentEntityId = ContinueStoringToJsonObjectField(
|
||||
outputValue, "Child Entity Order", childEntityOrderArray, defaultChildEntityOrderArray,
|
||||
azrtti_typeid<decltype(sortComponentInstance->m_childEntityOrderArray)>(), context);
|
||||
|
||||
result.Combine(resultParentEntityId);
|
||||
}
|
||||
|
||||
return context.Report(
|
||||
result,
|
||||
result.GetProcessing() != JSR::Processing::Halted ? "Successfully stored EditorEntitySortComponent information."
|
||||
: "Failed to store EditorEntitySortComponent information.");
|
||||
}
|
||||
} // namespace AzToolsFramework::Components
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AzToolsFramework::Components
|
||||
{
|
||||
class JsonEditorEntitySortComponentSerializer
|
||||
: public AZ::BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonEditorEntitySortComponentSerializer, "{5104782E-B34F-4D87-B1DF-BDFB1AF20D58}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
AZ::JsonSerializationResult::Result Load(
|
||||
void* outputValue, const AZ::Uuid& outputValueTypeId, const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context) override;
|
||||
|
||||
AZ::JsonSerializationResult::Result Store(
|
||||
rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const AZ::Uuid& valueTypeId,
|
||||
AZ::JsonSerializerContext& context) override;
|
||||
};
|
||||
} // namespace AzToolsFramework::Components
|
||||
Reference in New Issue
Block a user