GridSnappingTest.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "AzManipulatorTestFrameworkTestFixtures.h"
  9. #include <AZTestShared/Math/MathTestHelpers.h>
  10. #include <AzCore/std/smart_ptr/unique_ptr.h>
  11. #include <AzFramework/Viewport/ViewportScreen.h>
  12. #include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
  13. #include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
  14. #include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
  15. #include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
  16. #include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
  17. #include <AzToolsFramework/Manipulators/LinearManipulator.h>
  18. #include <AzToolsFramework/Manipulators/PlanarManipulator.h>
  19. #include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
  20. namespace UnitTest
  21. {
  22. class GridSnappingFixture : public ToolsApplicationFixture<>
  23. {
  24. public:
  25. GridSnappingFixture()
  26. : m_viewportManipulatorInteraction(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>(
  27. AZStd::make_shared<NullDebugDisplayRequests>()))
  28. , m_actionDispatcher(
  29. AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction))
  30. {
  31. }
  32. protected:
  33. void SetUpEditorFixtureImpl() override
  34. {
  35. m_cameraState =
  36. AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
  37. }
  38. public:
  39. const float m_boundsRadius = 1.0f;
  40. AZStd::unique_ptr<AzManipulatorTestFramework::ManipulatorViewportInteraction> m_viewportManipulatorInteraction;
  41. AZStd::unique_ptr<AzManipulatorTestFramework::ImmediateModeActionDispatcher> m_actionDispatcher;
  42. AzFramework::CameraState m_cameraState;
  43. };
  44. TEST_F(GridSnappingFixture, MouseDownWithSnappingEnabledSnapsToClosestGridSize)
  45. {
  46. AZStd::shared_ptr<AzToolsFramework::LinearManipulator> linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
  47. m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
  48. /*position=*/AZ::Vector3(0.0f, 50.0f, 0.0f),
  49. /*radius=*/m_boundsRadius));
  50. // the initial starting position of the manipulator (in front of the camera)
  51. const auto initialPositionWorld = linearManipulator->GetLocalPosition();
  52. // where the manipulator should end up (in front and to the left of the camera)
  53. const auto finalPositionWorld = AZ::Vector3(-10.0f, 50.0f, 0.0f);
  54. // perspective scale factor for manipulator distance to camera
  55. const auto scaledRadiusBound =
  56. AzToolsFramework::CalculateScreenToWorldMultiplier(initialPositionWorld, m_cameraState) * m_boundsRadius;
  57. // vector from camera to manipulator
  58. const auto vectorToInitialPositionWorld = (initialPositionWorld - m_cameraState.m_position).GetNormalized();
  59. // adjusted final world position taking into account the manipulator position relative to the camera
  60. const auto finalPositionWorldAdjusted = finalPositionWorld - (vectorToInitialPositionWorld * scaledRadiusBound);
  61. // calculate the position in screen space of the initial position of the manipulator
  62. const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, m_cameraState);
  63. // calculate the position in screen space of the final position of the manipulator
  64. const auto finalPositionScreen = AzFramework::WorldToScreen(finalPositionWorldAdjusted, m_cameraState);
  65. // callback to update the manipulator's current position
  66. linearManipulator->InstallMouseMoveCallback(
  67. [linearManipulator = linearManipulator.get()](const AzToolsFramework::LinearManipulator::Action& action)
  68. {
  69. linearManipulator->SetLocalPosition(action.LocalPosition());
  70. });
  71. m_actionDispatcher->SetSnapToGrid(true)
  72. ->GridSize(5.0f)
  73. ->CameraState(m_cameraState)
  74. ->MousePosition(initialPositionScreen)
  75. ->MouseLButtonDown()
  76. ->ExpectManipulatorBeingInteracted()
  77. ->MousePosition(finalPositionScreen)
  78. ->MouseLButtonUp()
  79. ->ExpectManipulatorNotBeingInteracted()
  80. ->ExpectTrue(linearManipulator->GetLocalPosition().IsClose(finalPositionWorld, 0.01f));
  81. }
  82. template<typename Manipulator>
  83. void ValidateManipulatorSnappingBehavior(
  84. AZStd::shared_ptr<Manipulator> manipulator,
  85. AzManipulatorTestFramework::ImmediateModeActionDispatcher* actionDispatcher,
  86. const AzFramework::CameraState& cameraState)
  87. {
  88. manipulator->SetLocalOrientation(AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(180.0f, 0.0f, 135.0f)));
  89. // the initial starting position of the manipulator (in front of the camera)
  90. const auto initialPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.15f);
  91. // where the manipulator should end up (unmoved)
  92. const auto finalPositionWorld = manipulator->GetLocalPosition();
  93. // where we should move the mouse to
  94. const auto attemptPositionWorld = manipulator->GetLocalPosition() + AZ::Vector3::CreateAxisX(0.35f);
  95. // calculate the position in screen space of the initial position of the manipulator
  96. const auto initialPositionScreen = AzFramework::WorldToScreen(initialPositionWorld, cameraState);
  97. // calculate the position in screen space of the final position of the manipulator
  98. const auto attemptPositionScreen = AzFramework::WorldToScreen(attemptPositionWorld, cameraState);
  99. // callback to update the manipulator's current position
  100. manipulator->InstallMouseMoveCallback(
  101. [manipulator = manipulator.get()](const typename Manipulator::Action& action)
  102. {
  103. manipulator->SetLocalPosition(action.LocalPosition());
  104. });
  105. actionDispatcher->SetSnapToGrid(true)
  106. ->GridSize(1.0f)
  107. ->CameraState(cameraState)
  108. ->MousePosition(initialPositionScreen)
  109. ->MouseLButtonDown()
  110. ->ExpectManipulatorBeingInteracted()
  111. ->MousePosition(attemptPositionScreen)
  112. ->MouseLButtonUp()
  113. ->ExpectManipulatorNotBeingInteracted()
  114. ->ExpectThat(manipulator->GetLocalPosition(), IsCloseTolerance(finalPositionWorld, 0.01f));
  115. }
  116. TEST_F(GridSnappingFixture, MouseDownAndMoveLinearManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize)
  117. {
  118. AZStd::shared_ptr<AzToolsFramework::LinearManipulator> linearManipulator(AzManipulatorTestFramework::CreateLinearManipulator(
  119. m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
  120. /*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f),
  121. /*radius=*/m_boundsRadius));
  122. linearManipulator->SetAxis(AZ::Vector3::CreateAxisY());
  123. ValidateManipulatorSnappingBehavior(linearManipulator, m_actionDispatcher.get(), m_cameraState);
  124. }
  125. TEST_F(GridSnappingFixture, MouseDownAndMovePlanarManipulatorDoesNotSnapWithMovementSmallerThanHalfGridSize)
  126. {
  127. AZStd::shared_ptr<AzToolsFramework::PlanarManipulator> planarManipulator(AzManipulatorTestFramework::CreatePlanarManipulator(
  128. m_viewportManipulatorInteraction->GetManipulatorManager().GetId(),
  129. /*position=*/AZ::Vector3(0.0f, 10.0f, 0.0f),
  130. /*radius=*/m_boundsRadius));
  131. planarManipulator->SetAxes(AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
  132. ValidateManipulatorSnappingBehavior(planarManipulator, m_actionDispatcher.get(), m_cameraState);
  133. }
  134. } // namespace UnitTest