Why is my model's scale changing after rotating it?

I have just started a simple flight simulator and have implemented Roll and pitch. In the beginning, testing went very well; however, after about 15-20 seconds of constantly moving the thumbsticks in a random or circular motion, my model's scale begins to grow. At first I thought the model was moving closer to the camera, but i set break points when it was happening and can confirm the translation of my orientation matrix remains 0,0,0. Is this a result of Gimbal Lock?

Does anyone see an obvious error in my code below?

    public override void Draw( Matrix view, Matrix projection )
{
Matrix[] transforms = new Matrix[Model.Bones.Count];
Model.CopyAbsoluteBoneTransformsTo( transforms );

Matrix translateMatrix = Matrix.Identity
* Matrix.CreateFromAxisAngle( _orientation.Right, MathHelper.ToRadians( pitch ) )
* Matrix.CreateFromAxisAngle( _orientation.Down, MathHelper.ToRadians( roll ) );

_orientation *= translateMatrix;

foreach ( ModelMesh mesh in Model.Meshes )
{
foreach ( BasicEffect effect in mesh.Effects )
{
effect.World = _orientation * transforms[mesh.ParentBone.Index];
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
}
mesh.Draw();
}
}

public void Update( GamePadState gpState )
{
roll = 5 * gpState.ThumbSticks.Left.X;
pitch = 5 * gpState.ThumbSticks.Left.Y;
}

• Is there a reason you're using the Down direction for rolling around? Cos that wouldn't technically be roll, but yaw. – Ray Dey Nov 20 '11 at 19:27
• That's what I thought. I originally had Forward, but then the plane was actually performing yaw. – Justin Self Nov 20 '11 at 19:32
• Wait a second, is this by any chance a model imported from 3DS Max or Maya? – Ray Dey Nov 20 '11 at 19:34
• Its a .fbx. I opened the file and saw this in at the top: FBX 6.1.0 project file ; Created by Blender FBX Exporter ; I downloaded this free model from turbosquid. – Justin Self Nov 20 '11 at 19:37
• Maybe this is a silly question, but why would Gimbal Lock cause your object to scale? – Amplify91 Nov 20 '11 at 19:38

You are combining matrices for the effect.World backwards.

It should be:

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


Most likely, there is some model root or local ModelMesh scaling coming from the 'transforms' (via the 3d modeling app usually). When you place the orientation first, it takes the system out of world space alignment. But then the 'transforms[]' is trying to scale in the direction of world space basis vectors so it scales it all out of local space alignment. Remember SRT, and if there is any scaling in the 'transforms[]', it must be dealt with first..

Also, it is very important that when you use CreateFromAxisAngle(), that the axis argument be exactly unit length (as been said by other posts). Can't stress that enough, if you do this, your _orientation matrix will last hours without shearing or distorting while being updated 60 times per second.

Try:

Matrix.CreateFromAxisAngle( Vector3.Normalize(_orientation.Right), MathHelper.ToRadians( pitch ) )

• This was it for me: normalizing when creating the Matrix. Also, showing me to multiply the correct order for the effect fixed the other issue I had of having to 'roll' off of the Down vector. – Justin Self Nov 21 '11 at 0:52

The _orientation matrix is likely accumulating error from the rotation that is applied each frame, and is losing its orthogonal property. Try normalizing it after applying the rotation:

_orientation.Forward = _orientation.Forward.Normalize;
_orientation.Left= _orientation.Left.Normalize;
_orientation.Up= _orientation.Up.Normalize;

• You also need to orthogonalize it or you could end up with shearing. See en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process. – Nathan Reed Nov 20 '11 at 19:36
• Did as you suggested, however it didn't work. I'm still scaling after a while. I know for sure it is scaling because I decomposed the _orientation matrix in the beginning and compare it after each translation. Once I begin to pitch, the scale changes. – Justin Self Nov 20 '11 at 20:11

You need to recompute the orientation each frame; currently you are just accumulating it;

_orientation *= translateMatrix;

needs to be more like

_orientation = Matrix.Identity;

_orientation *= translateMatrix;

Or you could use the Matrix.CreateWorld static method

• btw, this translateMatrix variable you have is not a translation matrix. A translation matrix describes displacement along the axes. – indeed005 Nov 20 '11 at 21:16
• When I do that, it resets the orientation every time and I can only ever pitch or yaw the model by the 5 degrees at most. – Justin Self Nov 20 '11 at 22:22
• The obvious solution is to store the current pitch and yaw and accumulate these values. – indeed005 Nov 20 '11 at 22:48
• Yes, you are correct. When I did that, the model would rotate out of control. Not really sure what is going on with it. I need to refresh in my math apparently. – Justin Self Nov 20 '11 at 23:00
• Right, that's because you are using CreateFromAxisAngle every frame, which would work if the orienation were reset before this each frame, but just use CreateFromYawPitchRoll instead – indeed005 Nov 21 '11 at 0:06