Dear Soya-users,
I believe there is a bug in the quaternion_slerp() function in
matrix.c. The function does not take into account the 'flip' flag when
the two quaternions are nearly parallel (that is, when cosTheta is
close to 1).
To demonstrate the problem, consider q1=[0,0,0,1] and q2=[0,0,0,-1],
and alpha=one_minus_alpha=0.5. In this case, the current algorithm
will produce, before normalization, q=[0,0,0,0] -- normalization will
produce NaNs (or perhaps raise a floating point exception). The
desired result is obviously either [0,0,0,1] or [0,0,0,-1].
If that case is a bit too degenerate, consider q1=[0,0,0,1] and
q2=[0,0,0.14,-0.99], with alpha=0.5 again. In this case the current
algorithm will produce unnormalized q=[0,0,0.07,0.005] and, once
normalized, the result is q=[0,0,1.0,0.07] (to two decimal
places). The desired result in this case is [0,0,-0.07,1.0].
The fix is fairly simple, and a patch is attached. It incidentally
also changes a trailing comma to a semicolon.
Without the patch, I find some rotations can exhibit strange jerky
behaviour.
Regards,
Rory
*** matrix.c.~1.7.~ 2005-11-02 20:50:17.000000000 +0200
--- matrix.c 2005-11-02 21:15:08.000000000 +0200
***************
*** 621,635 ****
} else {
GLfloat theta = acos(cosTheta);
GLfloat sinTheta = 1.0 / sin(theta);
! if (flip == 1)
! scale1 = - sin (theta * one_minus_alpha) * sinTheta;
! else
! scale1 = sin (theta * one_minus_alpha) * sinTheta;
scale2 = sin (theta * alpha) * sinTheta;
}
q[0] = scale1 * q1[0] + scale2 * q2[0];
q[1] = scale1 * q1[1] + scale2 * q2[1];
! q[2] = scale1 * q1[2] + scale2 * q2[2],
q[3] = scale1 * q1[3] + scale2 * q2[3];
quaternion_normalize (q);
}
--- 621,634 ----
} else {
GLfloat theta = acos(cosTheta);
GLfloat sinTheta = 1.0 / sin(theta);
! scale1 = sin (theta * one_minus_alpha) * sinTheta;
scale2 = sin (theta * alpha) * sinTheta;
}
+ if (flip == 1)
+ scale1 = -scale1;
q[0] = scale1 * q1[0] + scale2 * q2[0];
q[1] = scale1 * q1[1] + scale2 * q2[1];
! q[2] = scale1 * q1[2] + scale2 * q2[2];
q[3] = scale1 * q1[3] + scale2 * q2[3];
quaternion_normalize (q);
}