draders's picture

Translation and Rotation Matrix

I have an object that can be rotated and translated. The rotation is represented by a Quaternion and the position a Vector3. The code is loosely based around the code in http://www.opentk.com/node/1292

The following is how the object gets render with the given translation and rotation:

Matrix4 rotate = Matrix4.Rotate(this.Orientation);
Matrix4 translate = Matrix4.CreateTranslation(this.Position);
Matrix4 m = translate * rotate;
GL.PushMatrix();
GL.MultMatrix(ref m);
base.Render();
GL.PopMatrix();

My problem is the rotation seems to always rotate around the origin instead of it's position unless I change the multiplication to be:

Matrix4 m = rotate * translate;

But when I do that, it always translates relative to the default rotation of the object.

I noticed if I changed the RageWorld example linked above to be "rotate * translate", both wrong behaviour occurs. Any ideas on what I can do to the rotation Quaternion to correct it's behaviour?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
jmanson's picture

It seems like this is the correct behavior as you have described it. Is your object not centered at the origin to start with? If not, you should make sure that it is. Preferably, you would center your model in Blender or whatever in the first place. Otherwise, you can subtract, the centroid, rotate the object, add back the centroid, and finally, add a displacement.

draders's picture

The object itself is centred about the origin, I just translate it and rotate it using the GL.MultMatrix(ref m); method. The problem is that it continues to rotate about the origin even after its been translated.

jmanson's picture

Ah, I think I understand your problem now. Is the problem that the translation is applied in the local coordinates of the object rather than the global coordinates? There are 2 possible solutions if that is the case I think.

1. Don't use the translation matrix. Instead, once you have created the rotation matrix, modify it by setting the last column to be (X Y Z 1), where X, Y, and Z are the coords of the translation you want. Here I am assuming that OpenTK uses column vectors. If it uses row vectors instead (this is the unconventional, but OpenGL way), you would instead set the last row to those values.

2. Do like the first snippet of code, but first multiply Position by the transpose of the rotation matrix (the transpose is the same as the inverse for rotation matrices).

draders's picture

These didn't work either.

"If it uses row vectors instead (this is the unconventional, but OpenGL way), you would instead set the last row to those values."

That one seems to do the same as rotate * translation.

"Do like the first snippet of code, but first multiply Position by the transpose of the rotation matrix (the transpose is the same as the inverse for rotation matrices)."

This made it rotate in the opposite direction.

draders's picture

Maybe this could give a better idea. The orientation starts off as:

this.Orientation = Quaternion.Identity;

I then update using the following:

Vector3 angular = this.AngularVelocity * (float)time;                
this.Orientation *= Quaternion.FromAxisAngle(angular, angular.Length);

AngularVelocity is just a force vector pointing in the direction of the force being applied.

draders's picture

I figured out a solution, I modified the way I get the resultant direction of the force being applied for translation using this function.

        public static void RelativeToOrientation(ref Quaternion quat, ref Vector3 vector, out Vector3 result) {
            result = new Vector3();
 
            Vector3 right = quat.Apply(Vector3.UnitX);
            right.Y = -right.Y;
            right.Z = -right.Z;
            result += right * vector.X;
 
            Vector3 forward = quat.Apply(Vector3.UnitY);
            forward.X = -forward.X;
            forward.Z = -forward.Z;
            result += forward * vector.Y;
 
            Vector3 up = quat.Apply(Vector3.UnitZ);
            up.X = -up.X;
            up.Y = -up.Y;
            result += up * vector.Z;            
        }