diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h index 01f86b7329..184420b4db 100644 --- a/libs/math/include/math/TQuatHelpers.h +++ b/libs/math/include/math/TQuatHelpers.h @@ -251,7 +251,7 @@ public: static constexpr T value_eps = T(10) * std::numeric_limits::epsilon(); // Prevent blowing up when slerping between two quaternions that are very near each other. if ((T(1) - absd) < value_eps) { - return normalize(lerp(p, q, t)); + return normalize(lerp(d < 0 ? -p : p, q, t)); } const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1))); diff --git a/libs/math/tests/test_quat.cpp b/libs/math/tests/test_quat.cpp index 8d066e6779..6a5849875e 100644 --- a/libs/math/tests/test_quat.cpp +++ b/libs/math/tests/test_quat.cpp @@ -273,6 +273,20 @@ TEST_F(QuatTest, ArithmeticFunc) { qs = slerp(qa, qb, 0.5); EXPECT_NEAR(qs[3], -0.92, 0.1); EXPECT_NEAR(qs[2], +0.38, 0.1); + + // Create two quats that are near to each other, but with opposite signs. + qa = { 0.76, 0.39, 0.51, 0.19}; + qb = {-0.759, -0.385, -0.50, -0.19}; + qs = slerp(qa, qb, 0.5); + + // The rotation angle produced by v * slerp(A, B, .5) should be between the rotation angles + // produced by (v * A) and (v * B). + double3 v(0, 0, 1); + double3 va = qa * v; + double3 vb = qb * v; + double3 vs = qs * v; + EXPECT_LT(dot(v, va), dot(v, vs)); + EXPECT_LT(dot(v, vs), dot(v, vb)); } TEST_F(QuatTest, MultiplicationExhaustive) {