BlueG

Well, I don't know about a tutorial... and I'm certainly no expert on quaternions... but, the following is something I did to make the example from the XNA Game Studio's Express's help file more interesting.

First, you'll need to complete "Tutorial 1: Displaying a 3D Model on the Screen" (this is part of "Going Beyond: XNA Game Studio Express in 3D" of the Getting Started section).

Once you've completed that tutorial, the following steps will create a ship that properly rotates in 3 dimensions regardless of the direction its facing (for example, it will always pitch down, relative to itself, when you push forward on the left thumbstick).

**Step 1)** Change the member variables that hold the ship's position and rotation

The following is the code I used.

Vector3 _modelPosition;

Quaternion _modelRotation;

Note that I used different names for my variables than the tutorial did. You'll need to adjust your code accordingly.

**Step 2)** Initialize the new variables in your constructor (you could probably do this declaratively instead, but I tend to put more complex initializers in my constructors instead):

_modelPosition = Vector3.Zero;

_modelRotation = Quaternion.Identity;

**Step 3)** Change the Update method to be as follows:

protected override void Update( GameTime gameTime )

{

GamePadState gamepad = GamePad.GetState( PlayerIndex.One );

if( gamepad.Buttons.Back == ButtonState.Pressed )

{

this.Exit();

}

else if( gamepad.Buttons.A == ButtonState.Pressed )

{

_modelPosition = Vector3.Zero;

_modelRotation = Quaternion.Identity;

}

else

{

float yaw = gamepad.ThumbSticks.Left.X * MathHelper.ToRadians( -0.5f );

float pitch = gamepad.ThumbSticks.Left.Y * MathHelper.ToRadians( -0.5f );

float roll = gamepad.ThumbSticks.Right.X * MathHelper.ToRadians( -0.5f );

Quaternion rot = Quaternion.CreateFromAxisAngle( Vector3.Right, pitch ) * Quaternion.CreateFromAxisAngle( Vector3.Up, yaw ) * Quaternion.CreateFromAxisAngle( Vector3.Backward, roll );

_modelRotation *= rot;

_modelPosition += Vector3.Transform( new Vector3( 0.0f, 0.0f, gamepad.Triggers.Right * -20.0f ), Matrix.CreateFromQuaternion( _modelRotation ) );

base.Update( gameTime );

}

}

This is the bulk of the code that uses quaternions. I set it up so that pushing up or down on the left thumbstick causes the ship to pitch in the opposite direction, pushing left or right on the left thumbstick causes the ship to yaw in the same direction, and pushing left or right on the right thumbstick causes the ship to roll in the same direction. These rotations are always relative to the ship's current direction. Pulling the right trigger will cause the ship to move. I left out the acceleration/deceleration code to keep the movement simple and the vibration effect because I don't like vibration effects. Pressing the A button will reset the ship's position (it's easy enough to get lost or move out of the clip area).

**Step 4)** Change the Draw method to be as follows:

protected override void Draw( GameTime gameTime )

{

Matrix[] transforms = new Matrix[_model.Bones.Count];

_model.CopyAbsoluteBoneTransformsTo( transforms );

_graphics.GraphicsDevice.Clear( Color.CornflowerBlue );

Matrix rot = Matrix.CreateFromQuaternion( _modelRotation );

Matrix pos = Matrix.CreateTranslation( _modelPosition );

foreach( ModelMesh mesh in _model.Meshes )

{

foreach( BasicEffect effect in mesh.Effects )

{

effect.EnableDefaultLighting();

effect.World = transforms[mesh.ParentBone.Index] * rot * pos;

effect.View = Matrix.CreateLookAt( _cameraPosition, Vector3.Zero, Vector3.Up );

effect.Projection = Matrix.CreatePerspectiveFieldOfView( MathHelper.ToRadians( 45.0f ), 4.0f / 3.0f, 1.0f, 20000.0f );

}

mesh.Draw();

}

}

The key difference here is that I create my rotation matrix from the quaternion. Beware how I changed the variable names again. You might also notice that I doubled the distance to the far clip plane of the camera. That was so I had more space to move around in. Elsewhere (not shown in any of this code) I also moved the camera further away from the scene's origin.

As a humorous aside, when I first went to create my Draw method, I kept getting just the blue background when I first created my new method. Took me about fifteen minutes to figure out what I had done wrong. As I had been adding code to this method, I had, without thinking, I kept putting it before the Device.Clear call. So, after drawing the model, I was erasing the display. I just about slapped myself when I figured it out.

In any case, if you were careful about the variable names (remember, I changed the names I used), this should give you a working program. When you try it out, try rotating the ship to get a feel for its rotation before moving it.

Last note: This is my first attempt at using a highlighted code block within my posts. So, hopefully, it'll turn out okay...

Edit: I gave up on the highlighting the code blocks... still can't seem to get it to work. Also, anyone who wants to can feel free to reuse the parts of code that I wrote here (the rest was from Game Studio Express's Tutorials, so it should be safe as well).

Wil Burton

Thanks that helps alot. I thought thats how it would work in the Update, but I wasn't sure about the Draw stuff.

Thanks that helps alot. I thought thats how it would work in the Update, but I wasn't sure about the Draw stuff.

VThornheart

Hmm... I've heard of Quaternions before, but I don't know much about them. Why can't you just apply the rotation matrix Is there an advantage to using Quaternions instead I'm very curious on the subject =)

Hmm... I've heard of Quaternions before, but I don't know much about them. Why can't you just apply the rotation matrix Is there an advantage to using Quaternions instead I'm very curious on the subject =)

dczraptor

VThornheart wrote:

Hmm... I've heard of Quaternions before, but I don't know much about them. Why can't you just apply the rotation matrix Is there an advantage to using Quaternions instead I'm very curious on the subject =)

There's a phenomena where rotation matricies can cancel each other out depending on which ones you multiply first. For example, if you had 3 rotation matricies, one for each axis, multiplying them in a different order will give you a different result. Quaternions do not have this problem. I'm not sure what this is called, but i've heard of it multiple times.

BlueG

I guess that won't make much sense if you don't know what a quaternion is. The mathmatical explanation is complex and I've stopped caring about it. If you remember complex numbers from high school, then I'll warn you beforehand that quaternions are much much wierder. A quaternion is a 4 dimensional number. Mind you, I mean a 4 dimensional number, not a vector of 4 elements. For comparison, a complex number is two dimensional. That's why I tell you that quaternions are even wierder. A search on Wikipedia will give a brief explanation of the math involved. Be sure to have a large pot of coffee ready first. Oh, and caution: serious dangers to your mental health are ahead!

For 3D game development, however, it is useful to simply think of a quaternion as a rotation about an arbitrary axis. That is, a single rotation around a single arbitrary axis. Compare that to Euler angles (your x rotation, y rotation, and z rotation) where you have three rotations around three fixed axes. The "phenomena where rotation matrices can cancel each other out" is known as gimbal lock and is actually a trait of the Euler angles used to construct those matrices, not matrices themselves. A matrix, as used in games, just defines a mapping between two coordinate spaces.

In my example, there were two pieces of information that I needed to track. The ship's location and its orientation. Tracking its location was easy enough. I did that with a vector. I then had four main choices about how to store its orientation:

- A set of Euler angles (an x rotation, a y rotation, and a z rotation)
- A vector pointing in the proper direction
- A matrix
- A quaternion

The Euler angles, although at first blush they do exactly what I was thinking of, actually are only useful in scenarios where the rotation is restricted. The problem, as you apply each in sequence, it modifies the axis that is used for the remaining rotations. That's what can lead to gimbal lock. This can also be awkward to deal with in a case such as my example where I want to update the ship's orientation from it's perspective rather than the viewer's perspective. To explain, consider what happens if you turn the ship around to face the viewer (initialy, in case you haven't tried the example, it is facing away from the viewer). So far, so good. But now, its nose is in the opposite direction than it was before. At that point, if I pressed forward on the stick, I wanted it to nose down, just as it would if it had been facing in the original direction (so that the nose would go down and the tail would go up). Only, with Euler angles, accomplishing that isn't straight forward as you would at first hope. To understand why, consider when the ship has nosed down 45 degrees. To get the desired behavior when the ship was facing in its original direction, the Euler angles would be 0 degrees for the yaw and 45 degrees for the pitch. However, if we're applying the yaw rotation before the pitch rotation, then when the ship is facing the viewer, then those angles become 180 degrees for the yaw and -45 degrees for the pitch. Why Because we are still rotating it around the original x axis (the viewer or world's x axis), not its own x axis. It's even more awkward when you aren't working in 90 degree increments (though, 90 degree angles are also a part of what leads to gimbal lock). Still, working around this isn't too hard to accomplish. The key is rotating the set of axis used to update the ships orientation. But, now we're talking about rotating around an arbitrary axis rather than one of the three fixed ones. To do that is going to require the use of either a quaternion or a matrix.

Moving on, I could also have stored the ship's location as a vector pointing in the proper direction. Because I wanted to be also be able to roll, however, a simple vector wasn't going to work. I could have stored both a vector describing the direction the nose was pointed and a value that represented that roll, but updating that would again be difficult. I would have to translate it to some other method first and then back again. I mention the vector as an option, however, because it is sometimes a convenient way to store an object's orientation. An example where it can be useful is a third person camera. The thing that makes it useful there is that we typically want to keep the camera's up direction the same as the world's up direction. As a bonus, a vector can store distance as well as direction. Still, it wasn't suited to this application.

The third option was a matrix. Make no mistake, a single matrix can completely store an object's orientation and position. And, through some matrix magic, it is possible to work around the scenario I described before about wanting to update the ship's orientation relative to its own orienation rather than the viewer's. However, regarding storage, the matrix takes 16 elements as opposed to a vector and quaternion (the vector for location and the quaternion for orientation), which combined take only seven (three for the vector and four for the quaternion). And, regarding updating the ship's orientation, updating that information when its stored quaternion takes far fewer operations than a matrix. This is in part because the quaternion has fewer elements and in part because you don't have to perform an intial step where you modify the rotation matrix to be oriented correctly before applying it to your object. Make no mistake, though... matrices are powerful. They can not only perform the rotation and translation being discussed, but scaling, projection, and a variety of other operations. What's more is that multiple matrices can be combined into a single matrix that does all the operations of a series of matrices. However, as powerful as they are, quaternions are more convenient for my example.

So, that leaves us with the quaternion. As I said, the quaternion is a wierd beast. I'm not going to pretend to understand it. The following are some traits of quaternions worth knowing, however:

- Quaternions can fully represent the orientation of an object in 3D space (much like the directional vector and a rotation described before, although it can't represent the magnitude)
- Quaternions can also represent a rotation around any axis in 3D space (not just the X, Y, and Z axis like euler rotations)
- If you multiple a quaternion representing an object's orientation by a quaternion reprsenting a rotation, the coordinate system used in to define the axis of rotation is the rotational quaternion is from the object's space (it is defined by the object's orientation)
- If you multiplly quaternions representing a series of rotations together (in order), then the resulting quaternion is able to apply those rotations in one operation (much like matrices)

The first three traits made them perfect for my example. I was able to fully represent the ship's orientation in a structure using four elements (a quaternion) and I was able to easily create the rotations to apply to the ship's orientation using its own coordinate system rather than the world or viewer's coordinate system. Also, by doing it this way I avoided the possibility of gimbal lock and it was faster than using a matrix to store the orientation. Of course, to display the ship on screen, I needed to use a matrix, but converting a quaternion to a matrix is a pretty simple thing to do, especially with a helper library like in XNA.

Incidentally, the thing you'll hear the most about quaternions is that they avoid gimbal lock and that they can be smoothly interpolated between. But, in my case, I was just using them to store the ship's orientation and simplify the rotations.

JUNDWD

Thanks for the Quaternion intro. (You've rescued me from pulling my hair out)

Thanks for the Quaternion intro. (You've rescued me from pulling my hair out)

ClydeCoulter

I made a small change to your code sample BlueG and it seemed to smooth things out a bit.......

Matrix rotMat = Matrix.CreateFromQuaternion(_playerRot);

Vector3 right = Vector3.Transform(Vector3.Right, rotMat);

Vector3 up = Vector3.Transform(Vector3.Up, rotMat);

Vector3 back = Vector3.Transform(Vector3.Backward, rotMat);

Quaternion rot = Quaternion.CreateFromAxisAngle(right, pitch)

* Quaternion.CreateFromAxisAngle(up, yaw)

* Quaternion.CreateFromAxisAngle(back, roll);

_playerRot = rot * _playerRot;

(how did you do the code formatting above )

This puts the right, up and backward vectors into the players direction before appling the new yaw, pitch, roll.

Thanks for the tutorial, BlueG, I have new included quaternions in my Octtree scene management project sample game code.

SeanK.com

What unsmoothness did you see

I had implemented it with Matrix and didn't even see any gimble lock, but I did notice that if I held both thumbsticks so that it continuously spun in all three directions it would evenutally start drifting. This is the code I had:

```
```Vector3 modelAxisY = Vector3.Transform(Vector3.Up, _modelRotation);
_modelRotation *= Matrix.CreateFromAxisAngle(modelAxisY, deltaRotationY);
Vector3 modelAxisX = Vector3.Transform(Vector3.Right, _modelRotation);
_modelRotation *= Matrix.CreateFromAxisAngle(modelAxisX, deltaRotationX);
Vector3 modelAxisZ = Vector3.Transform(Vector3.Forward, _modelRotation);
_modelRotation *= Matrix.CreateFromAxisAngle(modelAxisZ, -deltaRotationZ);

After reading this post I changed _modelRotation into a Quaternion and did the following:

```
```_modelRotation *= Quaternion.CreateFromAxisAngle(Vector3.Up, deltaRotationY)
* Quaternion.CreateFromAxisAngle(Vector3.Right, deltaRotationX)
* Quaternion.CreateFromAxisAngle(Vector3.Backward, deltaRotationZ);
// I use modelAxisZ as the unit vector to calculate my VelocityAdd from the tutorial.
Vector modelAxisZ = Vector3.Transform(Vector3.Forward, Matrix.CreateFromQuaternion(_modelRotation);

I didn't notice any loose of smoothness and the drift went away. I don't pretend to understand Quaternion, but it appears they can accumulate the rotations on the three axises without needing to orient the axises again the current rotation.

ClydeCoulter

I only see roughness in certain orientations. It seemed to "step" lilke jaggies in a pixel line in really low resolution (like 70x70).

I ended up see some of the same problems with my "so called" fix (orienting the up,right,back to the current direction).

SO, I changed to a currrentDir, currentUp (nothing new) kinda camera, and it's really smooth and easy to read.

You'll see it in the TestOcttree demo that comes with the Octtree space scene managent project once it's on codeplex.

BlueG

** OFF TOPIC **

It took me a while to find help on how to format that code too (there is another community in the forums for discussing it, but it took me forever to find and then find my answer). To get the code formatted nice, I had to copy to Word and then paste it from there. It's also suppose to be possible to use the [code][/code] tags to mark code so that it puts it on a different background, but everytime I try it, the tags show up instead.

BlueG

From what I understand, you should actually see less drift and other erros from quaternions than you will from calculating rotation in other ways. The reason why is because less errors accumulate from multiplications because there are less multiplications. That was why I multiplied the three rotations together before applying them to the ship's orientation.

Now I'm wondering if that also led to the error someone reported about holding all three at once. I'll have to experiment with that when I have a chance. I've had and will have a lot of computer consulting work this week, so I don't know when that will be.

For a camera, it is often easier and gives better results to use the solution you describe.

I'll have to look for your demo when it comes out. I understand octrees, but I haven't tried implementing one yet, so it will be interesting. Though, it's actually quadtrees I'm considering for my own application (for all the difference that makes... none ). Obviously, that's because objects in my game will be layed out primarily in two dimensions.

ClydeCoulter

You can download the project (first release) at http://hosted.filefront.com/ClydeCoulter

It is in the folder XNA and call octtree_0_1_1.zip

It will go into a codeplex project once they setup it up for me....Let me know if you see improvements I need to make (or wanna join the project :)

Grotius

I just found this thread, and it's very helpful! I tried to mark replies as "helpful" but was rebuffed by the forum software. I tried switching all 3 rotations to one thumbstick, and it still seems to work just fine. (That might leave another thumbstick free for a free camera or targeting or something.)

Now I'm trying to get the pesky camera to follow the ship. I was hoping I could just borrow the position and rotation information for the ship model, but it doesn't quite work right. Back at it...