Update to how entity space is treated in the viewport (#4263)

* first pass fixes for how entity space is handled in the viewport interaction model

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* small updates to simplify space handling

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* fix for influence group with one entity selected

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* some tidy-up and fix for scale manipulator snapping

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* couple of small fixes for scale manipulator

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* small comment update

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* fixes for integration test failures

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* add test for rotation manipulator

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* add test coverage for rotation manipulators

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* add tests for translation manipulators and some other tidy-up changes

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* add tests for translating a group of entities

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* add some tests for scale manipulators

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* some updates and formatting changes (clang-format) to ViewportScreen

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* simplify usage of lround code

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* update missed name updates

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* update usage of WorldToScreenNdc

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* updates following review feedback

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* some more small tidy-up changes

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* move static variables to be marked inline

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* small formatting fixes

Signed-off-by: hultonha <hultonha@amazon.co.uk>
This commit is contained in:
hultonha
2021-09-28 09:26:20 +01:00
committed by GitHub
parent 96d99c2814
commit 7da866e81a
10 changed files with 868 additions and 224 deletions
@@ -242,11 +242,11 @@ namespace UnitTest
{
// the initial starting position of the entities
AZ::TransformBus::Event(
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity1WorldTranslation));
m_entityId1, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity1WorldTranslation));
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity2WorldTranslation));
m_entityId2, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity2WorldTranslation));
AZ::TransformBus::Event(
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(m_entity3WorldTranslation));
m_entityId3, &AZ::TransformBus::Events::SetWorldTM, AZ::Transform::CreateTranslation(Entity3WorldTranslation));
}
static void PositionCamera(AzFramework::CameraState& cameraState)
@@ -261,9 +261,10 @@ namespace UnitTest
AZ::EntityId m_entityId1;
AZ::EntityId m_entityId2;
AZ::EntityId m_entityId3;
AZ::Vector3 m_entity1WorldTranslation = AZ::Vector3(5.0f, 15.0f, 10.0f);
AZ::Vector3 m_entity2WorldTranslation = AZ::Vector3(5.0f, 14.0f, 10.0f);
AZ::Vector3 m_entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f);
static inline const AZ::Vector3 Entity1WorldTranslation = AZ::Vector3(5.0f, 15.0f, 10.0f);
static inline const AZ::Vector3 Entity2WorldTranslation = AZ::Vector3(5.0f, 14.0f, 10.0f);
static inline const AZ::Vector3 Entity3WorldTranslation = AZ::Vector3(5.0f, 16.0f, 10.0f);
};
void ArrangeIndividualRotatedEntitySelection(const AzToolsFramework::EntityIdList& entityIds, const AZ::Quaternion& orientation)
@@ -371,16 +372,16 @@ namespace UnitTest
// Given
AzToolsFramework::SelectEntity(m_entityId1);
ArrangeIndividualRotatedEntitySelection(m_entityIds, AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
const auto entityTransform = AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)));
ArrangeIndividualRotatedEntitySelection(m_entityIds, entityTransform.GetRotation());
RefreshManipulators(EditorTransformComponentSelectionRequestBus::Events::RefreshType::All);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::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()));
// check preconditions - manipulator transform matches the entity transform
EXPECT_THAT(manipulatorTransformBefore, IsClose(entityTransform));
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -624,7 +625,7 @@ namespace UnitTest
EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position
const auto entity1ScreenPosition = AzFramework::WorldToScreen(m_entity1WorldTranslation, m_cameraState);
const auto entity1ScreenPosition = AzFramework::WorldToScreen(Entity1WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -649,7 +650,7 @@ namespace UnitTest
EXPECT_TRUE(selectedEntitiesBefore.empty());
// calculate the position in screen space of the initial entity position
const auto entity1ScreenPosition = AzFramework::WorldToScreen(m_entity1WorldTranslation, m_cameraState);
const auto entity1ScreenPosition = AzFramework::WorldToScreen(Entity1WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -728,7 +729,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -754,7 +755,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -780,7 +781,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -806,7 +807,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -832,7 +833,7 @@ namespace UnitTest
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(true)
@@ -858,7 +859,7 @@ namespace UnitTest
AzToolsFramework::SelectEntities({ m_entityId1, m_entityId2 });
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// click the entity in the viewport
m_actionDispatcher->SetStickySelect(false)
@@ -1001,7 +1002,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// single click select entity2
m_actionDispatcher->SetStickySelect(false)
@@ -1035,7 +1036,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// single click select entity2
m_actionDispatcher->SetStickySelect(GetParam())
@@ -1056,7 +1057,7 @@ namespace UnitTest
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
}
TEST_P(
@@ -1069,7 +1070,7 @@ namespace UnitTest
AzToolsFramework::SelectEntity(m_entityId1);
// calculate the position in screen space of the second entity
const auto entity2ScreenPosition = AzFramework::WorldToScreen(m_entity2WorldTranslation, m_cameraState);
const auto entity2ScreenPosition = AzFramework::WorldToScreen(Entity2WorldTranslation, m_cameraState);
// position in space above the entities
const auto clickOffPositionWorld = AZ::Vector3(5.0f, 15.0f, 12.0f);
@@ -1096,7 +1097,7 @@ namespace UnitTest
manipulatorTransform, AzToolsFramework::GetEntityContextId(),
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity2WorldTranslation));
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity2WorldTranslation));
})
->MousePosition(clickOffPositionScreen)
->KeyboardModifierDown(AzToolsFramework::ViewportInteraction::KeyboardModifier::Control)
@@ -1113,11 +1114,560 @@ namespace UnitTest
&AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform);
// manipulator transform is reset
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(m_entity1WorldTranslation));
EXPECT_THAT(manipulatorTransform->GetTranslation(), IsClose(Entity1WorldTranslation));
}
INSTANTIATE_TEST_CASE_P(All, EditorTransformComponentSelectionViewportPickingManipulatorTestFixtureParam, testing::Values(true, false));
// create alias for EditorTransformComponentSelectionViewportPickingManipulatorTestFixture to help group tests
using EditorTransformComponentSelectionManipulatorInteractionTestFixture =
EditorTransformComponentSelectionViewportPickingManipulatorTestFixture;
// type to group related inputs and outcomes for parameterized tests (single entity)
struct ManipulatorOptionsSingle
{
AzToolsFramework::ViewportInteraction::KeyboardModifier m_keyboardModifier;
AZ::Transform m_expectedManipulatorTransformAfter;
AZ::Transform m_expectedEntityTransformAfter;
};
class EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsSingle>
{
};
TEST_P(
EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam,
RotatingASingleEntityWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
PositionCamera(m_cameraState);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
AzToolsFramework::SelectEntity(m_entityId1);
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(Entity1WorldTranslation, m_cameraState);
const float manipulatorRadius = 2.0f * screenToWorldMultiplier;
const auto rotationManipulatorStartHoldWorldPosition = Entity1WorldTranslation +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-45.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
const auto rotationManipulatorEndHoldWorldPosition = Entity1WorldTranslation +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-135.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
// calculate screen space positions
const auto rotationManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorStartHoldWorldPosition, m_cameraState);
const auto rotationManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorEndHoldWorldPosition, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(rotationManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(rotationManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntityTransform = GetParam().m_expectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransform = GetManipulatorTransform();
const auto entityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
EXPECT_THAT(*manipulatorTransform, IsClose(expectedManipulatorTransform));
EXPECT_THAT(entityTransform, IsClose(expectedEntityTransform));
}
static const AZ::Transform ExpectedTransformAfterLocalRotationManipulatorMotion = AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)),
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation);
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionRotationManipulatorSingleEntityTestFixtureParam,
testing::Values(
// this replicates rotating an entity in local space with no modifiers held
// manipulator and entity rotate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating an entity in local space with the alt modifier held
// manipulator and entity rotate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating an entity in world space with the shift modifier held
// entity rotates, manipulator remains aligned to world
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation),
ExpectedTransformAfterLocalRotationManipulatorMotion },
// this replicates rotating the manipulator in local space with the ctrl modifier held (entity is unchanged)
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalRotationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation) }));
// type to group related inputs and outcomes for parameterized tests (two entities)
struct ManipulatorOptionsMultiple
{
AzToolsFramework::ViewportInteraction::KeyboardModifier m_keyboardModifier;
AZ::Transform m_expectedManipulatorTransformAfter;
AZ::Transform m_firstExpectedEntityTransformAfter;
AZ::Transform m_secondExpectedEntityTransformAfter;
};
class EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
TEST_P(
EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam,
RotatingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
PositionCamera(m_cameraState);
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Rotation);
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
// manipulator should be centered between the two entities
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier =
AzToolsFramework::CalculateScreenToWorldMultiplier(initialManipulatorTransform->GetTranslation(), m_cameraState);
const float manipulatorRadius = 2.0f * screenToWorldMultiplier;
const auto rotationManipulatorStartHoldWorldPosition = initialManipulatorTransform->GetTranslation() +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-45.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
const auto rotationManipulatorEndHoldWorldPosition = initialManipulatorTransform->GetTranslation() +
AZ::Quaternion::CreateRotationX(AZ::DegToRad(-135.0f)).TransformVector(AZ::Vector3::CreateAxisY(-manipulatorRadius));
// calculate screen space positions
const auto rotationManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorStartHoldWorldPosition, m_cameraState);
const auto rotationManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(rotationManipulatorEndHoldWorldPosition, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(rotationManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(rotationManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsClose(expectedManipulatorTransform));
EXPECT_THAT(entity2Transform, IsClose(expectedEntity2Transform));
EXPECT_THAT(entity3Transform, IsClose(expectedEntity3Transform));
}
// note: The aggregate manipulator position will be the average of entity 2 and 3 combined which
// winds up being the same as entity 1
static const AZ::Vector3 AggregateManipulatorPositionWithEntity2and3Selected =
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation;
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion =
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f))) *
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(-1.0f));
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion =
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f))) *
AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisY(1.0f));
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualRotationManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualRotationManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionRotationManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates rotating a group of entities in local space with no modifiers held
// manipulator and entity rotate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion },
// this replicates rotating a group of entities in local space with the alt modifier held
// manipulator and entity rotate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalRotationManipulatorMotion,
ExpectedEntity2TransformAfterLocalIndividualRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualRotationManipulatorMotion },
// this replicates rotating a group of entities in world space with the shift modifier held
// entity rotates, manipulator remains aligned to world
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation),
ExpectedEntity2TransformAfterLocalGroupRotationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupRotationManipulatorMotion },
// this replicates rotating the manipulator in local space with the ctrl modifier held (entity is unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalRotationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
class EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsSingle>
{
};
static const float LinearManipulatorYAxisMovement = -3.0f;
static const float LinearManipulatorZAxisMovement = 2.0f;
TEST_P(
EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam,
TranslatingASingleEntityWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 14.5, 11.0f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
AzToolsFramework::SelectEntity(m_entityId1);
const auto entity1Transform = AzToolsFramework::GetWorldTransform(m_entityId1);
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation(), m_cameraState);
// calculate positions for two click and drag motions (moving a linear manipulator)
// begin each click in the center of the line of the linear manipulators
const auto translationManipulatorStartHoldWorldPosition1 =
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() + entity1Transform.GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement);
const auto translationManipulatorStartHoldWorldPosition2 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement) - entity1Transform.GetBasisY() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition2 =
translationManipulatorStartHoldWorldPosition2 + AZ::Vector3::CreateAxisY(LinearManipulatorYAxisMovement);
// transform to screen space
const auto translationManipulatorStartHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
const auto translationManipulatorStartHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition2, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition2, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(translationManipulatorStartHoldScreenPosition1)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition1)
->MouseLButtonUp()
->MousePosition(translationManipulatorStartHoldScreenPosition2)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition2)
->MouseLButtonUp();
const auto expectedEntityTransform = GetParam().m_expectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransform = GetManipulatorTransform();
const auto entityTransform = AzToolsFramework::GetWorldTransform(m_entityId1);
EXPECT_THAT(*manipulatorTransform, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entityTransform, IsCloseTolerance(expectedEntityTransform, 0.01f));
}
static const AZ::Transform ExpectedTransformAfterLocalTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
// where the manipulator should end up after the input from TranslatingMultipleEntitiesWithDifferentModifierCombinations
static const AZ::Transform ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
AggregateManipulatorPositionWithEntity2and3Selected +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionTranslationManipulatorSingleEntityTestFixtureParam,
testing::Values(
// this replicates translating an entity in local space with no modifiers held
// manipulator and entity translate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating an entity in local space with the alt modifier held
// manipulator and entity translate (to the user, equivalent to no modifiers with one entity selected)
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating an entity in world space with the shift modifier held
// manipulator and entity translate
ManipulatorOptionsSingle{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
ExpectedTransformAfterLocalTranslationManipulatorMotion,
ExpectedTransformAfterLocalTranslationManipulatorMotion },
// this replicates translating the manipulator in local space with the ctrl modifier held
// entity is unchanged, manipulator moves
ManipulatorOptionsSingle{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl, ExpectedTransformAfterLocalTranslationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity1WorldTranslation) }));
class EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
static const AZ::Transform Entity2RotationForLocalTranslation =
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)));
TEST_P(
EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam,
TranslatingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 14.5, 11.0f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Translation);
// give entity 2 a different orientation to entity 3 so when moving in local space their translation vectors will be different
AZ::TransformBus::Event(
m_entityId2, &AZ::TransformBus::Events::SetWorldRotationQuaternion, Entity2RotationForLocalTranslation.GetRotation());
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier = AzToolsFramework::CalculateScreenToWorldMultiplier(
AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation(), m_cameraState);
// calculate positions for two click and drag motions (moving a linear manipulator)
// begin each click in the center of the line of the linear manipulators
const auto translationManipulatorStartHoldWorldPosition1 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
initialManipulatorTransform->GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement);
const auto translationManipulatorStartHoldWorldPosition2 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovement) - initialManipulatorTransform->GetBasisY() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition2 =
translationManipulatorStartHoldWorldPosition2 + AZ::Vector3::CreateAxisY(LinearManipulatorYAxisMovement);
// transform to screen space
const auto translationManipulatorStartHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition1 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
const auto translationManipulatorStartHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition2, m_cameraState);
const auto translationManipulatorEndHoldScreenPosition2 =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition2, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(translationManipulatorStartHoldScreenPosition1)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition1)
->MouseLButtonUp()
->MousePosition(translationManipulatorStartHoldScreenPosition2)
->MouseLButtonDown()
->MousePosition(translationManipulatorEndHoldScreenPosition2)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entity2Transform, IsCloseTolerance(expectedEntity2Transform, 0.01f));
EXPECT_THAT(entity3Transform, IsCloseTolerance(expectedEntity3Transform, 0.01f));
}
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion =
AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement)) *
Entity2RotationForLocalTranslation;
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
// note: as entity has been rotated by 90 degrees about Z in TranslatingMultipleEntitiesWithDifferentModifierCombinations then
// LinearManipulatorYAxisMovement is now aligned to the world x-axis
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualTranslationManipulatorMotion =
AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation +
AZ::Vector3(-LinearManipulatorYAxisMovement, 0.0f, LinearManipulatorZAxisMovement)) *
Entity2RotationForLocalTranslation;
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualTranslationManipulatorMotion = AZ::Transform::CreateTranslation(
EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation +
AZ::Vector3(0.0f, LinearManipulatorYAxisMovement, LinearManipulatorZAxisMovement));
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionTranslationManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates translating a group of entities in local space with no modifiers held (group influence)
// manipulator and entity translate
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion },
// this replicates translating a group of entities in local space with the alt modifier held
// entities move in their own local space (individual influence)
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalIndividualTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualTranslationManipulatorMotion },
// this replicates translating a group of entities in world space with the shift modifier held
// entities and manipulator move in world space
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
ExpectedEntity2TransformAfterLocalGroupTranslationManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupTranslationManipulatorMotion },
// this replicates translating the manipulator in local space with the ctrl modifier held (entities are unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl,
ExpectedManipulatorTransformAfterGroupTranslationManipulatorMotion,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
Entity2RotationForLocalTranslation,
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
class EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam
: public EditorTransformComponentSelectionManipulatorInteractionTestFixture
, public ::testing::WithParamInterface<ManipulatorOptionsMultiple>
{
};
static const float LinearManipulatorZAxisMovementScale = 0.5f;
TEST_P(
EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam,
ScalingMultipleEntitiesWithDifferentModifierCombinations)
{
using AzToolsFramework::EditorTransformComponentSelectionRequestBus;
PositionEntities();
// move camera up and to the left so it's just above the normal row of entities
AzFramework::SetCameraTransform(
m_cameraState,
AZ::Transform::CreateFromQuaternionAndTranslation(
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 90.0f)), AZ::Vector3(10.0f, 15.0f, 10.1f)));
SetTransformMode(EditorTransformComponentSelectionRequestBus::Events::Mode::Scale);
AzToolsFramework::SelectEntities({ m_entityId2, m_entityId3 });
// manipulator should be centered between the two entities
const auto initialManipulatorTransform = GetManipulatorTransform();
const float screenToWorldMultiplier =
AzToolsFramework::CalculateScreenToWorldMultiplier(initialManipulatorTransform->GetTranslation(), m_cameraState);
const auto translationManipulatorStartHoldWorldPosition1 = AzToolsFramework::GetWorldTransform(m_entityId1).GetTranslation() +
initialManipulatorTransform->GetBasisZ() * screenToWorldMultiplier;
const auto translationManipulatorEndHoldWorldPosition1 =
translationManipulatorStartHoldWorldPosition1 + AZ::Vector3::CreateAxisZ(LinearManipulatorZAxisMovementScale);
// calculate screen space positions
const auto scaleManipulatorHoldScreenPosition =
AzFramework::WorldToScreen(translationManipulatorStartHoldWorldPosition1, m_cameraState);
const auto scaleManipulatorEndHoldScreenPosition =
AzFramework::WorldToScreen(translationManipulatorEndHoldWorldPosition1, m_cameraState);
m_actionDispatcher->CameraState(m_cameraState)
->MousePosition(scaleManipulatorHoldScreenPosition)
->KeyboardModifierDown(GetParam().m_keyboardModifier)
->MouseLButtonDown()
->MousePosition(scaleManipulatorEndHoldScreenPosition)
->MouseLButtonUp();
const auto expectedEntity2Transform = GetParam().m_firstExpectedEntityTransformAfter;
const auto expectedEntity3Transform = GetParam().m_secondExpectedEntityTransformAfter;
const auto expectedManipulatorTransform = GetParam().m_expectedManipulatorTransformAfter;
const auto manipulatorTransformAfter = GetManipulatorTransform();
const auto entity2Transform = AzToolsFramework::GetWorldTransform(m_entityId2);
const auto entity3Transform = AzToolsFramework::GetWorldTransform(m_entityId3);
EXPECT_THAT(*manipulatorTransformAfter, IsCloseTolerance(expectedManipulatorTransform, 0.01f));
EXPECT_THAT(entity2Transform, IsCloseTolerance(expectedEntity2Transform, 0.01f));
EXPECT_THAT(entity3Transform, IsCloseTolerance(expectedEntity3Transform, 0.01f));
}
static const AZ::Transform ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, -1.0f, 0.0f)) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateTranslation(AZ::Vector3(0.0f, 1.0f, 0.0f)) * AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity2TransformAfterLocalIndividualScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
static const AZ::Transform ExpectedEntity3TransformAfterLocalIndividualScaleManipulatorMotion =
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) *
AZ::Transform::CreateUniformScale(LinearManipulatorZAxisMovement);
INSTANTIATE_TEST_CASE_P(
All,
EditorTransformComponentSelectionScaleManipulatorMultipleEntityTestFixtureParam,
testing::Values(
// this replicates scaling a group of entities in local space with no modifiers held
// entities scale relative to manipulator pivot
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::None,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion },
// this replicates scaling a group of entities in local space with the alt modifier held
// entities scale about their own pivot
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Alt,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalIndividualScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalIndividualScaleManipulatorMotion },
// this replicates scaling a group of entities in world space with the shift modifier held
// entities scale relative to manipulator pivot in world space
ManipulatorOptionsMultiple{ AzToolsFramework::ViewportInteraction::KeyboardModifier::Shift,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
ExpectedEntity2TransformAfterLocalGroupScaleManipulatorMotion,
ExpectedEntity3TransformAfterLocalGroupScaleManipulatorMotion },
// this has no effect (entities and manipulator are unchanged)
ManipulatorOptionsMultiple{
AzToolsFramework::ViewportInteraction::KeyboardModifier::Ctrl,
AZ::Transform::CreateTranslation(AggregateManipulatorPositionWithEntity2and3Selected),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity2WorldTranslation),
AZ::Transform::CreateTranslation(EditorTransformComponentSelectionViewportPickingFixture::Entity3WorldTranslation) }));
using EditorTransformComponentSelectionManipulatorTestFixture =
IndirectCallManipulatorViewportInteractionFixtureMixin<EditorTransformComponentSelectionFixture>;
@@ -1661,7 +2211,7 @@ namespace UnitTest
All,
EditorTransformComponentSelectionSingleEntityPivotAndOverrideFixture,
testing::Values(
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, ChildExpectedPivotLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Local, PivotOverrideLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::Parent, PivotOverrideLocalOrientationInWorldSpace },
ReferenceFrameWithOrientation{ AzToolsFramework::ReferenceFrame::World, AZ::Quaternion::CreateIdentity() }));
@@ -6,8 +6,8 @@
*
*/
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/VectorConversions.h>
@@ -20,18 +20,17 @@
namespace UnitTest
{
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
AZ::Vector2 ScreenNDCToWorldToScreenNDC(
const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
// transform a point from normalized device coordinates to world space, and then from world space back to normalized device coordinates
AZ::Vector2 ScreenNdcToWorldToScreenNdc(const AZ::Vector2& ndcPoint, const AzFramework::CameraState& cameraState)
{
const auto worldResult = AzFramework::ScreenNDCToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto ndcResult = AzFramework::WorldToScreenNDC(worldResult, CameraView(cameraState), CameraProjection(cameraState));
const auto worldResult =
AzFramework::ScreenNdcToWorld(ndcPoint, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto ndcResult = AzFramework::WorldToScreenNdc(worldResult, CameraView(cameraState), CameraProjection(cameraState));
return AZ::Vector3ToVector2(ndcResult);
}
// transform a point from screen space to world space, and then from world space back to screen space
AzFramework::ScreenPoint ScreenToWorldToScreen(
const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
AzFramework::ScreenPoint ScreenToWorldToScreen(const AzFramework::ScreenPoint& screenPoint, const AzFramework::CameraState& cameraState)
{
const auto worldResult = AzFramework::ScreenToWorld(screenPoint, cameraState);
return AzFramework::WorldToScreen(worldResult, cameraState);
@@ -47,25 +46,25 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
{
const auto expectedScreenPoint = ScreenPoint{600, 450};
const auto expectedScreenPoint = ScreenPoint{ 600, 450 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
{
const auto expectedScreenPoint = ScreenPoint{400, 300};
const auto expectedScreenPoint = ScreenPoint{ 400, 300 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
{
const auto expectedScreenPoint = ScreenPoint{0, 0};
const auto expectedScreenPoint = ScreenPoint{ 0, 0 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
{
const auto expectedScreenPoint = ScreenPoint{800, 600};
const auto expectedScreenPoint = ScreenPoint{ 800, 600 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
@@ -81,7 +80,7 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto expectedScreenPoint = ScreenPoint{200, 300};
const auto expectedScreenPoint = ScreenPoint{ 200, 300 };
const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, cameraState);
EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
}
@@ -93,46 +92,46 @@ namespace UnitTest
using AzFramework::ScreenPoint;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraTransform =
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{400, 300}, cameraState);
const auto worldResult = AzFramework::ScreenToWorld(ScreenPoint{ 400, 300 }, cameraState);
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// NDC tests
TEST(ViewportScreen, WorldToScreenNDCAndScreenNDCToWorldReturnsTheSameValueIdentityCameraOffsetFromOrigin)
{
using NdcPoint = AZ::Vector2;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraPosition = AZ::Vector3::CreateAxisY(-10.0f);
const auto cameraState = AzFramework::CreateIdentityDefaultCamera(cameraPosition, screenDimensions);
{
const auto expectedNdcPoint = NdcPoint{0.75f, 0.75f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.75f, 0.75f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
{
const auto expectedNdcPoint = NdcPoint{0.5f, 0.5f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.5f, 0.5f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
{
const auto expectedNdcPoint = NdcPoint{0.0f, 0.0f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.0f, 0.0f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
{
const auto expectedNdcPoint = NdcPoint{1.0f, 1.0f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 1.0f, 1.0f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
}
@@ -147,8 +146,8 @@ namespace UnitTest
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto expectedNdcPoint = NdcPoint{0.25f, 0.5f};
const auto resultNdcPoint = ScreenNDCToWorldToScreenNDC(expectedNdcPoint, cameraState);
const auto expectedNdcPoint = NdcPoint{ 0.25f, 0.5f };
const auto resultNdcPoint = ScreenNdcToWorldToScreenNdc(expectedNdcPoint, cameraState);
EXPECT_THAT(resultNdcPoint, IsClose(expectedNdcPoint));
}
@@ -159,12 +158,13 @@ namespace UnitTest
using NdcPoint = AZ::Vector2;
const auto screenDimensions = AZ::Vector2(800.0f, 600.0f);
const auto cameraTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) *
AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraTransform =
AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 0.0f, 0.0f)) * AZ::Transform::CreateRotationZ(AZ::DegToRad(-90.0f));
const auto cameraState = AzFramework::CreateDefaultCamera(cameraTransform, screenDimensions);
const auto worldResult = AzFramework::ScreenNDCToWorld(NdcPoint{0.5f, 0.5f}, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
const auto worldResult =
AzFramework::ScreenNdcToWorld(NdcPoint{ 0.5f, 0.5f }, InverseCameraView(cameraState), InverseCameraProjection(cameraState));
EXPECT_THAT(worldResult, IsClose(AZ::Vector3(10.1f, 0.0f, 0.0f)));
}
@@ -175,7 +175,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenPoint{100, 200} - ScreenPoint{10, 20};
const ScreenVector screenVector = ScreenPoint{ 100, 200 } - ScreenPoint{ 10, 20 };
EXPECT_EQ(screenVector, ScreenVector(90, 180));
}
@@ -184,7 +184,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{100, 200} + ScreenVector{50, 25};
const ScreenPoint screenPoint = ScreenPoint{ 100, 200 } + ScreenVector{ 50, 25 };
EXPECT_EQ(screenPoint, ScreenPoint(150, 225));
}
@@ -193,7 +193,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{120, 200} - ScreenVector{50, 20};
const ScreenPoint screenPoint = ScreenPoint{ 120, 200 } - ScreenVector{ 50, 20 };
EXPECT_EQ(screenPoint, ScreenPoint(70, 180));
}
@@ -202,7 +202,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenVector{100, 200} + ScreenVector{50, 25};
const ScreenVector screenVector = ScreenVector{ 100, 200 } + ScreenVector{ 50, 25 };
EXPECT_EQ(screenVector, ScreenVector(150, 225));
}
@@ -211,7 +211,7 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenVector screenVector = ScreenVector{100, 200} - ScreenVector{50, 25};
const ScreenVector screenVector = ScreenVector{ 100, 200 } - ScreenVector{ 50, 25 };
EXPECT_EQ(screenVector, ScreenVector(50, 175));
}
@@ -220,8 +220,8 @@ namespace UnitTest
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
const ScreenPoint screenPoint = ScreenPoint{100, 200};
const ScreenVector screenVector = ScreenVector{50, 25};
const ScreenPoint screenPoint = ScreenPoint{ 100, 200 };
const ScreenVector screenVector = ScreenVector{ 50, 25 };
const AZ::Vector2 fromScreenPoint = AzFramework::Vector2FromScreenPoint(screenPoint);
const AZ::Vector2 fromScreenVector = AzFramework::Vector2FromScreenVector(screenVector);
@@ -295,6 +295,58 @@ namespace UnitTest
EXPECT_NEAR(AzFramework::ScreenVectorLength(ScreenVector(12, 15)), 19.20937f, 0.001f);
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarUpwards)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(5, 10);
auto scaledScreenVector = screenVector * 2.0f;
EXPECT_EQ(scaledScreenVector, ScreenVector(10, 20));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarWithRounding)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(1, 6);
auto scaledScreenVector = screenVector * 0.1f;
// value less than 0.5 rounds down, greater than or equal to 0.5 rounds up
EXPECT_EQ(scaledScreenVector, ScreenVector(0, 1));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarWithRoundingAtHalfwayBoundary)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(5, 10);
auto scaledScreenVector = screenVector * 0.1f;
// value less than 0.5 rounds down, greater than or equal to 0.5 rounds up
EXPECT_EQ(scaledScreenVector, ScreenVector(1, 1));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarDownwards)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(6, 12);
auto scaledScreenVector = screenVector * 0.5f;
EXPECT_EQ(scaledScreenVector, ScreenVector(3, 6));
}
TEST(ViewportScreen, ScreenVectorTransformedByScalarInplace)
{
using AzFramework::ScreenVector;
auto screenVector = ScreenVector(13, 37);
screenVector *= 10.0f;
EXPECT_EQ(screenVector, ScreenVector(130, 370));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
// Other tests
TEST(ViewportScreen, CanGetCameraTransformFromCameraViewAndBack)