It's Tek again with quaternion class. I've been using jogamp.graph.math.Quaternion.slerp() to rotate our model but it seems like slerp() is implemented incorrectly or I am not understanding the math behind it. Currently, the slerp code does the following: if ((1.0f+cosom) > MathFloat.E) { ... } else { x =-a.y; y = a.x; z =-a.w; w = a.z; sclp = MathFloat.sin((1.0f-t) * MathFloat.PI * 0.5f); sclq = MathFloat.sin(t * MathFloat.PI * 0.5f); x = sclp*a.x + sclq*b.x; y = sclp*a.y + sclq*b.y; z = sclp*a.z + sclq*b.z; } I am not sure why anyone would set x value to a.y, y to a.x, z to a.w, and w to a.z. This explains why my quaternion is jumping during animation. I believe "if ((1.0f+cosom) > MathFloat.E)" is trying to see if cosom is negative but I am not sure why only x and z will have opposite of a.y and a.w. Also, angle, w, is not being set at all so that explains why my model doesn't animate until the last frame of display. I am not a Quaternion expert so I don't know the full math behind Quaternion but if I am misinterpreting the slerp(), please let me know. I've implemented a slerp method according to Nick Bobic (http://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php?page=2) /** Set this quaternion from a Sphereical interpolation * of two param quaternion, used mostly for rotational animation * @param a initial quaternion * @param b target quaternion * @param t float between 0 and 1 representing interp. */ @Override public void slerp(Quaternion a,Quaternion b, float t) { float omega, cosom, sinom, sclp, sclq; cosom = a.getX()*b.getX() + a.getY()*b.getY() + a.getZ()*b.getZ() + a.getW()*b.getW(); //adjust the signs (if necessary) if(cosom<0.0) { cosom = -cosom; b.setX(-b.getX()); b.setY(-b.getY()); b.setZ(-b.getZ()); b.setW(-b.getW()); } // calculate coefficients // standard case (slerp) if ((1.0f-cosom) > MathFloat.E) { omega = (float)MathFloat.acos(cosom); sinom = (float)MathFloat.sin(omega); sclp = (float)MathFloat.sin((1.0f-t)*omega) / sinom; sclq = (float)MathFloat.sin(t*omega) / sinom; } else { // "from" and "to" quaternions are very close // ... so we can do a linear interpolation sclp = 1.0f - t; sclq = t; } //calculate the final values x = sclp*a.getX() + sclq*b.getX(); y = sclp*a.getY() + sclq*b.getY(); z = sclp*a.getZ() + sclq*b.getZ(); w = sclp*a.getW() + sclq*b.getW(); } It's working correctly with my model and it was bit easier to understand. Please advise that I had to override slerp() from a class that extended Quaternion. Therefore, I had to use getters and setters.
Thank you Tek, I hope Rami can solve it.
This code looks correct to me. It's derived from: m = ((sin(θ) * (1 - t)) / sin(θ)) * qa n = ((sin(θ) * t) / sin(θ)) * qb q(t) = m + n Where: θ = arcos(qa · qb) According to "Mathematics for 3D Game Programming". Unfortunately, I don't have unit tests for it.
(In reply to comment #2) > This code looks correct to me. > > It's derived from: > > m = ((sin(θ) * (1 - t)) / sin(θ)) * qa > n = ((sin(θ) * t) / sin(θ)) * qb > Hmm, something seems off in the above...doesn't that simplify down to simple LERP ((sin(θ) * (1 - t)) / sin(θ)) .. (sin(θ) / sin(θ)) * (1 - t) .. (1 - t) Looking at the link, I think what you wanted was: m = ((sin(θ * (1 - t))) / sin(θ)) * qa n = ((sin(θ * t)) / sin(θ)) * qb Also, the above falls down when sin(θ) == 0 (quarternions aligned or extactly 180 degress apart) Have a look at the following: http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ Which covers the aligned and nearly-opposite cases correctly. Harvey
Not compiled, not tested, just the code I have lying around from lunch. public void slerp(Quaternion a, Quaternion b, float t) { float cosom = a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; float t1 = 1.0f - t; // if the two quaternions are close, just use linear interpolation if (cosom >= 0.95f) { x = a.x * t1 + b.x * t; y = a.y * t1 + b.y * t; z = a.z * t1 + b.z * t; w = a.w * t1 + b.w * t; return; } // the quaternions are nearly opposite, we can pick any axis normal to a,b // to do the rotation if (cosom <= -0.99f) { x = 0.5f * (a.x + b.x); y = 0.5f * (a.y + b.y); z = 0.5f * (a.z + b.z); w = 0.5f * (a.w + b.w); return; } // cosom is now withion range of acos, do a SLERP float sinom = FloatUtil.sqrt(1.0f - cosom * cosom); float omega = FloatUtil.acos(cosom); float scla = (float)FloatUtil.sin(t1 * omega) / sinom; float sclb = (float)FloatUtil.sin( t * omega) / sinom; x = a.x * scla + b.x * sclb; y = a.y * scla + b.y * sclb; z = a.z * scla + b.z * sclb; w = a.w * scla + b.w * sclb; }
(In reply to comment #4) > Not compiled, not tested, just the code I have lying around from lunch. > ... http://jogamp.org/git/?p=jogl.git;a=commit;h=2b3bb9426385d97375c3312f5c0f4e2a827b1fbb Thank you! @Tek: Please test. And if you can, please add a unit tests or two! (Via email .. or better git pull req.)
Let me test the code. I'll get back to you in few hours