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.
204 lines
8.2 KiB
C++
204 lines
8.2 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#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/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>>
|
|
{
|
|
};
|
|
|
|
// Taken from Atom::MatrixUtils for testing purposes, this can be removed if MakePerspectiveFovMatrixRH makes it into AZ
|
|
static AZ::Matrix4x4 MakePerspectiveMatrixRH(float fovY, float aspectRatio, float nearClip, float farClip)
|
|
{
|
|
float sinFov, cosFov;
|
|
AZ::SinCos(0.5f * fovY, sinFov, cosFov);
|
|
float yScale = cosFov / sinFov; //cot(fovY/2)
|
|
float xScale = yScale / aspectRatio;
|
|
|
|
AZ::Matrix4x4 out;
|
|
out.SetRow(0, xScale, 0.f, 0.f, 0.f );
|
|
out.SetRow(1, 0.f, yScale, 0.f, 0.f );
|
|
out.SetRow(2, 0.f, 0.f, farClip / (nearClip - farClip), nearClip*farClip / (nearClip - farClip) );
|
|
out.SetRow(3, 0.f, 0.f, -1.f, 0.f );
|
|
return out;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 = MakePerspectiveMatrixRH(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
|