Quaternions
This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me) |
These are primarily notes It won't be complete in any sense. It exists to contain fragments of useful information. |
Contents
Introduction
In classical 3D programming, it was usual to store an object's current rotation as a vector/matrix (Euler representation), meaning multiple linear transformations in sequence, which can introduce gimbal lock (a zero-coordinate problem that happens when one of the transforms rotating a vector exactly into an axis. Happens more noticeably when you keep only coordinate state that you keep updating, since that way it won't get out of it).
You can sometimes avoid such problems by introducing some constraints, but the more automatic the movement/orientation is managed (think physics engines), the harder and more annoying that is to do.
When you alter a quaternion rotation, you avoid that problem, and you can freely rotate in any direction. When you use a quaternion for rotation state, calculating rotations can be done without the gimbal lock problems and, when used for just rotation, also be done a little faster and using less storage.
The order you apply the quaternion in matters, analogously to the way order matters in a series of 3D matrix multiplications.
You may in fact want to pretend a quaternion is a compact matrix made purely for rotation, because the fact quaternions contain three three orthogonal complex dimensions will break most people's brains:P
Some notes
Quaternions are four-dimensional imaginary tuples. I prefer to think of them as 'surreal matrixy vector thangs,' because there is little hope that I will understand or care for a ll its mathematical properties.
They are apparently more expressive than vectors for three dimensions, but are usually (computerwise, at least) only used to rotate 3-D vectors.
Notation, some definition
It is common to use the notation w + xi + yj + zk, where w, x, y, and z are all real numbers; x,y,z specifies the axis to rotate about, w the angle. (i = j = k, each just sqrt(-1), but they are apparently not necessary when all you are using quaterions is rotation.(verify))
Some meddling with them
I have done some meddling in Second Life scripting, which chooses to only use quaternions internally. It chooses a naming slightly less apt to misinterpretation, namely <x,y,z,s>.
Each of these numbers is expressed as a function of the angle/2. As an illustration you probably do not care about, the following function creates a rotation from a particular axis and angle (duplicating the functionality of llAxisAngle2Rot):
rotation make_quaternion(vector around, float radians) { rotation quat; vector unit_axis = llVecNorm(around); float sine_half_angle = llSin(radians/2); float cosine_half_angle = llCos(radians/2); quat.x = sine_half_angle * unit_axis.x; quat.y = sine_half_angle * unit_axis.y; quat.z = sine_half_angle * unit_axis.z; quat.s = cosine_half_angle; return quat; }
This is similar to llEuler2Rot(<rad_x,rad_y,rad_z>), which specifies the rotation around each axis.
Note that <x,y,z,w> is a vector that starts to be interpreted as a quaternion when you cast it to a rotation, and the library makes it nicer to deal with. You can in fact do:
rotation rUp = <0, 0, llSin(rad/2), llCos(rad/2)>;
You can convert between the euler rotation vector - human readable, not directly usable, hence the conversion function - and quaternion with llEuler2Rot and llRot2Euler.
vector vUp = <0,0,1>; vector vSide = <0,1,0>; vector vDeep = <1,0,0>; rotation rUp = llEuler2Rot(<0,0,PI>); //180 deg around the same rotation rSide = llEuler2Rot(<0,PI,0>); rotation rDeep = llEuler2Rot(<PI,0,0>); //for example, rotate the pointing-to-one-side vector 180 degrees (PI // radians) with the around-straight-up-rotating quaternion. Effect: //points to the other side. <0,1,0> -> <0,-1,0> llOwnerSay( (string) (vSide*rUp) );
You can use the same thing to rotate one object around another - that is, you can take the difference vector between the two and rotate it.
Rotation can be /to/ a rotation (calculated from the null rotation,<0,0,0,1>), though often enough you will incrementally rotate, which you do by multiplying rotation quaternions:
rotation eighth = llEuler2Rot(<0,0,TWO_PI/8>); //45 degrees around Z rotation objrot = llGetRot(); //One of the fun-and-crazier things about quaternions is this: llSetRot(eighth*objrot); //rotate around global Z llSetRot(objrot*eighth); //rotat around local Z