You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1528 lines
80 KiB
C++
1528 lines
80 KiB
C++
/*
|
|
* 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 <AzCore/Math/ToString.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/UnitTest/TestTypes.h>
|
|
#include <AzFramework/Components/TransformComponent.h>
|
|
#include <AzFramework/Entity/EntityContext.h>
|
|
#include <AzFramework/Viewport/ViewportScreen.h>
|
|
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
|
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
|
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
|
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
|
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
|
#include <AzManipulatorTestFramework/ViewportInteraction.h>
|
|
#include <AzQtComponents/Components/GlobalEventFilter.h>
|
|
#include <AzTest/AzTest.h>
|
|
#include <AzToolsFramework/Application/ToolsApplication.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
|
#include <AzToolsFramework/Entity/EditorEntityModel.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
|
|
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
|
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
|
#include <AzToolsFramework/Viewport/ActionBus.h>
|
|
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
|
|
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemViewportSelectionRequestBus.h>
|
|
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.h>
|
|
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
|
|
|
using namespace AzToolsFramework;
|
|
|
|
namespace AZ
|
|
{
|
|
std::ostream& operator<<(std::ostream& os, const EntityId entityId)
|
|
{
|
|
return os << entityId.ToString().c_str();
|
|
}
|
|
} // namespace AZ
|
|
|
|
namespace UnitTest
|
|
{
|
|
class EditorEntityVisibilityCacheFixture : public ToolsApplicationFixture
|
|
{
|
|
public:
|
|
void CreateLayerAndEntityHierarchy()
|
|
{
|
|
// Set up entity layer hierarchy.
|
|
const AZ::EntityId a = CreateDefaultEditorEntity("A");
|
|
const AZ::EntityId b = CreateDefaultEditorEntity("B");
|
|
const AZ::EntityId c = CreateDefaultEditorEntity("C");
|
|
|
|
m_layerId = CreateEditorLayerEntity("Layer");
|
|
|
|
AZ::TransformBus::Event(a, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(b, &AZ::TransformBus::Events::SetParent, a);
|
|
AZ::TransformBus::Event(c, &AZ::TransformBus::Events::SetParent, b);
|
|
|
|
// Add entity ids we want to track, to the visibility cache.
|
|
m_entityIds.insert(m_entityIds.begin(), { a, b, c });
|
|
m_cache.AddEntityIds(m_entityIds);
|
|
}
|
|
|
|
EntityIdList m_entityIds;
|
|
AZ::EntityId m_layerId;
|
|
EditorVisibleEntityDataCache m_cache;
|
|
};
|
|
|
|
TEST_F(EditorEntityVisibilityCacheFixture, LayerLockAffectsChildEntitiesInEditorEntityCache)
|
|
{
|
|
// Given
|
|
CreateLayerAndEntityHierarchy();
|
|
|
|
// Check preconditions.
|
|
EXPECT_FALSE(m_cache.IsVisibleEntityLocked(m_cache.GetVisibleEntityIndexFromId(m_entityIds[0]).value()));
|
|
EXPECT_FALSE(m_cache.IsVisibleEntityLocked(m_cache.GetVisibleEntityIndexFromId(m_entityIds[1]).value()));
|
|
EXPECT_FALSE(m_cache.IsVisibleEntityLocked(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
|
|
|
|
// When
|
|
SetEntityLockState(m_layerId, true);
|
|
|
|
// Then
|
|
EXPECT_TRUE(m_cache.IsVisibleEntityLocked(m_cache.GetVisibleEntityIndexFromId(m_entityIds[0]).value()));
|
|
EXPECT_TRUE(m_cache.IsVisibleEntityLocked(m_cache.GetVisibleEntityIndexFromId(m_entityIds[1]).value()));
|
|
EXPECT_TRUE(m_cache.IsVisibleEntityLocked(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
|
|
}
|
|
|
|
TEST_F(EditorEntityVisibilityCacheFixture, LayerVisibilityAffectsChildEntitiesInEditorEntityCache)
|
|
{
|
|
// Given
|
|
CreateLayerAndEntityHierarchy();
|
|
|
|
// Check preconditions.
|
|
EXPECT_TRUE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[0]).value()));
|
|
EXPECT_TRUE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[1]).value()));
|
|
EXPECT_TRUE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
|
|
|
|
// When
|
|
SetEntityVisibility(m_layerId, false);
|
|
|
|
// Then
|
|
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[0]).value()));
|
|
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[1]).value()));
|
|
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
|
|
}
|
|
|
|
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
|
|
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
|
|
{
|
|
public:
|
|
void SetUpEditorFixtureImpl() override
|
|
{
|
|
m_entity1 = CreateDefaultEditorEntity("Entity1");
|
|
m_entityIds.push_back(m_entity1);
|
|
}
|
|
|
|
void ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation);
|
|
AZStd::optional<AZ::Transform> GetManipulatorTransform() const;
|
|
void RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType refreshType);
|
|
void SetTransformMode(EditorTransformComponentSelectionRequests::Mode transformMode);
|
|
void OverrideManipulatorOrientation(const AZ::Quaternion& orientation);
|
|
void OverrideManipulatorTranslation(const AZ::Vector3& translation);
|
|
|
|
public:
|
|
AZ::EntityId m_entity1;
|
|
EntityIdList m_entityIds;
|
|
};
|
|
|
|
void EditorTransformComponentSelectionFixture::ArrangeIndividualRotatedEntitySelection(const AZ::Quaternion& orientation)
|
|
{
|
|
for (auto entityId : m_entityIds)
|
|
{
|
|
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetLocalRotationQuaternion, orientation);
|
|
}
|
|
}
|
|
|
|
AZStd::optional<AZ::Transform> EditorTransformComponentSelectionFixture::GetManipulatorTransform() const
|
|
{
|
|
AZStd::optional<AZ::Transform> manipulatorTransform;
|
|
EditorTransformComponentSelectionRequestBus::EventResult(
|
|
manipulatorTransform, GetEntityContextId(), &EditorTransformComponentSelectionRequests::GetManipulatorTransform);
|
|
return manipulatorTransform;
|
|
}
|
|
|
|
void EditorTransformComponentSelectionFixture::RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType refreshType)
|
|
{
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequests::RefreshManipulators, refreshType);
|
|
}
|
|
|
|
void EditorTransformComponentSelectionFixture::SetTransformMode(EditorTransformComponentSelectionRequests::Mode transformMode)
|
|
{
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequests::SetTransformMode, transformMode);
|
|
}
|
|
|
|
void EditorTransformComponentSelectionFixture::OverrideManipulatorOrientation(const AZ::Quaternion& orientation)
|
|
{
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorOrientation, orientation);
|
|
}
|
|
|
|
void EditorTransformComponentSelectionFixture::OverrideManipulatorTranslation(const AZ::Vector3& translation)
|
|
{
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequests::OverrideManipulatorTranslation, translation);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// EditorTransformComponentSelection Tests
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, ManipulatorOrientationIsResetWhenEntityOrientationIsReset)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AzToolsFramework::SelectEntity(m_entity1);
|
|
|
|
ArrangeIndividualRotatedEntitySelection(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
|
|
RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType::All);
|
|
|
|
SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation);
|
|
|
|
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
|
|
|
// check preconditions - manipulator transform matches parent/world transform (identity)
|
|
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
|
EXPECT_THAT(manipulatorTransformBefore.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// R - reset entity and manipulator orientation when in Rotation Mode
|
|
QTest::keyPress(&m_editorActions.m_defaultWidget, Qt::Key_R);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
|
|
|
// check postconditions - manipulator transform matches parent/world transform (identity)
|
|
EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
|
EXPECT_THAT(manipulatorTransformAfter.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
|
|
|
|
for (auto entityId : m_entityIds)
|
|
{
|
|
// create invalid starting orientation to guarantee correct data is coming from GetLocalRotationQuaternion
|
|
AZ::Quaternion entityOrientation = AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), 90.0f);
|
|
AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
|
|
|
|
// manipulator orientation matches entity orientation
|
|
EXPECT_THAT(entityOrientation, IsClose(manipulatorTransformAfter.GetRotation()));
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, EntityOrientationRemainsConstantWhenOnlyManipulatorOrientationIsReset)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AzToolsFramework::SelectEntity(m_entity1);
|
|
|
|
const AZ::Quaternion initialEntityOrientation = AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f));
|
|
ArrangeIndividualRotatedEntitySelection(initialEntityOrientation);
|
|
|
|
// assign new orientation to manipulator which does not match entity orientation
|
|
OverrideManipulatorOrientation(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)));
|
|
|
|
SetTransformMode(EditorTransformComponentSelectionRequests::Mode::Rotation);
|
|
|
|
const AZ::Transform manipulatorTransformBefore = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
|
|
|
// check preconditions - manipulator transform matches manipulator orientation override (not entity transform)
|
|
EXPECT_THAT(manipulatorTransformBefore.GetBasisX(), IsClose(AZ::Vector3::CreateAxisY()));
|
|
EXPECT_THAT(manipulatorTransformBefore.GetBasisY(), IsClose(-AZ::Vector3::CreateAxisX()));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// Ctrl+R - reset only manipulator orientation when in Rotation Mode
|
|
QTest::keyPress(&m_editorActions.m_defaultWidget, Qt::Key_R, Qt::ControlModifier);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
const AZ::Transform manipulatorTransformAfter = GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity());
|
|
|
|
// check postconditions - manipulator transform matches parent/world space (manipulator override was cleared)
|
|
EXPECT_THAT(manipulatorTransformAfter.GetBasisY(), IsClose(AZ::Vector3::CreateAxisY()));
|
|
EXPECT_THAT(manipulatorTransformAfter.GetBasisZ(), IsClose(AZ::Vector3::CreateAxisZ()));
|
|
|
|
for (auto entityId : m_entityIds)
|
|
{
|
|
AZ::Quaternion entityOrientation;
|
|
AZ::TransformBus::EventResult(entityOrientation, entityId, &AZ::TransformBus::Events::GetLocalRotationQuaternion);
|
|
|
|
// entity transform matches initial (entity transform was not reset, only manipulator was)
|
|
EXPECT_THAT(entityOrientation, IsClose(initialEntityOrientation));
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, TestComponentPropertyNotificationIsSentAfterModifyingSlice)
|
|
{
|
|
AUTO_RESULT_IF_SETTING_TRUE(UnitTest::prefabSystemSetting, true)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AZ::Entity* grandParent = nullptr;
|
|
AZ::Entity* parent = nullptr;
|
|
AZ::Entity* child = nullptr;
|
|
|
|
AZ::EntityId grandParentId = CreateDefaultEditorEntity("GrandParent", &grandParent);
|
|
AZ::EntityId parentId = CreateDefaultEditorEntity("Parent", &parent);
|
|
AZ::EntityId childId = CreateDefaultEditorEntity("Child", &child);
|
|
|
|
AZ::TransformBus::Event(childId, &AZ::TransformInterface::SetParent, parentId);
|
|
AZ::TransformBus::Event(parentId, &AZ::TransformInterface::SetParent, grandParentId);
|
|
|
|
UnitTest::SliceAssets sliceAssets;
|
|
const auto sliceAssetId = UnitTest::SaveAsSlice({ grandParent }, GetApplication(), sliceAssets);
|
|
|
|
EntityList instantiatedEntities = UnitTest::InstantiateSlice(sliceAssetId, sliceAssets);
|
|
|
|
const AZ::EntityId entityIdToMove = instantiatedEntities.back()->GetId();
|
|
EditorEntityComponentChangeDetector editorEntityChangeDetector(entityIdToMove);
|
|
|
|
AzToolsFramework::SelectEntity(entityIdToMove);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequests::CopyOrientationToSelectedEntitiesIndividual,
|
|
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(90.0f)));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(editorEntityChangeDetector.ChangeDetected());
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
UnitTest::DestroySlices(sliceAssets);
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, InvertSelectionIgnoresLockedAndHiddenEntities)
|
|
{
|
|
using ::testing::UnorderedElementsAreArray;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
// note: entity1 is created in the fixture setup
|
|
AzToolsFramework::SelectEntity(m_entity1);
|
|
|
|
AZ::EntityId entity2 = CreateDefaultEditorEntity("Entity2");
|
|
AZ::EntityId entity3 = CreateDefaultEditorEntity("Entity3");
|
|
AZ::EntityId entity4 = CreateDefaultEditorEntity("Entity4");
|
|
AZ::EntityId entity5 = CreateDefaultEditorEntity("Entity5");
|
|
AZ::EntityId entity6 = CreateDefaultEditorEntity("Entity6");
|
|
|
|
SetEntityVisibility(entity2, false);
|
|
SetEntityLockState(entity3, true);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// 'Invert Selection' shortcut
|
|
QTest::keyPress(&m_editorActions.m_defaultWidget, Qt::Key_I, Qt::ControlModifier | Qt::ShiftModifier);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
AzToolsFramework::EntityIdList selectedEntities;
|
|
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
|
|
|
AzToolsFramework::EntityIdList expectedSelectedEntities = { entity4, entity5, entity6 };
|
|
|
|
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, SelectAllIgnoresLockedAndHiddenEntities)
|
|
{
|
|
using ::testing::UnorderedElementsAreArray;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AZ::EntityId entity2 = CreateDefaultEditorEntity("Entity2");
|
|
AZ::EntityId entity3 = CreateDefaultEditorEntity("Entity3");
|
|
AZ::EntityId entity4 = CreateDefaultEditorEntity("Entity4");
|
|
AZ::EntityId entity5 = CreateDefaultEditorEntity("Entity5");
|
|
AZ::EntityId entity6 = CreateDefaultEditorEntity("Entity6");
|
|
|
|
SetEntityVisibility(entity5, false);
|
|
SetEntityLockState(entity6, true);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// 'Select All' shortcut
|
|
QTest::keyPress(&m_editorActions.m_defaultWidget, Qt::Key_A, Qt::ControlModifier);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
AzToolsFramework::EntityIdList selectedEntities;
|
|
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
|
|
|
AzToolsFramework::EntityIdList expectedSelectedEntities = { m_entity1, entity2, entity3, entity4 };
|
|
|
|
EXPECT_THAT(selectedEntities, UnorderedElementsAreArray(expectedSelectedEntities));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
using EditorTransformComponentSelectionManipulatorTestFixture =
|
|
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
|
|
|
|
TEST_F(EditorTransformComponentSelectionManipulatorTestFixture, CanMoveEntityUsingManipulatorMouseMovement)
|
|
{
|
|
// the initial starting position of the entity (in front and to the left of the camera)
|
|
const auto initialTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, 10.0f, 0.0f));
|
|
// where the entity should end up (in front and to the right of the camera)
|
|
const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f));
|
|
|
|
// calculate the position in screen space of the initial position of the entity
|
|
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
|
|
// calculate the position in screen space of the final position of the entity
|
|
const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
|
|
|
|
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
|
|
AzToolsFramework::SelectEntity(m_entity1);
|
|
// move the entity to its starting position
|
|
AzToolsFramework::SetWorldTransform(m_entity1, initialTransformWorld);
|
|
|
|
// refresh the manipulators so that they update to the position of the entity
|
|
// note: could skip this by selecting the entity after moving it but its useful to have this for reference
|
|
RefreshManipulators(EditorTransformComponentSelectionRequests::RefreshType::All);
|
|
|
|
// create an offset along the linear manipulator pointing along the x-axis (perpendicular to the camera view)
|
|
const auto mouseOffsetOnManipulator = AzFramework::ScreenVector(10, 0);
|
|
// store the mouse down position on the manipulator
|
|
const auto mouseDownPosition = initialPositionScreen + mouseOffsetOnManipulator;
|
|
// final position in screen space of the mouse
|
|
const auto mouseMovePosition = finalPositionScreen + mouseOffsetOnManipulator;
|
|
|
|
m_actionDispatcher->CameraState(m_cameraState)
|
|
->MousePosition(mouseDownPosition)
|
|
->MouseLButtonDown()
|
|
->MousePosition(mouseMovePosition)
|
|
->MouseLButtonUp();
|
|
|
|
// read back the position of the entity now
|
|
const AZ::Transform finalEntityTransform = AzToolsFramework::GetWorldTransform(m_entity1);
|
|
|
|
// ensure final world positions match
|
|
EXPECT_TRUE(finalEntityTransform.IsClose(finalTransformWorld, 0.01f));
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionManipulatorTestFixture, TranslatingEntityWithLinearManipulatorNotifiesOnEntityTransformChanged)
|
|
{
|
|
EditorEntityComponentChangeDetector editorEntityChangeDetector(m_entity1);
|
|
|
|
// the initial starting position of the entity (in front and to the left of the camera)
|
|
const auto initialTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(-10.0f, 10.0f, 0.0f));
|
|
// where the entity should end up (in front and to the right of the camera)
|
|
const auto finalTransformWorld = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 10.0f, 0.0f));
|
|
|
|
// calculate the position in screen space of the initial position of the entity
|
|
const auto initialPositionScreen = AzFramework::WorldToScreen(initialTransformWorld.GetTranslation(), m_cameraState);
|
|
// calculate the position in screen space of the final position of the entity
|
|
const auto finalPositionScreen = AzFramework::WorldToScreen(finalTransformWorld.GetTranslation(), m_cameraState);
|
|
|
|
// move the entity to its starting position
|
|
AzToolsFramework::SetWorldTransform(m_entity1, initialTransformWorld);
|
|
// select the entity (this will cause the manipulators to appear in EditorTransformComponentSelection)
|
|
AzToolsFramework::SelectEntity(m_entity1);
|
|
|
|
// create an offset along the linear manipulator pointing along the x-axis (perpendicular to the camera view)
|
|
const auto mouseOffsetOnManipulator = AzFramework::ScreenVector(10, 0);
|
|
// store the mouse down position on the manipulator
|
|
const auto mouseDownPosition = initialPositionScreen + mouseOffsetOnManipulator;
|
|
// final position in screen space of the mouse
|
|
const auto mouseMovePosition = finalPositionScreen + mouseOffsetOnManipulator;
|
|
|
|
m_actionDispatcher->CameraState(m_cameraState)
|
|
->MousePosition(mouseDownPosition)
|
|
->MouseLButtonDown()
|
|
->MousePosition(mouseMovePosition)
|
|
->MouseLButtonUp();
|
|
|
|
// verify a EditorTransformChangeNotificationBus::OnEntityTransformChanged occurred
|
|
using ::testing::UnorderedElementsAreArray;
|
|
EXPECT_THAT(editorEntityChangeDetector.m_entityIds, UnorderedElementsAreArray(m_entityIds));
|
|
}
|
|
|
|
// simple widget to listen for a mouse wheel event and then forward it on to the ViewportSelectionRequestBus
|
|
class WheelEventWidget : public QWidget
|
|
{
|
|
using MouseInteractionResult = AzToolsFramework::ViewportInteraction::MouseInteractionResult;
|
|
|
|
public:
|
|
WheelEventWidget(QWidget* parent = nullptr)
|
|
: QWidget(parent)
|
|
{
|
|
}
|
|
|
|
void wheelEvent(QWheelEvent* ev) override
|
|
{
|
|
namespace vi = AzToolsFramework::ViewportInteraction;
|
|
vi::MouseInteraction mouseInteraction;
|
|
mouseInteraction.m_interactionId.m_cameraId = AZ::EntityId();
|
|
mouseInteraction.m_interactionId.m_viewportId = 0;
|
|
mouseInteraction.m_mouseButtons = vi::BuildMouseButtons(ev->buttons());
|
|
mouseInteraction.m_mousePick = vi::MousePick();
|
|
mouseInteraction.m_keyboardModifiers = vi::BuildKeyboardModifiers(ev->modifiers());
|
|
|
|
AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus::EventResult(
|
|
m_mouseInteractionResult, AzToolsFramework::GetEntityContextId(),
|
|
&EditorInteractionSystemViewportSelectionRequestBus::Events::InternalHandleAllMouseInteractions,
|
|
vi::MouseInteractionEvent(mouseInteraction, ev->angleDelta().y()));
|
|
}
|
|
|
|
MouseInteractionResult m_mouseInteractionResult;
|
|
};
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, MouseScrollWheelSwitchesTransformMode)
|
|
{
|
|
using ::testing::Eq;
|
|
namespace vi = AzToolsFramework::ViewportInteraction;
|
|
|
|
const auto transformMode = []()
|
|
{
|
|
EditorTransformComponentSelectionRequests::Mode transformMode;
|
|
EditorTransformComponentSelectionRequestBus::EventResult(
|
|
transformMode, GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::GetTransformMode);
|
|
return transformMode;
|
|
};
|
|
|
|
// given
|
|
// preconditions
|
|
EXPECT_THAT(transformMode(), EditorTransformComponentSelectionRequests::Mode::Translation);
|
|
|
|
auto wheelEventWidget = WheelEventWidget();
|
|
// attach the global event filter to the placeholder widget
|
|
AzQtComponents::GlobalEventFilter globalEventFilter(QApplication::instance());
|
|
wheelEventWidget.installEventFilter(&globalEventFilter);
|
|
|
|
// example mouse wheel event (does not yet factor in position of mouse in relation to widget)
|
|
auto wheelEvent = QWheelEvent(
|
|
QPointF(0.0f, 0.0f), QPointF(0.0f, 0.0f), QPoint(0, 1), QPoint(0, 0), Qt::MouseButton::NoButton,
|
|
Qt::KeyboardModifier::ControlModifier, Qt::ScrollPhase::ScrollBegin, false,
|
|
Qt::MouseEventSource::MouseEventSynthesizedBySystem);
|
|
|
|
// when (trigger mouse wheel event)
|
|
QApplication::sendEvent(&wheelEventWidget, &wheelEvent);
|
|
|
|
// then
|
|
// transform mode has changed and mouse event was handled
|
|
EXPECT_THAT(transformMode(), Eq(EditorTransformComponentSelectionRequests::Mode::Rotation));
|
|
EXPECT_THAT(wheelEventWidget.m_mouseInteractionResult, Eq(vi::MouseInteractionResult::Viewport));
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, EntityPositionsCanBeSnappedToGrid)
|
|
{
|
|
using ::testing::Pointwise;
|
|
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
|
|
const AZStd::vector<AZ::Vector3> initialUnsnappedPositions = { AZ::Vector3(1.2f, 3.5f, 6.7f), AZ::Vector3(13.2f, 15.6f, 11.4f),
|
|
AZ::Vector3(4.2f, 103.2f, 16.6f) };
|
|
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[0]);
|
|
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[1]);
|
|
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPositions[2]);
|
|
|
|
AzToolsFramework::SelectEntities(m_entityIds);
|
|
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 2.0f);
|
|
|
|
AZStd::vector<AZ::Vector3> entityPositionsAfterSnap;
|
|
AZStd::transform(
|
|
m_entityIds.cbegin(), m_entityIds.cend(), AZStd::back_inserter(entityPositionsAfterSnap),
|
|
[](const AZ::EntityId& entityId)
|
|
{
|
|
return GetWorldTranslation(entityId);
|
|
});
|
|
|
|
const AZStd::vector<AZ::Vector3> expectedSnappedPositions = { AZ::Vector3(2.0f, 4.0f, 6.0f), AZ::Vector3(14.0f, 16.0f, 12.0f),
|
|
AZ::Vector3(4.0f, 104.0f, 16.0f) };
|
|
EXPECT_THAT(entityPositionsAfterSnap, Pointwise(ContainerIsClose(), expectedSnappedPositions));
|
|
}
|
|
|
|
TEST_F(EditorTransformComponentSelectionFixture, ManipulatorStaysAlignedToEntityTranslationAfterSnap)
|
|
{
|
|
const auto initialUnsnappedPosition = AZ::Vector3(1.2f, 3.5f, 6.7f);
|
|
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetWorldTranslation, initialUnsnappedPosition);
|
|
|
|
AzToolsFramework::SelectEntities(m_entityIds);
|
|
|
|
EditorTransformComponentSelectionRequestBus::Event(
|
|
GetEntityContextId(), &EditorTransformComponentSelectionRequestBus::Events::SnapSelectedEntitiesToWorldGrid, 1.0f);
|
|
|
|
const auto entityPositionAfterSnap = GetWorldTranslation(m_entity1);
|
|
const AZ::Vector3 manipulatorPositionAfterSnap =
|
|
GetManipulatorTransform().value_or(AZ::Transform::CreateIdentity()).GetTranslation();
|
|
|
|
const auto expectedSnappedPosition = AZ::Vector3(1.0f, 4.0f, 7.0f);
|
|
EXPECT_THAT(entityPositionAfterSnap, IsClose(expectedSnappedPosition));
|
|
EXPECT_THAT(expectedSnappedPosition, IsClose(manipulatorPositionAfterSnap));
|
|
}
|
|
|
|
// struct to contain input reference frame and expected orientation outcome based on
|
|
// the reference frame, selection and entity hierarchy
|
|
struct ReferenceFrameWithOrientation
|
|
{
|
|
ReferenceFrame m_referenceFrame; // the input reference frame (Local/Parent/World)
|
|
AZ::Quaternion m_orientation; // the orientation of the manipulator transform
|
|
};
|
|
|
|
// custom orientation to compare against for leaf/child entities (when ReferenceFrame is Local)
|
|
static const AZ::Quaternion ChildExpectedPivotLocalOrientationInWorldSpace =
|
|
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisZ(), AZ::DegToRad(45.0f));
|
|
|
|
// custom orientation to compare against for branch/parent entities (when ReferenceFrame is Parent)
|
|
static const AZ::Quaternion ParentExpectedPivotLocalOrientationInWorldSpace =
|
|
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisX(), AZ::DegToRad(45.0f));
|
|
|
|
// custom orientation to compare against for orientation/pivot override
|
|
static const AZ::Quaternion PivotOverrideLocalOrientationInWorldSpace =
|
|
AZ::Quaternion::CreateFromAxisAngle(AZ::Vector3::CreateAxisY(), AZ::DegToRad(90.0f));
|
|
|
|
class EditorTransformComponentSelectionSingleEntityPivotFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(EditorTransformComponentSelectionSingleEntityPivotFixture, PivotOrientationMatchesReferenceFrameSingleEntity)
|
|
{
|
|
using ETCS::CalculatePivotOrientation;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculatePivotOrientation(m_entityIds[0], referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionSingleEntityPivotFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionSingleEntityWithParentPivotFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(EditorTransformComponentSelectionSingleEntityWithParentPivotFixture, PivotOrientationMatchesReferenceFrameEntityWithParent)
|
|
{
|
|
using ETCS::CalculatePivotOrientation;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
const AZ::EntityId parentEntityId = CreateDefaultEditorEntity("Parent");
|
|
AZ::TransformBus::Event(m_entityIds[0], &AZ::TransformBus::Events::SetParent, parentEntityId);
|
|
|
|
AZ::TransformBus::Event(
|
|
parentEntityId, &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(
|
|
ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculatePivotOrientation(m_entityIds[0], referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// with a single entity selected with a parent the orientation reference frames follow as you'd expect
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionSingleEntityWithParentPivotFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionMultipleEntitiesPivotFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(EditorTransformComponentSelectionMultipleEntitiesPivotFixture, PivotOrientationMatchesReferenceFrameMultipleEntities)
|
|
{
|
|
using ETCS::CalculatePivotOrientationForEntityIds;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
|
|
// setup entities in arbitrary triangle arrangement
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
|
|
|
// note: EntityIdManipulatorLookup{} is unused during this test
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[1], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculatePivotOrientationForEntityIds(lookups, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// with a group selection, when the entities are not in a hierarchy, no matter what reference frame,
|
|
// we will always get an orientation aligned to the world
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionMultipleEntitiesPivotFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(
|
|
EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture,
|
|
PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParent)
|
|
{
|
|
using ETCS::CalculatePivotOrientationForEntityIds;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(
|
|
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
|
|
|
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
|
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
|
|
|
// note: EntityIdManipulatorLookup{} is unused during this test
|
|
// only select second two entities that are children of m_entityIds[0]
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculatePivotOrientationForEntityIds(lookups, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// here two entities are selected with the same parent - local and parent will match parent space, with world
|
|
// giving the identity (aligned to world axes)
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionMultipleEntitiesWithSameParentPivotFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ParentExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(
|
|
EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture,
|
|
PivotOrientationMatchesReferenceFrameMultipleEntitiesDifferentParent)
|
|
{
|
|
using ETCS::CalculatePivotOrientationForEntityIds;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity4"));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(
|
|
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
|
|
|
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
|
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[3]);
|
|
|
|
// note: EntityIdManipulatorLookup{} is unused during this test
|
|
// only select second two entities that are children of different m_entities
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculatePivotOrientationForEntityIds(lookups, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// if multiple entities are selected without a parent in common, orientation will always be world again
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionMultipleEntitiesWithDifferentParentPivotFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(
|
|
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
|
|
PivotOrientationMatchesReferenceFrameSingleEntityOptionalOverride)
|
|
{
|
|
using ETCS::CalculateSelectionPivotOrientation;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(ChildExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateZero()));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} } };
|
|
|
|
// set override frame (orientation only)
|
|
OptionalFrame optionalFrame;
|
|
optionalFrame.m_orientationOverride = PivotOverrideLocalOrientationInWorldSpace;
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculateSelectionPivotOrientation(lookups, optionalFrame, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// local reference frame will still return local orientation for entity, but pivot override will trump parent
|
|
// space (world will still give identity alignment for axes)
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(
|
|
EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture,
|
|
PivotOrientationMatchesReferenceFrameMultipleEntitiesOptionalOverride)
|
|
{
|
|
using ETCS::CalculateSelectionPivotOrientation;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
|
|
|
// note: EntityIdManipulatorLookup{} is unused during this test
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[1], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
OptionalFrame optionalFrame;
|
|
optionalFrame.m_orientationOverride = PivotOverrideLocalOrientationInWorldSpace;
|
|
|
|
const PivotOrientationResult pivotResult =
|
|
CalculateSelectionPivotOrientation(lookups, optionalFrame, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// with multiple entities selected, override frame wins in both local and parent reference frames
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionMultipleEntitiesPivotAndOverrideFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, PivotOverrideLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(
|
|
EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture,
|
|
PivotOrientationMatchesReferenceFrameMultipleEntitiesNoOptionalOverride)
|
|
{
|
|
using ETCS::CalculateSelectionPivotOrientation;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(-10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
|
|
|
// note: EntityIdManipulatorLookup{} is unused during this test
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[0], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[1], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
OptionalFrame optionalFrame;
|
|
const PivotOrientationResult pivotResult =
|
|
CalculateSelectionPivotOrientation(lookups, optionalFrame, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// multiple entities selected (no hierarchy) always get world aligned axes (identity)
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionMultipleEntitiesPivotAndNoOverrideFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, AZ::Quaternion::CreateIdentity() },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture
|
|
: public EditorTransformComponentSelectionFixture
|
|
, public ::testing::WithParamInterface<ReferenceFrameWithOrientation>
|
|
{
|
|
};
|
|
|
|
TEST_P(
|
|
EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture,
|
|
PivotOrientationMatchesReferenceFrameMultipleEntitiesSameParentNoOptionalOverride)
|
|
{
|
|
using ETCS::CalculateSelectionPivotOrientation;
|
|
using ETCS::PivotOrientationResult;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity2"));
|
|
m_entityIds.push_back(CreateDefaultEditorEntity("Entity3"));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[0], &AZ::TransformBus::Events::SetWorldTM,
|
|
AZ::Transform::CreateFromQuaternionAndTranslation(
|
|
ParentExpectedPivotLocalOrientationInWorldSpace, AZ::Vector3::CreateAxisZ(-5.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[1], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX(10.0f)));
|
|
|
|
AZ::TransformBus::Event(
|
|
m_entityIds[2], &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(10.0f)));
|
|
|
|
AZ::TransformBus::Event(m_entityIds[1], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
|
AZ::TransformBus::Event(m_entityIds[2], &AZ::TransformBus::Events::SetParent, m_entityIds[0]);
|
|
|
|
// note: EntityIdManipulatorLookup{} is unused during this test
|
|
EntityIdManipulatorLookups lookups{ { m_entityIds[1], EntityIdManipulatorLookup{} },
|
|
{ m_entityIds[2], EntityIdManipulatorLookup{} } };
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
const ReferenceFrameWithOrientation referenceFrameWithOrientation = GetParam();
|
|
|
|
OptionalFrame optionalFrame;
|
|
const PivotOrientationResult pivotResult =
|
|
CalculateSelectionPivotOrientation(lookups, optionalFrame, referenceFrameWithOrientation.m_referenceFrame);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_THAT(pivotResult.m_worldOrientation, IsClose(referenceFrameWithOrientation.m_orientation));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// no optional frame, same parent, local and parent both get parent alignment (world reference frame
|
|
// gives world alignment (identity))
|
|
INSTANTIATE_TEST_CASE_P(
|
|
All,
|
|
EditorTransformComponentSelectionMultipleEntitiesSameParentPivotAndNoOverrideFixture,
|
|
testing::Values(
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Local, ParentExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::Parent, ParentExpectedPivotLocalOrientationInWorldSpace },
|
|
ReferenceFrameWithOrientation{ ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
|
|
|
|
class EditorEntityModelVisibilityFixture
|
|
: public ToolsApplicationFixture
|
|
, private EditorEntityVisibilityNotificationBus::Router
|
|
, private EditorEntityInfoNotificationBus::Handler
|
|
{
|
|
public:
|
|
void SetUpEditorFixtureImpl() override
|
|
{
|
|
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
|
|
EditorEntityInfoNotificationBus::Handler::BusConnect();
|
|
}
|
|
|
|
void TearDownEditorFixtureImpl() override
|
|
{
|
|
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
|
|
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
|
|
}
|
|
|
|
bool m_entityInfoUpdatedVisibilityForLayer = false;
|
|
AZ::EntityId m_layerId;
|
|
|
|
private:
|
|
// EditorEntityVisibilityNotificationBus ...
|
|
void OnEntityVisibilityChanged(bool /*visibility*/) override
|
|
{
|
|
// for debug purposes
|
|
}
|
|
|
|
// EditorEntityInfoNotificationBus ...
|
|
void OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool /*visible*/) override
|
|
{
|
|
if (entityId == m_layerId)
|
|
{
|
|
m_entityInfoUpdatedVisibilityForLayer = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
// all entities in a layer are the same state, modifying the layer
|
|
// will also notify the UI to refresh
|
|
TEST_F(EditorEntityModelVisibilityFixture, LayerVisibilityNotifiesEditorEntityModelState)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
const AZ::EntityId a = CreateDefaultEditorEntity("A");
|
|
const AZ::EntityId b = CreateDefaultEditorEntity("B");
|
|
const AZ::EntityId c = CreateDefaultEditorEntity("C");
|
|
|
|
m_layerId = CreateEditorLayerEntity("Layer");
|
|
|
|
AZ::TransformBus::Event(a, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(b, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(c, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
SetEntityVisibility(a, false);
|
|
SetEntityVisibility(b, false);
|
|
SetEntityVisibility(c, false);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_FALSE(IsEntityVisible(a));
|
|
EXPECT_FALSE(IsEntityVisible(b));
|
|
EXPECT_FALSE(IsEntityVisible(c));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
SetEntityVisibility(m_layerId, false);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_FALSE(IsEntityVisible(m_layerId));
|
|
EXPECT_TRUE(m_entityInfoUpdatedVisibilityForLayer);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// reset property
|
|
m_entityInfoUpdatedVisibilityForLayer = false;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
SetEntityVisibility(m_layerId, true);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(m_entityInfoUpdatedVisibilityForLayer);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
TEST_F(EditorEntityModelVisibilityFixture, UnhidingEntityInInvisibleLayerUnhidesAllEntitiesThatWereNotIndividuallyHidden)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
const AZ::EntityId a = CreateDefaultEditorEntity("A");
|
|
const AZ::EntityId b = CreateDefaultEditorEntity("B");
|
|
const AZ::EntityId c = CreateDefaultEditorEntity("C");
|
|
|
|
const AZ::EntityId d = CreateDefaultEditorEntity("D");
|
|
const AZ::EntityId e = CreateDefaultEditorEntity("E");
|
|
const AZ::EntityId f = CreateDefaultEditorEntity("F");
|
|
|
|
m_layerId = CreateEditorLayerEntity("Layer1");
|
|
const AZ::EntityId secondLayerId = CreateEditorLayerEntity("Layer2");
|
|
|
|
AZ::TransformBus::Event(a, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(b, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(c, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
|
|
AZ::TransformBus::Event(secondLayerId, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
|
|
AZ::TransformBus::Event(d, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
|
AZ::TransformBus::Event(e, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
|
AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
|
|
|
// Layer1
|
|
// A
|
|
// B
|
|
// C
|
|
// Layer2
|
|
// D
|
|
// E
|
|
// F
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// hide top layer
|
|
SetEntityVisibility(m_layerId, false);
|
|
|
|
// hide a and c (a and see are 'set' not to be visible and are not visible)
|
|
SetEntityVisibility(a, false);
|
|
SetEntityVisibility(c, false);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(!IsEntityVisible(a));
|
|
EXPECT_TRUE(!IsEntitySetToBeVisible(a));
|
|
|
|
// b will not be visible but is not 'set' to be hidden
|
|
EXPECT_TRUE(!IsEntityVisible(b));
|
|
EXPECT_TRUE(IsEntitySetToBeVisible(b));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// same for nested layer
|
|
SetEntityVisibility(secondLayerId, false);
|
|
|
|
SetEntityVisibility(d, false);
|
|
SetEntityVisibility(f, false);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(!IsEntityVisible(e));
|
|
EXPECT_TRUE(IsEntitySetToBeVisible(e));
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// set visibility of most nested entity to true
|
|
SetEntityVisibility(d, true);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(IsEntitySetToBeVisible(m_layerId));
|
|
EXPECT_TRUE(IsEntitySetToBeVisible(secondLayerId));
|
|
|
|
// a will still be set to be not visible and won't be visible as parent layer is now visible
|
|
EXPECT_TRUE(!IsEntitySetToBeVisible(a));
|
|
EXPECT_TRUE(!IsEntityVisible(a));
|
|
|
|
// b will now be visible as it was not individually
|
|
// set to be visible and the parent layer is now visible
|
|
EXPECT_TRUE(IsEntitySetToBeVisible(b));
|
|
EXPECT_TRUE(IsEntityVisible(b));
|
|
|
|
// same story for e as for b
|
|
EXPECT_TRUE(IsEntitySetToBeVisible(e));
|
|
EXPECT_TRUE(IsEntityVisible(e));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
TEST_F(EditorEntityModelVisibilityFixture, UnlockingEntityInLockedLayerUnlocksAllEntitiesThatWereNotIndividuallyLocked)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
const AZ::EntityId a = CreateDefaultEditorEntity("A");
|
|
const AZ::EntityId b = CreateDefaultEditorEntity("B");
|
|
const AZ::EntityId c = CreateDefaultEditorEntity("C");
|
|
|
|
const AZ::EntityId d = CreateDefaultEditorEntity("D");
|
|
const AZ::EntityId e = CreateDefaultEditorEntity("E");
|
|
const AZ::EntityId f = CreateDefaultEditorEntity("F");
|
|
|
|
m_layerId = CreateEditorLayerEntity("Layer1");
|
|
const AZ::EntityId secondLayerId = CreateEditorLayerEntity("Layer2");
|
|
|
|
AZ::TransformBus::Event(a, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(b, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(c, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
|
|
AZ::TransformBus::Event(secondLayerId, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
|
|
AZ::TransformBus::Event(d, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
|
AZ::TransformBus::Event(e, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
|
AZ::TransformBus::Event(f, &AZ::TransformBus::Events::SetParent, secondLayerId);
|
|
|
|
// Layer1
|
|
// A
|
|
// B
|
|
// C
|
|
// Layer2
|
|
// D
|
|
// E
|
|
// F
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// lock top layer
|
|
SetEntityLockState(m_layerId, true);
|
|
|
|
// lock a and c (a and see are 'set' not to be visible and are not visible)
|
|
SetEntityLockState(a, true);
|
|
SetEntityLockState(c, true);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(IsEntityLocked(a));
|
|
EXPECT_TRUE(IsEntitySetToBeLocked(a));
|
|
|
|
// b will be locked but is not 'set' to be locked
|
|
EXPECT_TRUE(IsEntityLocked(b));
|
|
EXPECT_TRUE(!IsEntitySetToBeLocked(b));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// same for nested layer
|
|
SetEntityLockState(secondLayerId, true);
|
|
|
|
SetEntityLockState(d, true);
|
|
SetEntityLockState(f, true);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(IsEntityLocked(e));
|
|
EXPECT_TRUE(!IsEntitySetToBeLocked(e));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// set visibility of most nested entity to true
|
|
SetEntityLockState(d, false);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(!IsEntitySetToBeLocked(m_layerId));
|
|
EXPECT_TRUE(!IsEntitySetToBeLocked(secondLayerId));
|
|
|
|
// a will still be set to be not visible and won't be visible as parent layer is now visible
|
|
EXPECT_TRUE(IsEntitySetToBeLocked(a));
|
|
EXPECT_TRUE(IsEntityLocked(a));
|
|
|
|
// b will now be visible as it was not individually
|
|
// set to be visible and the parent layer is now visible
|
|
EXPECT_TRUE(!IsEntitySetToBeLocked(b));
|
|
EXPECT_TRUE(!IsEntityLocked(b));
|
|
|
|
// same story for e as for b
|
|
EXPECT_TRUE(!IsEntitySetToBeLocked(e));
|
|
EXPECT_TRUE(!IsEntityLocked(e));
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
// test to ensure the visibility flag on a layer entity is not modified
|
|
// instead we rely on SetLayerChildrenVisibility and AreLayerChildrenVisible
|
|
TEST_F(EditorEntityModelVisibilityFixture, LayerEntityVisibilityFlagIsNotModified)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
const AZ::EntityId a = CreateDefaultEditorEntity("A");
|
|
const AZ::EntityId b = CreateDefaultEditorEntity("B");
|
|
const AZ::EntityId c = CreateDefaultEditorEntity("C");
|
|
|
|
m_layerId = CreateEditorLayerEntity("Layer1");
|
|
|
|
AZ::TransformBus::Event(a, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(b, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
AZ::TransformBus::Event(c, &AZ::TransformBus::Events::SetParent, m_layerId);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
SetEntityVisibility(m_layerId, false);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(!IsEntitySetToBeVisible(m_layerId));
|
|
EXPECT_TRUE(!IsEntityVisible(m_layerId));
|
|
|
|
bool flagSetVisible = false;
|
|
EditorVisibilityRequestBus::EventResult(flagSetVisible, m_layerId, &EditorVisibilityRequestBus::Events::GetVisibilityFlag);
|
|
|
|
// even though a layer is set to not be visible, this is recorded by SetLayerChildrenVisibility
|
|
// and AreLayerChildrenVisible - the visibility flag will not be modified and remains true
|
|
EXPECT_TRUE(flagSetVisible);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
class EditorEntityInfoRequestActivateTestComponent : public AzToolsFramework::Components::EditorComponentBase
|
|
{
|
|
public:
|
|
AZ_EDITOR_COMPONENT(
|
|
EditorEntityInfoRequestActivateTestComponent,
|
|
"{849DA1FC-6A0C-4CB8-A0BB-D90DEE7FF7F7}",
|
|
AzToolsFramework::Components::EditorComponentBase);
|
|
|
|
static void Reflect(AZ::ReflectContext* context);
|
|
|
|
// AZ::Component ...
|
|
void Activate() override
|
|
{
|
|
// ensure we can successfully read IsVisible and IsLocked (bus will be connected to in entity Init)
|
|
EditorEntityInfoRequestBus::EventResult(m_visible, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsVisible);
|
|
EditorEntityInfoRequestBus::EventResult(m_locked, GetEntityId(), &EditorEntityInfoRequestBus::Events::IsLocked);
|
|
}
|
|
|
|
void Deactivate() override
|
|
{
|
|
}
|
|
|
|
bool m_visible = false;
|
|
bool m_locked = true;
|
|
};
|
|
|
|
void EditorEntityInfoRequestActivateTestComponent::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
|
{
|
|
serializeContext->Class<EditorEntityInfoRequestActivateTestComponent>()->Version(0);
|
|
}
|
|
}
|
|
|
|
class EditorEntityModelEntityInfoRequestFixture : public ToolsApplicationFixture
|
|
{
|
|
public:
|
|
void SetUpEditorFixtureImpl() override
|
|
{
|
|
GetApplication()->RegisterComponentDescriptor(EditorEntityInfoRequestActivateTestComponent::CreateDescriptor());
|
|
}
|
|
};
|
|
|
|
TEST_F(EditorEntityModelEntityInfoRequestFixture, EditorEntityInfoRequestBusRespondsInComponentActivate)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AZ::Entity* entity = nullptr;
|
|
const AZ::EntityId entityId = CreateDefaultEditorEntity("Entity", &entity);
|
|
|
|
entity->Deactivate();
|
|
const auto* entityInfoComponent = entity->CreateComponent<EditorEntityInfoRequestActivateTestComponent>();
|
|
|
|
// This is necessary to prevent a warning in the undo system.
|
|
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
|
|
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId());
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
entity->Activate();
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_TRUE(entityInfoComponent->m_visible);
|
|
EXPECT_FALSE(entityInfoComponent->m_locked);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
TEST_F(EditorEntityModelEntityInfoRequestFixture, EditorEntityInfoRequestBusRespondsInComponentActivateInLayer)
|
|
{
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Given
|
|
AZ::Entity* entity = nullptr;
|
|
const AZ::EntityId entityId = CreateDefaultEditorEntity("Entity", &entity);
|
|
const AZ::EntityId layerId = CreateEditorLayerEntity("Layer");
|
|
|
|
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetParent, layerId);
|
|
|
|
SetEntityVisibility(layerId, false);
|
|
SetEntityLockState(layerId, true);
|
|
|
|
entity->Deactivate();
|
|
auto* entityInfoComponent = entity->CreateComponent<EditorEntityInfoRequestActivateTestComponent>();
|
|
|
|
// This is necessary to prevent a warning in the undo system.
|
|
AzToolsFramework::ToolsApplicationRequests::Bus::Broadcast(
|
|
&AzToolsFramework::ToolsApplicationRequests::Bus::Events::AddDirtyEntity, entity->GetId());
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// When
|
|
// invert initial state to be sure we know Activate does what it's supposed to
|
|
entityInfoComponent->m_visible = true;
|
|
entityInfoComponent->m_locked = false;
|
|
entity->Activate();
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Then
|
|
EXPECT_FALSE(entityInfoComponent->m_visible);
|
|
EXPECT_TRUE(entityInfoComponent->m_locked);
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
} // namespace UnitTest
|