Files
HandmadeMath/test/categories/QuaternionOps.h
2023-10-31 13:09:20 -06:00

276 lines
8.9 KiB
C

#include "../HandmadeTest.h"
TEST(QuaternionOps, Inverse)
{
HMM_Quat q1 = HMM_Q(1.0f, 2.0f, 3.0f, 4.0f);
HMM_Quat inverse = HMM_InvQ(q1);
HMM_Quat result = HMM_MulQ(q1, inverse);
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(QuaternionOps, Dot)
{
HMM_Quat q1 = HMM_Q(1.0f, 2.0f, 3.0f, 4.0f);
HMM_Quat q2 = HMM_Q(5.0f, 6.0f, 7.0f, 8.0f);
{
float result = HMM_DotQ(q1, q2);
EXPECT_FLOAT_EQ(result, 70.0f);
}
#ifdef __cplusplus
{
float result = HMM_Dot(q1, q2);
EXPECT_FLOAT_EQ(result, 70.0f);
}
#endif
}
TEST(QuaternionOps, Normalize)
{
HMM_Quat q = HMM_Q(1.0f, 2.0f, 3.0f, 4.0f);
{
HMM_Quat result = HMM_NormQ(q);
EXPECT_NEAR(result.X, 0.1825741858f, 0.001f);
EXPECT_NEAR(result.Y, 0.3651483717f, 0.001f);
EXPECT_NEAR(result.Z, 0.5477225575f, 0.001f);
EXPECT_NEAR(result.W, 0.7302967433f, 0.001f);
}
#ifdef __cplusplus
{
HMM_Quat result = HMM_Norm(q);
EXPECT_NEAR(result.X, 0.1825741858f, 0.001f);
EXPECT_NEAR(result.Y, 0.3651483717f, 0.001f);
EXPECT_NEAR(result.Z, 0.5477225575f, 0.001f);
EXPECT_NEAR(result.W, 0.7302967433f, 0.001f);
}
#endif
}
TEST(QuaternionOps, NLerp)
{
HMM_Quat from = HMM_Q(0.0f, 0.0f, 0.0f, 1.0f);
HMM_Quat to = HMM_Q(0.5f, 0.5f, -0.5f, 0.5f);
HMM_Quat result = HMM_NLerp(from, 0.5f, to);
EXPECT_NEAR(result.X, 0.28867513f, 0.001f);
EXPECT_NEAR(result.Y, 0.28867513f, 0.001f);
EXPECT_NEAR(result.Z, -0.28867513f, 0.001f);
EXPECT_NEAR(result.W, 0.86602540f, 0.001f);
}
TEST(QuaternionOps, SLerp)
{
HMM_Quat from = HMM_Q(0.0f, 0.0f, 0.0f, 1.0f);
HMM_Quat to = HMM_Q(0.5f, 0.5f, -0.5f, 0.5f);
{
HMM_Quat result = HMM_SLerp(from, 0.0f, to);
EXPECT_NEAR(result.X, 0.0f, 0.001f);
EXPECT_NEAR(result.Y, 0.0f, 0.001f);
EXPECT_NEAR(result.Z, 0.0f, 0.001f);
EXPECT_NEAR(result.W, 1.0, 0.001f);
}
{
HMM_Quat result = HMM_SLerp(from, 0.25f, to);
EXPECT_NEAR(result.X, 0.149429246f, 0.001f);
EXPECT_NEAR(result.Y, 0.149429246f, 0.001f);
EXPECT_NEAR(result.Z, -0.149429246f, 0.001f);
EXPECT_NEAR(result.W, 0.965925812f, 0.001f);
}
{
HMM_Quat result = HMM_SLerp(from, 0.5f, to);
EXPECT_NEAR(result.X, 0.28867513f, 0.001f);
EXPECT_NEAR(result.Y, 0.28867513f, 0.001f);
EXPECT_NEAR(result.Z, -0.28867513f, 0.001f);
EXPECT_NEAR(result.W, 0.86602540f, 0.001f);
}
{
HMM_Quat result = HMM_SLerp(from, 0.75f, to);
EXPECT_NEAR(result.X, 0.40824830f, 0.001f);
EXPECT_NEAR(result.Y, 0.40824830f, 0.001f);
EXPECT_NEAR(result.Z, -0.40824830f, 0.001f);
EXPECT_NEAR(result.W, 0.70710676f, 0.001f);
}
{
HMM_Quat result = HMM_SLerp(from, 1.0f, to);
EXPECT_NEAR(result.X, 0.5f, 0.001f);
EXPECT_NEAR(result.Y, 0.5f, 0.001f);
EXPECT_NEAR(result.Z, -0.5f, 0.001f);
EXPECT_NEAR(result.W, 0.5f, 0.001f);
}
}
TEST(QuaternionOps, QuatToMat4)
{
const float abs_error = 0.001f;
HMM_Quat rot = HMM_Q(0.707107f, 0.0f, 0.0f, 0.707107f);
HMM_Mat4 result = HMM_QToM4(rot);
EXPECT_NEAR(result.Elements[0][0], 1.0f, abs_error);
EXPECT_NEAR(result.Elements[0][1], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[0][2], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[0][3], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[1][0], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[1][1], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[1][2], 1.0f, abs_error);
EXPECT_NEAR(result.Elements[1][3], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[2][0], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[2][1], -1.0f, abs_error);
EXPECT_NEAR(result.Elements[2][2], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[2][3], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][0], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][1], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][2], 0.0f, abs_error);
EXPECT_NEAR(result.Elements[3][3], 1.0f, abs_error);
}
TEST(QuaternionOps, Mat4ToQuat)
{
const float abs_error = 0.0001f;
// Rotate 90 degrees on the X axis
{
HMM_Mat4 m = HMM_Rotate_RH(HMM_AngleDeg(90.0f), HMM_V3(1, 0, 0));
HMM_Quat result = HMM_M4ToQ_RH(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, sinf, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 90 degrees on the Y axis (axis not normalized, just for fun)
{
HMM_Mat4 m = HMM_Rotate_RH(HMM_AngleDeg(90.0f), HMM_V3(0, 2, 0));
HMM_Quat result = HMM_M4ToQ_RH(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, 0.0f, abs_error);
EXPECT_NEAR(result.Y, sinf, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 90 degrees on the Z axis
{
HMM_Mat4 m = HMM_Rotate_RH(HMM_AngleDeg(90.0f), HMM_V3(0, 0, 1));
HMM_Quat result = HMM_M4ToQ_RH(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, 0.0f, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, sinf, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 45 degrees on the X axis (this hits case 4)
{
HMM_Mat4 m = HMM_Rotate_RH(HMM_AngleDeg(45.0f), HMM_V3(1, 0, 0));
HMM_Quat result = HMM_M4ToQ_RH(m);
float cosf = 0.9238795325f; // cos(90/2 degrees)
float sinf = 0.3826834324f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, sinf, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
/* NOTE(lcf): Left-handed cases. Since both Rotate and M4ToQ are LH results should be
the same with no changes to input. */
// Rotate 90 degrees on the X axis
{
HMM_Mat4 m = HMM_Rotate_LH(HMM_AngleDeg(90.0f), HMM_V3(1, 0, 0));
HMM_Quat result = HMM_M4ToQ_LH(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, sinf, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 90 degrees on the Y axis (axis not normalized, just for fun)
{
HMM_Mat4 m = HMM_Rotate_LH(HMM_AngleDeg(90.0f), HMM_V3(0, 2, 0));
HMM_Quat result = HMM_M4ToQ_LH(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, 0.0f, abs_error);
EXPECT_NEAR(result.Y, sinf, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 90 degrees on the Z axis
{
HMM_Mat4 m = HMM_Rotate_LH(HMM_AngleDeg(90.0f), HMM_V3(0, 0, 1));
HMM_Quat result = HMM_M4ToQ_LH(m);
float cosf = 0.707107f; // cos(90/2 degrees)
float sinf = 0.707107f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, 0.0f, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, sinf, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
// Rotate 45 degrees on the X axis (this hits case 4)
{
HMM_Mat4 m = HMM_Rotate_LH(HMM_AngleDeg(45.0f), HMM_V3(1, 0, 0));
HMM_Quat result = HMM_M4ToQ_LH(m);
float cosf = 0.9238795325f; // cos(90/2 degrees)
float sinf = 0.3826834324f; // sin(90/2 degrees)
EXPECT_NEAR(result.X, sinf, abs_error);
EXPECT_NEAR(result.Y, 0.0f, abs_error);
EXPECT_NEAR(result.Z, 0.0f, abs_error);
EXPECT_NEAR(result.W, cosf, abs_error);
}
}
TEST(QuaternionOps, RotateVectorAxisAngle)
{
{
HMM_Vec3 axis = HMM_V3(0.0f, 1.0f, 0.0f);
float angle = HMM_AngleTurn(1.0/4);
HMM_Vec3 result = HMM_RotateV3AxisAngle_LH(HMM_V3(1.0f, 0.0f, 0.0f), axis, angle);
EXPECT_NEAR(result.X, 0.0f, 0.001f);
EXPECT_NEAR(result.Y, 0.0f, 0.001f);
EXPECT_NEAR(result.Z, 1.0f, 0.001f);
}
{
HMM_Vec3 axis = HMM_V3(1.0f, 0.0f, 0.0f);
float angle = HMM_AngleTurn(1.0/8);
HMM_Vec3 result = HMM_RotateV3AxisAngle_RH(HMM_V3(0.0f, 0.0f, 1.0f), axis, angle);
EXPECT_NEAR(result.X, 0.0f, 0.001f);
EXPECT_NEAR(result.Y, -0.707170f, 0.001f);
EXPECT_NEAR(result.Z, 0.707170f, 0.001f);
}
}