Fixed Pipeline Axis Relative Rotations

The self-contained GLUT demo below shows a way to implement OpenGL axis-relative rotations using fixed-function OpenGL. A simple robot model is interactively rotated around the screen axes (xyz) or its own axes (uvw) using the corresponding keys on the keyboard. Each key depression rotates the robot +5.0 or -5.0 degs (lower case or upper case letter). The number or order of rotations does not matter. The robot always rotates around the selected axis. Note that glRotate commands are not used in the Display function. glMultMatrixf(omat) is used instead, where ‘omat’ (the orientation matrix) is generated and altered prior to displaying the robot via the routines Screen_Orientation and Object_Orientation. omat is a global variable initialized to the identity matrix.


// -------------------------------------------------------------------------------------------------
// -------------   OpenGL Fixed Function Axis Relative Rotations - Carmine Feb. 2016   -------------

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

short org[3] = {0,0,0}, xup[3] = {1,0,0}, yup[3] = {0,1,0}, zup[3] = {0,0,1};

short kube[8][3] = {{-1, -1,  1}, { 1, -1,  1}, { 1, -1, -1}, {-1, -1, -1},
                    {-1,  1,  1}, { 1,  1,  1}, { 1,  1, -1}, {-1,  1, -1}};

GLfloat omat[16] = {1, 0, 0, 0,     // Orientation matrix initialized to identity.
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1};

float white[3] = {0.8, 0.9, 0.9}, orange[3] = {0.8, 0.4, 0.2};

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-----------------------------------   Object_Orientation   ---------------------------------------

//  Post-multiply orientation matrix by incremental rotation matrix.

void Object_Orientation (int a)
{
    glLoadMatrixf (omat);

    switch (a)  {
       case 0:  glLoadIdentity ();        break;
       case 1:  glRotatef ( 5.0, 1,0,0);  break;
       case 2:  glRotatef ( 5.0, 0,1,0);  break;
       case 3:  glRotatef ( 5.0, 0,0,1);  break;
       case 4:  glRotatef (-5.0, 1,0,0);  break;
       case 5:  glRotatef (-5.0, 0,1,0);  break;
       case 6:  glRotatef (-5.0, 0,0,1);  break;
    };

    glGetFloatv (GL_MODELVIEW_MATRIX, omat);     // Save new 'omat' for use in 'Display' function.
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-----------------------------------   Screen_Orientation   ---------------------------------------

//  Pre-multiply orientation matrix by incremental rotation matrix.

void Screen_Orientation (int a)
{
    glLoadIdentity ();

    switch (a)  {
       case 1:  glRotatef ( 5.0, 1,0,0);  break;
       case 2:  glRotatef ( 5.0, 0,1,0);  break;
       case 3:  glRotatef ( 5.0, 0,0,1);  break;
       case 4:  glRotatef (-5.0, 1,0,0);  break;
       case 5:  glRotatef (-5.0, 0,1,0);  break;
       case 6:  glRotatef (-5.0, 0,0,1);  break;
    };

    glMultMatrixf (omat);

    glGetFloatv (GL_MODELVIEW_MATRIX, omat);    // Save new 'omat' for use in 'Display' function.
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-----------------------------------------   Keybord   --------------------------------------------

void Keybord (unsigned char ch, int x, int y)
{
    switch (ch)  {
       case 'r' :  Object_Orientation (0);   break;
       case 'u' :  Object_Orientation (1);   break;
       case 'v' :  Object_Orientation (2);   break;
       case 'w' :  Object_Orientation (3);   break;
       case 'U' :  Object_Orientation (4);   break;
       case 'V' :  Object_Orientation (5);   break;
       case 'W' :  Object_Orientation (6);   break;

       case 'x' :  Screen_Orientation (1);   break;
       case 'y' :  Screen_Orientation (2);   break;
       case 'z' :  Screen_Orientation (3);   break;
       case 'X' :  Screen_Orientation (4);   break;
       case 'Y' :  Screen_Orientation (5);   break;
       case 'Z' :  Screen_Orientation (6);   break;

       case  27 :  exit (0);                 break;
    }

    glutPostRedisplay();
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//----------------------------------------   Wire_Box   --------------------------------------------

void Wire_Box (float kolor[3], float sx, float sy, float sz)
{
    int v;

    glColor3fv (kolor);

    glPushMatrix ();
       glScalef (sx, sy, sz);
       glBegin (GL_LINE_LOOP);  for (v = 0; v < 4; v++)  glVertex3sv (kube[v]);     glEnd ();
       glBegin (GL_LINE_LOOP);  for (v = 4; v < 8; v++)  glVertex3sv (kube[v]);     glEnd ();
       glBegin (GL_LINES    );  for (v = 0; v < 4; v++) {glVertex3sv (kube[v]);
                                                         glVertex3sv (kube[v+4]);}  glEnd ();
    glPopMatrix ();
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//---------------------------------------   Draw_Triad   -------------------------------------------

void Draw_Triad (float kolor[3], float siz, char a, char b, char c)
{
    glColor3fv (kolor);

    glPushMatrix ();
       glScalef (siz, siz, siz);

       glBegin (GL_LINES);
          glVertex3sv (org);  glVertex3sv (xup);
          glVertex3sv (org);  glVertex3sv (yup);
          glVertex3sv (org);  glVertex3sv (zup);
       glEnd ();

       glRasterPos3sv (xup);  glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, a);
       glRasterPos3sv (yup);  glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, b);
       glRasterPos3sv (zup);  glutBitmapCharacter (GLUT_BITMAP_HELVETICA_18, c);

    glPopMatrix ();
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//--------------------------------------   Draw_Carmine   ------------------------------------------

void Draw_Carmine (void)
{
    glPushMatrix ();
       glScalef (0.5, 0.5, 0.5);             // Fit Carmine fit into unit cube.

       Wire_Box  (orange, 0.5, 0.8, 0.3);    // Carmine's body

       glPushMatrix ();
          glTranslatef (0.3, -1.4, 0.0);     // Carmine's left leg
          Wire_Box (orange, 0.1, 0.6, 0.1);
       glPopMatrix ();

       glPushMatrix ();
          glTranslatef (-0.3, -1.4, 0.0);    // Carmine's right leg
          Wire_Box (orange, 0.1, 0.6, 0.1);
       glPopMatrix ();

       glPushMatrix ();
          glTranslatef (0.0, 1.0, 0.0);      // Carmine's head
          Wire_Box (orange, 0.2, 0.2, 0.2);
       glPopMatrix ();

       glPushMatrix ();
          glTranslatef (0.9, 0.6, 0.0);      // Carmine's left arm
          Wire_Box (orange, 0.4, 0.1, 0.1);
       glPopMatrix ();

       glPushMatrix ();
          glTranslatef (-0.9, 0.6, 0.0);     // Carmine's right arm
          Wire_Box (orange, 0.4, 0.1, 0.1);
       glPopMatrix ();

    glPopMatrix ();
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-----------------------------------------   Display   --------------------------------------------

void Display (void)
{
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode   (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60.0, 1.5, 1.0, 10.0);

    glMatrixMode   (GL_MODELVIEW);
    glLoadIdentity ();

    glTranslatef  (0.0, 0.0, -3.5);             // Put objects between near and far clip planes.
    Draw_Triad    ( white, 1.5, 'X', 'Y', 'Z');
    glMultMatrixf (omat);
    Draw_Triad    (orange, 1.0, 'u', 'v', 'w');
    Draw_Carmine  ();

    glutSwapBuffers();
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//-----------------------------------------   Init_GL   --------------------------------------------

int Init_GL (void)
{
    glLineWidth (2.0);

    glEnable    (GL_BLEND);
    glEnable    (GL_LINE_SMOOTH);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glClearColor (0.2, 0.3, 0.3, 1.0);
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//----------------------------------------   Init_GLUT   -------------------------------------------

int Init_GLUT (void)
{
    glutInitDisplayMode    (GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowPosition (200, 200);
    glutInitWindowSize     (900, 600);
    glutCreateWindow       ("Axis Relative Rotations - Carmine");

    glutKeyboardFunc (Keybord);
    glutDisplayFunc  (Display);
}

//--+----4----+----3----+----2----+----1----+----|----+----1----+----2----+----3----+----4----+----5
//------------------------------------------   main   ----------------------------------------------

int main (int argc, char **argv)
{
    glutInit (&argc, argv);
    Init_GLUT ();
    Init_GL ();
    glutMainLoop ();
    return (1);
}

// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------

This animation demonstrates the code in action.
The rotations are driven by pressing the keys on my keyboard.
The order of rotations are: +x, -y, +v, -u, and +y.