Im pretty sure there's a bug. With the code you should be able to reproduce this.
My first guess was that the threshold constants are too small, but it happens only at a specific angle, so i'm not sure.
But if so, the squared 1.0e-5f in CalcAverageOmega seems also very small to me (no fp expert).
So, here comes the code - hopefully i've pasted the right values at the end, otherwise tell my and i try again...
- Code: Select all
if (pickedBody)
{
float mix = 0.4f;
handPos.m_x = handPos.m_x * (1-mix) + targetPos.m_x * mix;
handPos.m_y = handPos.m_y * (1-mix) + targetPos.m_y * mix;
handPos.m_z = handPos.m_z * (1-mix) + targetPos.m_z * mix;
dQuaternion storeInit = handOrn;
if (1) // this generates error
{
handOrn = handOrn.Slerp (targetOrn, mix);
handOrn.Normalize();
}
if (1) // this is ok
{
dQuaternion storeError = handOrn;
handOrn = storeInit;
quaternionf _handOrn (handOrn.m_q1, handOrn.m_q2, handOrn.m_q3, handOrn.m_q0);
quaternionf _targetOrn (targetOrn.m_q1, targetOrn.m_q2, targetOrn.m_q3, targetOrn.m_q0);
_handOrn.Slerp (&_handOrn, &_targetOrn, mix);
_handOrn.Normalize();
handOrn.m_q0 = _handOrn.w;
handOrn.m_q1 = _handOrn.x;
handOrn.m_q2 = _handOrn.y;
handOrn.m_q3 = _handOrn.z;
float error = storeError.m_q0 - handOrn.m_q0 + storeError.m_q1 - handOrn.m_q1 +
storeError.m_q2 - handOrn.m_q2 + storeError.m_q2 - handOrn.m_q2;
if (error > 0.5f)
{
int breakpoint = 1;
/*
debugger output:
storeError {m_q0=0.67136699 m_q1=0.081991978 m_q2=0.72294796 m_q3=0.14103185} // buggy
handOrn = {m_q0=0.91231567 m_q1=-0.045301240 m_q2=-0.39945686 m_q3 = -0.077860512} // ok
// reproduce with following input, slerping from storeInit to targetOrn with t = 0.4f should produce a wrong result:
storeInit = {m_q0=1.0000000 m_q1=1.9579682e-006 m_q2=-2.3370687e-012 m_q3=5.0701885e-005}
targetOrn = {m_q0=-0.49344867 m_q1=0.096223623 m_q2=0.84845477 m_q3 = 0.16545060}
*/
}
}
Also here the code from my quat slerp:
- Code: Select all
#define DELTAF 0.0001
void quaternionf::Slerp (quaternionf * from, quaternionf * to, float t)
{
float to1[4];
float omega, cosom, sinom;
float scale0, scale1;
// calc cosine
cosom = from->x * to->x + from->y * to->y + from->z * to->z
+ from->w * to->w;
// adjust signs (if necessary)
if ( cosom < 0.0f ) // todo: optimize like lerp
{
cosom = -cosom;
to1[0] = - to->x;
to1[1] = - to->y;
to1[2] = - to->z;
to1[3] = - to->w;
} else {
to1[0] = to->x;
to1[1] = to->y;
to1[2] = to->z;
to1[3] = to->w;
}
// calculate coefficients
if ( (1.0 - cosom) > DELTAF )
{
// standard case (slerp)
omega = (float)acos(cosom);
sinom = (float)sin(omega);
scale0 = (float)sin((1.0f - t) * omega) / sinom;
scale1 = (float)sin(t * omega) / sinom;
} else {
// "from" and "to" quaternions are very close
// ... so we can do a linear interpolation
scale0 = 1.0f - t;
scale1 = t;
}
// calculate final values
x = scale0 * from->x + scale1 * to1[0];
y = scale0 * from->y + scale1 * to1[1];
z = scale0 * from->z + scale1 * to1[2];
w = scale0 * from->w + scale1 * to1[3];
}