Notes for Tuesday March 27:
Animating hierarchical models

 

Saving and restoring matrix values

The easiest way to implement hierarchical models is by using a matrix stack. When you want to do some transformations that you will then want to later forget, you push a copy of the current matrix value onto this stack.

Later, when you pop the stack, the original, unmodified version of the matrix will then be waiting on the top of the stack.

In my own code, I've implemented this via save() and restore() methods. Assume that you have an array of matrixValues[], and a stackPointer.

You initialize the stack by setting stackPointer = 0, and by setting matrixValues[stackPointer] to the Identity matrix.

All of your matrix operations, other than save() and restore(), will operate on the value on the top of the stack matrixValues[stackPointer].

Your save() method needs to do two things:

  1. Copy matrixValues[stackPointer] to matrixValues[stackPointer+1];
  2. Increment stackPointer.
Your restore() method just needs to do one thing: Decrement stackPointer.

In your update code, the calls to save() and restore() should always occur in matching pairs. Otherwise you will get either a stack overflow or a stack underflow.

Creating your own hierarchical animated model

Once you have implemented save() and restore(), you will have all of the matrix operations you need to do modeling and animation using your sphere, cylinder and cube shape primitives.

For guidance, at the bottom of these notes is the relevant part of the code for the walking figure that I implemented in class.

Note especially how sometimes I use matrix math to animate body parts over time, and sometimes I use the same matrix math to create shapes (such as the elongated ellipsoidal limb parts in my walking figure). You can do the same with your matrix operations.

Homework, due before class on Tuesday April 3

Choose some simple idea for an animated scene that inspires you. It can be an automobile, a robot, a waddling penguin, a dancing tree or flower, the sun rising over the ocean, a rocket ship, the Solar System, an electric motor, or anything else that interests you.

Make sure to create hierarchical scenes via your implementation of save() and restore().

Code exerpted from the walking figure we made in class

   function drawRod(length, radius, color) {
      m.save();
      m.scale(radius, radius, length);
      mSphere(); // note, this should just render a sphere, transformed by matrix m
      m.restore();
   }

   function drawLimb(ampl, phase, len, wristFwd) {
      var a = ampl * (.5 + .5 * sin(phase));
      var b = ampl * (.5 + .5 * cos(phase));
      m.save();
         m.rotateX(PI/2);

         m.rotateY(-a);
         m.translate(0,0,len/2);
         drawRod(len/2, .1);

         m.translate(0,0,len/2);
         m.rotateY(2 * b);
         m.translate(0,0,len/2);
         drawRod(len/2, .1);

         m.translate(0,0,len/2);
         m.rotateY(wristFwd - b);
         m.translate(0,0, .2 * len);
         drawRod(.2 * len, .1);
      m.restore();
   }

   function update(elapsed) {

      phase += PI * speed * elapsed;

      // DRAW LEGS

      m.save();
         m.translate(0,0,-hipWidth);
         drawLimb(-amplitude, phase, legLength, PI/2);
      m.restore();

      m.save();
         m.translate(0,0, hipWidth);
         drawLimb(-amplitude, phase + PI, legLength, PI/2);
      m.restore();

      // DRAW TORSO

      m.rotateZ(-PI/2 * pitch);
      m.translate(0, torsoLength/2, 0);

      m.save();
        m.scale(.1,torsoLength/2,.1);
        mSphere();
      m.restore();

      m.translate(0, torsoLength/2, 0);
      m.rotateZ( PI/2 * pitch);

      // DRAW ARMS

      m.save();
         m.translate(0,0, shoulderWidth);
         drawLimb(amplitude, -phase + PI, armLength, PI/6 + pitch*PI/3);
      m.restore();

      m.save();
         m.translate(0,0,-shoulderWidth);
         drawLimb(amplitude, -phase, armLength, PI/6 + pitch*PI/3);
      m.restore();
   }