feat: 添加Math库和Google Test测试框架
- 新增Math库: Vector2/3/4, Matrix3/4, Quaternion, Transform, Color等 - 新增测试框架: Google Test (gtest) - 新增140个单元测试,覆盖Vector, Matrix, Quaternion, Geometry - VolumeRenderer支持vcpkg的NanoVDB - 添加TESTING.md测试文档
This commit is contained in:
33
tests/math/CMakeLists.txt
Normal file
33
tests/math/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# ============================================================
|
||||
# Math Library Tests
|
||||
# ============================================================
|
||||
|
||||
set(MATH_TEST_SOURCES
|
||||
test_vector.cpp
|
||||
test_matrix.cpp
|
||||
test_quaternion.cpp
|
||||
test_geometry.cpp
|
||||
)
|
||||
|
||||
add_executable(xcengine_math_tests ${MATH_TEST_SOURCES})
|
||||
|
||||
# Exclude all static runtime libraries to avoid conflicts
|
||||
if(MSVC)
|
||||
set_target_properties(xcengine_math_tests PROPERTIES
|
||||
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(xcengine_math_tests
|
||||
PRIVATE
|
||||
XCEngine
|
||||
GTest::gtest
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
target_include_directories(xcengine_math_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/tests/fixtures
|
||||
)
|
||||
|
||||
add_test(NAME MathTests COMMAND xcengine_math_tests)
|
||||
385
tests/math/test_geometry.cpp
Normal file
385
tests/math/test_geometry.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Math/Ray.h>
|
||||
#include <XCEngine/Math/Sphere.h>
|
||||
#include <XCEngine/Math/Box.h>
|
||||
#include <XCEngine/Math/Plane.h>
|
||||
#include <XCEngine/Math/Bounds.h>
|
||||
#include <XCEngine/Math/Frustum.h>
|
||||
#include <XCEngine/Math/Matrix4.h>
|
||||
|
||||
using namespace XCEngine::Math;
|
||||
|
||||
namespace {
|
||||
|
||||
// ============================================================
|
||||
// Ray Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Ray, DefaultConstructor) {
|
||||
Ray ray;
|
||||
EXPECT_FLOAT_EQ(ray.origin.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(ray.origin.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(ray.origin.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(ray.direction.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(ray.direction.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(ray.direction.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, ParameterConstructor) {
|
||||
Vector3 origin(1, 2, 3);
|
||||
Vector3 direction(0, 0, 1);
|
||||
Ray ray(origin, direction);
|
||||
|
||||
EXPECT_FLOAT_EQ(ray.origin.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(ray.origin.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(ray.origin.z, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, GetPoint) {
|
||||
Ray ray(Vector3::Zero(), Vector3::Right());
|
||||
Vector3 point = ray.GetPoint(5.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(point.x, 5.0f);
|
||||
EXPECT_FLOAT_EQ(point.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(point.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, IntersectsSphere_Hit) {
|
||||
Ray ray(Vector3(-5, 0, 0), Vector3::Right());
|
||||
Sphere sphere(Vector3::Zero(), 1.0f);
|
||||
|
||||
float t;
|
||||
bool hit = ray.Intersects(sphere, t);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
EXPECT_GE(t, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, IntersectsSphere_Miss) {
|
||||
Ray ray(Vector3(0, 5, 0), Vector3::Right());
|
||||
Sphere sphere(Vector3::Zero(), 1.0f);
|
||||
|
||||
float t;
|
||||
bool hit = ray.Intersects(sphere, t);
|
||||
|
||||
EXPECT_FALSE(hit);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, IntersectsBox_Hit) {
|
||||
Ray ray(Vector3(-5, 0, 0), Vector3::Right());
|
||||
Box box(Vector3::Zero(), Vector3(1, 1, 1));
|
||||
|
||||
float t;
|
||||
bool hit = ray.Intersects(box, t);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
EXPECT_GE(t, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, IntersectsBox_Miss) {
|
||||
Ray ray(Vector3(0, 5, 0), Vector3::Right());
|
||||
Box box(Vector3(10, 0, 0), Vector3(1, 1, 1));
|
||||
|
||||
float t;
|
||||
bool hit = ray.Intersects(box, t);
|
||||
|
||||
EXPECT_FALSE(hit);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, IntersectsPlane_Hit) {
|
||||
Ray ray(Vector3(0, 0, -5), Vector3::Forward());
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
|
||||
float t;
|
||||
bool hit = ray.Intersects(plane, t);
|
||||
|
||||
EXPECT_TRUE(hit);
|
||||
EXPECT_NEAR(t, 5.0f, 1e-3f);
|
||||
}
|
||||
|
||||
TEST(Math_Ray, IntersectsPlane_Miss) {
|
||||
Ray ray(Vector3(0, 0, -5), Vector3::Back());
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
|
||||
float t;
|
||||
bool hit = ray.Intersects(plane, t);
|
||||
|
||||
EXPECT_FALSE(hit);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Sphere Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Sphere, DefaultConstructor) {
|
||||
Sphere sphere;
|
||||
EXPECT_FLOAT_EQ(sphere.center.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(sphere.radius, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Sphere, ParameterConstructor) {
|
||||
Sphere sphere(Vector3(1, 2, 3), 5.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(sphere.center.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(sphere.center.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(sphere.center.z, 3.0f);
|
||||
EXPECT_FLOAT_EQ(sphere.radius, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Sphere, Contains_Inside) {
|
||||
Sphere sphere(Vector3::Zero(), 2.0f);
|
||||
EXPECT_TRUE(sphere.Contains(Vector3(1, 0, 0)));
|
||||
}
|
||||
|
||||
TEST(Math_Sphere, Contains_Outside) {
|
||||
Sphere sphere(Vector3::Zero(), 1.0f);
|
||||
EXPECT_FALSE(sphere.Contains(Vector3(5, 0, 0)));
|
||||
}
|
||||
|
||||
TEST(Math_Sphere, Contains_OnSurface) {
|
||||
Sphere sphere(Vector3::Zero(), 1.0f);
|
||||
EXPECT_TRUE(sphere.Contains(Vector3(1, 0, 0)));
|
||||
}
|
||||
|
||||
TEST(Math_Sphere, Intersects_Overlap) {
|
||||
Sphere a(Vector3::Zero(), 1.0f);
|
||||
Sphere b(Vector3(1.5f, 0, 0), 1.0f);
|
||||
|
||||
EXPECT_TRUE(a.Intersects(b));
|
||||
}
|
||||
|
||||
TEST(Math_Sphere, Intersects_Separate) {
|
||||
Sphere a(Vector3::Zero(), 1.0f);
|
||||
Sphere b(Vector3(5, 0, 0), 1.0f);
|
||||
|
||||
EXPECT_FALSE(a.Intersects(b));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Plane Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Plane, DefaultConstructor) {
|
||||
Plane plane;
|
||||
EXPECT_FLOAT_EQ(plane.normal.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(plane.normal.y, 1.0f);
|
||||
EXPECT_FLOAT_EQ(plane.normal.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(plane.distance, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Plane, ParameterConstructor) {
|
||||
Plane plane(Vector3::Forward(), 5.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(plane.normal.z, 1.0f);
|
||||
EXPECT_FLOAT_EQ(plane.distance, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Plane, FromPoints) {
|
||||
Plane plane = Plane::FromPoints(
|
||||
Vector3(0, 0, 0),
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(0, 1, 0)
|
||||
);
|
||||
|
||||
EXPECT_FLOAT_EQ(plane.normal.z, -1.0f);
|
||||
EXPECT_FLOAT_EQ(plane.distance, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Plane, GetDistanceToPoint) {
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
float dist = plane.GetDistanceToPoint(Vector3(0, 0, 5));
|
||||
|
||||
EXPECT_FLOAT_EQ(dist, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Plane, GetClosestPoint) {
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
Vector3 point = plane.GetClosestPoint(Vector3(3, 4, 5));
|
||||
|
||||
EXPECT_FLOAT_EQ(point.x, 3.0f);
|
||||
EXPECT_FLOAT_EQ(point.y, 4.0f);
|
||||
EXPECT_FLOAT_EQ(point.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Plane, GetSide_Positive) {
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
EXPECT_TRUE(plane.GetSide(Vector3(0, 0, 1)));
|
||||
}
|
||||
|
||||
TEST(Math_Plane, GetSide_Negative) {
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
EXPECT_FALSE(plane.GetSide(Vector3(0, 0, -1)));
|
||||
}
|
||||
|
||||
TEST(Math_Plane, IntersectsSphere_Inside) {
|
||||
Plane plane(Vector3::Forward(), 0.0f);
|
||||
Sphere sphere(Vector3(0, 0, 0), 1.0f);
|
||||
|
||||
EXPECT_TRUE(plane.Intersects(sphere));
|
||||
}
|
||||
|
||||
TEST(Math_Plane, IntersectsSphere_Outside) {
|
||||
Plane plane(Vector3::Forward(), 5.0f);
|
||||
Sphere sphere(Vector3(0, 0, 0), 1.0f);
|
||||
|
||||
EXPECT_FALSE(plane.Intersects(sphere));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Bounds Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Bounds, DefaultConstructor) {
|
||||
Bounds bounds;
|
||||
EXPECT_FLOAT_EQ(bounds.center.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(bounds.extents.x, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, ParameterConstructor) {
|
||||
Bounds bounds(Vector3(5, 5, 5), Vector3(10, 10, 10));
|
||||
|
||||
EXPECT_FLOAT_EQ(bounds.center.x, 5.0f);
|
||||
EXPECT_FLOAT_EQ(bounds.extents.x, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, GetMin) {
|
||||
Bounds bounds(Vector3(5, 5, 5), Vector3(10, 10, 10));
|
||||
Vector3 min = bounds.GetMin();
|
||||
|
||||
EXPECT_FLOAT_EQ(min.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(min.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(min.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, GetMax) {
|
||||
Bounds bounds(Vector3(5, 5, 5), Vector3(10, 10, 10));
|
||||
Vector3 max = bounds.GetMax();
|
||||
|
||||
EXPECT_FLOAT_EQ(max.x, 10.0f);
|
||||
EXPECT_FLOAT_EQ(max.y, 10.0f);
|
||||
EXPECT_FLOAT_EQ(max.z, 10.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, SetMinMax) {
|
||||
Bounds bounds;
|
||||
bounds.SetMinMax(Vector3(0, 0, 0), Vector3(10, 10, 10));
|
||||
|
||||
EXPECT_FLOAT_EQ(bounds.center.x, 5.0f);
|
||||
EXPECT_FLOAT_EQ(bounds.extents.x, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Contains_Inside) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(10, 10, 10));
|
||||
EXPECT_TRUE(bounds.Contains(Vector3::Zero()));
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Contains_Outside) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
EXPECT_FALSE(bounds.Contains(Vector3(5, 5, 5)));
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Contains_OnSurface) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
EXPECT_TRUE(bounds.Contains(Vector3(1, 1, 1)));
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Intersects_Overlap) {
|
||||
Bounds a(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
Bounds b(Vector3(1, 1, 1), Vector3(2, 2, 2));
|
||||
|
||||
EXPECT_TRUE(a.Intersects(b));
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Intersects_Separate) {
|
||||
Bounds a(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
Bounds b(Vector3(10, 10, 10), Vector3(2, 2, 2));
|
||||
|
||||
EXPECT_FALSE(a.Intersects(b));
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Encapsulate_Point) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
bounds.Encapsulate(Vector3(5, 5, 5));
|
||||
|
||||
EXPECT_GT(bounds.GetMax().x, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Encapsulate_Bounds) {
|
||||
Bounds a(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
Bounds b(Vector3(5, 5, 5), Vector3(2, 2, 2));
|
||||
a.Encapsulate(b);
|
||||
|
||||
EXPECT_GT(a.GetMax().x, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, Expand) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
bounds.Expand(2.0f);
|
||||
|
||||
EXPECT_GT(bounds.GetMax().x, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, GetVolume) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(2, 4, 6));
|
||||
float volume = bounds.GetVolume();
|
||||
|
||||
EXPECT_FLOAT_EQ(volume, 48.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Bounds, GetClosestPoint) {
|
||||
Bounds bounds(Vector3::Zero(), Vector3(2, 2, 2));
|
||||
Vector3 point = bounds.GetClosestPoint(Vector3(10, 10, 10));
|
||||
|
||||
EXPECT_FLOAT_EQ(point.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(point.y, 1.0f);
|
||||
EXPECT_FLOAT_EQ(point.z, 1.0f);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Frustum Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Frustum, Contains_Point) {
|
||||
Frustum frustum;
|
||||
Plane nearPlane;
|
||||
nearPlane.normal = Vector3::Forward();
|
||||
nearPlane.distance = 1.0f;
|
||||
frustum.planes[4] = nearPlane;
|
||||
|
||||
EXPECT_TRUE(frustum.Contains(Vector3(0, 0, 2)));
|
||||
}
|
||||
|
||||
TEST(Math_Frustum, Contains_Sphere) {
|
||||
Frustum frustum;
|
||||
Plane nearPlane;
|
||||
nearPlane.normal = Vector3::Forward();
|
||||
nearPlane.distance = 1.0f;
|
||||
frustum.planes[4] = nearPlane;
|
||||
|
||||
Sphere sphere(Vector3(0, 0, 5), 1.0f);
|
||||
EXPECT_TRUE(frustum.Contains(sphere));
|
||||
}
|
||||
|
||||
TEST(Math_Frustum, Intersects_Sphere) {
|
||||
Frustum frustum;
|
||||
Plane nearPlane;
|
||||
nearPlane.normal = Vector3::Forward();
|
||||
nearPlane.distance = 1.0f;
|
||||
frustum.planes[4] = nearPlane;
|
||||
|
||||
Sphere sphere(Vector3(0, 0, 2), 0.5f);
|
||||
EXPECT_TRUE(frustum.Intersects(sphere));
|
||||
}
|
||||
|
||||
TEST(Math_Frustum, Intersects_Bounds) {
|
||||
Frustum frustum;
|
||||
Plane nearPlane;
|
||||
nearPlane.normal = Vector3::Forward();
|
||||
nearPlane.distance = 1.0f;
|
||||
frustum.planes[4] = nearPlane;
|
||||
|
||||
Bounds bounds(Vector3(0, 0, 2), Vector3(1, 1, 1));
|
||||
EXPECT_TRUE(frustum.Intersects(bounds));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
312
tests/math/test_matrix.cpp
Normal file
312
tests/math/test_matrix.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Math/Matrix4.h>
|
||||
#include <XCEngine/Math/Vector3.h>
|
||||
#include <XCEngine/Math/Quaternion.h>
|
||||
|
||||
using namespace XCEngine::Math;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Math_Matrix4, DefaultConstructor_InitializesToZero) {
|
||||
Matrix4x4 m;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
EXPECT_FLOAT_EQ(m.m[i][j], 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Identity_IsCorrect) {
|
||||
Matrix4x4 id = Matrix4x4::Identity();
|
||||
|
||||
EXPECT_FLOAT_EQ(id.m[0][0], 1.0f);
|
||||
EXPECT_FLOAT_EQ(id.m[1][1], 1.0f);
|
||||
EXPECT_FLOAT_EQ(id.m[2][2], 1.0f);
|
||||
EXPECT_FLOAT_EQ(id.m[3][3], 1.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(id.m[0][1], 0.0f);
|
||||
EXPECT_FLOAT_EQ(id.m[1][2], 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Zero_IsCorrect) {
|
||||
Matrix4x4 zero = Matrix4x4::Zero();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
EXPECT_FLOAT_EQ(zero.m[i][j], 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Translation) {
|
||||
Vector3 translation(1.0f, 2.0f, 3.0f);
|
||||
Matrix4x4 m = Matrix4x4::Translation(translation);
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][3], 1.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[1][3], 2.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[2][3], 3.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[3][3], 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Translation_TransformsPoint) {
|
||||
Matrix4x4 m = Matrix4x4::Translation(Vector3(1, 2, 3));
|
||||
Vector3 point(0, 0, 0);
|
||||
|
||||
Vector3 result = m.MultiplyPoint(point);
|
||||
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Scale) {
|
||||
Vector3 scale(2.0f, 3.0f, 4.0f);
|
||||
Matrix4x4 m = Matrix4x4::Scale(scale);
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][0], 2.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[1][1], 3.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[2][2], 4.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[3][3], 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Scale_TransformsPoint) {
|
||||
Matrix4x4 m = Matrix4x4::Scale(Vector3(2, 3, 4));
|
||||
Vector3 point(1, 1, 1);
|
||||
|
||||
Vector3 result = m.MultiplyPoint(point);
|
||||
EXPECT_FLOAT_EQ(result.x, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 3.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, RotationX) {
|
||||
Matrix4x4 m = Matrix4x4::RotationX(PI * 0.5f);
|
||||
|
||||
EXPECT_NEAR(m.m[1][1], 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[1][2], -1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[2][1], 1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[2][2], 0.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, RotationY) {
|
||||
Matrix4x4 m = Matrix4x4::RotationY(PI * 0.5f);
|
||||
|
||||
EXPECT_NEAR(m.m[0][0], 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[0][2], 1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[2][0], -1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[2][2], 0.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, RotationZ) {
|
||||
Matrix4x4 m = Matrix4x4::RotationZ(PI * 0.5f);
|
||||
|
||||
EXPECT_NEAR(m.m[0][0], 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[0][1], -1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[1][0], 1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[1][1], 0.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, TRS) {
|
||||
Vector3 translation(1, 2, 3);
|
||||
Quaternion rotation = Quaternion::Identity();
|
||||
Vector3 scale(2, 2, 2);
|
||||
|
||||
Matrix4x4 m = Matrix4x4::TRS(translation, rotation, scale);
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][3], 1.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[1][3], 2.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[2][3], 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, LookAt) {
|
||||
Vector3 eye(0, 0, 5);
|
||||
Vector3 target(0, 0, 0);
|
||||
Vector3 up(0, 1, 0);
|
||||
|
||||
Matrix4x4 m = Matrix4x4::LookAt(eye, target, up);
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][3], 0.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[1][3], 0.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[2][3], -5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Perspective) {
|
||||
float fov = PI * 0.25f;
|
||||
float aspect = 16.0f / 9.0f;
|
||||
float nearPlane = 0.1f;
|
||||
float farPlane = 100.0f;
|
||||
|
||||
Matrix4x4 m = Matrix4x4::Perspective(fov, aspect, nearPlane, farPlane);
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][0], 1.0f / (aspect * std::tan(fov * 0.5f)));
|
||||
EXPECT_FLOAT_EQ(m.m[1][1], 1.0f / std::tan(fov * 0.5f));
|
||||
EXPECT_FLOAT_EQ(m.m[2][2], -(farPlane + nearPlane) / (farPlane - nearPlane));
|
||||
EXPECT_FLOAT_EQ(m.m[2][3], -(2.0f * farPlane * nearPlane) / (farPlane - nearPlane));
|
||||
EXPECT_FLOAT_EQ(m.m[3][2], -1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Orthographic) {
|
||||
float left = -10.0f, right = 10.0f;
|
||||
float bottom = -10.0f, top = 10.0f;
|
||||
float nearPlane = 0.1f, farPlane = 100.0f;
|
||||
|
||||
Matrix4x4 m = Matrix4x4::Orthographic(left, right, bottom, top, nearPlane, farPlane);
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][0], 0.1f);
|
||||
EXPECT_FLOAT_EQ(m.m[1][1], 0.1f);
|
||||
EXPECT_FLOAT_EQ(m.m[2][2], -2.0f / (farPlane - nearPlane));
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Multiply_MatrixWithMatrix) {
|
||||
Matrix4x4 a = Matrix4x4::Identity();
|
||||
Matrix4x4 b = Matrix4x4::Translation(Vector3(1, 2, 3));
|
||||
|
||||
Matrix4x4 result = a * b;
|
||||
|
||||
EXPECT_FLOAT_EQ(result.m[0][3], 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.m[1][3], 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.m[2][3], 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Multiply_MatrixWithVector4) {
|
||||
Matrix4x4 m = Matrix4x4::Translation(Vector3(1, 2, 3));
|
||||
Vector4 v(1, 1, 1, 1);
|
||||
|
||||
Vector4 result = m * v;
|
||||
|
||||
EXPECT_FLOAT_EQ(result.x, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 3.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, MultiplyPoint) {
|
||||
Matrix4x4 m = Matrix4x4::Translation(Vector3(1, 2, 3));
|
||||
Vector3 p(0, 0, 0);
|
||||
|
||||
Vector3 result = m.MultiplyPoint(p);
|
||||
|
||||
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, MultiplyVector) {
|
||||
Matrix4x4 m = Matrix4x4::Scale(Vector3(2, 2, 2));
|
||||
Vector3 v(1, 1, 1);
|
||||
|
||||
Vector3 result = m.MultiplyVector(v);
|
||||
|
||||
EXPECT_FLOAT_EQ(result.x, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 2.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Transpose) {
|
||||
Matrix4x4 m;
|
||||
m.m[0][1] = 1.0f;
|
||||
m.m[1][2] = 2.0f;
|
||||
m.m[2][3] = 3.0f;
|
||||
|
||||
Matrix4x4 t = m.Transpose();
|
||||
|
||||
EXPECT_FLOAT_EQ(t.m[1][0], 1.0f);
|
||||
EXPECT_FLOAT_EQ(t.m[2][1], 2.0f);
|
||||
EXPECT_FLOAT_EQ(t.m[3][2], 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Inverse_Identity_ReturnsIdentity) {
|
||||
Matrix4x4 id = Matrix4x4::Identity();
|
||||
Matrix4x4 inv = id.Inverse();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
EXPECT_NEAR(inv.m[i][j], id.m[i][j], 1e-5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Inverse_Translation) {
|
||||
Matrix4x4 m = Matrix4x4::Translation(Vector3(1, 2, 3));
|
||||
Matrix4x4 inv = m.Inverse();
|
||||
|
||||
Vector3 p(0, 0, 0);
|
||||
Vector3 transformed = m.MultiplyPoint(p);
|
||||
Vector3 recovered = inv.MultiplyPoint(transformed);
|
||||
|
||||
EXPECT_NEAR(recovered.x, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(recovered.y, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(recovered.z, 0.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Inverse_OfInverse_EqualsOriginal) {
|
||||
Matrix4x4 original = Matrix4x4::TRS(
|
||||
Vector3(1, 2, 3),
|
||||
Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f),
|
||||
Vector3(2, 2, 2)
|
||||
);
|
||||
|
||||
Matrix4x4 inverted = original.Inverse();
|
||||
Matrix4x4 recovered = inverted.Inverse();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
EXPECT_NEAR(original.m[i][j], recovered.m[i][j], 1e-4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Determinant_Identity_EqualsOne) {
|
||||
Matrix4x4 id = Matrix4x4::Identity();
|
||||
EXPECT_FLOAT_EQ(id.Determinant(), 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, GetTranslation) {
|
||||
Vector3 translation(1, 2, 3);
|
||||
Matrix4x4 m = Matrix4x4::Translation(translation);
|
||||
|
||||
Vector3 result = m.GetTranslation();
|
||||
|
||||
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, GetScale) {
|
||||
Vector3 scale(2, 3, 4);
|
||||
Matrix4x4 m = Matrix4x4::Scale(scale);
|
||||
|
||||
Vector3 result = m.GetScale();
|
||||
|
||||
EXPECT_NEAR(result.x, 2.0f, 1e-5f);
|
||||
EXPECT_NEAR(result.y, 3.0f, 1e-5f);
|
||||
EXPECT_NEAR(result.z, 4.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, Decompose_TRS) {
|
||||
Vector3 originalPos(1, 2, 3);
|
||||
Quaternion originalRot = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
Vector3 originalScale(2, 2, 2);
|
||||
|
||||
Matrix4x4 m = Matrix4x4::TRS(originalPos, originalRot, originalScale);
|
||||
|
||||
Vector3 pos, scale;
|
||||
Quaternion rot;
|
||||
m.Decompose(pos, rot, scale);
|
||||
|
||||
EXPECT_NEAR(pos.x, originalPos.x, 1e-4f);
|
||||
EXPECT_NEAR(pos.y, originalPos.y, 1e-4f);
|
||||
EXPECT_NEAR(pos.z, originalPos.z, 1e-4f);
|
||||
|
||||
EXPECT_NEAR(scale.x, originalScale.x, 1e-4f);
|
||||
EXPECT_NEAR(scale.y, originalScale.y, 1e-4f);
|
||||
EXPECT_NEAR(scale.z, originalScale.z, 1e-4f);
|
||||
}
|
||||
|
||||
TEST(Math_Matrix4, IndexOperator) {
|
||||
Matrix4x4 m;
|
||||
m[0][0] = 1.0f;
|
||||
|
||||
EXPECT_FLOAT_EQ(m[0][0], 1.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[0][0], 1.0f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
277
tests/math/test_quaternion.cpp
Normal file
277
tests/math/test_quaternion.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Math/Quaternion.h>
|
||||
#include <XCEngine/Math/Matrix4.h>
|
||||
#include <XCEngine/Math/Vector3.h>
|
||||
#include <XCEngine/Math/Math.h>
|
||||
|
||||
using namespace XCEngine::Math;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Math_Quaternion, DefaultConstructor) {
|
||||
Quaternion q;
|
||||
EXPECT_FLOAT_EQ(q.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.w, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, ParameterConstructor) {
|
||||
Quaternion q(1.0f, 2.0f, 3.0f, 4.0f);
|
||||
EXPECT_FLOAT_EQ(q.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(q.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(q.z, 3.0f);
|
||||
EXPECT_FLOAT_EQ(q.w, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Identity) {
|
||||
Quaternion q = Quaternion::Identity();
|
||||
EXPECT_FLOAT_EQ(q.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.w, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, FromAxisAngle) {
|
||||
Quaternion q = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
|
||||
float sinHalfAngle = std::sin(PI * 0.25f);
|
||||
float cosHalfAngle = std::cos(PI * 0.25f);
|
||||
|
||||
EXPECT_NEAR(q.x, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(q.y, sinHalfAngle, 1e-5f);
|
||||
EXPECT_NEAR(q.z, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(q.w, cosHalfAngle, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, FromAxisAngle_IdentityRotation) {
|
||||
Quaternion q = Quaternion::FromAxisAngle(Vector3::Up(), 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(q.w, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, FromEulerAngles) {
|
||||
Quaternion q = Quaternion::FromEulerAngles(PI * 0.5f, 0.0f, 0.0f);
|
||||
|
||||
float sinHalfPitch = std::sin(PI * 0.25f);
|
||||
float cosHalfPitch = std::cos(PI * 0.25f);
|
||||
|
||||
EXPECT_NEAR(q.x, sinHalfPitch, 1e-5f);
|
||||
EXPECT_NEAR(q.w, cosHalfPitch, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, ToEulerAngles_FromEulerAngles) {
|
||||
Vector3 euler(PI * 0.5f, PI * 0.25f, PI * 0.125f);
|
||||
Quaternion q = Quaternion::FromEulerAngles(euler.x, euler.y, euler.z);
|
||||
Vector3 result = q.ToEulerAngles();
|
||||
|
||||
EXPECT_NEAR(result.x, euler.x, 1e-4f);
|
||||
EXPECT_NEAR(result.y, euler.y, 1e-4f);
|
||||
EXPECT_NEAR(result.z, euler.z, 1e-4f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, ToMatrix4x4_Identity) {
|
||||
Quaternion q = Quaternion::Identity();
|
||||
Matrix4x4 m = q.ToMatrix4x4();
|
||||
|
||||
EXPECT_FLOAT_EQ(m.m[0][0], 1.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[1][1], 1.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[2][2], 1.0f);
|
||||
EXPECT_FLOAT_EQ(m.m[3][3], 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, ToMatrix4x4_RotationY) {
|
||||
Quaternion q = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
Matrix4x4 m = q.ToMatrix4x4();
|
||||
|
||||
EXPECT_NEAR(m.m[0][0], 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[0][2], 1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[2][0], -1.0f, 1e-5f);
|
||||
EXPECT_NEAR(m.m[2][2], 0.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, FromRotationMatrix) {
|
||||
Matrix4x4 m = Matrix4x4::RotationY(PI * 0.5f);
|
||||
Quaternion q = Quaternion::FromRotationMatrix(m);
|
||||
|
||||
EXPECT_NEAR(std::abs(q.y), 0.707f, 1e-3f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Slerp_IdentityToIdentity) {
|
||||
Quaternion a = Quaternion::Identity();
|
||||
Quaternion b = Quaternion::Identity();
|
||||
|
||||
Quaternion result = Quaternion::Slerp(a, b, 0.5f);
|
||||
|
||||
EXPECT_FLOAT_EQ(result.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.w, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Slerp_QuarterRotation) {
|
||||
Quaternion a = Quaternion::Identity();
|
||||
Quaternion b = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
|
||||
Quaternion result = Quaternion::Slerp(a, b, 0.5f);
|
||||
|
||||
float expectedAngle = PI * 0.25f;
|
||||
Quaternion expected = Quaternion::FromAxisAngle(Vector3::Up(), expectedAngle);
|
||||
|
||||
EXPECT_NEAR(result.x, expected.x, 1e-4f);
|
||||
EXPECT_NEAR(result.y, expected.y, 1e-4f);
|
||||
EXPECT_NEAR(result.z, expected.z, 1e-4f);
|
||||
EXPECT_NEAR(result.w, expected.w, 1e-4f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Slerp_Start_ReturnsFirst) {
|
||||
Quaternion a(0.1f, 0.2f, 0.3f, 0.9f);
|
||||
Quaternion b(0.9f, 0.1f, 0.1f, 0.1f);
|
||||
|
||||
Quaternion result = Quaternion::Slerp(a, b, 0.0f);
|
||||
|
||||
EXPECT_NEAR(result.x, a.x, 1e-5f);
|
||||
EXPECT_NEAR(result.y, a.y, 1e-5f);
|
||||
EXPECT_NEAR(result.z, a.z, 1e-5f);
|
||||
EXPECT_NEAR(result.w, a.w, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Slerp_End_ReturnsSecond) {
|
||||
Quaternion a(0.1f, 0.2f, 0.3f, 0.9f);
|
||||
Quaternion b(0.9f, 0.1f, 0.1f, 0.1f);
|
||||
|
||||
Quaternion result = Quaternion::Slerp(a, b, 1.0f);
|
||||
|
||||
EXPECT_NEAR(result.x, b.x, 1e-4f);
|
||||
EXPECT_NEAR(result.y, b.y, 1e-4f);
|
||||
EXPECT_NEAR(result.z, b.z, 1e-4f);
|
||||
EXPECT_NEAR(result.w, b.w, 1e-4f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Slerp_ClampsT) {
|
||||
Quaternion a = Quaternion::Identity();
|
||||
Quaternion b = Quaternion::FromAxisAngle(Vector3::Up(), PI);
|
||||
|
||||
Quaternion result1 = Quaternion::Slerp(a, b, -0.5f);
|
||||
Quaternion result2 = Quaternion::Slerp(a, b, 1.5f);
|
||||
|
||||
EXPECT_NEAR(result1.x, a.x, 1e-5f);
|
||||
EXPECT_NEAR(result2.x, b.x, 1e-4f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, LookRotation_Forward) {
|
||||
Quaternion q = Quaternion::LookRotation(Vector3::Forward());
|
||||
|
||||
EXPECT_NEAR(q.x, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(q.y, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(q.z, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(q.w, 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, LookRotation_Up) {
|
||||
Quaternion q = Quaternion::LookRotation(Vector3::Up());
|
||||
|
||||
EXPECT_NEAR(q.x, 0.707f, 1e-3f);
|
||||
EXPECT_NEAR(q.w, 0.707f, 1e-3f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, LookRotation_Right) {
|
||||
Quaternion q = Quaternion::LookRotation(Vector3::Right());
|
||||
|
||||
EXPECT_NEAR(q.y, -0.707f, 1e-3f);
|
||||
EXPECT_NEAR(q.w, 0.707f, 1e-3f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Multiply_ProducesRotation) {
|
||||
Quaternion q1 = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
Quaternion q2 = Quaternion::FromAxisAngle(Vector3::Right(), PI * 0.5f);
|
||||
|
||||
Quaternion result = q1 * q2;
|
||||
|
||||
EXPECT_TRUE(result.Magnitude() > 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Multiply_Identity) {
|
||||
Quaternion q(0.1f, 0.2f, 0.3f, 0.9f);
|
||||
Quaternion identity = Quaternion::Identity();
|
||||
|
||||
Quaternion result = q * identity;
|
||||
|
||||
EXPECT_NEAR(result.x, q.x, 1e-5f);
|
||||
EXPECT_NEAR(result.y, q.y, 1e-5f);
|
||||
EXPECT_NEAR(result.z, q.z, 1e-5f);
|
||||
EXPECT_NEAR(result.w, q.w, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Multiply_Vector3) {
|
||||
Quaternion q = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
Vector3 v = Vector3::Right();
|
||||
|
||||
Vector3 result = q * v;
|
||||
|
||||
EXPECT_NEAR(result.x, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(result.z, 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Inverse_Identity) {
|
||||
Quaternion q = Quaternion::Identity();
|
||||
Quaternion inv = q.Inverse();
|
||||
|
||||
EXPECT_FLOAT_EQ(inv.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(inv.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(inv.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(inv.w, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Inverse_Rotation) {
|
||||
Quaternion q = Quaternion::FromAxisAngle(Vector3::Up(), PI * 0.5f);
|
||||
Quaternion inv = q.Inverse();
|
||||
|
||||
Quaternion product = q * inv;
|
||||
|
||||
EXPECT_NEAR(product.x, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(product.y, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(product.z, 0.0f, 1e-5f);
|
||||
EXPECT_NEAR(product.w, 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Dot) {
|
||||
Quaternion a(1, 0, 0, 0);
|
||||
Quaternion b(1, 0, 0, 0);
|
||||
|
||||
float result = a.Dot(b);
|
||||
|
||||
EXPECT_FLOAT_EQ(result, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Dot_Orthogonal) {
|
||||
Quaternion a(1, 0, 0, 0);
|
||||
Quaternion b(0, 1, 0, 0);
|
||||
|
||||
float result = a.Dot(b);
|
||||
|
||||
EXPECT_FLOAT_EQ(result, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Magnitude) {
|
||||
Quaternion q(1, 0, 0, 0);
|
||||
EXPECT_FLOAT_EQ(q.Magnitude(), 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Normalized) {
|
||||
Quaternion q(1, 2, 3, 4);
|
||||
Quaternion normalized = q.Normalized();
|
||||
|
||||
EXPECT_NEAR(normalized.Magnitude(), 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Quaternion, Normalize) {
|
||||
Quaternion q(1, 2, 3, 4);
|
||||
Quaternion result = Quaternion::Normalize(q);
|
||||
|
||||
EXPECT_NEAR(result.Magnitude(), 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
346
tests/math/test_vector.cpp
Normal file
346
tests/math/test_vector.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <XCEngine/Math/Vector2.h>
|
||||
#include <XCEngine/Math/Vector3.h>
|
||||
#include <XCEngine/Math/Vector4.h>
|
||||
#include <XCEngine/Math/Math.h>
|
||||
|
||||
using namespace XCEngine::Math;
|
||||
|
||||
namespace {
|
||||
|
||||
// ============================================================
|
||||
// Vector2 Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Vector2, DefaultConstructor_InitializesToZero) {
|
||||
Vector2 v;
|
||||
EXPECT_FLOAT_EQ(v.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, ParameterConstructor) {
|
||||
Vector2 v(3.0f, 4.0f);
|
||||
EXPECT_FLOAT_EQ(v.x, 3.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, StaticFactories) {
|
||||
EXPECT_EQ(Vector2::Zero(), Vector2(0, 0));
|
||||
EXPECT_EQ(Vector2::One(), Vector2(1, 1));
|
||||
EXPECT_EQ(Vector2::Up(), Vector2(0, 1));
|
||||
EXPECT_EQ(Vector2::Down(), Vector2(0, -1));
|
||||
EXPECT_EQ(Vector2::Right(), Vector2(1, 0));
|
||||
EXPECT_EQ(Vector2::Left(), Vector2(-1, 0));
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Dot_Orthogonal_ReturnsZero) {
|
||||
EXPECT_FLOAT_EQ(Vector2::Dot(Vector2::Right(), Vector2::Up()), 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Dot_SameDirection_ReturnsMagnitudeProduct) {
|
||||
EXPECT_FLOAT_EQ(Vector2::Dot(Vector2(1, 0), Vector2(2, 0)), 2.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Cross_ReturnsScalar) {
|
||||
EXPECT_FLOAT_EQ(Vector2::Cross(Vector2::Right(), Vector2::Up()), 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Magnitude) {
|
||||
EXPECT_FLOAT_EQ(Vector2(3, 4).Magnitude(), 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, SqrMagnitude) {
|
||||
EXPECT_FLOAT_EQ(Vector2(3, 4).SqrMagnitude(), 25.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Normalize_NonZero_ReturnsUnitVector) {
|
||||
Vector2 v = Vector2(3, 4).Normalized();
|
||||
EXPECT_NEAR(v.Magnitude(), 1.0f, 1e-6f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Normalize_ZeroVector_ReturnsZeroVector) {
|
||||
Vector2 v = Vector2::Zero();
|
||||
Vector2 normalized = Vector2::Normalize(v);
|
||||
EXPECT_FLOAT_EQ(normalized.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(normalized.y, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Lerp) {
|
||||
Vector2 a(0, 0);
|
||||
Vector2 b(10, 10);
|
||||
|
||||
Vector2 result = Vector2::Lerp(a, b, 0.5f);
|
||||
EXPECT_FLOAT_EQ(result.x, 5.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Lerp_ClampsToZero) {
|
||||
Vector2 a(0, 0);
|
||||
Vector2 b(10, 10);
|
||||
|
||||
Vector2 result = Vector2::Lerp(a, b, -0.5f);
|
||||
EXPECT_FLOAT_EQ(result.x, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Lerp_ClampsToOne) {
|
||||
Vector2 a(0, 0);
|
||||
Vector2 b(10, 10);
|
||||
|
||||
Vector2 result = Vector2::Lerp(a, b, 1.5f);
|
||||
EXPECT_FLOAT_EQ(result.x, 10.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, MoveTowards) {
|
||||
Vector2 current(0, 0);
|
||||
Vector2 target(10, 0);
|
||||
|
||||
Vector2 result = Vector2::MoveTowards(current, target, 3.0f);
|
||||
EXPECT_FLOAT_EQ(result.x, 3.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, MoveTowards_AlreadyAtTarget) {
|
||||
Vector2 current(10, 10);
|
||||
Vector2 target(10, 10);
|
||||
|
||||
Vector2 result = Vector2::MoveTowards(current, target, 5.0f);
|
||||
EXPECT_EQ(result, target);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Operators) {
|
||||
Vector2 a(1, 2);
|
||||
Vector2 b(3, 4);
|
||||
|
||||
Vector2 sum = a + b;
|
||||
EXPECT_FLOAT_EQ(sum.x, 4.0f);
|
||||
EXPECT_FLOAT_EQ(sum.y, 6.0f);
|
||||
|
||||
Vector2 diff = b - a;
|
||||
EXPECT_FLOAT_EQ(diff.x, 2.0f);
|
||||
EXPECT_FLOAT_EQ(diff.y, 2.0f);
|
||||
|
||||
Vector2 scaled = a * 2.0f;
|
||||
EXPECT_FLOAT_EQ(scaled.x, 2.0f);
|
||||
EXPECT_FLOAT_EQ(scaled.y, 4.0f);
|
||||
|
||||
Vector2 divided = a / 2.0f;
|
||||
EXPECT_FLOAT_EQ(divided.x, 0.5f);
|
||||
EXPECT_FLOAT_EQ(divided.y, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector2, Equality) {
|
||||
Vector2 a(1.0f, 2.0f);
|
||||
Vector2 b(1.0f, 2.0f);
|
||||
Vector2 c(1.0f, 3.0f);
|
||||
|
||||
EXPECT_TRUE(a == b);
|
||||
EXPECT_FALSE(a == c);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Vector3 Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Vector3, DefaultConstructor_InitializesToZero) {
|
||||
Vector3 v;
|
||||
EXPECT_FLOAT_EQ(v.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(v.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, ParameterConstructor) {
|
||||
Vector3 v(1.0f, 2.0f, 3.0f);
|
||||
EXPECT_FLOAT_EQ(v.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(v.z, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, StaticFactories) {
|
||||
EXPECT_EQ(Vector3::Zero(), Vector3(0, 0, 0));
|
||||
EXPECT_EQ(Vector3::One(), Vector3(1, 1, 1));
|
||||
EXPECT_EQ(Vector3::Forward(), Vector3(0, 0, 1));
|
||||
EXPECT_EQ(Vector3::Back(), Vector3(0, 0, -1));
|
||||
EXPECT_EQ(Vector3::Up(), Vector3(0, 1, 0));
|
||||
EXPECT_EQ(Vector3::Down(), Vector3(0, -1, 0));
|
||||
EXPECT_EQ(Vector3::Right(), Vector3(1, 0, 0));
|
||||
EXPECT_EQ(Vector3::Left(), Vector3(-1, 0, 0));
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Dot_Orthogonal_ReturnsZero) {
|
||||
EXPECT_FLOAT_EQ(Vector3::Dot(Vector3::Right(), Vector3::Up()), 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Cross_Orthogonal_ReturnsPerpendicular) {
|
||||
Vector3 result = Vector3::Cross(Vector3::Right(), Vector3::Up());
|
||||
EXPECT_FLOAT_EQ(result.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 1.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Cross_SameVector_ReturnsZero) {
|
||||
Vector3 result = Vector3::Cross(Vector3::Right(), Vector3::Right());
|
||||
EXPECT_FLOAT_EQ(result.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Magnitude) {
|
||||
EXPECT_FLOAT_EQ(Vector3(1, 2, 2).Magnitude(), 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, SqrMagnitude) {
|
||||
EXPECT_FLOAT_EQ(Vector3(1, 2, 2).SqrMagnitude(), 9.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Normalize) {
|
||||
Vector3 v = Vector3(3, 4, 0).Normalized();
|
||||
EXPECT_NEAR(v.Magnitude(), 1.0f, 1e-6f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Lerp) {
|
||||
Vector3 a(0, 0, 0);
|
||||
Vector3 b(10, 10, 10);
|
||||
|
||||
Vector3 result = Vector3::Lerp(a, b, 0.5f);
|
||||
EXPECT_FLOAT_EQ(result.x, 5.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 5.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 5.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Project_OntoNormal) {
|
||||
Vector3 v(1, 1, 0);
|
||||
Vector3 normal(1, 0, 0);
|
||||
|
||||
Vector3 result = Vector3::Project(v, normal);
|
||||
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, ProjectOnPlane) {
|
||||
Vector3 v(1, 1, 1);
|
||||
Vector3 planeNormal(0, 0, 1);
|
||||
|
||||
Vector3 result = Vector3::ProjectOnPlane(v, planeNormal);
|
||||
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Angle) {
|
||||
float angle = Vector3::Angle(Vector3::Right(), Vector3::Up());
|
||||
EXPECT_NEAR(angle, 90.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Reflect) {
|
||||
Vector3 direction(1, -1, 0);
|
||||
Vector3 normal(0, 1, 0);
|
||||
|
||||
Vector3 result = Vector3::Reflect(direction, normal);
|
||||
EXPECT_FLOAT_EQ(result.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.y, 1.0f);
|
||||
EXPECT_FLOAT_EQ(result.z, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, MoveTowards) {
|
||||
Vector3 current(0, 0, 0);
|
||||
Vector3 target(10, 0, 0);
|
||||
|
||||
Vector3 result = Vector3::MoveTowards(current, target, 4.0f);
|
||||
EXPECT_FLOAT_EQ(result.x, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, Operators) {
|
||||
Vector3 a(1, 2, 3);
|
||||
Vector3 b(4, 5, 6);
|
||||
|
||||
Vector3 sum = a + b;
|
||||
EXPECT_FLOAT_EQ(sum.x, 5.0f);
|
||||
EXPECT_FLOAT_EQ(sum.y, 7.0f);
|
||||
EXPECT_FLOAT_EQ(sum.z, 9.0f);
|
||||
|
||||
Vector3 diff = b - a;
|
||||
EXPECT_FLOAT_EQ(diff.x, 3.0f);
|
||||
EXPECT_FLOAT_EQ(diff.y, 3.0f);
|
||||
EXPECT_FLOAT_EQ(diff.z, 3.0f);
|
||||
|
||||
Vector3 scaled = a * 2.0f;
|
||||
EXPECT_FLOAT_EQ(scaled.x, 2.0f);
|
||||
EXPECT_FLOAT_EQ(scaled.y, 4.0f);
|
||||
EXPECT_FLOAT_EQ(scaled.z, 6.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector3, IndexOperator) {
|
||||
Vector3 v(1, 2, 3);
|
||||
EXPECT_FLOAT_EQ(v[0], 1.0f);
|
||||
EXPECT_FLOAT_EQ(v[1], 2.0f);
|
||||
EXPECT_FLOAT_EQ(v[2], 3.0f);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Vector4 Tests
|
||||
// ============================================================
|
||||
|
||||
TEST(Math_Vector4, DefaultConstructor_InitializesToZero) {
|
||||
Vector4 v;
|
||||
EXPECT_FLOAT_EQ(v.x, 0.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 0.0f);
|
||||
EXPECT_FLOAT_EQ(v.z, 0.0f);
|
||||
EXPECT_FLOAT_EQ(v.w, 0.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector4, ParameterConstructor) {
|
||||
Vector4 v(1.0f, 2.0f, 3.0f, 4.0f);
|
||||
EXPECT_FLOAT_EQ(v.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(v.z, 3.0f);
|
||||
EXPECT_FLOAT_EQ(v.w, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector4, ConstructFromVector3) {
|
||||
Vector3 v3(1, 2, 3);
|
||||
Vector4 v(v3, 4.0f);
|
||||
|
||||
EXPECT_FLOAT_EQ(v.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(v.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(v.z, 3.0f);
|
||||
EXPECT_FLOAT_EQ(v.w, 4.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector4, StaticFactories) {
|
||||
EXPECT_EQ(Vector4::Zero(), Vector4(0, 0, 0, 0));
|
||||
EXPECT_EQ(Vector4::One(), Vector4(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
TEST(Math_Vector4, Dot) {
|
||||
Vector4 a(1, 2, 3, 4);
|
||||
Vector4 b(2, 3, 4, 5);
|
||||
|
||||
float result = Vector4::Dot(a, b);
|
||||
EXPECT_FLOAT_EQ(result, 40.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector4, ToVector3) {
|
||||
Vector4 v(1, 2, 3, 4);
|
||||
Vector3 v3 = v.ToVector3();
|
||||
|
||||
EXPECT_FLOAT_EQ(v3.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(v3.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(v3.z, 3.0f);
|
||||
}
|
||||
|
||||
TEST(Math_Vector4, Operators) {
|
||||
Vector4 a(1, 2, 3, 4);
|
||||
Vector4 b(5, 6, 7, 8);
|
||||
|
||||
Vector4 sum = a + b;
|
||||
EXPECT_FLOAT_EQ(sum.x, 6.0f);
|
||||
EXPECT_FLOAT_EQ(sum.y, 8.0f);
|
||||
EXPECT_FLOAT_EQ(sum.z, 10.0f);
|
||||
EXPECT_FLOAT_EQ(sum.w, 12.0f);
|
||||
|
||||
Vector4 scaled = a * 2.0f;
|
||||
EXPECT_FLOAT_EQ(scaled.x, 2.0f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user