Tudor Pascu
Tudor Pascu

Reputation: 203

OpenGL simultaneous translate and rotate around local axis

I am working on an application that has similar functionality to MotionBuilder in its viewport interactions. It has three buttons: Button 1 rotates the viewport around X and Y depending on X/Y mouse drags. Button 2 translates the viewport around X and Y depending on X/Y mouse drags. Button 3 "zooms" the viewport by translating along Z.

The code is simple:

glTranslatef(posX,posY,posZ);
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);   

Now, the problem is that if I translate first, the translation will be correct but the rotation then follows the world axis. I've also tried rotating first:

glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glTranslatef(posX,posY,posZ);

^ the rotation works, but the translation works according to world axis.

My question is, how can I do both so I achieve the translation from code snippet one and the rotation from code snippet 2.

EDIT I drew this rather crude image to illustrate what I mean by world and local rotations/translations. I need the camera to rotate and translate around its local axis. http://i45.tinypic.com/2lnu3rs.jpg

Upvotes: 1

Views: 3485

Answers (3)

Kristian Duske
Kristian Duske

Reputation: 1779

I suggest that you bite the apple and implement a camera class that stores the current state of the camera (position, view direction, up vector, right vector) and manipulate that state according to your control scheme. Then you can set up the projection matrix using gluLookAt(). Then, the order of operations becomes unimportant. Here is an example:

Let camPos be the current position of the camera, camView its view direction, camUp the up vector and camRight the right vector.

To translate the camera by moveDelta, simply add moveDelta to camPos. Rotation is a bit more difficult, but if you understand quaternions you'll be able to understand it quickly.

First you need to create a quaternion for each of your two rotations. I assume that your horizontal rotation is always about the positive Z axis (which points at the "ceiling" if you will). Let hQuat be the quaternion representing the horizontal rotation. I further assume that you want to rotate the camera about its right axis for your vertical rotation (creating a pitch effect). For this, you must apply the horizontal rotation to the camera's current angle. The result is the rotation axis for your vertical rotation hQuat. The total rotation quaternion is then rQuat = hQuat * vQuat. Then you apply rQuat to the camera's view direction, up, and right vectors.

Quat hRot(rotX, 0, 0, 1);      // creates a quaternion that rotates by angle rotX about the positive Z axis
Vec3f vAxis = hRot * camRight; // applies hRot to the camera's right vector
Quat vRot(rotY, vAxis);        // creates a quaternion that rotates by angle rotY about the rotated camera's right vector
Quat rQuat = hRot * vRot;      // creates the total rotation
camUp = rQuat * camUp;
camRight = rQuat * camRight;
camView = rQuat * camView;

Hope this helps you solve your problem.

Upvotes: 1

JCooper
JCooper

Reputation: 6525

glRotate always works around the origin. If you do:

glPushMatrix();
glTranslated(x,y,z);
glRotated(theta,1,0,0);
glTranslated(-x,-y,-z);
drawObject();
glPopMatrix();

Then the 'object' is rotate around (x,y,z) instead of the origin, because you moved (x,y,z) to the origin, did the rotation, and then pushed (x,y,z) back where it started.

However, I don't think that's going to be enough to get the effect you're describing. If you always want transformations to be done with respect to the current frame of reference, then you need to keep track of the transformation matrix yourself. This why people use Quaternion based cameras.

Upvotes: 0

Tim
Tim

Reputation: 35943

Ok, the image makes things a bit clearer.

If you were just talking about an object, then your first code snippet would be fine, but for the camera it's quite different.

Since there's technically no object as a 'camera' in opengl, what you're doing when building a camera is just moving everything by the inverse of how you're moving the camera. I.e. you don't move the camera up by +1 on the Y axis, you just move the world by -1 on the y axis, which achieves the same visual effect of having a camera.

Imagine you have a camera at position (Cx, Cy, Cz), and it has x/y rotation angles (CRx, CRy). If this were just a regular object, and not the camera, you would transform this by:

glTranslate(Cx, Cy, Cz);
glRotate(CRx, 1, 0, 0);
glRotate(CRy, 0, 1, 0);

But because this is the camera, we need to do the inverse of this operation instead (we just want to move the world by (-Cx, -Cy, and -Cz) to emulate the moving of a 'camera'. To invert the matrix, you just have to do the opposite of each individual transform, and do them in reverse order.

glRotate(-CRy, 0, 1, 0); 
glRotate(-CRx, 1, 0, 0);
glTranslate(-Cx, -Cy, -Cz);

I think this will give you the kind of camera you're mentioning in your image.

Upvotes: 1

Related Questions