Camera/Object Rotations in Local Axes

Hello all,
Well, I’m new and this is my first post to these forums… I hope I’m not wearing out my welcome with the length and number of questions presented. I’ve tried to find answers to these questions elsewhere (books, tutorial/doc links from OpenGL.org, etc.) with no luck.
I’m having difficulty wrapping my brain around OpenGL’s rotations. What I want to do is to have a number of objects (any number) in my scene, each of which I can rotate with 3DOF relative to it’s own local coordinate system. Next, I’d like to be able to place the viewpoint within any one of these objects at any given time.
I did this in the past (years ago) with a 3D DOS engine which I derived from the engine developed in the book “The Black Art of 3D Programming”. If I remember correctly, the engine was based on matrices (with all of the matrix math coded into the engine), but most of the work I did to enhance it worked with vectors. I was able to get true 6DOF local coordinate control of my objects (that is, pressing my “yaw”, “roll” and “pitch” control keys would change the orientation of the object based on it’s current orientation, not world axes). I accomplished this using some functions that I got from a guy on CompuServe’s Game Developers Forum (back in the good ole days…). If I remember correctly, the program he gave me was called “yawpitch.exe”, and it used two functions… one was called, I think, alt_az_to_eq() and I seem to remember getting the impression that this function’s purpose was to convert from azimuth to equatorial coordinates… but I really never fully understood how the functions worked. What I did understand about the functions was that they used a lot of complex math, including many divides and a few square roots. Sorry for all the detail - I’m hoping it helps illustrate what I’m trying to do.
So, not initially worrying about camera angle, the first thing I did in my new OpenGL program (just to get things moving around on the screen) was basically this:

get_user_input(); // pitch, yaw, roll changes

total_pitch += pitch_change;
total_yaw += yaw_change;
total_roll += roll_change;

glLoadIdentity();
glRotatef(total_pitch, 1.0f, 0.0f, 0.0f);
glRotatef(total_yaw, 0.0f, 1.0f, 0.0f);
glRotatef(total_roll, 0.0f, 0.0f, 1.0f);
drawObject();

As you can imagine, this did not give me the local 3DOF rotations I was looking for (and I didn't expect it to).  Putting off the task of digging up the "yawpitch" program and figuring out (again) how to implement it's functions, I just started fiddling around with what I had.  So, I changed my program a bit, and to my complete astonishment, the local 3DOF control I was looking for materialized.  I still don't exactly understand why it works and if someone could enlighten me, I'd appreciate it.  Here's basically what I did in my loop:

get_user_input();
glLoadMatixf(object_matrix);
glRotatef(pitch_change, 1.0f, 0.0f, 0.0f);
glRotatef(yaw_change, 0.0f, 1.0f, 0.0f);
glRotatef(roll_change, 0.0f, 0.0f, 1.0f);
glGetFloatv(GL_MODELVIEW_MATRIX, object_matrix);
drawObject();

I'm loading/saving the matrix because I have several objects, and I'd like to keep each object's matrix around so that I can (eventually) extract information from the "current state" matrix for any given object (more on that in a moment) -- is this a good idea for a real-time engine?
So, groovy, my object now works the way I'd like it to (like an airplane in a flight sim)... but how do I get my "camera" to work the same way?  I tried implementing the same incremental transformations with a separate matrix called (creatively enough) "camera", and then loading up this matrix and multiplying it with object_matrix, but it didn't work... implementing the camera this way still gives me camera rotations relative to world axes.
It looks like I could use the gluLookAt() function that I hear so much about... but I'm somewhat reluctant to stray from the core OpenGL API (I'm not really sure why... I think it's got something to do with the "purity" of this little learning project of mine).  Is there an easy way to apply my rotations (like the easy way I stumbled onto 3DOF for my object) that would allow the camera to move in the same way, without using gluLookAt() or implementing my own (with that alt_az_to_eq() function that I don't understand)?
If I could get the ijk vectors out of object_matrix, I could use them, along with it's position, to get my "look at" point and "up" vector -- this would place my camera into the object, which is really what I want to do.  Does this sound like the correct approach?  If so, how exactly do I get these vectors out of my matrix?

Thanks a bunch,
 -BradlyP

it works because you are actually rotating by small degrees your object.

there’s a theorem wich states that if angles are little enough, then the order of rotations is not meaningful (but i don’t remember the name of this theorem, if it has one )

and since you’re working incrementally (wich is THE WAY to go) your angles are small enough to create a local rotation.

now, i do the same (conceptually) by the use of a class i wrote, CdmyBasis, wich holds what is called a “orthonormal basis”.

in this class, i hold the orientation and position of a object, and i modify its state by applying modifiers like rotate, move, place and so on.

then, i call method named make_object_mv() wich generates a modelview matrix wich reflects the state of the basis.

by keeping separate the basis from opengl, i don’t need to ask the current matrix: i only need to supply it with a glLoadMatrix of glMultMatrix (in case of chained rotations, like a camera mounted on the hull of a spaceship)

and about glLookAt, it don’t suit as a solution for your problem.
glLookAt needs a “up vector”, wich is used to tell where the up direction is.
up exist where you really need it, like in quake: but as if understood, you’re looking for a control system more resembling braeben’s elite/frontier.

DMY

[This message has been edited by dmy (edited 11-26-2000).]

Howdy again,
Thanks for the response, DMY.

Originally posted by dmy:
it works because you are actually rotating by small degrees your object.
there’s a theorem wich states that if angles are little enough, then the order of rotations is not meaningful

since you’re working incrementally (wich is THE WAY to go) your angles are small enough to create a local rotation.

Hmmm… I see a potential problem with doing it this way. The amount of my incremental rotations is based on frame rate. I determine how long the last frame took and use this amount to calculate how much to rotate in the next frame (for consistent rotation speeds regardless of framerate). So the size of my rotations is inversely proportional to the frame rate. If I were to run my simulation on a machine with an incredibly low frame rate (which I’d like to be able to do), won’t this cause the rotations to deviate from being relative to the local axes, and approach being relative to the world axes? This would be weird - say, having your contorl input be equally divided between applying rotations relative to the world and local axes.

[b]
now, i do the same (conceptually) by the use of a class i wrote, CdmyBasis, wich holds what is called a “orthonormal basis”.

in this class, i hold the orientation and position of a object, and i modify its state by applying modifiers like rotate, move, place and so on.
[/b]

I’m not sure of the correct terminology, but doesn’t “orthonormal basis” refer to the i j and k vectors that are mutually orthogonal and describe an object’s local orientation relative to world axes? If so, couldn’t you use your j vector (local “up”) with gluLookAt()? You could also use your k vector (treating it as a point) to easily compute your target (“to”) for gluLookAt(). This is what I am attempting to do, but I’m having a little trouble with maintaining my ijk vectors. (I think I just figured out what I’m doing wrong while writing this… I love it when that happens!) What I’m currently doing is storing each of the three vectors as a seperate constant matrix - each is an identity matricies with one translational component(i[12] = 1, j[13] = 1, k[14] = 1), then loading them up and applying my object’s rotation to them (to keep them oriented with the object). If my thinking is correct (and it may not be) and I apply the rotations correctly (which I may not be), I should be able to get the “up” and “target” information that I need for gluLookAt(). I have yet to be 100% successful doing this.

One thing I’m trying to do is to leave all rotations to OpenGL, which has two potential benefits: (1) I don’t have to get my hands too dirty messing with the rotations and matricies manually, and (2) I’m hoping to reap some performance increase if the OpenGL implementation and/or video hardware has transform and lighting capabilities, which I would expect to be significantly faster than the rotations routines and math/trig that I would otherwise do myself. Does this sound like valid reasoning?


up exist where you really need it, like in quake: but as if understood, you’re looking for a control system more resembling braeben’s elite/frontier.

It seems to me that any vector is a valid “up” vector for gluLookAt(), which would mean that gluLookAt can be used for complete 360 degree 3DOF rotations as long as you supply it with the correct “up” (even if “up” is really down in world space). Is this not correct?

Also, I’m not familiar with Braeben’s Elite/Frontier. But I think that we’re on the same wavelength here… the best example I can think of for what I’m trying to do would be any of the Descent series of games.

Thanks again,
-BradlyP

Just a quick update: the lightbulb that went off in my head during my last post was right on the money. My ijk matricies + gluLookAt() is working perfectly now. I can switch my viewpoint from the default view at the origin to any one of my objects which have 6DOF in thier local coords. After I clean it up a bit I’ll post what I wound up with to accomplish this.

Regards,
-BradlyP

How fast is this method? Does the fact that you’re doing all these matrix mults slow things down much? I’m trying to do the very same thing and was wondering how to rotate around local axes rather than world axes as well.

I’ve got a function that creates a rotation matrix which rotates vertices around a vector. I’m currently having a problem exactly in that stuff.

So, I start off with rotating my axises around the x axis. After that I rotate the axises around the y axis and the same according to the z axis. At the end, I hope to have a cool rotation matrix, and at least in my skybox, that is centered at the viewpoint, it works, it’s rotating around my local axises. I can as well display the axises on the screen, making sure that it works. But now comes my problem. I loaded that matrix up, and wanted to move the viewer. That is, I do a glTranslatef with the negatives of the actual position. But, if I move around in the world, it begins to rotate really weird. What am I missing.

When I get that running, I’l have no problem posting that code here Punchey!

I appreciate any help from you guys out there! -Michael

So have you resigned yourself to use gluLookAt()? Or did you figure out a way to do it with plain ol’ OpenGL?

Hello all,

Originally posted by Punchey:
How fast is this method? Does the fact that you’re doing all these matrix mults slow things down much?

I’m not really sure how efficient this method is… my little world currently has just two simple objects (cubes), and my program’s internal framerate timing is not quite working correctly. It tells me that I’m running at 100fps, which is probably close to correct, but I’m pretty sure that it’s not working right because it either says 100, or 50 (nothing in between), and these numbers don’t even correlate with my video refresh rate. I’m using timeGetTime(), and I think it’s just a precision/rounding problem. NeHe has a tutorial up that has much better timing code (which, at first glance, looks very similar to the timing code in Quake), but I haven’t had time to implement it.

As far as efficiency/speed goes, I’m currently working under the premis that if I need to do something, such as rotate an object or the camera, OpenGL’s functions will be faster than ones I write myself (unless it’s something simple like sticking a new number into a matrix for translation). My reasoning is twofold: (1) I’m not a C/C++ expert, and I don’t know very much about optimization, or even what is considered slow/fast for a PII or greater processor (floating point math, trig, etc.), and (2) I’m assuming that the OGL driver will take advantage of hardware capabilities whenever possible which will only give me an advantage if I use OGL’s functions, not if I do them myself. What I’m not sure of is wether or not a driver/video adapter that has hardware T&L capabilities (I have a GeForce II MX) will be able to use those features to accelerate matrix transformations the way I’m doing them (loading a matrix into the ModelView matrix, using glRotatef()/glTranslatef(), and then saving the matrix back to my local array variables).

Originally posted by Michael Steinberg:
…it works, it’s rotating around my local axises. I can as well display the axises on the screen, making sure that it works. But now comes my problem. I loaded that matrix up, and wanted to move the viewer. That is, I do a glTranslatef with the negatives of the actual position. But, if I move around in the world, it begins to rotate really weird. What am I missing.

I’m not really sure what the problem is, but it sounds like it might be that the rotation you’re applying is being applied to your star field about the world origin with your local rotations… which would look weird… are you translating the star field along with the viewer? The way I’m doing things, I think that you would be able to never apply any rotations to the star field, just translate it to the viewer’s locatoion before drawing (so that you never wind up running into the skybox)… I think(?). I’ll post my code when I clean it up (and figure out how/where to post it).

Originally posted by Punchey:
So have you resigned yourself to use gluLookAt()? Or did you figure out a way to do it with plain ol’ OpenGL?

Well, I’ve come to the conclusion that using gluLookAt() is probably the best solution. I don’t know much about how/where the glu functions are actually implemented, but I think there’s only two possibilities, and in either case it would be better than doing it manually. As far as I can tell, the two cases are (1) the glu functions are implemented in the hardware specific driver - this would be optimal because, as I said before, this would mean that the possibility exists for hardware acceleration of functions like gluLookAt(), or (2) the glu functions are in a generic library that hardware vendors do not include in thier optimized driver. In this second case, gluLookAt() is not optimized for the hardware, and just makes calls to the core (driver) functions (glRotate, etc.). If this is the case, I could impelment my own viewing matrix manipulations that could (theoretically) be optimized to my particular needs and be more efficient than gluLookAt(). However, as I stated, I’m not that good , and anything I wrote myself would probably not be nearly as good as the stuff written by the people that really know what they are doing.

How/where should I post my code? I could just isolate the relavent code and post it in a message here, but if I can zip up the whole MSVC 6.0 project along with the textures and everything, you guys could see exatly what I’m doing and run my program to see it work.

Regards,
-BradlyP

[This message has been edited by BradlyP (edited 11-28-2000).]

[This message has been edited by BradlyP (edited 11-28-2000).]

I implemented that rotaion stuff myself.
Okay, gonna make my problem clearer.

I start with an identity matrix that is

1000
0100
0010
0001

Now, it seemed to me, that the first row represents the x-Axis and so on. So I tried it, and if I’m at the world’s origin, and only apply rotation, it rotates beautifully around the viewpoint. Now, I wanted to move, ie: apply the position-translation only to the object, not to the skybox. If I’m at the origin, the objects rotate well. Now when I’m at another place, when I rotate now, it makes really weird results. It seems that it’s is not rotating around my position, but still around the origin. Maybe, I’m not too sure. Should I post some code here?

Generally, what is the way to go.
Set Rotation-matrix
Set Translation-matrix
Or the other way around? Or something else?

I can’t use gluLookAt, since I have to hold my own rotation matrix, because I need to rotate the world around these axises in the matrix.

Or, how to extract an axis out of a matrix, is it so simple as I thought it would be?

Not sure if this info will help you any, but the glTranslate/glRotate functions work in the reverse order that you use them. They do this becuase of properties of matrices and matrix mathematics. Say you have coordinates for a cube that gets drawn at the world origin with no transformations applied. If you do this…

glTranslatef(0.0, 0.0, -10.0);
glRotatef(45, 0.0, 1.0, 0.0);
drawCube();

The cube first gets rotated 45 degrees around it’s y-axis, then moved down the z-axis 10 units. Since the cube’s coordinates center it on the world origin, it is the same as rotating it around it’s local coordinate system.

If instead you do this…
glRotatef(45, 0.0, 1.0, 0.0);
glTranslatef(0.0, 0.0, -10.0);
drawCube();

The cube is first translated and then rotated. Since both of these functions basically operate on the world coordinate system you are rotating around the world’s y-axis 45 degrees instead of the cube’s “local” y-axis.

So generally, you’d want to do the glTranslate then glRotate. The same goes for multiplying your own matrices. If you have a matrix T for a translation and a matrix R for rotation doing TR is like calling glTranslate/glRotate and doing RT is like calling glRotate/glTranslate.

Does that help any? Or did I miss the real meaning of the problem and my explanations are totally irrelavent?

Hi Michael,

Originally posted by Michael Steinberg:
[b]I start with an identity matrix that is

1000
0100
0010
0001

Now, it seemed to me, that the first row represents the x-Axis and so on.[/b]

I’m not really a matrix expert, but I think you’re oversimplifying how they work. From what I understand, rotation around the X axis is defined by a matrix such as:

1 0 0 0
0 cos(x) sin(x) 0
0 -sin(x) cos(x) 0
0 0 0 1

Rotation about the Y axis is

cos(x) 0 -sin(x) 0
0 1 0 0
sin(x) 0 cos(x) 0
0 0 0 1

Rotation about the Z axis is

cos(x) sin(x) 0 0
-sin(x) cos(x) 0 0
0 0 1 0
0 0 0 1

The x’s in the above represent the angle in degrees that you want to rotate. Maybe you know this, but it’s not clear from your post. You have to calculate (or look up) the sin’s and cos’s, then plug them into the appropriate places, then multiply your current matrix by the resulting matrix. If you want to rotate in more than one axis, you have to create the seperate matricies for each rotation, then mult them together (and different multiplication orders will give different results). Of course, there are shortcut methods to getting these rotations into your matrix, but that’s the standard “correct” method (and I don’t know the shortcuts). This seems a lot harder to me than using glRotate, which if used incrementally gives you local rotations.

The translations are simpler - the translation matrix is

1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1

Where x, y and z are the translations in their respective axes. When you multiply this matrix with your existing matrix, it moves your object in it’s local coordinate system (I think).

If you specify rotations and translations in one operation, I think that OGL does the rotations first, then translates in the newly rotated axes… does that make any sense? If you use glRotate() and glTranslate() you can control in which order the rotation/translation happen (you can move an object to a desired place in world space, then rotate it in that position).

So I tried it, and if I’m at the world’s origin, and only apply rotation, it rotates beautifully around the viewpoint. Now, I wanted to move, ie: apply the position-translation only to the object, not to the skybox. If I’m at the origin, the objects rotate well. Now when I’m at another place, when I rotate now, it makes really weird results. It seems that it’s is not rotating around my position, but still around the origin.

Sounds like it’s rotating, then translating in the rotated axes.

Generally, what is the way to go.
Set Rotation-matrix
Set Translation-matrix
Or the other way around? Or something else?

I think the other way around would give better results… that is, you would translate the object to the desired position (which I assume is stored in world coordinates), then rotate it. Again, I’m just learning (or re-learning) this stuff myself, so don’t take what I say too seriously.

I can’t use gluLookAt, since I have to hold my own rotation matrix, because I need to rotate the world around these axises in the matrix.

I’m storing my own rotation matrix and using gluLookAt(). I think the difference is that I’m only using OGL functions to manipulate the matrix - I load the matrix with glLoadMatrixf(), then use glRotate()/glTranslate, then save the matrix back to my local array with glGetFloatv(GL_MODELVIEW_MATRIX, my_matrix_array)

Or, how to extract an axis out of a matrix, is it so simple as I thought it would be?

I haven’t figured out how to extract an object’s current axes directly out of it’s matrix… I’m guessing it would be difficult/expensive. I’m using two constant matricies to figure out the y and z axes (the ones you need for gluLookAt) Here’s basically what I do to (note that I’m using my object as the “camera”):

Define matrix for the “j” vector (y axis):
const GLfloat jVector[16]

Load the values into the vector
1 0 0 0
0 1 0 1
0 0 1 0
0 0 0 1

Define matrix for the “k” vector (z axis):
const GLfloat kVector[16]

Load the values into the vector
1 0 0 0
0 1 0 0
0 0 1 1
0 0 0 1

Remember the way that OGL stores matricies in arrays:

[0] [4] [8] [12]
[1] [5] [9] [13]
[2] [6] [10] [14]
[3] [7] [11] [15]

Then I take my object’s matrix (objectMatrix), which has been rotated/translated with glRotate/glTranslate and, and copy it to a temporary matrix (tempMatrix). Next I remove it’s translational components so I have a matrix that just has the rotational components. Then I multiply my jMatrix with the temporary matrix. This rotates jMatrix into the same orientation as my object and leaves it at the world origin. Now jMatrix’s translation components ([12], [13], [14]) represent my object’s local y-axis relationship to the world axes - this is exactly what you need for gluLookAt():

memcpy(tempMatrix, objectMatrix, sizeof(objectMatrix));  // copy objectMatrix to tempMatrix
tempMatrix[12] = 0;
tempMatrix[13] = 0;
tempMatrix[14] = 0;

glLoadMatrixf(jMatrix);
glMultMatrix(tempMatrix);
glGetFloatv(GL_MODELVIEW_MATRIX, tempMatrix);  /* save this matix so we can pull elements out */

upX = tempMatrix[12];
upY = tempMatrix[13];
upZ = tempMatrix[14];

Now, for the z-axis, I don’t want to remove the translations, because this is used as the “look at” point, not a vector at the origin (this is what I was doing wrong with my first crack at using gluLookAt):

memcpy(tempMatrix, objectMatrix, sizeof(objectMatrix));  // copy objectMatrix to tempMatrix
glLoadMatrixf(kMatrix);
glMultMatrix(tempMatrix);
glGetFloatv(GL_MODELVIEW_MATRIX, tempMatrix);

atX = tempMatrix[12];
atY = tempMatrix[13];
atZ = tempMatrix[14];

Now I have all the info I need for gluLookAt. To draw a sky box, I would load up it’s matrix and translate it to your viewpoint before calling gluLookAt… I think this will work as long as you never rotate the sky box’s matrix:

glLoadMatrix(skyBoxMatrix);
glTranslatef(objectMatrix[12], objectMatrix[13], objectMatrix[14]);
glGetFloatv(GL_MODELVIEW_MATRIX, skyBoxMatrix);
// Now call gluLookAt:
gluLookAt(objectMatrix[12],
          objectMatrix[13],
          objectMatrix[14],
          atX,
          atY,
          atZ,
          upX,
          upY,
          upZ);

Of course, there’s better ways to handle the matricies w/o so much saving and loading (I’ll optimize later). Now, just draw you’re objects like so:

glPushMatrix(); // save the view matrix set up w/ gluLookAt.
glMultMatrixf(skyBoxMatrix);
drawSkybox(); // or glCallList(skyBoxList);, whatever

glPopMatrix();
glPushMatrix();
glMultMatrixf(object1Matrix);
drawObject1();

glPopMatrix();
glPushMatrix();
glMultMatrixf(object2Matrix);
drawObject2();
//etc...

I think that should work (I just wrote this code in here instead of copying it out of my working program, so I may have screwed something up). If you give me your email address, I’ll happily zip up my program and email it to you (it might be kind of big…). I’m using MSVC 6, but I’m not using any MFC stuff.

Hope this helps,
-BradlyP

[This message has been edited by BradlyP (edited 11-28-2000).]

My e-mail address is aerotechts@spamsucks.usa.net (remove “spamsucks.” from this address). Also, I think you meant radians when you said that “x” represented the angle in degrees? Anyway, somewhat symantic, yet the difference will be night and day. Anyway, thanks for all the help and thanks in advance for the code!

Punchey,
I just sent you my program. It wound up being a lot smaller than I expected once I trimmed a little fat from it. It’s about 90k zipped, so if anyone else wants it, just let me know.

Regards,
-BradlyP

could i get it as well? my email is anaddana2@spam.aol.com

remove the spam, of course.

thanks

Originally posted by BradlyP:
[b]Punchey,
I just sent you my program. It wound up being a lot smaller than I expected once I trimmed a little fat from it. It’s about 90k zipped, so if anyone else wants it, just let me know.

Regards,
-BradlyP[/b]

Howdy again all,

Originally posted by dnstapes:
could i get it as well? my email is anaddana2@spam.aol.com
remove the spam, of course.
thanks

I just sent it to you, dnstapes. One thing I forgot to mention in the emails to yourself and Punchey is that you can easily change the speeds at which the car and camera rotate and move, and how quickly they stop rotating/moving by playing with the defines: LINEARACCEL, MAXSPEED, DAMPER, ANGULARACCEL, MAXANGULARSPEED and ANGULARDAMPER. Also, the things that are relavent to this conversation are all located in WinMain() (near the bottom) and DrawGLScene(). I’m sure you could figure these things out yourself, but it’s always easier if someone points you in the right dirrection .

Have fun, and let me know what you think (not that there is really much there to think about).

Regards,
-BradlyP

Maybe I’m a complete idiot but, as you stated, the rotation matrices around x look like this:

1 0 0 0
0 cos(x) sin(x) 0
0 -sin(x) cos(x) 0
0 0 0 1

Means, x does nothing, y’s y and z value get changed, z’s y and z value get changed. That really looks to me as if any row of the first three would make up a vector.
Okay, but to be sure, I just got it, that I can multiply the local front vector with this matrix too, and it should give a correct result in any way.
Yeah, my teacher told me today that I’d have to do calc it all the other way around, too. Thanks. Oh by the way, is there anything in the web that could teach me why?

If I have an x-Axis rotation, an y axis rotation and a z-Axis rotation, all at the same time, what is the correct result?
x-Matrix * y-Matrix * z-Matrix? or other way around? Or what?

Thanks to you all!

Oh, I’m pretty sure that my matrices are in the opengl order, I converted the mathlib some weeks ago to make it ogl conform.

Yeaaaahhhh!!!
Guys, it really works!!!
glEnable( GL_BRAIN );

It really seems to be that the rows show the local axises.
The problem seems to have been somewhere else, don’t know for sure. I ended up, cleaning all that opengl status stuff up, defining a standard status, that any function has to leave after its work is done. Thanks for all of your support!

glDisable( GL_BRAIN );

Oh, punchey!
You still want to have a function that creates a rotation matrix that rotates a vertex around a vector? Drop me a line!

i’m glad you all found solutions to your problems!

DMY