Rotation: Matrices, Quaternions, and Euler Angle Vectors
One of the most common types of transformations that are simulated in 3D Engines is rotation. There are a number of way of representing rotations: in matrices, quaternions, angle vectors, and in degrees and radians. The most accurate and least limited way of storing them is in matrices. A matrix is a mathematical concept that is basically a grid of numbers. When these numbers are applied to another matrix, or number, or most commonly in a 3D engine, a point, in the correct order, they can modify the values of what they are being applied to.
For example, a transformation matrix just takes a point and moves it in 3D space. This is pretty simple. Other matrices are more complicated, however. A rotation matrix can take the point and rotate it (obviously) so that it ends up in a new position in space. This is really only useful when combined with translation of course, because a rotated point at (0, 0, 0) isn’t going anywher. However, if we translate after rotating, we will translate in the correct direction. The last common matrix is a scaling matrix. This simply takes the transformations and scales them down to a certain size. This will also make the model look smaller on screen when drawn. I won’t get into the math behind matrices, but Riemeer Grootjans does an excellent job here:
http://www.riemers.net/eng/ExtraReading/matrices_maths.php
So, this is all great, now we know how matrices work, but how do we use them? Well, if you have been following along in the Game Engine Tutorial, you already are. The I3DComponent has a Rotation Matrix to store rotation, and calculates the other two transformations when drawing the model, from the Position and Scale Vector3. I should note here that this is probably the easiest way to store position and scale. A three part Vector is very convenient for storing position information as a three dimensional point, and the same goes for scale, as we can store a scalar amount for each axis. But what about rotation?
Well, the problem is that representing rotations as a 3 part vector is a fundamentally flawed approach. I won’t get into the details here, but just know that this is generally a very bad itea™. If you really need to know why, read this article:
http://www.sjbaker.org/steve/omniv/eulers_are_evil.html
Unfortunately, although the “best” way to represent rotations are with matrices, they aren’t very practical for normal human use. Nobody wants to be calculating the value for each of the 16 parts of a matrix whenever they want to update the rotation of an object in their scene. The easiest way would be to have a Vector3 that can represent Rotation around each axis, and convert back and forth to the matrix itself (As a side note, the Vector3 we would use here is called a Euler Angle Vector) We obviously don’t want to do this all the time, but it’s OK if it’s only used when setting a new value from a person (Like in a game editor). There are a number of ways to convert between matrices and euler angle vectors, but they aren’t perfect. The following section will detail the way I have found to be the most reliable. If you’re wondering, the idea for this came from this thread on the XNA Creators Club website:
http://forums.xna.com/forums/p/4574/23763.aspx
Converting Between Matrices and Euler Angle Vectors
Converting from an Euler Angle Vector is easy, the hard part is converting back. XNA provides a method for creating the rotation matrix, but it doesn’t provide a way back, so we will have to make our own.
First, we will go over converting to a rotation matrix. The Matrix.CreateFromYawPitchRoll() method will do this for us. If we were using a Euler Angle Vector for this, we would supply it’s coordinates in the following order:
Yaw: The Y axis of the Euler Angle Vector
Pitch: The X axis of the Euler Angle Vector
Roll: Z axis of the Euler Angle Vector
If you think about this, it makes sense. If you image a plane, its yaw would be the angle its nose is pointing horizonally, which is rotation around the Y axis. Its pitch would be the horizonal angle, which is rotation around the X axis. Finally, its roll would be how much the plane is rolling (obviously), which is rotation around the Z axis. The following code demonstrates this process:
// Converts a rotation vector into a rotation matrix
Matrix Vector3ToMatrix(Vector3 Rotation)
{
return Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z);
}
This method will give us a matrix that represents these rotations. Now for the hard part: converting back.
The process here is to break the matrix down into it’s parts: A Vecto3 for positiona and another for scale, and a Quaternion that represents rotation. We then need to break this Quaternion down into a Euler Angle Vector Vector3.
I’m not going to get into detail about the math behind the following code, but if you are curious you can read the descriptions in the thread I linked to above. That said, here is the code we would need to convert from a rotation (only) matrix to a Euler Angle Vector:
// Returns Euler angles that point from one point to another
Vector3 AngleTo(Vector3 from, Vector3 location)
{
Vector3 angle = new Vector3();
Vector3 v3 = Vector3.Normalize(location - from);
angle.X = (float)Math.Asin(v3.Y);
angle.Y = (float)Math.Atan2((double)-v3.X, (double)-v3.Z);
return angle;
}
// Converts a Quaternion to Euler angles (X = Yaw, Y = Pitch, Z = Roll)
Vector3 QuaternionToEulerAngleVector3(Quaternion rotation)
{
Vector3 rotationaxes = new Vector3();
Vector3 forward = Vector3.Transform(Vector3.Forward, rotation);
Vector3 up = Vector3.Transform(Vector3.Up, rotation);
rotationaxes = AngleTo(new Vector3(), forward);
if (rotationaxes.X == MathHelper.PiOver2)
{
rotationaxes.Y = (float)Math.Atan2((double)up.X, (double)up.Z);
rotationaxes.Z = 0;
}
else if (rotationaxes.X == -MathHelper.PiOver2)
{
rotationaxes.Y = (float)Math.Atan2((double)-up.X, (double)-up.Z);
rotationaxes.Z = 0;
}
else
{
up = Vector3.Transform(up, Matrix.CreateRotationY(-rotationaxes.Y));
up = Vector3.Transform(up, Matrix.CreateRotationX(-rotationaxes.X));
rotationaxes.Z = (float)Math.Atan2((double)-up.Z, (double)up.Y);
}
return rotationaxes;
}
// Converts a Rotation Matrix to a quaternion, then into a Vector3 containing
// Euler angles (X: Pitch, Y: Yaw, Z: Roll)
Vector3 MatrixToEulerAngleVector3(Matrix Rotation)
{
Vector3 translation, scale;
Quaternion rotation;
Rotation.Decompose(out scale, out rotation, out translation);
Vector3 eulerVec = QuaternionToEulerAngleVector3(rotation);
return eulerVec;
}
It should be noted that all of the rotation info is represented in radians. To get degrees (which you would want if working with a person who is not used to working in radians: ie, not a programmer or mathematician) use the MathHelper.ToDegrees() function. Convert back with the MathHelper.ToRadians() function. Remember to convert back and forth when working with rotations:
Vector3 RadiansToDegrees(Vector3 Vector)
{
return new Vector3(
MathHelper.ToDegrees(Vector.X),
MathHelper.ToDegrees(Vector.Y),
MathHelper.ToDegrees(Vector.Z));
}
Vector3 DegreesToRadians(Vector3 Vector)
{
return new Vector3(
MathHelper.ToRadians(Vector.X),
MathHelper.ToRadians(Vector.Y),
MathHelper.ToRadians(Vector.Z));
}
You could use these functions by having a property on your objects that work with Matrix rotations to make things simpler for a human working with them:
public Vector3 EulerRotation
{
get { return RadiansToDegrees(MatrixToEulerAngleVector3(Rotation)); }
set { Rotation = Vector3ToMatrix(DegreesToRadians(value)); }
}

It works amazing, thanks a lot, I was precisely looking for a solution and you saved my day!
Regarding the method
Vector3 QuaternionToEulerAngleVector3(Quaternion rotation) …
More specifically, this line:
(37) rotationaxes.Z = (float)Math.Atan2((double)-up.Z, (double)up.Y);
Should it not read as follows?
rotationaxes.Z = (float)Math.Atan2((double)-up.X, (double)up.Y);
The line 37 is wrong, should be as SigmaThree says.
I am now not sure the place you are getting your information, but great topic. I must spend a while finding out more or figuring out more. Thank you for excellent info I used to be looking for this info for my mission.