are you ready for a mindfuck ? are you ready for a mindfuck?

....::::Menu::::....
...::About::...
...::Articles::...
...::Contact::...
...::Home & News::...
...::Links & Credits::... OpenGL logo
Valid XHTML 1.0!

Axis

by Elie De Brauwer

Goal of this file

This file is a first step into the third dimension. We create a X, Y and Z axis (both in a different color). We put the command to draw the coordinate system in a separate procedure so it can be included in future projects. This can be usefull for people with few 3d experience in order not to loose their orientation.

History of this file

  • 19 ocotber 2003: created

The concept and a first piece of code

Now how will we create such an axis ? Well we will first create three lines (or vectors):

  • (0.8,0.0,0.0): will represent the X-axis
  • (0.0,0.8,0.0): will represent the Y-axis
  • (0.0,0.0,0.8): will represent the Z-axis
By default the coordinate system of the OpenGL window goes from -1 to 1 in both the X and the Y direction (see also circle).
In code this will look like this:

#include <GL/glut.h>
#include <iostream>
using namespace std;

void disp(void);
void drawAxis(void);

int main(int argc,char **argv){

  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutCreateWindow("Axis1");
  glClearColor(0.0,0.0,0.0,0.0);
  glutDisplayFunc(disp);
  glutMainLoop();
}



void disp(void){
  glClear(GL_COLOR_BUFFER_BIT);
  drawAxis();
  glutSwapBuffers();
}


void drawAxis(){
  glBegin(GL_LINES);
  glColor3f(1.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.80,0.0,0.0);
  glColor3f(0.0,1.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.80,0.0);
  glColor3f(0.0,0.0,1.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.80);
  glEnd();
}

In essence this is nothing but drawing three colored lines in a double buffered window. In this program resize and keyboard handling are not implemented but I assume that if you need them you can find out how to do that by yourself. You can get this piece of code here. The result is shown in the following image:

snapshot 1

What a fraud is this, we are expecting three lines and we only got two. Yes I know this is because the blue line is perpendicular on the red and the green line thus also perpendicular on your screen. We might make things a bit more visual by defining a glutIdleFunc() that makes our object rotate around the (1,1,0) vector.

#include <GL/glut.h>
#include <iostream>
#include <cstdlib>
using namespace std;

typedef unsigned char uchar;

void disp(void);
void idle(void);
void drawAxis(void);
void keyb(uchar key, int x, int y);

static float angle=0;
int main(int argc,char **argv){

  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutCreateWindow("Axis2");
  glClearColor(0.0,0.0,0.0,0.0);
  glutDisplayFunc(disp);
  glutIdleFunc(idle);
  glutKeyboardFunc(keyb);
  glutMainLoop();
}


void disp(void){
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  glRotatef(angle,1.0,1.0,0.0);
  drawAxis();
  glutSwapBuffers();
}


void drawAxis(){
  /* unmodified */
}

void idle(void){
  angle++;
  while(angle>360){
    angle-=360;
  }
  glutPostRedisplay();
}

void keyb(uchar k, int x, int y){
  if(k == 's'){
    glutIdleFunc(NULL);
  }else if(k == 'S'){
    glutIdleFunc(idle);
  }else if(k == 'q'){
    exit(0);
  }
}

The code is also vailable here. Also some keyboard bindings were added to start and pause the rotation.
Why do we have a glLoadIdentity() call ? Well, a glLoadIdentity replaces the current matrix with the Identity matrix. This all makes sence if you know some maths but what if you do not know that much about mathematics ? A transformation (scale, translate, rotate, projection,...) is in fact nothing more than a matrix operation. When you do a call to glRotatef() the current matrix is multiplied with the transformationmatrix which means if you rotated the first time over 1 degree, the second time you will rotate over 3 (1+2) degrees and the third time over 6 (3+3) degrees. Try removing the glLoadIdentity() call and you will see that the rotation speed accelerates en diminishes. The glLoadIdentity simply starts with a new matrix so that each difference (in degrees is the same). This may look wierd to you know but wisdom will come over time.
Anyhow: a result may look like the following:

the second snapshot

And now the magic (introducing matrix stacks)

The length of our vectors is 0.8 this means we still have a space of 0.2 to add some eye candy. If we add a glutSolidCone with a height of 0.1 we can create an arrow alike look and it does not only look nice it also introduces some nice new topics like matrix stacks.
So we need three cones, if we look at the function prototype: void glutSolidCone(GLdouble base, GLdouble height, GLint slices, GLint stacks); we see that we can only define the radius of the base the height and the number of subdivisions along and around the Z-axis. This implies we have to rotate and move the cones into the right position. There are some things that you should know before doing this. First there is a huge difference between rotating first and translating afterwards and translating first and rotating afterwards. Second, things you do last you have to write first in OpenGL (confused yet ?). But the following example will make everything clear.

void disp(void){
  glClear(GL_COLOR_BUFFER_BIT);
  drawAxis();

  // store the position
  glPushMatrix(); 
  // rotate 45 degrees around the Z axis
  glRotatef(45,0.0,0.0,1.0);
  // move 0.5 in the positive X direction
  glTranslatef(0.5,0.0,0.0);

  // draw the white teapot.
  glColor3f(1.0,1.0,1.0);

  glutSolidTeapot(0.1);
  
  // return to the original position
  glPopMatrix();
  // save the original position
  glPushMatrix();

  // move 0.5 in the positive X direction
  glTranslatef(0.5,0.0,0.0);
  // rotate 45 degrees around the Z axis
  glRotatef(45,0.0,0.0,1.0);

  // make it a yellow one
  glColor3f(1.0,1.0,0.0);
  // this is a teapot which is FIRST rotated 
  // and AFTER that translated.
  glutSolidTeapot(0.1);

  glPopMatrix();
  glutSwapBuffers();
}


You can also get the source here and like we all know a picture sometimes says more than a thousand words:

snapshot3

In the previous example you also saw the usage of glPopMatrix() and glPushMatrix(). glPushMatrix saves the current location in the matrix stack and glPopMatrix() pops it from the matrix stack and returns to the last location. For example:

(current center is (0,0))
glPushMatrix();
glRotatef(45,0.0,0.0,1.0);
glTranslatef(1.0,0.0,0.0);
// we are currently at (cos(45),sin(45))
// we draw something here 
glPopMatrix();
// current center is (0,0)
// we store it again
glPushMatrix();
glRotatef(45,0.0,0.0,1.0);
glTranslatef(-1.0,0.0,0.0);
// we are currently at (-cos(45),-sin(45))
// we draw something here 
glPopMatrix();
// and now we are back at (0,0).
glPushMatrix();
glRotatef(45,0.0,0.0,1.0);
// everything you draw here will be 45 degrees rotated 
// around the Z axis
glPopMatrix();
// everything you draw here will not be rotated.

Putting it all together

In the code below you and on the first snapshot you can see how the cones are transformed to their place. You can also see that the first call to glRotatef() is carried the entire program, so the cones stay on their place. To undo this we could do a call to glLoadIdentity(), this is illustrated in the second snapshot if we do a glLoadIdentity() before we draw the cones we can let all the axis rotate but the cones will remain in place. Since this is done before the glPushMatrix() all of the cones remain in place if it were done after the glPushMatrix() only the red cone would remain where it was first drawn (see snapshot 6).

snapshot4 like it should be
a wrong glLoadIdentity() before glPushMatrix()
a wrong glLoadIdentity() after glPushMatrix()
#include <GL/glut.h>
#include <iostream>
#include <cstdlib>
using namespace std;

typedef unsigned char uchar;

void disp(void);
void idle(void);
void drawAxis(void);
void keyb(uchar key, int x, int y);

static float angle=0;
int main(int argc,char **argv){

  glutInit(&argc,argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutCreateWindow("Axis4");
  glClearColor(0.0,0.0,0.0,0.0);
  glutDisplayFunc(disp);
  glutIdleFunc(idle);
  glutKeyboardFunc(keyb);
  glutMainLoop();
}


void disp(void){
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  glRotatef(angle,1.0,1.0,0.0);

  drawAxis();
  glutSwapBuffers();
}


void drawAxis(){
  glBegin(GL_LINES);
  glColor3f(1.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.80,0.0,0.0);
  glColor3f(0.0,1.0,0.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.80,0.0);
  glColor3f(0.0,0.0,1.0);
  glVertex3f(0.0,0.0,0.0);
  glVertex3f(0.0,0.0,0.80);
  glEnd();

  //--------->  glLoadIdentity(); for snapshot 5

  glPushMatrix();
  //--------->  glLoadIdentity(); for snapshot 6
  glColor3f(1.0,0.0,0.0);
  glTranslatef(0.80,0.0,0.0);
  glRotatef(90,0.0,1.0,0.0);
  glutSolidCone(0.05,0.1,10,10);
  glPopMatrix();
  glPushMatrix();
  glColor3f(0.0,1.0,0.0);
  glTranslatef(0.0,0.80,0.0);
  glRotatef(-90,1.0,0.0,0.0);
  glutSolidCone(0.05,0.1,10,10);
  glPopMatrix();
  glPushMatrix();
  glColor3f(0.0,0.0,1.0);
  glTranslatef(0.0,0.0,0.80);
  glutSolidCone(0.05,0.1,10,10);
  glPopMatrix();
}


void idle(void){
  angle++;
  while(angle>360){
    angle-=360;
  }
  glutPostRedisplay();
}

void keyb(uchar k, int x, int y){
  if(k == 's'){
    glutIdleFunc(NULL);
  }else if(k == 'S'){
    glutIdleFunc(idle);
  }else if(k == 'q'){
    exit(0);
  }
}

You can download the code here