Search code examples
c++opengl3dquaternions

Turn a mesh based on its local coordinates


I have a plane in a 3D-World and its orientation is saved in any way (e.g. pitch, yaw and roll). Now when I want the plane to turn left, but glRotatef doesn't do the job as it sticks to global coordinates and does not care about the rotation of the plane, and simply changing the yaw doesn't help either as this is also not relative to the planes actual rotation and would only mean "left" when the plane flies straight to the horizon. What I would need would be like this:

    float pitch = 10 , yaw = 20, roll = 30;        //some initial values
    Turn(&pitch, &yaw, &roll ,  5, 0 , 0 )   //calculate new pitch, yaw and roll as if 
    //the plane had turned 5 degrees to the right (relative to its current orientation and roll!)
    //pitch, yaw and roll are updated to reflect the new orientation.

Many people suggest usage of Quaternions but I have no idea on how to implement them to a Turn function (one working example is Blitz3D, which has a "RotateEntity" function for global rotation like glRotatef and "TurnEntity" for rotation based on the orientation) I think the function internally works like this:

  • transform pitch, yaw, roll to a Quaternion like EulerToQuat in OpenGL + SDL rotation around local axis
  • perform the local rotation using Quaternion mathematics (no source found)
  • transform Quaternion back to yaw, roll, pitch (no source found)

Solution

  • I finally solved the problem by switching to carrying a matrix for every ship around all the time. Pitch, yaw and roll are only calculated when needed, which is rarely the case. Finally, glRotatef does the job - you only have to apply it to the already rotated matrix - and save the result, so that the change is not dropped.

    Following code is my implementation on a ship structure which carries x,y,z, Matrix[16], dx, dy, dz. (Note that all ship arrays have to be initialized with the IdentityMatrix):

    //*************************** Turn Ship **********************************
    void TurnShip(long nr,float yaw, float pitch=0,float roll=0) {
      //Turns ship by pitch, yaw and roll degrees.
      glMatrixMode(GL_MODELVIEW);
    
      glLoadMatrixf(&ship[nr].Matrix[0]); 
      glRotatef(yaw,0,1,0);  
      glGetFloatv(GL_MODELVIEW_MATRIX,&ship[nr].Matrix[0]);
    
      glLoadMatrixf(&ship[nr].Matrix[0]); 
      glRotatef(pitch,1,0,0); 
      glGetFloatv(GL_MODELVIEW_MATRIX,&ship[nr].Matrix[0]);
    
      glLoadMatrixf(&ship[nr].Matrix[0]); 
      glRotatef(roll,0,0,1); 
      glGetFloatv(GL_MODELVIEW_MATRIX,&ship[nr].Matrix[0]);  
    }
    

    What the function does is loading the ships matrix stored in a simple float array, manipulate it and then save it back to the array. In the graphics section of the game, this array will be loaded with glLoadMatrixf and thus will be applied to the ship without any hassle or mathematics.

    //*************************** Accelerate Ship relative to own orientation **
    void AccelerateShip(long nr,float speedx,float speedy, float speedz)   
    {   //use speedz to move object "forward".
    ship[nr].dx+= speedx*ship[nr].Matrix[0];   //accelerate sidewards (x-vector)
    ship[nr].dy+= speedx*ship[nr].Matrix[1];  //accelerate sidewards (x-vector)
    ship[nr].dz+= speedx*ship[nr].Matrix[2]; //accelerate sidewards (x-vector)
    
    ship[nr].dx+= speedy*ship[nr].Matrix[4];   //accelerate upwards (y-vector)
    ship[nr].dy+= speedy*ship[nr].Matrix[5];  //accelerate upwards (y-vector)
    ship[nr].dz+= speedy*ship[nr].Matrix[6]; //accelerate upwards (y-vector)
    
    ship[nr].dx+= speedz*ship[nr].Matrix[8];   //accelerate forward (z-vector)
    ship[nr].dy+= speedz*ship[nr].Matrix[9];  //accelerate forward (z-vector)
    ship[nr].dz+= speedz*ship[nr].Matrix[10]; //accelerate forward (z-vector)
    }  
    

    This is the best part of what I learned today - the part the tutorials often don't tell you as they are all about maths - I can pull the vectors that point up, left and in front of my ship right out of the matrix and apply acceleration upon them so that my ship can strave left,right,up down, accelerate and brake - and glRotatef cares for them so they are always updated and no maths involved at our side at all :-)