ViewportInteractionImplTests.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 <Atom/RPI.Public/View.h>
  9. #include <Atom/RPI.Public/ViewportContextBus.h>
  10. #include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
  11. #include <AzCore/Math/IntersectSegment.h>
  12. #include <AzCore/Math/MatrixUtils.h>
  13. #include <AzCore/Name/NameDictionary.h>
  14. #include <AzCore/UnitTest/TestTypes.h>
  15. #include <AzFramework/Viewport/ScreenGeometry.h>
  16. #include <AzFramework/Viewport/ViewportScreen.h>
  17. #include <AzTest/AzTest.h>
  18. #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
  19. #include <Tests/Utils/Printers.h>
  20. namespace UnitTest
  21. {
  22. class ViewportInteractionImplFixture : public ::testing::Test
  23. {
  24. public:
  25. static inline constexpr AzFramework::ViewportId TestViewportId = 1234;
  26. static inline constexpr AzFramework::ScreenSize ScreenDimensions = AzFramework::ScreenSize(1280, 720);
  27. static AzFramework::ScreenPoint ScreenCenter()
  28. {
  29. const auto halfScreenDimensions = ScreenDimensions * 0.5f;
  30. return AzFramework::ScreenPoint(halfScreenDimensions.m_width, halfScreenDimensions.m_height);
  31. }
  32. void SetUp() override
  33. {
  34. AZ::NameDictionary::Create();
  35. m_view = AZ::RPI::View::CreateView(AZ::Name("TestView"), AZ::RPI::View::UsageCamera);
  36. const auto aspectRatio = aznumeric_cast<float>(ScreenDimensions.m_width) / aznumeric_cast<float>(ScreenDimensions.m_height);
  37. AZ::Matrix4x4 viewToClipMatrix;
  38. AZ::MakePerspectiveFovMatrixRH(viewToClipMatrix, AZ::DegToRad(60.0f), aspectRatio, 0.1f, 1000.f, true);
  39. m_view->SetViewToClipMatrix(viewToClipMatrix);
  40. m_viewportInteractionImpl = AZStd::make_unique<AtomToolsFramework::ViewportInteractionImpl>(m_view);
  41. m_viewportInteractionImpl->m_deviceScalingFactorFn = []
  42. {
  43. return 1.0f;
  44. };
  45. m_viewportInteractionImpl->m_screenSizeFn = []
  46. {
  47. return ScreenDimensions;
  48. };
  49. m_viewportInteractionImpl->Connect(TestViewportId);
  50. }
  51. void TearDown() override
  52. {
  53. m_viewportInteractionImpl->Disconnect();
  54. m_viewportInteractionImpl.reset();
  55. m_view.reset();
  56. AZ::NameDictionary::Destroy();
  57. }
  58. AZ::RPI::ViewPtr m_view;
  59. AZStd::unique_ptr<AtomToolsFramework::ViewportInteractionImpl> m_viewportInteractionImpl;
  60. };
  61. // transform a point from screen space to world space, and then from world space back to screen space
  62. AzFramework::ScreenPoint ScreenToWorldToScreen(
  63. const AzFramework::ScreenPoint& screenPoint,
  64. AzToolsFramework::ViewportInteraction::ViewportInteractionRequests& viewportInteractionRequests)
  65. {
  66. const auto worldResult = viewportInteractionRequests.ViewportScreenToWorld(screenPoint);
  67. return viewportInteractionRequests.ViewportWorldToScreen(worldResult);
  68. }
  69. #if AZ_TRAIT_DISABLE_FAILED_ARM64_TESTS
  70. TEST_F(ViewportInteractionImplFixture, DISABLED_ViewportInteractionRequestsMapsFromScreenToWorldAndBack)
  71. #else
  72. TEST_F(ViewportInteractionImplFixture, ViewportInteractionRequestsMapsFromScreenToWorldAndBack)
  73. #endif // AZ_TRAIT_DISABLE_FAILED_ARM64_TESTS
  74. {
  75. using AzFramework::ScreenPoint;
  76. m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  77. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(10.0f, 0.0f, 5.0f)));
  78. {
  79. const auto expectedScreenPoint = ScreenPoint{ 600, 450 };
  80. const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
  81. EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
  82. }
  83. {
  84. auto expectedScreenPoint = ScreenCenter();
  85. const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
  86. EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
  87. }
  88. {
  89. const auto expectedScreenPoint = ScreenPoint{ 0, 0 };
  90. const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
  91. EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
  92. }
  93. {
  94. const auto expectedScreenPoint = ScreenPoint{ ScreenDimensions.m_width, ScreenDimensions.m_height };
  95. const auto resultScreenPoint = ScreenToWorldToScreen(expectedScreenPoint, *m_viewportInteractionImpl);
  96. EXPECT_EQ(resultScreenPoint, expectedScreenPoint);
  97. }
  98. }
  99. TEST_F(ViewportInteractionImplFixture, ScreenToWorldReturnsPositionOnNearClipPlaneInWorldSpace)
  100. {
  101. m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  102. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-90.0f)), AZ::Vector3(20.0f, 0.0f, 0.0f)));
  103. const auto worldResult = m_viewportInteractionImpl->ViewportScreenToWorld(ScreenCenter());
  104. EXPECT_THAT(worldResult, IsClose(AZ::Vector3(20.1f, 0.0f, 0.0f)));
  105. }
  106. // note: values produced by reproducing in the editor viewport
  107. TEST_F(ViewportInteractionImplFixture, WorldToScreenGivesExpectedScreenCoordinates)
  108. {
  109. using AzFramework::ScreenPoint;
  110. {
  111. m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  112. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(160.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-18.0f)),
  113. AZ::Vector3(-21.0f, 2.5f, 6.0f)));
  114. const auto screenResult = m_viewportInteractionImpl->ViewportWorldToScreen(AZ::Vector3(-21.0f, -1.5f, 5.0f));
  115. EXPECT_EQ(screenResult, ScreenPoint(420, 326));
  116. }
  117. {
  118. m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  119. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(175.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-90.0f)),
  120. AZ::Vector3(-10.0f, -11.0f, 2.5f)));
  121. const auto screenResult = m_viewportInteractionImpl->ViewportWorldToScreen(AZ::Vector3(-10.0f, -10.5f, 0.5f));
  122. EXPECT_EQ(screenResult, ScreenPoint(654, 515));
  123. }
  124. {
  125. m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  126. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(70.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(65.0f)),
  127. AZ::Vector3(-22.5f, -10.0f, 1.5f)));
  128. const auto screenResult = m_viewportInteractionImpl->ViewportWorldToScreen(AZ::Vector3(-23.0f, -9.5f, 3.0f));
  129. EXPECT_EQ(screenResult, ScreenPoint(754, 340));
  130. }
  131. }
  132. TEST_F(ViewportInteractionImplFixture, ScreenToWorldRayGivesGivesExpectedOriginAndDirection)
  133. {
  134. using AzFramework::ScreenPoint;
  135. m_view->SetCameraTransform(AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  136. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(34.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-24.0f)),
  137. AZ::Vector3(-9.3f, -9.8f, 4.0f)));
  138. const auto ray = m_viewportInteractionImpl->ViewportScreenToWorldRay(ScreenPoint(832, 226));
  139. float unused;
  140. auto intersection =
  141. AZ::Intersect::IntersectRaySphere(ray.m_origin, ray.m_direction, AZ::Vector3(-14.0f, 5.7f, 0.75f), 0.5f, unused);
  142. EXPECT_EQ(intersection, AZ::Intersect::SphereIsectTypes::ISECT_RAY_SPHERE_ISECT);
  143. }
  144. TEST_F(ViewportInteractionImplFixture, ViewportInteractionRequestsReturnsNewViewWhenItIsChanged)
  145. {
  146. // Given
  147. const auto primaryViewTransform = AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  148. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(90.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-45.0f)),
  149. AZ::Vector3(-10.0f, -15.0f, 20.0f));
  150. m_view->SetCameraTransform(primaryViewTransform);
  151. AZ::RPI::ViewPtr secondaryView = AZ::RPI::View::CreateView(AZ::Name("SecondaryView"), AZ::RPI::View::UsageCamera);
  152. const auto secondaryViewTransform = AZ::Matrix3x4::CreateFromMatrix3x3AndTranslation(
  153. AZ::Matrix3x3::CreateRotationZ(AZ::DegToRad(-90.0f)) * AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(30.0f)),
  154. AZ::Vector3(-50.0f, -25.0f, 10.0f));
  155. secondaryView->SetCameraTransform(secondaryViewTransform);
  156. // When
  157. AZ::RPI::ViewportContextIdNotificationBus::Event(
  158. TestViewportId, &AZ::RPI::ViewportContextIdNotificationBus::Events::OnViewportDefaultViewChanged, secondaryView);
  159. // retrieve updated camera transform
  160. AzFramework::CameraState cameraState;
  161. AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::EventResult(
  162. cameraState, TestViewportId, &AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Events::GetCameraState);
  163. const auto cameraTransform = AzFramework::CameraTransform(cameraState);
  164. // Then
  165. // camera transform matches that of the secondary view
  166. EXPECT_THAT(cameraTransform, IsClose(secondaryViewTransform));
  167. }
  168. } // namespace UnitTest