CECS 361 Computer Graphics I

MVL Homepage Department of Computer Engineering and Computer Science

Assignment #6: Point and Polygon Animation

Polygon animation can be implemented using a sequence of geometric transformations applied to verticies. Use the four polygons (defining the dinosaur) from assignment #4 to implement the animation sequences specified below. Your polygons should be filled correctly. You can use the OpenGL filled polygon routines instead of the previous assignment.

A) Your Own Geometric Transformations

Write your own transformation subroutines to do translation, rotation, and scaling (i.e. my_translate( ), my_rotate( ), my_scale( ) ). Implement matrix multiplication in software, do not use GLMULT. Use the 's' key to repeat the animation and the 'ESC' key to quit from the program. You can use the right arrow key or the '1' (one-) key to step through each frame of the animation.

To enable use of the extended keyboard (arrow keys, function keys, etc.) you will need to define an additional glut function which can still use the same keyboard() callback function:

  glutKeyboardFunc(keyboard);
  glutSpecialFunc(keyboard);

  1. Translate all polygons so that the object moves as a whole to the locations: (50, 0), (50, 50), (0, 50), (0,0). Use 5 steps between each set of coordinates for the animation. The dinosaur should move east, north, west, finally south in a rectangular pattern; between each set of slides the dinosaur should NOT return back to the origin.
  2. Rotate polygon3 about pivot point (90, 50) by angle theta = 90 in steps of 5 degree increments, then back to theta = 0 degrees also in 5-degree decrements to achieve a swinging motion.
  3. Scale polygon4 about its centroid by the x-, y-scale factors (2, 1/3) and then by (1/2, 3) to undo the scaling in 10 steps in each case. Both the x- and y- scaling should be done simultaneously to create the animation. This should achieve a differential (non-uniform) expanding and contracting motion.
  4. Scale all polygons by a factor of 3 in ten steps. You should scale about a single fixed point such as the origin or the centroid of the object so that the entire object (ie all polygons) expand uniformly. Note that there will be translation side effects; you could also use fixed point scaling of each polygon about its centroid. This should achieve a uniform expanding and shrinking motion with tail fixed at origin.

B) OpenGL Transformations

Implement the transformations from the first part using the OpenGL geometric transformation routines.

User Interface Requirements

Use menus from the glut library for implementing the following choices:

EXTRA POINTS - Create a 3-D dinosaur and enable control of 3-D tranformations including translation, rotation, and scaling.

OpenGL Issues

You can use glIdleFunction( ) to specify the transformations to be done automatically without any user intervention. This function is called when there are no user events to process.

OpenGL correctly fills convex polygons. Non-convex polygons must be tessellated into convex sub-polygons in OpenGL. Use the following function to tessellate the polygons in the OpenGL part of the assignment (the compiler should not produce any warning messages since the callback functions are typecast to GLvoid pointer to a function. Note that typecasting of callback functions so that your code will run on both Windows and Unix is bit tricky (see Page 473 in the OpenGL Programming Guide or "Red Book"). In the straightforward (but inefficient) approach the nonConvexPolygon( ) function is called before drawing each polygon as shown in the following example that translates an object composed of four non-convex polygons.

  glTranslatef(pos_x, pos_y,0);
  glColor3f(0.0, 0.50, 0.0);
  nonConvexPolygon(poly1, num_of_vertices1);
  glColor3f(1.0, 1.0, 0.0);
  nonConvexPolygon(poly2, num_of_vertices2);
  glColor3f(0.67, 0.31, 0.0);
  nonConvexPolygon(poly3, num_of_vertices3);
  glColor3f(1.0, 0.0, 0.0);
  nonConvexPolygon(poly4, num_of_vertices4);


/*
 * This function will fill any simple polygon using OpenGL
 * tessellation functions.
 *
 * input: 
 *    vertices: 2-d array of floats containing polygon vertices
 *    num: number of vertices in the polygon
 */
void nonConvexPolygon( float vertices[][2], int num)
{
   GLUtriangulatorObj *tobj;   /* tessellation object */
   GLdouble v[3];              /* passed to gluTessVertex, prototype used 3d */
   int i;

   /* create a new tessellation object */
   tobj = gluNewTess();

   /* specify function callback for starting of a primitive */
   gluTessCallback(tobj, GLU_BEGIN, (GLvoid (*)())glBegin);

   /* specify function callback used for vertices */
   gluTessCallback(tobj, GLU_VERTEX, (GLvoid (*)())glVertex2fv);

   /* specify function callback for ending a primitive */
   gluTessCallback(tobj, GLU_END, glEnd);

   /* begin polygon vertices for tessellation */
   gluBeginPolygon(tobj);
   {
      for (i = 0; i < num; i++) {
         /* send vertex for tessellation, it expects 3d array of double */
         v[0] = vertices[i][0];
         v[1] = vertices[i][1];
         v[2] = 0.0;
         gluTessVertex(tobj, v, (void *)vertices[i]);
      }
   }
   /* end polygon vertices for tessellation */
   gluEndPolygon(tobj);

   /* delete the tessellation object */
   gluDeleteTess(tobj);
}

If you just use regular convex polygon drawing without tessellation as in the following sample code, then your dinosaur will be incorrectly drawn as shown in the figure below.

void ConvexPolygon(polygon_type polygon, int num_of_vertices) {
  int i;

  glBegin(GL_POLYGON);
    for (i = 0; i < num_of_vertices; i++) {
      glVertex2f( polygon[i][0], polygon[i][1] );
    }
  glEnd();
  }

The OpenGL 1.2 version uses a newer tessellation interface (see page 485 of Red Book). The following source code conforms to the new version.
void nonConvexPolygon(polygon_type polygon, int num_of_vertices) {
  GLUtesselator *tobj;   /* tessellation object */
  GLdouble v[3];              /* passed to gluTessVertex, prototype used 3d */
  int i;

  /* create a new tessellation object */
  tobj = gluNewTess();

  /* specify function callback for starting of a primitive */
  gluTessCallback(tobj, GLU_TESS_BEGIN, (GLvoid (*)())glBegin);

  /* specify function callback used for vertices */
  gluTessCallback(tobj, GLU_TESS_VERTEX, (GLvoid (*)())glVertex2fv);

  /* specify function callback for ending a primitive */
  gluTessCallback(tobj, GLU_TESS_END, (GLvoid (*)())glEnd);

  /* begin polygon vertices for tessellation */
  gluTessBeginPolygon(tobj, NULL);
  gluTessBeginContour(tobj);
  {
    for (i = 0; i < num_of_vertices; i++) {
      /* send vertex for tessellation, it expects 3d array of double */
      v[0] = polygon[i][0];
      v[1] = polygon[i][1];
      v[2] = 0.0;
      gluTessVertex(tobj, v, (void *)polygon[i]);
    }
  }
  /* end polygon vertices for tessellation */
  gluTessEndContour(tobj);
  gluTessEndPolygon(tobj);

  /* delete the tessellation object */
  gluDeleteTess(tobj);
}


What to hand in: