summaryrefslogtreecommitdiff
path: root/src/unittest/test_utilities.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/unittest/test_utilities.cpp')
-rw-r--r--src/unittest/test_utilities.cpp114
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));
+}