problem with rotation along rotated normals

complicated title, because of my english. Sorry.

Problem:
I have an object i want to rotate along the objects x- and z-axis.

when i rotate the object along the x-axis the z-axis of the object is also rotated. so in the next rotation, along the z-axis is not the rotation i want to achieve.

my code:

 
 glPushMatrix();
    glTranslatef(zagx,zagy, zagz);
	glRotatef(zagrotx, 1.0, 0.0, 0.0);
        glRotatef(zagrotz, 0.0, 0.0, 1.0);
    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_TRIANGLES);
                glVertex3f(-2,0.0,-0.5);
                glVertex3f(0,0,1.0);
                glVertex3f(2.0,0.0,-0.5);
    glEnd();
	glPopMatrix();
 

If only your question had been formulated more clearly than your title…

If you want to do something like pitch and heading, then you’ll want to rotate the pitch first, then the heading:

Suppose the following coordinate system (common in 3D rendering APIs, but probably not very natural when you’re used to CAD software):
the forward direction is the Z-axis,
the up-direction is the Y-axis,
and the X-axis is to the right

To do this, you’ll want to rotate around the X-axis first (pitching the nose up), then about the Y-axis (heading).
If you also want roll, then you need to rotate around the Z-axis before all the other rotations.

(If you want to rotate back you need to rotate in the opposite direction, in the opposite order)

If you don’t want to do pitch/heading, you probably don’t want to rotate around the object’s axes but around some other axis.
In which case you’ll really need to formulate your question more clearly.

Let me add one more thing:
you need to rotate the object before you move it, so you need to do the "glRotate"s before the glTranslate

Sorry for my bad explanation. But this stuff is not only hard of my leaking engish capabilities, but also because my school ist 15 years ago. So please be patient :slight_smile:

I try to explane the problem. I want to program a little simulator of a plane. you can pitch (x-axis) and roll (z-axis) the plane. I made a graphic.

So for example, I pitch 10° up and roll 10° left.
So I first do the pitch-rotation (x-axis). No problem till now.
Next i do the roll-rotation (z-axis). PROBLEM!!!
I have to roll on the already by the pitch-rotation rotated z-axis. (Oh my god, its happening again. I stumble over my english an my confused brain :slight_smile: )
How do i achieve that.
I read something about CONONICAL ROTATION to rotate an arbitrary axis, but i hav just rotate on the x-axis. So its not completely arbitrary :slight_smile: . Is there a simple way to do this.

I hope, it`s a little bit cleare now.

OK, that’s what I suspected.

basically I already explained what you need to do, but here it is:

  1. apply roll (around Z-axis, since that is forward)
  2. apply pitch (around X-axis, since that is the side)
  3. apply compass heading (around Y-axis, since that is the top) - I do assume you’ll want to have heading at some point.
  4. apply position (the glTranslate)

You need to do these steps in this exact order, or you’ll get wrong results.

This is simple to do with quaternions.
This needs only four values to store the 3D orientation and can be added, interpolated, normalized(!), etc.
To get the orientation into OpenGL you just need a function which converts the quaternion into a 4x4 modelview matrix.

There is ready-to-use code in the GLUT library examples. Search for “trackball.c” on the internet.
There are a lot of quaternion tutorials on the net, too.
Here’s one: http://www.gamedev.net/reference/articles/article1095.asp

@RELIC

I have read the Quaternions Tutorial. But its quite hard, because its not in my language. I tried to find something simular in german, but found nothing.

I found the trackball example. But the rotations are on the X,Y,Z Axis. No axis is rotated. I don´t get it.

Michael, do yourself a favour:

  1. roll
  2. pitch
  3. heading
  4. translation

Works for me.

I suppose quaternions have their advantages but they just don’t feel “natural”.

(Also, if I understand it correctly, before you can apply a rotation from a quaternion you need to multiply a quat with itself and then apply that matrix - if there’s any advantage it must be in chaining them together)

@T101

Sorry, but your Way don´t work for me.

The problem is, in real life when i roll the plane (z-axis), the axis for pitching (x-axsis) also rolls with the plan. (Hope you can understand my english. Not so sure what i am talking… :slight_smile: )

That does not work so easy in opengl, because the rotation axis is always in the euler coordinate system.

No, the rotation axis is always the local axis after past transformations. That means, when you first rotate around X and then rotate around Y (for example), the second rotation is applied to the new Y axis (that is, the one produced by the first rotation). That is why the order of transformations is important (and you will most likely get it wrong the first time :wink: ).

This one looks German to me:
http://www.aacm.ch/rbdoc/chapter.php?id=73

Yes, the benefit is the chaining.
If you have any orientation in 3D described by a quaternion and want to add another incremental angle for tilt, roll, or yaw starting there, you just multiple the quaternions, renormalize them to remove any numerical degeneration, convert it to an OpenGL matrix and do glLoadMatrixf(), done.
I find this elegant and intuitive.
You need three functions, the multiply, normalize and matrix conversion of a quaternion and are done.

@overmind (T101 also)

I don´t get it. what you say works not for me, or my programm.

I have uploaded the programm to my webspace, so you can download it and test it.

Maybe a have a big bug in my code.

programcode

You can rotate the triangle by cursor. You see, if you pitch the triangle till its 90° and then roll, it don´t roll, it rotates.

Thanx for the Link to the german site for quaternions. my brain is already boiling.

You just got the order of rotations wrong.

Thats the correct order of function calls (I added a rotation around the y axis, too):

    glTranslatef(zagx,zagy, zagz);
    glRotatef(zagroty, 0.0, 1.0, 0.0); // heading
    glRotatef(zagrotx, 1.0, 0.0, 0.0); // pitch
    glRotatef(zagrotz, 0.0, 0.0, 1.0); // roll

Thats basically what T101 posted, too. Some people like to look at transformations as object movements instead of coordinate system changes, but then you have to reverse the transformations.

Overmind’s right.

The thing with OpenGL is that the model transformation (where in space is a certain point) is combined with the view transformation (where according to the eye is a certain point) into one matrix.

The “execute” order of a matrix is reversed, which is also why the model transformations have to be performed after the viewing transformations.

I know my angles - they work - but to apply them you have to "gl"perform the transformations in the reverse order.

You were all right. Not that this realisation soles my problem :slight_smile:

Now I understand, I rotate the Object Matrix, not the Object. The Problem is, the rotation of the Object Matrix is done in the Worldmatrix. That means when i rotate the obejctmatrix on the Worlds x-axis, the objectsmatrx z-axis is rotated. But when i follw this rotation with a rotation araound the z-axis, i rotate around the Objectmartix around the Worls z-axis, and not around the rotated z-axis of the objectmatrix.

Am i right, till now?

So how do I rotate the objectmatrix around her own axis?

I´m sorry for my poor capabilities in geometry, but school is a long way in the past.
Is there a watercooling for brains on the internet?

No, its exactly the other way round. Every rotation is relative to the Object coordinate system, NOT the World coordinate system…

Imagine you have a modelview matrix M, and two transforms A and B, and you apply them:

First you transform by A, with the current coordinate system being M (the world coordinate system). The result is M*A.

Now your current coordinate system is (MA), and B is applied to this new coordinate system, giving (MA)B. Thus B rotates around the axis in the MA (=transformed) coordinate system, not in the world coordinate system.

Just try the code I posted, it works as expected :wink:

@ overmind
I tried your code, and its not working as i expected.

I´m soory for my lack understanding.

I think i must have another problem in my code. But on the other hand, look at the teapott example. its rotating just as my program.

I realy have to thank you for your patience.

@overmind

Here is the programm:

 #include <math.h>
#include <stdlib.h>
#include <GLUT/glut.h>

float arc1=0, arc2=0;

void init(void)
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel (GL_FLAT);
}

void display(void)
{
   //arc1=arc1+.1;
   //arc2=arc2+.2;
   glClear (GL_COLOR_BUFFER_BIT);
   glColor3f (1.0, 1.0, 1.0);

   glLoadIdentity ();

   gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
   glScalef (1.0, 2.0, 1.0);
   glTranslatef (0.0, 0.0, 0.0);
  
   glRotatef (arc2, 0.0, 0.0, 1.0);
    glRotatef (arc1, 1.0, 0.0, 0.0);

           glutWireCube (2.0);
           glFlush ();
}
        void reshape (int w, int h)
{
   glViewport (0, 0, (GLsizei) w, (GLsizei) h);
           glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
   glMatrixMode (GL_MODELVIEW);
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27:
         exit(0);
         break;
   }
}

void presskey(int key, int x, int y)
{
    switch (key)
    {
        case GLUT_KEY_UP : arc1=arc1+.5;break;
        case GLUT_KEY_DOWN : arc1=arc1-.5;break;
        case GLUT_KEY_LEFT : arc2=arc2+.5;break;
        case GLUT_KEY_RIGHT : arc2=arc2-.5;break;
    }
}



int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize (500, 500);
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutDisplayFunc(display);
   glutIdleFunc(display);
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutSpecialFunc(presskey);
   glutMainLoop();
   return 0;
}

 

Ok, finally I understand your real problem :smiley:
Sorry for the confusion…

To solve your problem, you have to save the current rotation directly, for example as quaternion.

Quaternions are really easy to use. It’s just a bunch of formulas. Here are some functions:

float QuatX = 0, QuatY = 0, QuatZ = 0, QuatW = 1;

void multQuat(float x, float y, float z, float w)
{
  float W = QuatW * w - QuatX * x - QuatY * y - QuatZ * z;
  float X = QuatX * w + QuatW * x + QuatZ * y - QuatY * z;
  float Y = QuatY * w - QuatZ * x + QuatW * y + QuatX * z;
  float Z = QuatZ * w + QuatY * x - QuatX * y + QuatW * z;

  QuatW = W; QuatX = X; QuatY = Y; QuatZ = Z;
}

void normalizeQuat()
{
  float len = QuatX*QuatX + QuatY*QuatY + QuatZ*QuatZ + QuatW*QuatW;
  if (len != 1.0) {
    len = sqrt(len);
    QuatX /= len;
    QuatY /= len;
    QuatZ /= len;
    QuatW /= len;
  }
}

void quatToMatrix(float mat[16])
{
    float X = QuatX, Y = QuatY, Z = QuatZ, W = QuatW;
    float xx, xy, xz, xw, yy, yz, yw, zz, zw;
 
    xx      = X * X;
    xy      = X * Y;
    xz      = X * Z;
    xw      = X * W;
    yy      = Y * Y;
    yz      = Y * Z;
    yw      = Y * W;
    zz      = Z * Z;
    zw      = Z * W;
    mat[0]  = 1 - 2 * ( yy + zz );
    mat[1]  =     2 * ( xy - zw );
    mat[2]  =     2 * ( xz + yw );
    mat[4]  =     2 * ( xy + zw );
    mat[5]  = 1 - 2 * ( xx + zz );
    mat[6]  =     2 * ( yz - xw );
    mat[8]  =     2 * ( xz - yw );
    mat[9]  =     2 * ( yz + xw );
    mat[10] = 1 - 2 * ( xx + yy );
    mat[3]  = mat[7] = mat[11] = mat[12] = mat[13] = mat[14] = 0;
    mat[15] = 1;
}

QuatX, QuatY, QuatZ, QuatW are the variables storing the current rotation in the form of a quaternion.

multQuat multiplies another quaternion onto it. If you want to rotate your object phi radians around the axis (x,y,z), you have to call:
multQuat(xsin(phi/2), ysin(phi/2), z*sin(phi/2), cos(phi/2));

normalizeQuat normalizes the quaternion. Normally it should neven happen that the quaternion gets denormalized, but it will happen because of rounding errors. quatToMatrix converts the quaternion to a matrix that you can load with glMultMatrixf.

So your transformation code should look like this:

  glTranslatef (0.0, 0.0, 0.0);

  normalizeQuat();
  quatToMatrix(rot);
  glMultMatrixf(rot);

  glScalef (1.0, 2.0, 1.0);

(note I moved the scale after the rotation, too)

And your keyboard function should look like this:

    case GLUT_KEY_UP : multQuat(sin(.005), 0, 0, cos(.5));break;
    case GLUT_KEY_DOWN : multQuat(sin(-.005), 0, 0, cos(-.005));break;
    case GLUT_KEY_LEFT : multQuat(0, 0, sin(-.005), cos(-.005));break;
    case GLUT_KEY_RIGHT : multQuat(0, 0, sin(.005), cos(.005));break;

(don’t forget the sin and cos functions take radians as argument)

Thanx Overmind.

I read a few documents about quaternions, and when you forgett about the irrational numbers and the 4-dimensional sphere, it´s not so complicated.

I think it´s perfect for my use.