Model Matrix Stack

Imagine a more complex scene in which many objects are moving, each according to its own rules. Some of these objects may move together as a group, which in turn may be a member of another group, thus forming a hierarchical tree of objects. For example, a walking man's arms and hands swing as groups. The hands, arms, and the rest of the body, in turn, form another major group. We don't want the hands to be detached from the arms and we don't want the arms to be detached from the body.

As another example, satellites rotate around planets, which in turn revolve around the sun. In this case, a planet and its satellites form a group. It is always easier to determine the location of the moon if we can somehow remember the position of the Earth and then use the relative information, instead of re-calculating everything from scratch.

The key to properly drawing a hierarchy of objects lies in implementing a stack of model matrices. Everytime before we progress to a deeper level of the hierarchy, we push the current model matrix to a stack. To backtrack, we pop and recall the model matrix. The last pushed matrix is always retrieved first. Conveniently, JavaScript arrays can be manipulated as a stack using push() and pop().

If we are to animate the solar system, the code should resemble the following.
//……
var matrixStack = [];
function pushMatrix(m) {
  var m2 = new Matrix4(m);
  matrixStack.push(m2);
}
function popMatrix() {
  return matrixStack.pop();
}
var modelMatrix = new Matrix4();
function drawSolarSystem(){
   draw(sun/* ... */);
   pushMairix(modelMatrix);
      modelMatrix.rotate(/* ... */);
      modelMatrix.translate(/* ... */);
      draw(planet1/* ... */);
      pushMatrix(modelMatrix);
         modelMatrix.rotate(/* ... */);
         modelMatrix.translate(/* ... */);
         draw(satellite1/* ... */);
      modelMatrix = popMatrix();
         pushMatrix(modelMatrix);
         modelMatrix.rotate(/* ... */);
         modelMatrix.translate(/* ... */);
         draw(satellite2/* ... */);
      modelMatrix = popMatrix();
      modelMatrix = popMatrix();
   pushMairix(modelMatrix);
      modelMatrix.rotate(/* ... */);
      modelMatrix.translate(/* ... */);
      draw(planet2/* ... */);
      pushMatrix(modelMatrix);
         modelMatrix.rotate(/* ... */);
         modelMatrix.translate(/* ... */);
         draw(satellite3/* ... */);
      modelMatrix = popMatrix();
         pushMatrix(modelMatrix);
         modelMatrix.rotate(/* ... */);
         modelMatrix.translate(/* ... */);
         draw(satellite4/* ... */);
      modelMatrix = popMatrix();
      modelMatrix = popMatrix();
   //……
}
//……

If the manipulation of a model matrix seems confusing, you may mentally visualize the three axes in a 3D space instead. A translation moves the origin of the three axes. A rotation rotates the three axes. A scaling shrinks or elongates an axis. Using these transformed axes, draw an object, or make a further axes transformation. Popping a model matrix stack will cause the last stored axes to be used.