Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you combine X, Y, and Z rotations in a model's local space (not global)?

I am starting out on Molehill and am having a lot of problems grasping Matrix3D. My first problem is how to manage a Matrix3D that describes a single model's orientation.

I use Matrix3D.appendRotation() to do this. But depending on the order I append in I get a different result (obviously, since I am rotating one at a time in the global space). I know this is a common problem, I have run into it with 3d software modeling. But this time, I have to fix it programtically. This is where I need help.

First, in case there is any confusion, here is the problem in pictures:

Step 1: I render a model.

Start position

Step 2a: I rotate it on the X-axis. Looks correct!

X-axis rotation

Step 2b: I rotate it on the Z-axis. Looks correct!

Z-axis rotation

So... if I rotate on the model's X AND Z axis I want this:

desired XZ rotation

But, sadface, I get this:

enter image description here

I can see the problem. I am appending the rotations one at a time in the global space. I need to rotate in the object's local space. I have NO idea how to do this or what I should even be searching for (terminology, etc.). The code is simple (and wrong):

modelLocalMatrix.identity();
modelLocalMatrix.appendRotation(modelZRot, Vector3D.Z_AXIS);
modelLocalMatrix.appendRotation(modelXRot, Vector3D.X_AXIS);
//eventually y-axis rotation as well...
renderMatrix.append(modelLocalMatrix);

I am guessing that instead of using the Vector3D axis constants, I want to use some normalized vector and some... uh, thing... in place of modelZRot, modelXRot, and eventually modelYRot. Can anyone tell me what the best-practice solution is for applying the desired type of rotation above?

UPDATE: Having spent the day hitting the "books" (aka KhanAcademy on YouTube) and starting on this presentation here: The Mathematics of the 3d Rotation Matrix I have stumbled upon a sort of "look at" method which is very close to my desired solution. Unfortunately, I only 50% understand it. I still hope someone can shed some light on this topic!

var angleOfRotation:Number = Math.PI / 2; //90 degrees...
var axisOfRotation:Vector3D = new Vector3D(0, 1, 0); //some point to look at... 
//normalize the vector!
axisOfRotation.normalize();
var x:Number = axisOfRotation.x;
var y:Number = axisOfRotation.y;
var z:Number = axisOfRotation.z;
var c:Number = Math.cos(angleOfRotation);
var s:Number = Math.sin(angleOfRotation);
var t:Number = 1 - c;

//Graphics Gems (Glassner, Academic Press, 1990). 
modelLocalMatrix = new Matrix3D(new <Number>[
  t * (x * x) + c,   t * x * y - s * z,   t * x * z + s * y,   0, 
  t * x * y + s * z,   t * (y * y) + c,   t * y * z - s * x,   0, 
  t * x * z - s * y,   t * y * z + s * x,   t * (z * z) + c,   0, 
  0,           0,           0,           1
]);

Thing is, this seems rather clunky. Especially since the angle of rotation for a Vector3D should be the fourth (w) value. More importantly, Matrix3D already appears to have a lookAt() method. I don't get it yet... please someone save me from hours of trial and error!

like image 871
jpwrunyan Avatar asked Nov 23 '11 06:11

jpwrunyan


1 Answers

Ok, I have basically figured out how to do this. It is MUCH simpler than I was making it. It also helped to review the Matrix3D API (it has got some awesome functions!).

First, the source code I used to rotate my model on its local axes:

var raw:Vector.<Number> = modelLocalMatrix.rawData;
var right:Vector3D = new Vector3D(raw[0], raw[1], raw[2]);
var up:Vector3D = new Vector3D(raw[4], raw[5], raw[6]);
var out:Vector3D = new Vector3D(raw[8], raw[9], raw[10]);

modelLocalMatrix.appendRotation(modelXRot, right);
modelLocalMatrix.appendRotation(modelYRot, up);
modelLocalMatrix.appendRotation(modelZRot, out);

The "up", "right", and "out" normalized vectors are discussed in the link in my original post. They are basically the model's pitch, yaw, and roll axes. Suffice to say that when you initialize your object, these vectors are identical to Vector3D.X_AXIS, Y_AXIS, and Z_AXIS. I have extracted them using Matrix3D.rawData property although there may be a better way.

Now, you can rotate along any of these axes as you choose, but the instant you do, the other two axes' orientation changes immediately. This makes sense. Your model has rotated and its "right", "up", and "forward" are now different! So if you start by rotating on the model's "right" axis (remember, this starts out the same as the global X_AXIS) and then you want to roll your model on its "out" axis, you need to get it from the model's local matrix. Obviously, it is no longer the same as the global Z_AXIS. Note that I do not call Matrix3D.identity(). If I did that, then my model's axes would be reset! This is ok for my render loop, although I had to change my code (and way of thinking) a bit since I had been reseting all my matrices before applying transformations and rotations. Note that modelXRot, modelYRot, and modelZRot store a rotation value in degrees.

I sure hope this helps someone. I can't believe such a common problem with such a simple solution was nowhere to be found. Anyway, it sure seems to be doing what I want it to.

like image 117
jpwrunyan Avatar answered Oct 25 '22 02:10

jpwrunyan