diff options
Diffstat (limited to 'src/unittest')
-rw-r--r-- | src/unittest/test_utilities.cpp | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 234f622d5..8e8958d18 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -53,6 +53,7 @@ public: void testIsPowerOfTwo(); void testMyround(); void testStringJoin(); + void testEulerConversion(); }; static TestUtilities g_test_instance; @@ -82,6 +83,7 @@ void TestUtilities::runTests(IGameDef *gamedef) TEST(testIsPowerOfTwo); TEST(testMyround); TEST(testStringJoin); + TEST(testEulerConversion); } //////////////////////////////////////////////////////////////////////////////// @@ -394,3 +396,115 @@ void TestUtilities::testStringJoin() UASSERT(str_join(input, " and ") == "one and two and three"); } + +static bool within(const f32 value1, const f32 value2, const f32 precision) +{ + return std::fabs(value1 - value2) <= precision; +} + +static bool within(const v3f &v1, const v3f &v2, const f32 precision) +{ + return within(v1.X, v2.X, precision) && within(v1.Y, v2.Y, precision) + && within(v1.Z, v2.Z, precision); +} + +static bool within(const core::matrix4 &m1, const core::matrix4 &m2, + const f32 precision) +{ + const f32 *M1 = m1.pointer(); + const f32 *M2 = m2.pointer(); + for (int i = 0; i < 16; i++) + if (! within(M1[i], M2[i], precision)) + return false; + return true; +} + +static bool roundTripsDeg(const v3f &v, const f32 precision) +{ + core::matrix4 m; + setPitchYawRoll(m, v); + return within(v, getPitchYawRoll(m), precision); +} + +void TestUtilities::testEulerConversion() +{ + // This test may fail on non-IEEE systems. + // Low tolerance is 4 ulp(1.0) for binary floats with 24 bit mantissa. + // (ulp = unit in the last place; ulp(1.0) = 2^-23). + const f32 tolL = 4.76837158203125e-7f; + // High tolerance is 2 ulp(180.0), needed for numbers in degrees. + // ulp(180.0) = 2^-16 + const f32 tolH = 3.0517578125e-5f; + v3f v1, v2; + core::matrix4 m1, m2; + const f32 *M1 = m1.pointer(); + const f32 *M2 = m2.pointer(); + + // Check that the radians version and the degrees version + // produce the same results. Check also that the conversion + // works both ways for these values. + v1 = v3f(M_PI/3.0, M_PI/5.0, M_PI/4.0); + v2 = v3f(60.0f, 36.0f, 45.0f); + setPitchYawRollRad(m1, v1); + setPitchYawRoll(m2, v2); + UASSERT(within(m1, m2, tolL)); + UASSERT(within(getPitchYawRollRad(m1), v1, tolL)); + UASSERT(within(getPitchYawRoll(m2), v2, tolH)); + + // Check the rotation matrix produced. + UASSERT(within(M1[0], 0.932004869f, tolL)); + UASSERT(within(M1[1], 0.353553385f, tolL)); + UASSERT(within(M1[2], 0.0797927827f, tolL)); + UASSERT(within(M1[4], -0.21211791f, tolL)); + UASSERT(within(M1[5], 0.353553355f, tolL)); + UASSERT(within(M1[6], 0.911046684f, tolL)); + UASSERT(within(M1[8], 0.293892622f, tolL)); + UASSERT(within(M1[9], -0.866025448f, tolL)); + UASSERT(within(M1[10], 0.404508471f, tolL)); + + // Check that the matrix is still homogeneous with no translation + UASSERT(M1[3] == 0.0f); + UASSERT(M1[7] == 0.0f); + UASSERT(M1[11] == 0.0f); + UASSERT(M1[12] == 0.0f); + UASSERT(M1[13] == 0.0f); + UASSERT(M1[14] == 0.0f); + UASSERT(M1[15] == 1.0f); + UASSERT(M2[3] == 0.0f); + UASSERT(M2[7] == 0.0f); + UASSERT(M2[11] == 0.0f); + UASSERT(M2[12] == 0.0f); + UASSERT(M2[13] == 0.0f); + UASSERT(M2[14] == 0.0f); + UASSERT(M2[15] == 1.0f); + + // Compare to Irrlicht's results. To be comparable, the + // angles must come in a different order and the matrix + // elements to compare are different too. + m2.setRotationRadians(v3f(v1.Z, v1.X, v1.Y)); + UASSERT(within(M1[0], M2[5], tolL)); + UASSERT(within(M1[1], M2[6], tolL)); + UASSERT(within(M1[2], M2[4], tolL)); + + UASSERT(within(M1[4], M2[9], tolL)); + UASSERT(within(M1[5], M2[10], tolL)); + UASSERT(within(M1[6], M2[8], tolL)); + + UASSERT(within(M1[8], M2[1], tolL)); + UASSERT(within(M1[9], M2[2], tolL)); + UASSERT(within(M1[10], M2[0], tolL)); + + // Check that Eulers that produce near gimbal-lock still round-trip + UASSERT(roundTripsDeg(v3f(89.9999f, 17.f, 0.f), tolH)); + UASSERT(roundTripsDeg(v3f(89.9999f, 0.f, 19.f), tolH)); + UASSERT(roundTripsDeg(v3f(89.9999f, 17.f, 19.f), tolH)); + + // Check that Eulers at an angle > 90 degrees may not round-trip... + v1 = v3f(90.00001f, 1.f, 1.f); + setPitchYawRoll(m1, v1); + v2 = getPitchYawRoll(m1); + //UASSERT(within(v1, v2, tolL)); // this is typically false + // ... however the rotation matrix is the same for both + setPitchYawRoll(m2, v2); + UASSERT(within(m1, m2, tolL)); +} |