Skip to main content

Original Author: Cory Petkovsek (discipline in the forums)

Release Date: June 16th, 2005

Introduction

If you are reading this page, it is probably grudgingly, after working with your objects in Ogre and finding they don't rotate as you think they should. When I started with Ogre, I understood vectors, had taken trigonometry a long time ago, had a faint memory of learning matrices at some point and knew nothing about quaternions. This article has been compiled as a primer for the practical use of quaternion rotations in Ogre, skipping over math and matrices where possible.

Use this thread in the forums to discuss this article or ask questions.

A note to potential wiki editors: Quaternions are hard enough to understand. When posting code snippets try to make each point as clear as possible (i.e. describing why we multiply a vector by a quaternion). Define what each variable is used for and its type. Also only post code that you know works.

Table of contents

[Show/Hide]

Prerequisites

Before we're talking about quaternions, we'll review some basics regarding vectors, Euler angles, and matrices.

Furthermore, for this document and working within Ogre, we will use a right-handed coordinate system (see also Right-hand Rule). This means two things:

  • First, we consider +X to point to the right of your monitor (thumb), +Y to point to the top of your monitor (index finger) and +Z to point out of your monitor toward you (middle finger). (You can only do this with your right hand, hence the name.)
  • Next, to find out which direction +45 degrees rotates, point the thumb of your right hand down the axis of rotation (i.e. towards positive infinity). The direction your fingers curl is the positive angle.


A quick video introduction to Euler angles, matrices, and quaternions can be found in the Google Tech Talk Sensor Fusion on Android Devices: A Revolution in Motion Processing starting at 35:30.

Vectors

A vector can be thought of as an arrow from a given initial point to another point in 3D space. It describes both the direction and the length of this arrow. The direction not only describes the bearing of the arrow but also where the arrowhead points to. This separates a vector from a line segment which only has a bearing but is "bi-directional". A vector is also different from a ray as rays have an infinite length.

Describing two points would normally require six values for 3D space. In Ogre, vectors are stored in three values by assuming the first point is (0,0,0) and the second point is a relative offset. This is a common use of vectors. This given (0,0,0) can be the world origin, or it can be the origin of an object's local coordinate system. Be aware of which coordinate system you are working with when using transformations. Because vectors have only three values, they are also used to define 3D points.

When a vector is normalized, the vector is scaled to a length of 1. We use normalized vectors (or unit vectors) when we wish to describe only a direction. This is necessary for expected results with many mathematical algorithms.

In Ogre, vectors are commonly used to describe a single point, define a direction with a unit vector, or extract the distance between two points.

Here are some common vector operations and what they are used for:

  • Subtracting vector A from vector B generates a third vector that contains the direction and distance from A to B.
    Bv - Av = Av -> Bv
  • Adding vector A and vector B gives vector C. You can visualize this as if you take vector B, and place its tail on vector A's head (like two arrows drawing out a path). Then vector C is the vector from the start of the path (A's tail) to the end of the path (B's head). Equivalently, you could get the same vector C by lining up the vectors in the opposite order: placing vector A's head at vector B's tail.
  • Multiplying or dividing a vector by a scalar (read: a number) does not change the direction, but only changes the length of the vector. For instance the vector (1,1,1) describes a line that is angled 45 degrees off the XZ plane (horizontal), 45 degrees off the XY plane (vertical, faces towards you) and 45 degrees off the YZ plane (vertical, faces left/right). The length of the line is sqrt(x^2 + y^2 + z^2) - or, in this example, sqrt(3) which is about 1.73. Now if we multiply this vector by 2 we get (2,2,2). This describes a line with the exact same angles. However the length is now sqrt(12) or 3.46 (which is 1.73 times 2). See Vector3::length().
  • A cross product of two vectors provides a third vector that is perpendicular to the initial two. This is a very useful computation. If you want to figure out which way is up/out from a polygon take the cross product of two edges and you have a vector pointing out. The cross product can be calculated for you by ogre. Vector3::crossProduct(Vector3).
  • The dot product of two vectors allows us to find the angle between them. It is defined as
    DotPr(v1,v2) = Cos(Angle) * Len1 * Len2
    Where Len1 and Len2 are the length of the vectors. However typically you should normalize both vectors so the lengths are 1. This means the dot product is the cosine of the angle between the vectors. Stated another way:
    Angle = ArcCos(DotPr(v1,v2)) ; where Len1 = Len2 = 1
    This is also found in the API under Vector3::dotProduct(Vector3).

Euler Rotations

The method of rotation you may be more accustomed to is using yaw, roll and pitch.

  • Yaw would be left/right rotation around the Y axis (vertical) on the XZ plane. Yaw is used when driving a car.
  • Pitch is up/down rotation around the X axis (horizontal, pointing right) on the YZ plane. Pitch is used when flying a jet down or up, or when driving up hill or down.
  • Roll is tilt rotation around the Z axis (pointing towards you) on the XY plane. Roll is literally what happens to your car when you take a curve too fast!


Euler rotations can be useful and are supported in Ogre's API. There are problems however. One is that the order of operations is important. If you pitch an object 90 degrees, then yaw it 90 you get a different result than if yaw is done before pitch. Try it.

Another big problem is known as gimbal lock. A gimbal is an axis of rotation. This problem occurs when mathematically two axes of rotation become aligned and one gets canceled out.

Finally, euler angles are limited in their practical application. What I mean by this is that to rotate an object arbitrarily it is not very practical to use yaw, roll and pitch operations. They are useful say if you want a yaw/pitch camera like many fps games. With a dual sceneNode trick you can get a camera that will use yaw and pitch operations without inducing roll. However, when you have an object facing one way and you want it to face another object in an arbitrary location, retrieving the current individual yaw, roll and pitch values then calculating the difference needed for a rotation to the new direction is not very efficient. Also, what if you want to do a smooth rotation from the current facing to the new direction? Not very practical. Enter matrices.

Matrix Rotations

Because working with arbitrary three dimensional euler rotations can produce a large amount of operations, matrices are usually used to store and transform them.

One way of working with matrices is to use three, each specifying a certain angle offset from the X, Y and Z axes. This is our pitch (rotation on the X axis), yaw (Y axis) and roll (Z axis). We can store them in individual matrices, say matRotationX, matRotationY and matRotationZ. When we have decided on the angles we wish to rotate by and have stored them in our matrices, we can combine the operations into one transform matrix simply by multiplying them.

Copy to clipboard
matTransform = matRotationX * matRotationY * matRotationZ;

Fortunately for us, Ogre implements a Matrix3 class to handle 3x3 matrices. The class also has the * operator overloaded so the above statement will work right in your code. To rotate an object in Ogre you first need to create a quaternion with this matrix by using the appropriate quaternion constructor or using FromRotationMatrix on a quaternion.

Note that matrix multiplication is not commutative. A * B doesn't always equal B * A. The order in which you apply each matrix rotation is important, just as it is with straight euler angles. Also note that matrices as representations of euler angles are still subject to gimbal lock.

Matrices can also be used to handle axis/angle rotations. The matrices above used concepts of yaw, roll and pitch. Think of these instead as rotation around Y, Z and X axes, respectively. It is possible to achieve any rotation by defining a single arbitrary axis and an angle of rotation. In doing this we avoid the problem of gimbal lock.

There is a problem with both euler angle matrices and axis/angle matrices. It is called matrix drift, which I will describe in the quaternion section under the benefits list.

Quaternion Rotations

Quaternions are an interesting concept probably as new to you as they were to me when I first started this. As described above, when working with quaternions I find it easier to stop thinking about yaw, pitch and roll and instead think of rotation around axes. For instance, let's say we wanted to model a propeller on a jet. The jet faces down the Z axis. In euler terms the rotation would be called roll. In quaternions it is rotation around a vector pointing down the Z axis, or rotation around Vector3::UNIT_Z as we'd refer to it in Ogre.

A quaternion is composed of four components: a vector with x, y, z coordinates and a w rotation. This is an axis/angle representation just as I touched on at the end of the matrix section. Quaternion math can get quite involved, even incorporating imaginary numbers. Fortunately we don't need to use all of that math, nor understand it to work with rotations.

Try this exercise. Point your left index finger straight up. Next grab the tip of a pen with your right hand. Now by moving your wrist and hand around in a circle, try to spin the pen as flatly as possible, always perpendicular to your finger and parallel to the floor. It should look like you had just spun it on a table. Think of this as quaternion rotation around Vector3::UNIT_Y.

Now let's say we have a vector from (0,0,0) to (1,1,-1). With our finger pointing straight right, point it up 45 degrees and away from you 45 degrees. Now rotate the pen around your finger, keeping it perpendicular at all times. Notice how the angle of the axis we choose creates a different rotation.

Like matrices, we can combine quaternion rotations by multiplying them. However they are still not commutative. Q1 * Q2 != Q2 * Q1. Thus the order of application is still important. Also like matrices that represent axis/angle rotation, quaternions avoid gimbal lock.

Benefits of Quaternions

Quaternions do have advantages over matrices though. There are implementation pros and cons such as the number of multiplications, or additions during different operations, etc that have been discussed in the forum. Considering practical benefits though, here is a list :

  • Axis/angle representation avoids gimbal lock.
  • Modifying a rotation is easy. Say we have a rotation describing a -45 degree yaw (left turn). By creating a new quaternion describing a 10 degree yaw (right turn) and multiplying the two, we now have a rotation describing a -35 degree turn. You might use this when applying the same rotation to a number of different objects, or even multiple times to the same object (i.e. the player). The rotation factor increases or decreases depending on circumstances.
  • Avoids costly alignment of matrix drift. This drift occurs when performing lots of operations on matrices using finite point precision, which is used in your computer. Rounding of real numbers graudally builds up and inevitably mucks up the matrix. Abnormalities in the rotation will start to appear due to this drift. The matrix must be normalized in order to reset this, however it is a costly operation. Quaternions on the other hand can still suffer from this drift, but it is much cheaper to normalize having only 4 values instead of 9 or more.
  • Interpolation of rotations. When rotating an object, we might want the object to rotate smoothly over time. We'd like to say, "From your current orientation, rotate to face this point. But take about 300 frames to do it, so only move 1/300th of the rotation right now." Interpolation with matrices is possible, but then so is anything, isn't it? Quaternions provide two methods of interpolation: linear(slerp) and cubic(squad). Ogre provides three implementations: slerp, nlerp and squad. Linear interpolation allows one to specify the percentage of interpolation between the two quaternions. Linear means your "velocity" of movement is constant, which may feel jerky if used for a camera or object. Slerp and nlerp are both linear. Slerp is more accurate, but slower. Squad or cubic interpolation allows one to specify four quaternions a,p,q,b. P and Q are used to define a curve of interpolation "velocity" between points A and B. This allows us to slowly increase speed, stay constant, then decrease it during interpolation. See the External Resources section for links that will elaborate on this.
  • Ogre uses quaternions! That's probably why you are reading this page. 😊 Often, you'll need to get the current orientation of an object, which is returned as a quaternion.

Some useful (normalized) quaternions

wxyzDescription
1000Identity quaternion, no rotation
0100180° turn around X axis
0010180° turn around Y axis
0001180° turn around Z axis
sqrt(0.5)sqrt(0.5)0090° rotation around X axis
sqrt(0.5)0sqrt(0.5)090° rotation around Y axis
sqrt(0.5)00sqrt(0.5)90° rotation around Z axis
sqrt(0.5)-sqrt(0.5)00-90° rotation around X axis
sqrt(0.5)0-sqrt(0.5)0-90° rotation around Y axis
sqrt(0.5)00-sqrt(0.5)-90° rotation around Z axis


These numbers come from this property:

Copy to clipboard
[w, x, y, z] = [cos(a/2), sin(a/2) * nx, sin(a/2)* ny, sin(a/2) * nz]


Where a is the angle of rotation and {nx,ny,nz} is the axis of rotation. (From section 2e of Quaternion Spells)

Questions

This section is for questions or simple one or two line code snippets for the purpose of answering questions. More involved code samples go in the next section.

Quaternions

To get you started, here are some simple things you can do in Ogre with quaternions. This section also has some clarifications of other text. The code samples in the next section will help you with more difficult problems.

Q. How would I make my object rotate to face another point?

A. One way is to use the SceneNode::lookAt method. However this may have unexpected results when used as an object. Besides, we are talking about quaternions here.

Another way is through specifying the individual rotations then multiplying them together.
Rotate around the Z axis 90 degrees:

Quaternion q(Degree(90), Vector3::UNIT_Z);
Next, rotate around the X axis 90 degrees.

Quaternion r(Degree(90), Vector3::UNIT_X);
Rotate the object with both rotations in the above order. mNode is a SceneNode:

Copy to clipboard
q = q * r; mNode->rotate(q);

A better way is to use the Vector3::getRotationTo(Vector3) function to generate a quaternion for us. Look at the code samples to see how this will be used.

Q. When debugging, I've printed the individual x, y, z, and w values of the quaternion, but I can't make heads or tails of them. What do they mean?

A. X, Y and Z describe a vector which is the axis of rotation. In my current project, I have only flat XZ rotation, meaning I zero any potential rotations with pitch or roll. The code sample below shows how to do this. Thus my values will look something like 0, -.599, 0. This describes the Y axis, as it is the only value. Actually it is pointing down as it is negative, and I'm not sure why. I'll correct this when I learn why. It may be a bug in my code.
W is the amount of rotation around the vector axis. It is an odd value in itself. This is because it is cos(theta/2), where theta is the angle. Derive the angle like this, which produces an Ogre::Radian:

2*Math::ACos(quat.w))

So my example quaternion looks like this with W in degrees: (0.000,-0.599,-0.000, 73.602). The reason Y is not 1 or even -1 is because the quaternion is normalized. Normalization includes w, x, y and z, so they all need to give some to equal 1. Of course the 73 is the derived value so doesn't isn't a component of the 1.

Q. I see vectors and quaternions being multiplied together in the code. What does this mean?

A. Multiplying a quaternion by a vector gives us a vector describing the rotational offset from that vector (a rotated vector). For example when one loads the robot.mesh file, by default it points facing UNIT_X. When it has been rotated, we can get its orientation in quaternion form via mNode->getOrientation(). If we then multiply it by Vector3::UNIT_X, we will get a vector pointing in the direction the robot is currently facing.

Q. When do I use setOrientation() vs rotate()? When do I multiply quaternions against orientations or vectors? I'm so confused!

A. SceneNode::getOrientation() returns a quaternion describing how much of a rotational offset the object has acquired from its identity orientation (or the direction it started facing). getWorldOrientation() returns its own rotation offset, plus that of its parent's. If the node's parent is root, these functions return the same thing.

Vector3::getRotationTo(Vector3) returns a quaternion describing a relative rotation from one vector to the next. Remember that quaternions can be thought of as vectors with an angle attached? Remember that Vector A minus Vector B gives Vector C, a relative vector from B to A? Same thing with quaternions. Think of orientations as absolute, relative to the identity. Think of rotations as relative, usually relative to the current orientation.

SceneNode::setOrientation(Quaternion) does exactly that. It sets it based on the object's original facing direction. SceneNode::rotate(Quaternion) sets the orientation based on the current orientation. Thus rotate(q) is equivalent to setOrientation(q * getOrientation()).

If we have a scene node and a calculated rotation from getRotationTo, we only need to use the rotate(quat) function. In fact if we use setOrientation, we'll be rotating more than desired.

And what about slerp with rotations and orientations?

When I first started playing with slerp, I was slerping like this:

mRotSrc = mNode->getOrientation();

mRotDest = src.getRotationTo(mDirection);

...

mNode->rotate(Quaternion::Slerp(mRotProgress, mRotSrc, mRotDest));

My poor robot was spinning round and round like a top on steroids. I made two mistakes above. Can you spot them?
1) The first mistake is that Slerp takes two orientations, not relative rotations. mRotDest is only a relative rotation from mRotSrc to mDirection (where I want to point). I should be able to setOrientation(mRotSrc) or setOrientation(mRotDest) and have my object point to the source or destination accurately. That wouldn't happen with these values. The fix is:

mRotDest = src.getRotationTo(mDirection) * mRotSrc;
2) The next mistake is that Slerp returns an orientation, not a relative rotation. Since it is working with absolute rotations (absolute to the object's identity or starting point), this is what it returns. The fix is:

mNode->setOrientation(Quaternion::Slerp(mRotProgress, mRotSrc, mRotDest));

These were actually the same problem: A misunderstanding about Slerp. When working with these tools, consider whether you are working in relative rotations or orientations.

A few redundant points:

  • quaternion * vector is a vector rotationally offset by the quaternion
  • quaternion * quaternion is a quaternion with both rotations combined, 'therefore:'
  • rotation1 * rotation2 is a quaternion with both rotations, rotation1 is the orientation of the parent node and orientation2 is the orientation of the child node 'and'
  • setOrientation(getOrientation() * rotation) == rotate(rotation)

Q. Why do I occasionally get a flickering of my object while it is rotating? I'm using slerp to interpolate between two orientations, and calling setOrientation() with the interpolated quaternion.

A. Quaternion::Slerp(), Quaternion::nlerp() etc. have a "shortestPath" parameter that defaults to false. Ensure it is true, otherwise the interpolation occasionally goes the longer path, which might result in a few frames of completely discontinuous orientations.

Misc

Q. Why do the tutorials and people on the forum suggest using two scene nodes to attach my camera to?

A. The recommendation is to create a scene node, say "CamNode" and a pitch node, say "PitchNode". "PitchNode" is attached to "CamNode" and the camera is attached to "PitchNode". Movement and Yaw are applied to the CamNode. Pitch is applied to the "PitchNode", only. The reason for this is because one can generate roll through euler angles of pitch and yaw. Often one uses the mouse to control the camera look angle. One expects to move the mouse up to look up, right to look right, down to look down. However this is the same as pitch up, yaw right and pitch down. Try this with a desk object. That sequence induces roll. Using two separate nodes gives you the equivalent of a flat disk that floats around. Attached to the bottom of that disk is a camera that can look only up or down. By rotating the disk and moving the camera up or down you can view any angle. Yaw on the disk affects the camera, but pitch on the camera does not affect the disk. This provides the effect that most people expect.

Alternate A. For a simple Free Camera you can use the one node and just rotate in the appropriate coordinate space. Once you want to start mixing "roll" into this, you should go back to completely local space rotations---at which point you're probably not trying to make a simple Free Camera for debug purposes.

Copy to clipboard
node->pitch( mPitch, Node::TS_LOCAL ); node->yaw( mYaw, Node::TS_WORLD ); node->translate( mTranslate, Node::TS_LOCAL );


The effect of this is to pitch the scenenode relative to itself, but to yaw the scenenode relative to the world. This means yaw is always horizontal and no roll is induced by the two rotations. The translate call ensures you are always moving along the SceneNode's z axis. i.e., the direction it is facing.

Code Snippets

Flat XZ Rotation of Objects

Q. I have a character that walks along a set of points on a flat plane. How can I have it rotate to face the direction it is walking?

A. First of all, we need the initial facing vector of the mesh. When you load your mesh in Ogre, which direction is it facing, before any transforms are applied? Hint: The camera starts out facing down Vector3::NEGATIVE_UNIT_Z. You can also load the mesh in your modeller, but beware that the modeller axes are not necessarily aligned with Ogre's. For this code, the initial facing vector is Vector3::UNIT_X.

This code was adapted adapted from Intermediate Tutorial 1:

Copy to clipboard
Vector3 mDestination = mWalkList.front(); // mDestination is the next location Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B (see ((Quaternion and Rotation Primer|#Vectors|vector questions)) above) Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; // see (1) Real mDistance = mDirection.normalise(); // strip out distance, we only want direction Quaternion quat = src.getRotationTo(mDirection); // Get a quaternion rotation operation mNode->rotate(quat); // Actually rotate the object

(1) From the comments, getOrientation() returns a Quaternion describing the objects rotation in relation to the root. Multiplying it by UNIT_X, which is the object's initial facing position in this case, provides a vector describing its current facing direction.

This works because our object has never been rolled or pitched. All existing points given to it are on a flat plane so there is never any pitch or roll to be wrapped up in the quaternion.

Q. The above works great when I have a plane. Now I have an object walking up and down. When it rotates it starts rolling and pitching to all funny directions. How can I have it always have its head up?

A. We can adapt the above to strip out all Y components before they get into quaternions. This means it only does flat X/Z rotation.

Copy to clipboard
Vector3 mDestination = mWalkList.front( ); // mDestination is the next location Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B (see ((Quaternion and Rotation Primer|#Vectors|vector questions)) above) Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; // Orientation from initial direction src.y = 0; // Ignore pitch difference angle mDirection.y = 0; src.normalise(); Real mDistance = mDirection.normalise( ); // Both vectors modified so renormalize them Quaternion quat = src.getRotationTo(mDirection); mNode->rotate(quat);

Q. Sometimes my program quits and I get an Assert - Divide by Zero. What's up?

A. This happens due to a peculiarity with quaternion rotation by 180 degrees. When you tell Ogre to rotate 180 degrees, there are an infinite number of paths along which rotation could occur. Rather than picking one you may not want (i.e. rotating through the ground), Ogre asserts. You should make the selection within your code like this:

Copy to clipboard
Vector3 mDestination = mWalkList.front( ); // mDestination is the next location Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B (see ((Quaternion and Rotation Primer|#Vectors|vector questions)) above) Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; // Orientation from initial direction src.y = 0; // Ignore pitch difference angle mDirection.y = 0; src.normalise(); Real mDistance = mDirection.normalise( ); // Both vectors modified so renormalize them if ((1.0f + src.dotProduct(mDirection)) < 0.0001f) // Work around 180 degree quaternion rotation quirk { mNode->yaw(Degree(180)); } else { Quaternion quat = src.getRotationTo(mDirection); mNode->rotate(quat); }

Smooth Rotation

Q. How can I make my objects rotate smoothly? You mentioned slerp, etc?

A. You'll need to have two places in your code the rotation is established, and an area of shared variables. For my code, I have a class that manages all of my world objects, so the following code is found in one class, but in different methods. Slerp and nlerp are interchangeable here. Just change the name. The difference is slerp is more accurate, but requires more processing. This code has been tested by the author.

Shared variables:

Copy to clipboard
Ogre::Quaternion mOrientSrc; // Initial orientation Ogre::Quaternion mOrientDest; // Destination orientation Ogre::Real mRotProgress; // How far we've interpolated Ogre::Real mRotFactor; // Interpolation step boolean mRotating;


Next, the rotation is initiated somewhere like this. 'frames' is an integer in this case that describes the number of frames to rotate over. Use your own formula to figure the length of time you want to rotate.

Copy to clipboard
mRotating = true; mRotFactor = 1.0f / frames; mOrientSrc = mNode->getOrientation(); mOrientDest = quat * mOrientSrc; // We want dest orientation, not a relative rotation (quat) mRotProgress = 0;


Finally, during an update method called by your frameStarted method:

Copy to clipboard
if(mRotating) // Process timed rotation { mRotProgress += mRotFactor; if(mRotProgress>1) { mRotating = false; } else { Quaternion delta = Quaternion::Slerp(mRotProgress, mOrientSrc, mOrientDest, true); mNode->setOrientation(delta); } } // if mRotating

Q. You mentioned squad?

A. Squad allows you to apply a cubic interpolation instead of a linear interpolation. This means the rate of turn increases and decreases depending on the bezier/hermite curve given to the function. You'll have to experiment with the coefficients given as the first slerp parameters on IntA and IntB. These quaternions are intermediary rotations between the source and destination. I'm telling it to interpolate over the bezier curve mOrientSrc -> mOrientDest, and use mOrientIntA and mOrientIntB as intermediate curve points. Basically experiement and see what happens.

To use it make the following changes to the above sections:

Shared variables:

Copy to clipboard
+Ogre::Quaternion mOrientIntA; +Ogre::Quaternion mOrientIntB;


Initiation section (modify .3/.5 values to adjust intermediate points):

Copy to clipboard
mOrientDest = quat * mOrientSrc; +mOrientIntA = Quaternion::Slerp(.3, mOrientSrc, mOrientDest, true); +mOrientIntB = Quaternion::Slerp(.5, mOrientSrc, mOrientDest, true); mRotProgress = 0;


frameStarted section:

Copy to clipboard
-Quaternion delta = Quaternion::Slerp(mRotProgress, mOrientSrc, mOrientDest, true); +Quaternion delta = Quaternion::Squad(mRotProgress, mOrientSrc, mOrientIntA, mOrientIntB, mOrientDest, true); mNode->setOrientation(delta);

Q. Great. Now how do I use smooth rotations in two axis?

A. For example, suppose you want to pitch and yaw, but clamp the roll while still using Slerp. You can use a matrix to get the Euler angles and remove the roll component directly.

Let's say you already have your mOrientDest:

Copy to clipboard
Radian yRad, pRad, rRad; Matrix3 mat; mOrientDest.ToRotationMatrix(mat); mat.ToEulerAnglesYXZ(yRad, pRad, rRad);

Now update the pitch and yaw components, and remove roll:

Copy to clipboard
pRad +=Ogre::Degree(0.8 * timeSinceLastFrame); yRad +=Ogre::Degree(0.8 * timeSinceLastFrame); rRad = Ogre::Degree(0);

Update your matrix and reconvert mOrientDest:

Copy to clipboard
mat.FromEulerAnglesYXZ(yRad, pRad, rRad); mOrientDest.FromRotationMatrix(mat);

Everything else is the same:

Copy to clipboard
delta = Quaternion::nlerp(mRotProgress, mOrientSrc, mOrientDest, true); mCamera->setOrientation(delta)

Resetting Orientation

Q How can I make my objects stand upright after a bunch of rotations?

A. Remember that an object has three sets of axes. The world axes, parent axes and local axes. These are also refered to as Transform Spaces (see SceneNode). Assuming the positive Y axis is considered up for the object, all we need to do is figure out how far off the object's Y axis has gotten off from the world Y axis. Note, we could also apply this same principle, if we wanted the object to stand upright relative to it's parent, only. Imagine space (the world), with a starship (the parent) and an object walking on its surface. We want to reset its orientation to stand upright according to its parent, not the world. Finally, we simply rotate to that vector either instantly or by using slerp. One more thing to note is that to align the Y axes, we must rotate in either the parent's or the world's transform space, not the local space.

Copy to clipboard
// Get rotation to UP vector (UNIT_Y assumed) Vector3 localY = mNode->getOrientation() * Vector3::UNIT_Y; Quaternion quat = localY.getRotationTo(Vector3::UNIT_Y); mNode->rotate(quat, Node::TS_PARENT);

Q How can I make my objects resume their initial facing direction?

A. Since we need to know the initial facing vector of the mesh in order to rotate properly, we just rotate back to this vector. mInitFacing is a Vector3 that points down the initial direction your mesh faces upon loading, such as Vector3::UNIT_X.

Copy to clipboard
// Get rotation to original facing Vector3 currentFacing = mNode->getOrientation() * mInitFacing; Quaternion quat = currentFacing.getRotationTo(mInitFacing); mNode->rotate(quat);



Problems and solutions


Problems with implementing the article's suggestions and solutions to fix those problems. Even if you don't have these problems they might help your understanding of the tutorials.


-


First problem: The character was backflipping occasionally.

Solution: Actually do the next step that suggests getting rid of the y component even if the character is on a plane because the character is not a plane on equal level with the other plane (my rationalization, not necessarily correct).


-


Second problem: The character was rotating incorrectly in an inconsistent manner.

Solution: Actually use rotate() instead of setOrientation() because those are two very different functions, though you can rotate by multiplying the previous orientation by the quaternion you want to rotate to.


-

See Also
see_also.jpg



  • On the Recommended reading list, Snide recommended the book, "3D Math Primer for Graphics and Game Development" by Fletcher Dunn, Ian Parberry ISBN 1-55622-911-9. The author of this article has not reviewed it. The table of contents does look interesting with chapters on quaternions, matrices, vectors and other 3D math concepts. They also have a site with errata, a demo and other info.



Alias: Quaternion_and_Rotation_Primer