From 0f13a71bd284a22254b628b94c81241a22ce0ade Mon Sep 17 00:00:00 2001 From: Chris Galvan Date: Wed, 14 Apr 2021 16:01:26 -0500 Subject: [PATCH] [LYN-2255] Refactored some EditorEntityHelpers so they can be re-used. --- .../Entity/EditorEntityHelpers.cpp | 81 +++++++++++++++++++ .../Entity/EditorEntityHelpers.h | 6 ++ .../Prefab/PrefabPublicHandler.cpp | 19 +---- .../Prefab/PrefabPublicHandler.h | 1 - .../Tests/Entity/EditorEntityHelpersTests.cpp | 71 ++++++++++++++++ .../Tests/aztoolsframeworktests_files.cmake | 2 + .../SandboxIntegration.cpp | 16 +++- 7 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 Code/Framework/AzToolsFramework/Tests/Entity/EditorEntityHelpersTests.cpp diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp index 6ab3625dd2..629dcd639d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.cpp @@ -136,6 +136,48 @@ namespace AzToolsFramework return entity->GetName(); } + EntityList EntityIdListToEntityList(const EntityIdList& inputEntityIds) + { + EntityList entities; + entities.reserve(inputEntityIds.size()); + + for (AZ::EntityId entityId : inputEntityIds) + { + if (!entityId.IsValid()) + { + continue; + } + + if (auto entity = GetEntityById(entityId)) + { + entities.emplace_back(entity); + } + } + + return entities; + } + + EntityList EntityIdSetToEntityList(const EntityIdSet& inputEntityIds) + { + EntityList entities; + entities.reserve(inputEntityIds.size()); + + for (AZ::EntityId entityId : inputEntityIds) + { + if (!entityId.IsValid()) + { + continue; + } + + if (auto entity = GetEntityById(entityId)) + { + entities.emplace_back(entity); + } + } + + return entities; + } + void GetAllComponentsForEntity(const AZ::Entity* entity, AZ::Entity::ComponentArrayType& componentsOnEntity) { if (entity) @@ -1068,6 +1110,45 @@ namespace AzToolsFramework return !allEntityClonesContainer.m_entities.empty(); } + EntityIdSet GetCulledEntityHierarchy(const EntityIdList& entities) + { + EntityIdSet culledEntities; + + for (const AZ::EntityId& entityId : entities) + { + bool selectionIncludesTransformHeritage = false; + AZ::EntityId parentEntityId = entityId; + do + { + AZ::EntityId nextParentId; + AZ::TransformBus::EventResult( + /*result*/ nextParentId, + /*address*/ parentEntityId, + &AZ::TransformBus::Events::GetParentId); + parentEntityId = nextParentId; + if (!parentEntityId.IsValid()) + { + break; + } + for (const AZ::EntityId& parentCheck : entities) + { + if (parentCheck == parentEntityId) + { + selectionIncludesTransformHeritage = true; + break; + } + } + } while (parentEntityId.IsValid() && !selectionIncludesTransformHeritage); + + if (!selectionIncludesTransformHeritage) + { + culledEntities.insert(entityId); + } + } + + return culledEntities; + } + namespace Internal { void CloneSliceEntitiesAndChildren( diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h index 39f4c37c29..3ef03296f4 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/EditorEntityHelpers.h @@ -47,6 +47,9 @@ namespace AzToolsFramework AZStd::string GetEntityName(const AZ::EntityId& entityId, const AZStd::string_view& nameOverride = {}); + EntityList EntityIdListToEntityList(const EntityIdList& inputEntityIds); + EntityList EntityIdSetToEntityList(const EntityIdSet & inputEntityIds); + template struct AddComponents { @@ -202,4 +205,7 @@ namespace AzToolsFramework /// Wrap EBus SetSelectedEntities call. void SelectEntities(const AzToolsFramework::EntityIdList& entities); + /// Return a set of entities, culling any that have an ancestor in the list. + EntityIdSet GetCulledEntityHierarchy(const EntityIdList & entities); + }; // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp index 417a524e77..43ee7b7033 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.cpp @@ -61,8 +61,7 @@ namespace AzToolsFramework PrefabOperationResult PrefabPublicHandler::CreatePrefab(const AZStd::vector& entityIds, AZStd::string_view filePath) { // Retrieve entityList from entityIds - EntityList inputEntityList; - EntityIdListToEntityList(entityIds, inputEntityList); + EntityList inputEntityList = EntityIdListToEntityList(entityIds); // Find common root and top level entities bool entitiesHaveCommonRoot = false; @@ -419,8 +418,7 @@ namespace AzToolsFramework InstanceOptionalReference instance = GetOwnerInstanceByEntityId(entityIds[0]); // Retrieve entityList from entityIds - EntityList inputEntityList; - EntityIdListToEntityList(entityIds, inputEntityList); + EntityList inputEntityList = EntityIdListToEntityList(entityIds); AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); @@ -767,18 +765,5 @@ namespace AzToolsFramework return true; } - - void PrefabPublicHandler::EntityIdListToEntityList(const EntityIdList& inputEntityIds, EntityList& outEntities) - { - outEntities.reserve(inputEntityIds.size()); - - for (AZ::EntityId entityId : inputEntityIds) - { - if (entityId.IsValid()) - { - outEntities.emplace_back(GetEntityById(entityId)); - } - } - } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h index 46a7f946ba..4eb03a7abb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabPublicHandler.h @@ -70,7 +70,6 @@ namespace AzToolsFramework 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); - static void EntityIdListToEntityList(const EntityIdList& inputEntityIds, EntityList& outEntities); InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr; InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr; diff --git a/Code/Framework/AzToolsFramework/Tests/Entity/EditorEntityHelpersTests.cpp b/Code/Framework/AzToolsFramework/Tests/Entity/EditorEntityHelpersTests.cpp new file mode 100644 index 0000000000..6bf6beb878 --- /dev/null +++ b/Code/Framework/AzToolsFramework/Tests/Entity/EditorEntityHelpersTests.cpp @@ -0,0 +1,71 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ + +#include + +#include +#include +#include + +namespace UnitTest +{ + using namespace AZ; + using namespace AzToolsFramework; + + class EditorEntityHelpersTest + : public ToolsApplicationFixture + { + void SetUpEditorFixtureImpl() override + { + m_parent1 = CreateDefaultEditorEntity("Parent1"); + m_child1 = CreateDefaultEditorEntity("Child1"); + m_child2 = CreateDefaultEditorEntity("Child2"); + m_grandChild1 = CreateDefaultEditorEntity("GrandChild1"); + m_parent2 = CreateDefaultEditorEntity("Parent2"); + + AZ::TransformBus::Event(m_child1, &AZ::TransformBus::Events::SetParent, m_parent1); + AZ::TransformBus::Event(m_child2, &AZ::TransformBus::Events::SetParent, m_parent1); + AZ::TransformBus::Event(m_grandChild1, &AZ::TransformBus::Events::SetParent, m_child1); + } + + public: + AZ::EntityId m_parent1; + AZ::EntityId m_child1; + AZ::EntityId m_child2; + AZ::EntityId m_grandChild1; + AZ::EntityId m_parent2; + }; + + TEST_F(EditorEntityHelpersTest, EditorEntityHelpersTests_GetCulledEntityHierarchy) + { + EntityIdList testEntityIds{ m_parent1, m_child1, m_child2, m_grandChild1, m_parent2 }; + + EntityIdSet culledSet = GetCulledEntityHierarchy(testEntityIds); + + // There should only be two EntityIds returned (m_parent1, and m_parent2), + // since all the others should be culled out since they have a common ancestor + // in the list already + EXPECT_EQ(culledSet.size(), 2); + + EntityIdList foundEntityIds{ m_parent1, m_parent2 }; + for (auto& entityId : foundEntityIds) + { + EXPECT_TRUE(AZStd::find(culledSet.begin(), culledSet.end(), entityId) != culledSet.end()); + } + + EntityIdList culledEntityIds{ m_child1, m_child2, m_grandChild1 }; + for (auto& entityId : culledEntityIds) + { + EXPECT_FALSE(AZStd::find(culledSet.begin(), culledSet.end(), entityId) != culledSet.end()); + } + } +} diff --git a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake index 7a2cd373a2..e54aa187e4 100644 --- a/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake +++ b/Code/Framework/AzToolsFramework/Tests/aztoolsframeworktests_files.cmake @@ -85,7 +85,9 @@ set(FILES Prefab/SpawnableSortEntitiesTestFixture.cpp Prefab/SpawnableSortEntitiesTestFixture.h Entity/EditorEntityContextComponentTests.cpp + Entity/EditorEntityHelpersTests.cpp Entity/EditorEntitySearchComponentTests.cpp + Entity/EditorEntitySelectionTests.cpp SliceStabilityTests/SliceStabilityTestFramework.h SliceStabilityTests/SliceStabilityTestFramework.cpp SliceStabilityTests/SliceStabilityCreateTests.cpp diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index e5311dbea9..69040245f8 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -670,9 +670,13 @@ void SandboxIntegrationManager::PopulateEditorGlobalContextMenu(QMenu* menu, con action = menu->addAction(QObject::tr("Create layer")); QObject::connect(action, &QAction::triggered, [this] { ContextMenu_NewLayer(); }); + AzToolsFramework::EntityIdList entities; + AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult( + entities, + &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities); + SetupLayerContextMenu(menu); - AzToolsFramework::EntityIdSet flattenedSelection; - GetSelectedEntitiesSetWithFlattenedHierarchy(flattenedSelection); + AzToolsFramework::EntityIdSet flattenedSelection = AzToolsFramework::GetCulledEntityHierarchy(entities); AzToolsFramework::SetupAddToLayerMenu(menu, flattenedSelection, [this] { return ContextMenu_NewLayer(); }); SetupSliceContextMenu(menu); @@ -1220,8 +1224,12 @@ void SandboxIntegrationManager::CloneSelection(bool& handled) { AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzToolsFramework); - AzToolsFramework::EntityIdSet duplicationSet; - GetSelectedEntitiesSetWithFlattenedHierarchy(duplicationSet); + AzToolsFramework::EntityIdList entities; + AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult( + entities, + &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities); + + AzToolsFramework::EntityIdSet duplicationSet = AzToolsFramework::GetCulledEntityHierarchy(entities); if (duplicationSet.size() > 0) {