2
0

CameraState.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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 <AzCore/UnitTest/TestTypes.h>
  9. #include <AzCore/Math/Transform.h>
  10. #include <AzFramework/Viewport/CameraState.h>
  11. #include <AZTestShared/Math/MathTestHelpers.h>
  12. #include <AzCore/Math/SimdMath.h>
  13. #include <AzCore/Math/MatrixUtils.h>
  14. #include <AzCore/Math/Matrix4x4.h>
  15. namespace UnitTest
  16. {
  17. class CameraStateFixture
  18. : public ::testing::Test
  19. {
  20. public:
  21. void SetUp() override
  22. {
  23. m_cameraState = AzFramework::CreateDefaultCamera(AZ::Transform::CreateIdentity(), AzFramework::ScreenSize(1024, 768));
  24. }
  25. AzFramework::CameraState m_cameraState;
  26. };
  27. class Translation
  28. : public CameraStateFixture
  29. , public ::testing::WithParamInterface<AZStd::tuple<float, float, float>>
  30. {
  31. };
  32. class Rotation
  33. : public CameraStateFixture
  34. , public ::testing::WithParamInterface<AZStd::tuple<float, float, float>>
  35. {
  36. };
  37. class WorldFromViewMatrix
  38. : public CameraStateFixture
  39. , public ::testing::WithParamInterface<AZStd::tuple<AZ::Vector3, AZ::Vector3, AzFramework::ScreenSize>>
  40. {
  41. };
  42. class PerspectiveMatrix
  43. : public CameraStateFixture
  44. , public ::testing::WithParamInterface<AZStd::tuple<float, float, float, float>>
  45. {
  46. };
  47. TEST_P(Translation, Permutation)
  48. {
  49. // Given a position
  50. const auto [x, y, z] = GetParam();
  51. const AZ::Vector3 expectedPosition(x, y, z);
  52. // Set the camera state's transform to the expected position
  53. AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(expectedPosition));
  54. // Expect the camera state transform's position to be expected position
  55. EXPECT_THAT(m_cameraState.m_position, IsCloseTolerance(expectedPosition, 0.01f));
  56. // Expect the camera state transform's orientation to be identity
  57. EXPECT_THAT(m_cameraState.m_forward, IsCloseTolerance(AZ::Vector3::CreateAxisY(), 0.01f));
  58. EXPECT_THAT(m_cameraState.m_up, IsCloseTolerance(AZ::Vector3::CreateAxisZ(), 0.01f));
  59. EXPECT_THAT(m_cameraState.m_side, IsCloseTolerance(AZ::Vector3::CreateAxisX(), 0.01f));
  60. }
  61. INSTANTIATE_TEST_SUITE_P(
  62. CameraState,
  63. Translation,
  64. testing::Combine(
  65. testing::Values(0.0f, 1.0f),
  66. testing::Values(0.0f, 1.0f),
  67. testing::Values(0.0f, 1.0f))
  68. );
  69. TEST_P(Rotation, Permutation)
  70. {
  71. [[maybe_unused]] int expectedErrors = -1;
  72. AZ_TEST_START_TRACE_SUPPRESSION;
  73. // Given an orientation derived from the look at points
  74. const auto [x, y, z] = GetParam();
  75. const AZ::Vector3 from = AZ::Vector3::CreateZero();
  76. const AZ::Vector3 to = AZ::Vector3(x, y, z);
  77. const AZ::Transform expectedTransform = AZ::Transform::CreateLookAt(from, AZ::Vector3(x, y, z));
  78. // Set the camera's rotation to the expected orientation
  79. AzFramework::SetCameraTransform(m_cameraState, expectedTransform);
  80. // Expect the camera state transform's position to be identity
  81. EXPECT_THAT(m_cameraState.m_position, IsCloseTolerance(AZ::Vector3::CreateZero(), 0.01f));
  82. if (from.IsClose(to, 0.001f))
  83. {
  84. // Expect one error to be generated by AZ::Transform::CreateLookAt() due to from and to being the same position
  85. expectedErrors = 1;
  86. // If the look at points yield an invalid orientation, expect identity rotation basis vectors
  87. EXPECT_THAT(m_cameraState.m_forward, IsCloseTolerance(AZ::Vector3::CreateAxisY(), 0.01f));
  88. EXPECT_THAT(m_cameraState.m_up, IsCloseTolerance(AZ::Vector3::CreateAxisZ(), 0.01f));
  89. EXPECT_THAT(m_cameraState.m_side, IsCloseTolerance(AZ::Vector3::CreateAxisX(), 0.01f));
  90. }
  91. else
  92. {
  93. // Expect no errors to be generated by AZ::Transform::CreateLookAt()
  94. expectedErrors = 0;
  95. // If the look at points yield a valid orientation, expect the rotation basis vectors to be that of the orientation
  96. EXPECT_THAT(m_cameraState.m_forward, IsCloseTolerance(expectedTransform.GetBasisY(), 0.01f));
  97. EXPECT_THAT(m_cameraState.m_up, IsCloseTolerance(expectedTransform.GetBasisZ(), 0.01f));
  98. EXPECT_THAT(m_cameraState.m_side, IsCloseTolerance(expectedTransform.GetBasisX(), 0.01f));
  99. }
  100. AZ_TEST_STOP_TRACE_SUPPRESSION(expectedErrors);
  101. }
  102. INSTANTIATE_TEST_SUITE_P(
  103. CameraState,
  104. Rotation,
  105. testing::Combine(
  106. testing::Values(0.0f, 1.0f),
  107. testing::Values(0.0f, 1.0f),
  108. testing::Values(0.0f, 1.0f))
  109. );
  110. TEST_P(WorldFromViewMatrix, Permutation)
  111. {
  112. auto [translation, eulerRotation, viewportSize] = GetParam();
  113. // Quaternion::CreateFromEulerAnglesDegrees takes a non-const Vector3&
  114. AZ::Vector3 eulerRotationCopy = eulerRotation;
  115. AZ::Quaternion rotation = AZ::Quaternion::CreateFromEulerAnglesDegrees(eulerRotationCopy);
  116. AZ::Matrix4x4 worldFromView = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(rotation, translation);
  117. m_cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix(worldFromView, viewportSize);
  118. EXPECT_EQ(m_cameraState.m_viewportSize, viewportSize);
  119. EXPECT_THAT(m_cameraState.m_position, IsCloseTolerance(translation, 0.01f));
  120. // Translate back into a quaternion to safely compare rotations
  121. auto decomposedCameraStateRotation =
  122. AZ::Quaternion::CreateFromBasis(m_cameraState.m_side, m_cameraState.m_forward, m_cameraState.m_up);
  123. auto rotationDelta = 1.0f - decomposedCameraStateRotation.Dot(decomposedCameraStateRotation);
  124. EXPECT_NEAR(rotationDelta, 0.f, 0.01f);
  125. }
  126. INSTANTIATE_TEST_SUITE_P(
  127. CameraState,
  128. WorldFromViewMatrix,
  129. testing::Combine(
  130. testing::Values(AZ::Vector3{0.f, 0.f, 0.f}, AZ::Vector3{100.f, 0.f, 0.f}, AZ::Vector3{-5.f,10.f,-1.f}),
  131. testing::Values(AZ::Vector3{0.f, 0.f, 0.f}, AZ::Vector3{90.f, 0.f, 0.f}, AZ::Vector3{-45.f, -45.f, -45.f}),
  132. testing::Values(AzFramework::ScreenSize(100, 100))
  133. )
  134. );
  135. TEST_P(PerspectiveMatrix, Permutation)
  136. {
  137. auto [fovY, aspectRatio, nearClip, farClip] = GetParam();
  138. AZ::Matrix4x4 clipFromView;
  139. MakePerspectiveFovMatrixRH(clipFromView, fovY, aspectRatio, nearClip, farClip);
  140. AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(m_cameraState, clipFromView);
  141. EXPECT_NEAR(m_cameraState.m_nearClip, nearClip, 0.01f);
  142. EXPECT_NEAR(m_cameraState.m_farClip, farClip, 1.f);
  143. EXPECT_NEAR(m_cameraState.m_fovOrZoom, fovY, 0.01f);
  144. }
  145. INSTANTIATE_TEST_SUITE_P(
  146. CameraState,
  147. PerspectiveMatrix,
  148. testing::Combine(
  149. testing::Values(1.f, 0.5f, 2.f),
  150. testing::Values(1.f, 16.f / 9.f),
  151. testing::Values(1.f, 50.f),
  152. testing::Values(100.f, 10000.f)
  153. )
  154. );
  155. } // namespace UnitTest