- Home /
Calculate quaternion animation curve's tangents from a euler animation curve
I am creating a custom animation editor that lets users modify the animation curves of various components. One component I am having trouble with is the rotation of an object. I allow the user to edit the rotation's euler components (actually just the z rotation since the editor is for 2D). When generating the final animation, I convert the euler animation curve to a set of four quaternion curves.
The problem is that the tangents of the euler curve do not seem to correspond to the quaternion.
Here is the animation curve for the z component of a curve:
If I calculate my quaternion curve by adding the keys the following way, the animation jumps around erratically (this example is adding the middle keyframe):
float time = 1.7f;
float rotationZ = 90.0f;
float inTangent = -500.0f;
float outTangent = 500.0f
q = Quaternion.Euler(0, 0, rotationZ);
localRotationXKeyframes.Add(new Keyframe(time, q.x, inTangent, outTangent));
localRotationYKeyframes.Add(new Keyframe(time, q.y, inTangent, outTangent));
localRotationZKeyframes.Add(new Keyframe(time, q.z, inTangent, outTangent));
localRotationWKeyframes.Add(new Keyframe(time, q.w, inTangent, outTangent));
If I add the quaternion key without using the euler z curve's tangents, the animation interpolates smoothly, but doesn't follow the swings of the curve:
float time = 1.7f;
float rotationZ = 90.0f;
q = Quaternion.Euler(0, 0, rotationZ);
localRotationXKeyframes.Add(new Keyframe(time, q.x));
localRotationYKeyframes.Add(new Keyframe(time, q.y));
localRotationZKeyframes.Add(new Keyframe(time, q.z));
localRotationWKeyframes.Add(new Keyframe(time, q.w));
Obviously the tangents of the euler curve do not correspond with the tangents of the calculated quaternion curve.
My question is how do I create a quaternion animation curve based on the euler animation curve, with properly translated tangents?
The depressing thing is that quaternions "go the way they want" - it's annoying! Quite simply, make a quaternion that goes from north to south pointing. A human would expect it to swing around through 180 degrees, staying on the z=0 plane. As you'll see this is not the case. It "decides" to go another route (and why wouldn't it? it's all the same). It's one of the love/hate things about quaternions. As a broad-general principle, the "LookAt" quaternion functions rather than the "fromTo" quaternion functions can, sometimes, help you quickly hack through this in gameplay.
Answer by aldonaletto · Jan 13, 2012 at 11:36 AM
The quaternion is a math representation of a single rotation of some angle around an arbitrary axis, where x, y and z are the normalized vector that defines the axis orientation multiplied by sin(angle/2) and w is cos(angle/2). Since sin(0) is zero, the axis orientation becomes critical for small angles, and the interpolation functions may produce incredibly wrong orientations at these points.
If you can use an alternative format, convert the quaternion to its angle/axis representation (see the functions Quaternion.AngleAxis and Quaternion.ToAngleAxis):
float angle = 0; Vector3 axis = Vector3.zero; // get the angle/axis equivalent of "rotation" rotation.ToAngleAxis(out angle, out axis); localRotationXKeyframes.Add(new Keyframe(time, axis.x)); localRotationYKeyframes.Add(new Keyframe(time, axis.y)); localRotationZKeyframes.Add(new Keyframe(time, axis.z)); localRotationWKeyframes.Add(new Keyframe(time, angle));You will have to convert it back to quaternions using Quaternion.AngleAxis, of course.
In those cases where you rotate only around a fixed axis, you can store the angle alone and convert it back with Quaternion.Euler (or AngleAxis, if the axis isn't x, y or z).
EDITED Paying a little more attention to your animation curve, I noticed that it strongly depends on the mid-point tangent. As a rule of thumb, you should add points for each curvature inflection - the top and bottom points, in this case:
This avoids problems with the tangents, since they are always zero at these points.
Thanks for the advice! Unfortunately, I can't store the values as axis / angle temporarily since I'm dumping the quaternions directly into an Animation $$anonymous$$esh. I think I will tell users of my editor that they should try to add keyframes at the inflection points like you suggested.
I eventually wrote a procedure to "Bake" a quaternion curve by iterating over my euler curve for the z axis. I divide each segment of the curve into 5 pieces and then sample at an even interval, calculating the tangents by the vectors between points. It's not exact, but it comes out pretty close to the original euler representation. This also gets around the limitation of a quaternion only being able to rotate up to 180 degrees. Since I'm sampling in small increments of the angle, it always goes in the direction I'm wanting.
It's a good alternative: evaluating the curve at other points will make the result way more precise. Anyway, if you want to try the tangents, I suppose they can be calculated by something like this:
float delta = 0.1; // choose a suitable delta float t = curve.keys[n].time; Quaternion q = Quaternion.Euler(0,0,curve.keys[n].value); Quaternion q1 = Quaternion.Euler(0,0,curve.Evaluate(t-delta)); Quaternion q2 = Quaternion.Euler(0,0,curve.Evaluate(t+delta)); float inTgX = (q.x-q1.x)/delta; float outTgX = (q2.x-q.x)/delta; float inTgY = (q.y-q1.y)/delta; float outTgY = (q2.y-q.y)/delta; ...It's a pseudo-code, of course, but I believe the basic idea is valid.
Answer by tinyant · Dec 20, 2017 at 08:08 PM
Anyone make success transfer euler curve to quaternion Curve?
Your answer
Follow this Question
Related Questions
How can I set the tangents of Keyframes in an AnimationCurve through scripting? 1 Answer
am I using degrees, radians, rotation or quaternion ? 2 Answers
Inverse Quaternion data from Kinect 1 Answer
Quaternion.LookRotation with an offset. 1 Answer
How can I use lerp to rotate an object multiple times? 2 Answers