MOTION AND ANIMATION

For your next assignment, due Wednesday March 11, you will make things animate In order to do this, you can use a number of different techniques:

Although the assignment is not due until March 11, you should get a jump on things by building the nest object structure now, according to the notes below. More notes will be forthcoming, as we started to discuss in class, on how to vary parameters over time to create animation structure over time.

A good way to do this is to create a hierarchy of matrix transformations in your scene, and one way to implement that hierarchy is by allowing each Geometry object to contain child objects - recursively. These notes will go over how to do that.

IMPLEMENTING THE TRANSFORMATION HIERARCHY

As we discussed in class, a convenient way to organize a hierarchical tree of matrix transformations is to define each Geometry object to have not only a Matrix but also to have a number of children objects. To implement this you can include the following fields in your Geometry object:


class Geometry
...
   Matrix4x4 matrix;
...
   int nChildren;
   Geometry children[];
...
To add a child, you probably want to add a method to the Geometry class that allocates a child object, adds it to the child array, and returns the newly created child object.

In this way, an entire scene can be organized into a tree structure, with a "world" object at the top, containing an identity matrix, to which all top level objects are added.



   Geometry add();
When it comes time to transform your objects in order to display them in the scene, you need to traverse this tree in such a way that child objects will be transformed relative to their parents. This means that, in general, the transformation applied to any object will be a product of transformations - a concatenation of that object's matrix with the matrix of its parent, grandparent, and so on, all the way up.

Practically speaking, a good way to implement this is to start a the very top of the tree, at the "world" object, which will generally have a transformation matrix set to identity, and render each object, by proceeding down the true, with the transformation of each object followed by the transformation of each of its children, recursively.

In other words, After you have computed the final transformation matrix of any object, then for each child of that object you need to postMultiply that transformation matrix by the child's relative transformation matrix, and so on recursively.

SKELETONS AND SHAPES

As we discussed in class, one thing that is potentially confusing is that this same hierarchy of nested objects with transformation matrices can be used both to create the skeleton of hierarchical movements (eg: shoulder, elbow, wrist), and also to help define the local transformations of shapes in that hierarchy (eg: the shape of a forearm).

Here is an example to help clarify these two uses. Let's assume you already have a human "body" object You can add the skeleton of an arm to that body with the following code:


      shoulder = body.add();
      elbow    = shoulder.add();
      wrist    = elbow.add();
You can then animate this skeleton by transforming each of these three objects at every every animation frame. For example, assuming you've defined dimensions for parts of a human arm, such as shoulder position, upperArm length, lowerArm length, and so forth:

      matrix = shoulder.getMatrix();
      matrix.identity();
      matrix.translate(shoulderX, shoulderY, shoulderZ);
      matrix.rotateX(shoulderForward);
      matrix.rotateZ(shoulderOutward);

      matrix = elbow.getMatrix();
      matrix.identity();
      matrix.translate(0, -upperArmLength, 0);
      matrix.rotateX(elbowForward);
      matrix.rotateZ(elbowOutward);

      matrix = wrist.getMatrix();
      matrix.identity();
      matrix.translate(0, -lowerArmLength, 0);
      matrix.rotateX(wristForward);
      matrix.rotateZ(wristOutward);

You can also add visible shapes to each of these skeletal parts using the same hierarchy structure. For example:


      upperArm = shoulder.add().globe(16,8);

      lowerArm = elbow.add().globe(16,8);

      hand     = wrist.add().globe(16,8);

Each shape is positioned with respect to the appropriate part of the skeleton:


      matrix = upperArm.getMatrix(); 
      matrix.identity();
      matrix.scale(upperArmThickness/2, upperArmLength/2, upperArmThickness/2);
      matrix.translate(0, -1, 0);

      matrix = lowerArm.getMatrix(); 
      matrix.identity();
      matrix.scale(lowerArmThickness/2, lowerArmLength/2, lowerArmThickness/2);
      matrix.translate(0, -1, 0);

      matrix = hand.getMatrix();
      matrix.identity();
      matrix.scale(handWidth/2, handLength/2, handThickness/2);
      matrix.translate(0, -1, 0);