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.
o3de/Code/Framework/AzFramework/Tests/CameraState.cpp

186 lines
7.0 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Math/Transform.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AzCore/Math/SimdMath.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Matrix4x4.h>
namespace UnitTest
{
class CameraStateFixture
: public ::testing::Test
{
public:
void SetUp() override
{
m_cameraState = AzFramework::CreateDefaultCamera(AZ::Transform::CreateIdentity(), AZ::Vector2(1024, 768));
}
AzFramework::CameraState m_cameraState;
};
class Translation
: public CameraStateFixture
, public ::testing::WithParamInterface<AZStd::tuple<float, float, float>>
{
};
class Rotation
: public CameraStateFixture
, public ::testing::WithParamInterface<AZStd::tuple<float, float, float>>
{
};
class WorldFromViewMatrix
: public CameraStateFixture
, public ::testing::WithParamInterface<AZStd::tuple<AZ::Vector3, AZ::Vector3, AZ::Vector2>>
{
};
class PerspectiveMatrix
: public CameraStateFixture
, public ::testing::WithParamInterface<AZStd::tuple<float, float, float, float>>
{
};
TEST_P(Translation, Permutation)
{
// Given a position
const auto [x, y, z] = GetParam();
const AZ::Vector3 expectedPosition(x, y, z);
// Set the camera state's transform to the expected position
AzFramework::SetCameraTransform(m_cameraState, AZ::Transform::CreateTranslation(expectedPosition));
// Expect the camera state transform's position to be expected position
EXPECT_THAT(m_cameraState.m_position, IsCloseTolerance(expectedPosition, 0.01f));
// Expect the camera state transform's orientation to be identity
EXPECT_THAT(m_cameraState.m_forward, IsCloseTolerance(AZ::Vector3::CreateAxisY(), 0.01f));
EXPECT_THAT(m_cameraState.m_up, IsCloseTolerance(AZ::Vector3::CreateAxisZ(), 0.01f));
EXPECT_THAT(m_cameraState.m_side, IsCloseTolerance(AZ::Vector3::CreateAxisX(), 0.01f));
}
INSTANTIATE_TEST_CASE_P(
CameraState,
Translation,
testing::Combine(
testing::Values(0.0f, 1.0f),
testing::Values(0.0f, 1.0f),
testing::Values(0.0f, 1.0f))
);
TEST_P(Rotation, Permutation)
{
[[maybe_unused]] int expectedErrors = -1;
AZ_TEST_START_TRACE_SUPPRESSION;
// Given an orientation derived from the look at points
const auto [x, y, z] = GetParam();
const AZ::Vector3 from = AZ::Vector3::CreateZero();
const AZ::Vector3 to = AZ::Vector3(x, y, z);
const AZ::Transform expectedTransform = AZ::Transform::CreateLookAt(from, AZ::Vector3(x, y, z));
// Set the camera's rotation to the expected orientation
AzFramework::SetCameraTransform(m_cameraState, expectedTransform);
// Expect the camera state transform's position to be identity
EXPECT_THAT(m_cameraState.m_position, IsCloseTolerance(AZ::Vector3::CreateZero(), 0.01f));
if (from.IsClose(to, 0.001f))
{
// Expect one error to be generated by AZ::Transform::CreateLookAt() due to from and to being the same position
expectedErrors = 1;
// If the look at points yield an invalid orientation, expect identity rotation basis vectors
EXPECT_THAT(m_cameraState.m_forward, IsCloseTolerance(AZ::Vector3::CreateAxisY(), 0.01f));
EXPECT_THAT(m_cameraState.m_up, IsCloseTolerance(AZ::Vector3::CreateAxisZ(), 0.01f));
EXPECT_THAT(m_cameraState.m_side, IsCloseTolerance(AZ::Vector3::CreateAxisX(), 0.01f));
}
else
{
// Expect no errors to be generated by AZ::Transform::CreateLookAt()
expectedErrors = 0;
// If the look at points yield a valid orientation, expect the rotation basis vectors to be that of the orientation
EXPECT_THAT(m_cameraState.m_forward, IsCloseTolerance(expectedTransform.GetBasisY(), 0.01f));
EXPECT_THAT(m_cameraState.m_up, IsCloseTolerance(expectedTransform.GetBasisZ(), 0.01f));
EXPECT_THAT(m_cameraState.m_side, IsCloseTolerance(expectedTransform.GetBasisX(), 0.01f));
}
AZ_TEST_STOP_TRACE_SUPPRESSION(expectedErrors);
}
INSTANTIATE_TEST_CASE_P(
CameraState,
Rotation,
testing::Combine(
testing::Values(0.0f, 1.0f),
testing::Values(0.0f, 1.0f),
testing::Values(0.0f, 1.0f))
);
TEST_P(WorldFromViewMatrix, Permutation)
{
auto [translation, eulerRotation, viewportSize] = GetParam();
// Quaternion::CreateFromEulerAnglesDegrees takes a non-const Vector3&
AZ::Vector3 eulerRotationCopy = eulerRotation;
AZ::Quaternion rotation = AZ::Quaternion::CreateFromEulerAnglesDegrees(eulerRotationCopy);
AZ::Matrix4x4 worldFromView = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(rotation, translation);
m_cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix(worldFromView, viewportSize);
EXPECT_EQ(m_cameraState.m_viewportSize, viewportSize);
EXPECT_THAT(m_cameraState.m_position, IsCloseTolerance(translation, 0.01f));
// Translate back into a quaternion to safely compare rotations
auto decomposedCameraStateRotation = AZ::Quaternion::CreateFromBasis(m_cameraState.m_side, m_cameraState.m_forward, m_cameraState.m_up);
auto rotationDelta = 1.f - decomposedCameraStateRotation.Dot(decomposedCameraStateRotation);
EXPECT_NEAR(rotationDelta, 0.f, 0.01f);
}
INSTANTIATE_TEST_CASE_P(
CameraState,
WorldFromViewMatrix,
testing::Combine(
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}),
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}),
testing::Values(AZ::Vector2{100.f, 100.f})
)
);
TEST_P(PerspectiveMatrix, Permutation)
{
auto [fovY, aspectRatio, nearClip, farClip] = GetParam();
AZ::Matrix4x4 clipFromView;
MakePerspectiveFovMatrixRH(clipFromView, fovY, aspectRatio, nearClip, farClip);
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(m_cameraState, clipFromView);
EXPECT_NEAR(m_cameraState.m_nearClip, nearClip, 0.01f);
EXPECT_NEAR(m_cameraState.m_farClip, farClip, 1.f);
EXPECT_NEAR(m_cameraState.m_fovOrZoom, fovY, 0.01f);
}
INSTANTIATE_TEST_CASE_P(
CameraState,
PerspectiveMatrix,
testing::Combine(
testing::Values(1.f, 0.5f, 2.f),
testing::Values(1.f, 16.f / 9.f),
testing::Values(1.f, 50.f),
testing::Values(100.f, 10000.f)
)
);
} // namespace UnitTest