diff --git a/src/unittest/test_irr_matrix4.cpp b/src/unittest/test_irr_matrix4.cpp index 823166636..b89ebdc56 100644 --- a/src/unittest/test_irr_matrix4.cpp +++ b/src/unittest/test_irr_matrix4.cpp @@ -113,7 +113,9 @@ SECTION("getScale") { } SECTION("getRotationRadians") { - auto test_rotation_degrees = [](v3f rad, v3f scale) { + // Test that we can correctly extract a previously set rotation, + // even after applying a scale, with reasonable precision. + auto test_rotation_radians = [](v3f rad, v3f scale) { matrix4 S; S.setScale(scale); matrix4 R; @@ -121,12 +123,23 @@ SECTION("getRotationRadians") { v3f rot = (R * S).getRotationRadians(); matrix4 B; B.setRotationRadians(rot); - // TODO we would like to use a lower tolerance here, but that makes the test likely to fail. - f32 tol = 0.001f; - CHECK(matrix_equals(R, B, tol)); + // Decrease the precision when the angles are close to gimbal lock, as + // that breaks the expectations of precision with Tait-Bryan angles. + // Gimbal lock happens when pitch (the angle applied second) is 90 deg + if (std::abs(std::abs(rot.Y) - QUARTER_TURN) < 0.01f) { + f32 tol = 0.001f; + CHECK(matrix_equals(R, B, tol)); + } else { + CHECK(matrix_equals(R, B)); + } }; SECTION("returns a rotation equivalent to the original rotation") { - test_rotation_degrees({1.0f, 2.0f, 3.0f}, v3f(1)); + test_rotation_radians({1.0f, 2.0f, 3.0f}, v3f(1)); + // Test cases at or near gimbal lock. These cases fail when using a + // smaller tolerance. + test_rotation_radians({1.0f, QUARTER_TURN, 3.0f}, v3f(10.f)); + test_rotation_radians({1.0f, QUARTER_TURN + 0.01001f, 3.0f}, v3f(10.f)); + test_rotation_radians({1.0f, QUARTER_TURN + 0.00999f, 3.0f}, v3f(10.f)); Catch::Generators::RandomFloatingGenerator gen_angle(0.0f, 2 * core::PI, Catch::getSeed()); Catch::Generators::RandomFloatingGenerator gen_scale(0.1f, 10, Catch::getSeed()); auto draw = [](auto &gen) { @@ -138,13 +151,13 @@ SECTION("getRotationRadians") { return v3f{draw(gen), draw(gen), draw(gen)}; }; for (int i = 0; i < 1000; ++i) - test_rotation_degrees(draw_v3f(gen_angle), draw_v3f(gen_scale)); + test_rotation_radians(draw_v3f(gen_angle), draw_v3f(gen_scale)); for (f32 i = 0; i < 4; ++i) for (f32 j = 0; j < 4; ++j) for (f32 k = 0; k < 4; ++k) { v3f rad = core::PI / 4.0f * v3f(i, j, k); for (int l = 0; l < 100; ++l) { - test_rotation_degrees(rad, draw_v3f(gen_scale)); + test_rotation_radians(rad, draw_v3f(gen_scale)); } } }