The important part is that if you invert the cosine, you need to normalize the slerped quaternion.
temp.x = first.x * scale0 + second.x * scale1;temp.y = first.y * scale0 + second.y * scale1;temp.z = first.z * scale0 + second.z * scale1;temp.w = first.w * scale0 + second.w * scale1;if (cosineWasInverted) { result = temp.Normalize(); }else { result = temp; }
There are two other interesting things to note, instead of inverting the cosine, you should also be able to invert the scale0 factor. When you invert the cosine, what you're saying is, instead of slerping clockwise, slerp counter clockwise, because the shorter arc is in that direction. Inverting scale0 lets you precalculate scale0 and scale1 and then do an if{} block for the scale / normalize. I'm also inverting sine omega, because we want to minimize our slow floating-point divides for the CPU. I'd also recommend you implement inlined quaternion operators for *scale and +quaterion (that's definitely just me though).
double omega, sinom; // standard case (slerp) omega = acos(cosine); inv_sinom = 1/sin(omega); scale0 = static_cast<float>(sin((1.0 - time) * omega) * inv_sinom); scale1 = static_cast<float>(sin(time * omega) * inv_sinom); if (cosine < 0.0f) { // invert the scale and normalize scale0 = -scale0; // Would be nice to see some Quaternion operators here // e.g. result = first*scale0 + second*scale1 !!! result.x = first.x * scale0 + second.x * scale1; result.y = first.y * scale0 + second.y * scale1; result.z = first.z * scale0 + second.z * scale1; result.w = first.w * scale0 + second.w * scale1; // Now normalize the final quaternion result.Normalize(); } else { result.x = first.x * scale0 + second.x * scale1; result.y = first.y * scale0 + second.y * scale1; result.z = first.z * scale0 + second.z * scale1; result.w = first.w * scale0 + second.w * scale1; }
Check the code with breakpoints first. If the cosine inversion is ONLY happening with the left-arm bone, then you know there's a solid chance that this is your problem. Then try to apply the minimal fix from the first code snippet. If that works you can consider some other options for improving the slerp.