Lecture: Hierarchical Modelling in OpenGL

OpenGL has a modelview matrix stack which is used to support hierarchical modelling. The modelview matrix stack is at least 32 levels deep.

OpenGL also has a projection matrix stack, which is at least 2 levels deep, but is not needed for hierarchical modelling.

The matrix stacks may be pushed and popped using glPushMatrix() and glPopMatrix().

./images/matrix-stack.gif

A a hierarchical model may be thought of as a tree, and is typically traversed in a depth first manner.

Each time the traversal moves deeper into the hierarchy the transformation for the node currently being visited - and everything in the tree below that node - is added, by matrix postmultiplication, to the CTM, that is, the top of the matrix stack

By pushing the matrix stack before adding another transformation it is easy to restore the previous CTM by just popping the stack.

A Car Wheel Example

A simple example of hierarchical modelling is drawing a car.

To draw a car's four wheels, including a wheel's five bolts, we only have one wheel drawing function but we call it four times with different transformations (translations in this case) to position the wheel in the correct place.

Simlarly we only have one function to draw a bolt and call it five times for each wheel, after setting up the appropriate transformation.

The following program shows how to manipulate the matrix stack to draw a car in this manner:

draw_wheel_and_bolts() 
{    
  long i;
  draw_wheel();
  for(i=0;i<5;i++){
    glPushMatrix();
    glRotatef(72.0*i,0.0,0.0,1.0);
    glTranslatef(3.0,0.0,0.0);
    draw_bolt();
    glPopMatrix(); 
  }
} 

draw_body_and_wheel_and_bolts() {
  draw_car_body();
  glPushMatrix();  
  /*move to first wheel position*/
  glTranslatef(40,0,30);
  draw_wheel_and_bolts(); 
  glPopMatrix();
  glPushMatrix();
  /*move to 2nd wheel position*/
  glTranslatef(40,0,-30); 
  draw_wheel_and_bolts();
  glPopMatrix();
  /*draw last two wheel similarly*/
  ...
}
    

Solar System Planet Example

Another example is a solar system where planets revolve around the sun (and moons revolve around the planets).

./images/solar-system.svg

The OpenGL commands to draw the sun and planet are

glPushMatrix();
  /* draw sun */
  glutWireSphere(1.0, 20, 16);
  glRotatef ((GLfloat) year, 0.0, 1.0, 0.0);
  glTranslatef (2.0, 0.0, 0.0);
  glRotatef ((GLfloat) day, 0.0, 1.0, 0.0);
  /* draw small planet */
  glutWireSphere(0.2, 10, 8);
glPopMatrix();
    

The Robot Example

Articulated, hierarchical objects are objects which have rigid parts connected by joints.

People, legged animals and robots are examples of articulated objects.

A simple example is shown below &emdash; an arm of a robot. This is one of the classic "redbook" examples.

./images/robot-arm.gif

The source code for the robot arm robot.c

A slightly extended version robot-ext.c, which draws axes to show pivot points and effects of transformations, uses glut menus, and has mouse viewer-examiner controls.

Data Hierarchical Modelling/Scene Graph

The examples seen so far embed the model into the code, that is, in functions and data.

Using this approach it is possible to build a hierarchical model based on hierarchical function calls.

Another approach, common in computer graphics, is to use a hierarchical data structure --- a directed acyclic graph (DAG) or a tree. This data structure is often referred to as a scene graph

The data structure stores details of each object of the model such as:

To render the model the data structure is essentially interpreted by traversing it and executing the commands which change graphics state or context and drawing the primitives.

The simplest form of hierarchical data structure for this purpose is a binary tree.

The essence of a node structure for a tree is given in the definition below

struct object {

  float tx, ty, tz;  /* Translation relative to parent. */
  float rx, ry, rz;  /* Rotation relative to parent. */
  float sx, sy, sz;  /* Scaling relative to parent. */
  
  /* Other characteristics. For example, colour. */

  object_type type;  /* Type tag. */

  /* Tree pointers. */
  struct object *next_object;  /* Link to object at same level. */
  struct object *next_level;   /* Link to object at next level. */
}
    

The essence of the structure traversal routine is given below:

/* Render model by recursively traversing the tree (or DAG) structure. */
void render_object(struct object *curr_object)
{
  if (curr_object != NULL) {
    /* Push stack so we can come back. */
    glPushMatrix();

    /* Transformations for this object. */
    glTranslate(...);
    glScale(...);
    glRotate(...);

    /* Draw object. */
    switch (curr_object->type) {
    case box:
      draw_box( .....);
      break;
    /* Add other objects here */
    default:
      printf(``Error - unknown object type\n'');
      break;
    }
  
    /* Render next object lower in the hierarchy. */
    render_object(curr_object->next_level);

    /* Restore transformation matrix. */
    glPopMatrix();

    /* Render next object at same level in the hierarchy. */
    render_object(curr_object->next_object);
  }
}