OpenGL - modeling, animation, interaction

Today we round out the set of OpenGL lectures, with examples of hierarchical modeling, animation, interaction. There is no 'handout' for this lecture.

Some doc. links

You can find documentation for the gl*, glu* and glut* calls here:
http://www.opengl.org/documentation/specs

Here's more, from Apple:
http://developer.apple.com/documentation/Darwin/Reference/ManPages

Here is a paper describing the thinking behind OpenGL:
http://www.sun.com/software/graphics/opengl/OpenGLdesign.pdf

Sample programs [cross-platform source, PC binaries]

Following are sources for several programs (plus PC executables).

Hierarchical modeling

robot.c - robot arm
robot.exe
view code

/*
 * Copyright (c) 1993-1997, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED 
 * Permission to use, copy, modify, and distribute this software for 
 * any purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright notice
 * and this permission notice appear in supporting documentation, and that 
 * the name of Silicon Graphics, Inc. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission. 
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
 * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
 * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
 * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
 * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
 * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * US Government Users Restricted Rights 
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
 * (c)(1)(ii) of the Rights in Technical Data and Computer Software
 * clause at DFARS 252.227-7013 and/or in similar or successor
 * clauses in the FAR or the DOD or NASA FAR Supplement.
 * Unpublished-- rights reserved under the copyright laws of the
 * United States.  Contractor/manufacturer is Silicon Graphics,
 * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
 *
 * OpenGL(R) is a registered trademark of Silicon Graphics, Inc.
 */

/*
 * robot.c
 * This program shows how to composite modeling transformations
 * to draw translated and rotated hierarchical models.
 * Interaction:  pressing the s and e keys (shoulder and elbow)
 * alters the rotation of the robot arm.
 */
#include "glut.h"
#include 

static int shoulder = 0, elbow = 0;

void init(void) 
{
  glClearColor (0.0, 0.0, 0.0, 0.0);
  glShadeModel (GL_FLAT);
}

void display(void)
{
   glClear (GL_COLOR_BUFFER_BIT);
   glPushMatrix();
   glTranslatef (-1.0, 0.0, 0.0);
   glRotatef ((GLfloat) shoulder, 0.0, 0.0, 1.0);
   glTranslatef (1.0, 0.0, 0.0);
   glPushMatrix();
   glScalef (2.0, 0.4, 1.0);
   glutWireCube (1.0);
   glPopMatrix();

   glTranslatef (1.0, 0.0, 0.0);
   glRotatef ((GLfloat) elbow, 0.0, 0.0, 1.0);
   glTranslatef (1.0, 0.0, 0.0);
   glPushMatrix();
   glScalef (2.0, 0.4, 1.0);
   glutWireCube (1.0);
   glPopMatrix();

   glPopMatrix();
   glutSwapBuffers();
}

void reshape (int w, int h)
{
   glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef (0.0, 0.0, -5.0);
}

/* ARGSUSED1 */
void keyboard (unsigned char key, int x, int y)
{
   switch (key) {
      case 's':
         shoulder = (shoulder + 5) % 360;
         glutPostRedisplay();
         break;
      case 'S':
         shoulder = (shoulder - 5) % 360;
         glutPostRedisplay();
         break;
      case 'e':
         elbow = (elbow + 5) % 360;
         glutPostRedisplay();
         break;
      case 'E':
         elbow = (elbow - 5) % 360;
         glutPostRedisplay();
         break;
      case 27:
         exit(0);
         break;
      default:
         break;
   }
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
   glutInitWindowSize (500, 500); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutDisplayFunc(display); 
   glutReshapeFunc(reshape);
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0;
}


Interaction

picksquare.c - 'picking' squares
picksquare.exe
view code

/*
 * Copyright (c) 1993-1997, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED 
 * Permission to use, copy, modify, and distribute this software for 
 * any purpose and without fee is hereby granted, provided that the above
 * copyright notice appear in all copies and that both the copyright notice
 * and this permission notice appear in supporting documentation, and that 
 * the name of Silicon Graphics, Inc. not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission. 
 *
 * THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
 * GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
 * SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY
 * KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION,
 * LOSS OF PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF
 * THIRD PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN
 * ADVISED OF THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
 * POSSESSION, USE OR PERFORMANCE OF THIS SOFTWARE.
 * 
 * US Government Users Restricted Rights 
 * Use, duplication, or disclosure by the Government is subject to
 * restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
 * (c)(1)(ii) of the Rights in Technical Data and Computer Software
 * clause at DFARS 252.227-7013 and/or in similar or successor
 * clauses in the FAR or the DOD or NASA FAR Supplement.
 * Unpublished-- rights reserved under the copyright laws of the
 * United States.  Contractor/manufacturer is Silicon Graphics,
 * Inc., 2011 N.  Shoreline Blvd., Mountain View, CA 94039-7311.
 *
 * OpenGL(R) is a registered trademark of Silicon Graphics, Inc.
 */

/*
 * picksquare.c
 * Use of multiple names and picking are demonstrated.  
 * A 3x3 grid of squares is drawn.  When the left mouse 
 * button is pressed, all squares under the cursor position 
 * have their color changed.
 */
#include 
#include 
#include "glut.h"

int board[3][3];   /*  amount of color for each square	*/

/*  Clear color value for every square on the board   */
void init(void)
{
   int i, j;
   for (i = 0; i < 3; i++) 
      for (j = 0; j < 3; j ++)
         board[i][j] = 0;
   glClearColor (0.0, 0.0, 0.0, 0.0);
}

/*  The nine squares are drawn.  In selection mode, each 
 *  square is given two names:  one for the row and the 
 *  other for the column on the grid.  The color of each 
 *  square is determined by its position on the grid, and 
 *  the value in the board[][] array.
 */
void drawSquares(GLenum mode)
{
   GLuint i, j;
   for (i = 0; i < 3; i++) {
      if (mode == GL_SELECT)
         glLoadName (i);
      for (j = 0; j < 3; j ++) {
         if (mode == GL_SELECT)
            glPushName (j);
         glColor3f ((GLfloat) i/3.0, (GLfloat) j/3.0, 
                    (GLfloat) board[i][j]/3.0);
         glRecti (i, j, i+1, j+1);
         if (mode == GL_SELECT)
            glPopName ();
      }
   }
}

/*  processHits prints out the contents of the 
 *  selection array.
 */
void processHits (GLint hits, GLuint buffer[])
{
   unsigned int i, j;
   GLuint ii, jj, names, *ptr;

   printf ("hits = %d\n", hits);
   ptr = (GLuint *) buffer;
   for (i = 0; i < hits; i++) {	/*  for each hit  */
      names = *ptr;
      printf (" number of names for this hit = %d\n", names); ptr++;
      printf("  z1 is %g;", (float) *ptr/0x7fffffff); ptr++;
      printf(" z2 is %g\n", (float) *ptr/0x7fffffff); ptr++;
      printf ("   names are ");
      for (j = 0; j < names; j++) { /*  for each name */
         printf ("%d ", *ptr);
         if (j == 0)  /*  set row and column  */
            ii = *ptr;
         else if (j == 1)
            jj = *ptr;
         ptr++;
      }
      printf ("\n");
      board[ii][jj] = (board[ii][jj] + 1) % 3;
   }
}

/*  pickSquares() sets up selection mode, name stack, 
 *  and projection matrix for picking.  Then the 
 *  objects are drawn.
 */
#define BUFSIZE 512

void pickSquares(int button, int state, int x, int y)
{
   GLuint selectBuf[BUFSIZE];
   GLint hits;
   GLint viewport[4];

   if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN)
      return;

   glGetIntegerv (GL_VIEWPORT, viewport);

   glSelectBuffer (BUFSIZE, selectBuf);
   (void) glRenderMode (GL_SELECT);

   glInitNames();
   glPushName(0);

   glMatrixMode (GL_PROJECTION);
   glPushMatrix ();
   glLoadIdentity ();
/*  create 5x5 pixel picking region near cursor location	*/
   gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y), 
                  5.0, 5.0, viewport);
   gluOrtho2D (0.0, 3.0, 0.0, 3.0);
   drawSquares (GL_SELECT);

   glMatrixMode (GL_PROJECTION);
   glPopMatrix ();
   glFlush ();

   hits = glRenderMode (GL_RENDER);
   processHits (hits, selectBuf);
   glutPostRedisplay();
} 

void display(void)
{
   glClear(GL_COLOR_BUFFER_BIT);
   drawSquares (GL_RENDER);
   glFlush();
}

void reshape(int w, int h)
{
   glViewport(0, 0, w, h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluOrtho2D (0.0, 3.0, 0.0, 3.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

/* ARGSUSED1 */
void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27:
         exit(0);
         break;
   }
}

/* Main Loop */
int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize (100, 100);
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   init ();
   glutReshapeFunc (reshape);
   glutDisplayFunc(display); 
   glutMouseFunc (pickSquares);
   glutKeyboardFunc (keyboard);
   glutMainLoop();
   return 0; 
}


glpuzzle.c - 'sliding' puzzle blocks
trackball.h - 'trackball' header
trackball.c - 'trackball' source
glpuzzle.exe
view code


/* glpuzzle - written by Kevin Smith (kpsmith@engr.sgi.com) */

#include 
#include 
#include 
#include 
#include 
#include 
#include "glut.h"
#include "trackball.h"

#define WIDTH 4
#define HEIGHT 5
#define PIECES 10
#define OFFSETX -2
#define OFFSETY -2.5
#define OFFSETZ -0.5

typedef unsigned char Config[HEIGHT][WIDTH];

struct puzzle {
  struct puzzle *backptr;
  struct puzzle *solnptr;
  Config pieces;
  struct puzzle *next;
  unsigned hashvalue;
};

#define HASHSIZE 10691

struct puzzlelist {
  struct puzzle *puzzle;
  struct puzzlelist *next;
};

static char convert[PIECES + 1] =
{0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 4};

static unsigned char colors[PIECES + 1][3] =
{
  {0, 0, 0},
  {255, 255, 127},
  {255, 255, 127},
  {255, 255, 127},
  {255, 255, 127},
  {255, 127, 255},
  {255, 127, 255},
  {255, 127, 255},
  {255, 127, 255},
  {255, 127, 127},
  {255, 255, 255},
};

void changeState(void);

static struct puzzle *hashtable[HASHSIZE];
static struct puzzle *startPuzzle;
static struct puzzlelist *puzzles;
static struct puzzlelist *lastentry;

int curX, curY, visible;

#define MOVE_SPEED 0.2
static unsigned char movingPiece;
static float move_x, move_y;
static float curquat[4];
static int doubleBuffer = 1;
static int depth = 1;

static char xsize[PIECES + 1] =
{0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2};
static char ysize[PIECES + 1] =
{0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2};
static float zsize[PIECES + 1] =
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.6};

static Config startConfig =
{
  {8, 10, 10, 7},
  {8, 10, 10, 7},
  {6, 9, 9, 5},
  {6, 4, 3, 5},
  {2, 0, 0, 1}
};

static Config thePuzzle =
{
  {8, 10, 10, 7},
  {8, 10, 10, 7},
  {6, 9, 9, 5},
  {6, 4, 3, 5},
  {2, 0, 0, 1}
};

static int xadds[4] =
{-1, 0, 1, 0};
static int yadds[4] =
{0, -1, 0, 1};

static int W = 400, H = 300;
static GLint viewport[4];

#define srandom srand
#define random() (rand() >> 2)

unsigned
hash(Config config)
{
  int i, j, value;

  value = 0;
  for (i = 0; i < HEIGHT; i++) {
    for (j = 0; j < WIDTH; j++) {
      value = value + convert[config[i][j]];
      value *= 6;
    }
  }
  return (value);
}

int
solution(Config config)
{
  if (config[4][1] == 10 && config[4][2] == 10)
    return (1);
  return (0);
}

float boxcoords[][3] =
{
  {0.2, 0.2, 0.9},
  {0.8, 0.2, 0.9},
  {0.8, 0.8, 0.9},
  {0.2, 0.8, 0.9},
  {0.2, 0.1, 0.8},
  {0.8, 0.1, 0.8},
  {0.9, 0.2, 0.8},
  {0.9, 0.8, 0.8},
  {0.8, 0.9, 0.8},
  {0.2, 0.9, 0.8},
  {0.1, 0.8, 0.8},
  {0.1, 0.2, 0.8},
  {0.2, 0.1, 0.2},
  {0.8, 0.1, 0.2},
  {0.9, 0.2, 0.2},
  {0.9, 0.8, 0.2},
  {0.8, 0.9, 0.2},
  {0.2, 0.9, 0.2},
  {0.1, 0.8, 0.2},
  {0.1, 0.2, 0.2},
  {0.2, 0.2, 0.1},
  {0.8, 0.2, 0.1},
  {0.8, 0.8, 0.1},
  {0.2, 0.8, 0.1},
};

float boxnormals[][3] =
{
  {0, 0, 1},            /* 0 */
  {0, 1, 0},
  {1, 0, 0},
  {0, 0, -1},
  {0, -1, 0},
  {-1, 0, 0},
  {0.7071, 0.7071, 0.0000},  /* 6 */
  {0.7071, -0.7071, 0.0000},
  {-0.7071, 0.7071, 0.0000},
  {-0.7071, -0.7071, 0.0000},
  {0.7071, 0.0000, 0.7071},  /* 10 */
  {0.7071, 0.0000, -0.7071},
  {-0.7071, 0.0000, 0.7071},
  {-0.7071, 0.0000, -0.7071},
  {0.0000, 0.7071, 0.7071},  /* 14 */
  {0.0000, 0.7071, -0.7071},
  {0.0000, -0.7071, 0.7071},
  {0.0000, -0.7071, -0.7071},
  {0.5774, 0.5774, 0.5774},  /* 18 */
  {0.5774, 0.5774, -0.5774},
  {0.5774, -0.5774, 0.5774},
  {0.5774, -0.5774, -0.5774},
  {-0.5774, 0.5774, 0.5774},
  {-0.5774, 0.5774, -0.5774},
  {-0.5774, -0.5774, 0.5774},
  {-0.5774, -0.5774, -0.5774},
};

int boxfaces[][4] =
{
  {0, 1, 2, 3},         /* 0 */
  {9, 8, 16, 17},
  {6, 14, 15, 7},
  {20, 23, 22, 21},
  {12, 13, 5, 4},
  {19, 11, 10, 18},
  {7, 15, 16, 8},       /* 6 */
  {13, 14, 6, 5},
  {18, 10, 9, 17},
  {19, 12, 4, 11},
  {1, 6, 7, 2},         /* 10 */
  {14, 21, 22, 15},
  {11, 0, 3, 10},
  {20, 19, 18, 23},
  {3, 2, 8, 9},         /* 14 */
  {17, 16, 22, 23},
  {4, 5, 1, 0},
  {20, 21, 13, 12},
  {2, 7, 8, -1},        /* 18 */
  {16, 15, 22, -1},
  {5, 6, 1, -1},
  {13, 21, 14, -1},
  {10, 3, 9, -1},
  {18, 17, 23, -1},
  {11, 4, 0, -1},
  {20, 12, 19, -1},
};

#define NBOXFACES (sizeof(boxfaces)/sizeof(boxfaces[0]))

/* Draw a box.  Bevel as desired. */
void
drawBox(int piece, float xoff, float yoff)
{
  int xlen, ylen;
  int i, k;
  float x, y, z;
  float zlen;
  float *v;

  xlen = xsize[piece];
  ylen = ysize[piece];
  zlen = zsize[piece];

  glColor3ubv(colors[piece]);
  glBegin(GL_QUADS);
  for (i = 0; i < 18; i++) {
    glNormal3fv(boxnormals[i]);
    for (k = 0; k < 4; k++) {
      if (boxfaces[i][k] == -1)
        continue;
      v = boxcoords[boxfaces[i][k]];
      x = v[0] + OFFSETX;
      if (v[0] > 0.5)
        x += xlen - 1;
      y = v[1] + OFFSETY;
      if (v[1] > 0.5)
        y += ylen - 1;
      z = v[2] + OFFSETZ;
      if (v[2] > 0.5)
        z += zlen - 1;
      glVertex3f(xoff + x, yoff + y, z);
    }
  }
  glEnd();
  glBegin(GL_TRIANGLES);
  for (i = 18; i < NBOXFACES; i++) {
    glNormal3fv(boxnormals[i]);
    for (k = 0; k < 3; k++) {
      if (boxfaces[i][k] == -1)
        continue;
      v = boxcoords[boxfaces[i][k]];
      x = v[0] + OFFSETX;
      if (v[0] > 0.5)
        x += xlen - 1;
      y = v[1] + OFFSETY;
      if (v[1] > 0.5)
        y += ylen - 1;
      z = v[2] + OFFSETZ;
      if (v[2] > 0.5)
        z += zlen - 1;
      glVertex3f(xoff + x, yoff + y, z);
    }
  }
  glEnd();
}

float containercoords[][3] =
{
  {-0.1, -0.1, 1.0},
  {-0.1, -0.1, -0.1},
  {4.1, -0.1, -0.1},
  {4.1, -0.1, 1.0},
  {1.0, -0.1, 0.6},     /* 4 */
  {3.0, -0.1, 0.6},
  {1.0, -0.1, 0.0},
  {3.0, -0.1, 0.0},
  {1.0, 0.0, 0.0},      /* 8 */
  {3.0, 0.0, 0.0},
  {3.0, 0.0, 0.6},
  {1.0, 0.0, 0.6},
  {0.0, 0.0, 1.0},      /* 12 */
  {4.0, 0.0, 1.0},
  {4.0, 0.0, 0.0},
  {0.0, 0.0, 0.0},
  {0.0, 5.0, 0.0},      /* 16 */
  {0.0, 5.0, 1.0},
  {4.0, 5.0, 1.0},
  {4.0, 5.0, 0.0},
  {-0.1, 5.1, -0.1},    /* 20 */
  {4.1, 5.1, -0.1},
  {4.1, 5.1, 1.0},
  {-0.1, 5.1, 1.0},
};

float containernormals[][3] =
{
  {0, -1, 0},
  {0, -1, 0},
  {0, -1, 0},
  {0, -1, 0},
  {0, -1, 0},
  {0, 1, 0},
  {0, 1, 0},
  {0, 1, 0},
  {1, 0, 0},
  {1, 0, 0},
  {1, 0, 0},
  {-1, 0, 0},
  {-1, 0, 0},
  {-1, 0, 0},
  {0, 1, 0},
  {0, 0, -1},
  {0, 0, -1},
  {0, 0, 1},
  {0, 0, 1},
  {0, 0, 1},
  {0, 0, 1},
  {0, 0, 1},
  {0, 0, 1},
  {0, 0, 1},
};

int containerfaces[][4] =
{
  {1, 6, 4, 0},
  {0, 4, 5, 3},
  {1, 2, 7, 6},
  {7, 2, 3, 5},
  {16, 19, 18, 17},

  {23, 22, 21, 20},
  {12, 11, 8, 15},
  {10, 13, 14, 9},

  {15, 16, 17, 12},
  {2, 21, 22, 3},
  {6, 8, 11, 4},

  {1, 0, 23, 20},
  {14, 13, 18, 19},
  {9, 7, 5, 10},

  {12, 13, 10, 11},

  {1, 20, 21, 2},
  {4, 11, 10, 5},

  {15, 8, 19, 16},
  {19, 8, 9, 14},
  {8, 6, 7, 9},
  {0, 3, 13, 12},
  {13, 3, 22, 18},
  {18, 22, 23, 17},
  {17, 23, 0, 12},
};

#define NCONTFACES (sizeof(containerfaces)/sizeof(containerfaces[0]))

/* Draw the container */
void
drawContainer(void)
{
  int i, k;
  float *v;

  /* Y is reversed here because the model has it reversed */

  /* Arbitrary bright wood-like color */
  glColor3ub(209, 103, 23);
  glBegin(GL_QUADS);
  for (i = 0; i < NCONTFACES; i++) {
    v = containernormals[i];
    glNormal3f(v[0], -v[1], v[2]);
    for (k = 3; k >= 0; k--) {
      v = containercoords[containerfaces[i][k]];
      glVertex3f(v[0] + OFFSETX, -(v[1] + OFFSETY), v[2] + OFFSETZ);
    }
  }
  glEnd();
}

void
drawAll(void)
{
  int i, j;
  int piece;
  char done[PIECES + 1];
  float m[4][4];

  build_rotmatrix(m, curquat);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslatef(0, 0, -10);
  glMultMatrixf(&(m[0][0]));
  glRotatef(180, 0, 0, 1);

  if (depth) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  } else {
    glClear(GL_COLOR_BUFFER_BIT);
  }
  for (i = 1; i <= PIECES; i++) {
    done[i] = 0;
  }
  glLoadName(0);
  drawContainer();
  for (i = 0; i < HEIGHT; i++) {
    for (j = 0; j < WIDTH; j++) {
      piece = thePuzzle[i][j];
      if (piece == 0)
        continue;
      if (done[piece])
        continue;
      done[piece] = 1;
      glLoadName(piece);
      if (piece == movingPiece) {
        drawBox(piece, move_x, move_y);
      } else {
        drawBox(piece, j, i);
      }
    }
  }
}

void
redraw(void)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45, 1.0, 0.1, 100.0);

  drawAll();

  if (doubleBuffer)
    glutSwapBuffers();
  else
    glFinish();
}

void
solidifyChain(struct puzzle *puzzle)
{
  int i;
  char buf[256];

  i = 0;
  while (puzzle->backptr) {
    i++;
    puzzle->backptr->solnptr = puzzle;
    puzzle = puzzle->backptr;
  }
  sprintf(buf, "%d moves to complete!", i);
  glutSetWindowTitle(buf);
}

int
addConfig(Config config, struct puzzle *back)
{
  unsigned hashvalue;
  struct puzzle *newpiece;
  struct puzzlelist *newlistentry;

  hashvalue = hash(config);

  newpiece = hashtable[hashvalue % HASHSIZE];
  while (newpiece != NULL) {
    if (newpiece->hashvalue == hashvalue) {
      int i, j;

      for (i = 0; i < WIDTH; i++) {
        for (j = 0; j < HEIGHT; j++) {
          if (convert[config[j][i]] !=
            convert[newpiece->pieces[j][i]])
            goto nomatch;
        }
      }
      return 0;
    }
  nomatch:
    newpiece = newpiece->next;
  }

  newpiece = (struct puzzle *) malloc(sizeof(struct puzzle));
  newpiece->next = hashtable[hashvalue % HASHSIZE];
  newpiece->hashvalue = hashvalue;
  memcpy(newpiece->pieces, config, HEIGHT * WIDTH);
  newpiece->backptr = back;
  newpiece->solnptr = NULL;
  hashtable[hashvalue % HASHSIZE] = newpiece;

  newlistentry = (struct puzzlelist *) malloc(sizeof(struct puzzlelist));
  newlistentry->puzzle = newpiece;
  newlistentry->next = NULL;

  if (lastentry) {
    lastentry->next = newlistentry;
  } else {
    puzzles = newlistentry;
  }
  lastentry = newlistentry;

  if (back == NULL) {
    startPuzzle = newpiece;
  }
  if (solution(config)) {
    solidifyChain(newpiece);
    return 1;
  }
  return 0;
}

/* Checks if a space can move */
int
canmove0(Config pieces, int x, int y, int dir, Config newpieces)
{
  char piece;
  int xadd, yadd;
  int l, m;

  xadd = xadds[dir];
  yadd = yadds[dir];

  if (x + xadd < 0 || x + xadd >= WIDTH ||
    y + yadd < 0 || y + yadd >= HEIGHT)
    return 0;
  piece = pieces[y + yadd][x + xadd];
  if (piece == 0)
    return 0;
  memcpy(newpieces, pieces, HEIGHT * WIDTH);
  for (l = 0; l < WIDTH; l++) {
    for (m = 0; m < HEIGHT; m++) {
      if (newpieces[m][l] == piece)
        newpieces[m][l] = 0;
    }
  }
  xadd = -xadd;
  yadd = -yadd;
  for (l = 0; l < WIDTH; l++) {
    for (m = 0; m < HEIGHT; m++) {
      if (pieces[m][l] == piece) {
        int newx, newy;

        newx = l + xadd;
        newy = m + yadd;
        if (newx < 0 || newx >= WIDTH ||
          newy < 0 || newy >= HEIGHT)
          return 0;
        if (newpieces[newy][newx] != 0)
          return 0;
        newpieces[newy][newx] = piece;
      }
    }
  }
  return 1;
}

/* Checks if a piece can move */
int
canmove(Config pieces, int x, int y, int dir, Config newpieces)
{
  int xadd, yadd;

  xadd = xadds[dir];
  yadd = yadds[dir];

  if (x + xadd < 0 || x + xadd >= WIDTH ||
    y + yadd < 0 || y + yadd >= HEIGHT)
    return 0;
  if (pieces[y + yadd][x + xadd] == pieces[y][x]) {
    return canmove(pieces, x + xadd, y + yadd, dir, newpieces);
  }
  if (pieces[y + yadd][x + xadd] != 0)
    return 0;
  return canmove0(pieces, x + xadd, y + yadd, (dir + 2) % 4, newpieces);
}

int
generateNewConfigs(struct puzzle *puzzle)
{
  int i, j, k;
  Config pieces;
  Config newpieces;

  memcpy(pieces, puzzle->pieces, HEIGHT * WIDTH);
  for (i = 0; i < WIDTH; i++) {
    for (j = 0; j < HEIGHT; j++) {
      if (pieces[j][i] == 0) {
        for (k = 0; k < 4; k++) {
          if (canmove0(pieces, i, j, k, newpieces)) {
            if (addConfig(newpieces, puzzle))
              return 1;
          }
        }
      }
    }
  }
  return 0;
}

void
freeSolutions(void)
{
  struct puzzlelist *nextpuz;
  struct puzzle *puzzle, *next;
  int i;

  while (puzzles) {
    nextpuz = puzzles->next;
    free((char *) puzzles);
    puzzles = nextpuz;
  }
  lastentry = NULL;
  for (i = 0; i < HASHSIZE; i++) {
    puzzle = hashtable[i];
    hashtable[i] = NULL;
    while (puzzle) {
      next = puzzle->next;
      free((char *) puzzle);
      puzzle = next;
    }
  }
  startPuzzle = NULL;
}

int
continueSolving(void)
{
  struct puzzle *nextpuz;
  int i, j;
  int movedPiece;
  int movedir;
  int fromx, fromy;
  int tox, toy;

  if (startPuzzle == NULL)
    return 0;
  if (startPuzzle->solnptr == NULL) {
    freeSolutions();
    return 0;
  }
  nextpuz = startPuzzle->solnptr;
  movedPiece = 0;
  movedir = 0;
  for (i = 0; i < HEIGHT; i++) {
    for (j = 0; j < WIDTH; j++) {
      if (startPuzzle->pieces[i][j] != nextpuz->pieces[i][j]) {
        if (startPuzzle->pieces[i][j]) {
          movedPiece = startPuzzle->pieces[i][j];
          fromx = j;
          fromy = i;
          if (i < HEIGHT - 1 && nextpuz->pieces[i + 1][j] == movedPiece) {
            movedir = 3;
          } else {
            movedir = 2;
          }
          goto found_piece;
        } else {
          movedPiece = nextpuz->pieces[i][j];
          if (i < HEIGHT - 1 &&
            startPuzzle->pieces[i + 1][j] == movedPiece) {
            fromx = j;
            fromy = i + 1;
            movedir = 1;
          } else {
            fromx = j + 1;
            fromy = i;
            movedir = 0;
          }
          goto found_piece;
        }
      }
    }
  }
  glutSetWindowTitle("What!  No change?");
  freeSolutions();
  return 0;

found_piece:
  if (!movingPiece) {
    movingPiece = movedPiece;
    move_x = fromx;
    move_y = fromy;
  }
  move_x += xadds[movedir] * MOVE_SPEED;
  move_y += yadds[movedir] * MOVE_SPEED;

  tox = fromx + xadds[movedir];
  toy = fromy + yadds[movedir];

  if (move_x > tox - MOVE_SPEED / 2 && move_x < tox + MOVE_SPEED / 2 &&
    move_y > toy - MOVE_SPEED / 2 && move_y < toy + MOVE_SPEED / 2) {
    startPuzzle = nextpuz;
    movingPiece = 0;
  }
  memcpy(thePuzzle, startPuzzle->pieces, HEIGHT * WIDTH);
  changeState();
  return 1;
}

int
solvePuzzle(void)
{
  struct puzzlelist *nextpuz;
  char buf[256];
  int i;

  if (solution(thePuzzle)) {
    glutSetWindowTitle("Puzzle already solved!");
    return 0;
  }
  addConfig(thePuzzle, NULL);
  i = 0;

  while (puzzles) {
    i++;
    if (generateNewConfigs(puzzles->puzzle))
      break;
    nextpuz = puzzles->next;
    free((char *) puzzles);
    puzzles = nextpuz;
  }
  if (puzzles == NULL) {
    freeSolutions();
    sprintf(buf, "I can't solve it! (%d positions examined)", i);
    glutSetWindowTitle(buf);
    return 1;
  }
  return 1;
}

int
selectPiece(int mousex, int mousey)
{
  long hits;
  GLuint selectBuf[1024];
  GLuint closest;
  GLuint dist;

  glSelectBuffer(1024, selectBuf);
  (void) glRenderMode(GL_SELECT);
  glInitNames();

  /* Because LoadName() won't work with no names on the stack */
  glPushName(-1);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPickMatrix(mousex, H - mousey, 4, 4, viewport);
  gluPerspective(45, 1.0, 0.1, 100.0);

  drawAll();

  hits = glRenderMode(GL_RENDER);
  if (hits <= 0) {
    return 0;
  }
  closest = 0;
  dist = 4294967295U;
  while (hits) {
    if (selectBuf[(hits - 1) * 4 + 1] < dist) {
      dist = selectBuf[(hits - 1) * 4 + 1];
      closest = selectBuf[(hits - 1) * 4 + 3];
    }
    hits--;
  }
  return closest;
}

void
nukePiece(int piece)
{
  int i, j;

  for (i = 0; i < HEIGHT; i++) {
    for (j = 0; j < WIDTH; j++) {
      if (thePuzzle[i][j] == piece) {
        thePuzzle[i][j] = 0;
      }
    }
  }
}

void
multMatrices(const GLfloat a[16], const GLfloat b[16], GLfloat r[16])
{
  int i, j;

  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      r[i * 4 + j] =
        a[i * 4 + 0] * b[0 * 4 + j] +
        a[i * 4 + 1] * b[1 * 4 + j] +
        a[i * 4 + 2] * b[2 * 4 + j] +
        a[i * 4 + 3] * b[3 * 4 + j];
    }
  }
}

void
makeIdentity(GLfloat m[16])
{
  m[0 + 4 * 0] = 1;
  m[0 + 4 * 1] = 0;
  m[0 + 4 * 2] = 0;
  m[0 + 4 * 3] = 0;
  m[1 + 4 * 0] = 0;
  m[1 + 4 * 1] = 1;
  m[1 + 4 * 2] = 0;
  m[1 + 4 * 3] = 0;
  m[2 + 4 * 0] = 0;
  m[2 + 4 * 1] = 0;
  m[2 + 4 * 2] = 1;
  m[2 + 4 * 3] = 0;
  m[3 + 4 * 0] = 0;
  m[3 + 4 * 1] = 0;
  m[3 + 4 * 2] = 0;
  m[3 + 4 * 3] = 1;
}

/*
   ** inverse = invert(src)
 */
int
invertMatrix(const GLfloat src[16], GLfloat inverse[16])
{
  int i, j, k, swap;
  double t;
  GLfloat temp[4][4];

  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      temp[i][j] = src[i * 4 + j];
    }
  }
  makeIdentity(inverse);

  for (i = 0; i < 4; i++) {
    /* 
       ** Look for largest element in column */
    swap = i;
    for (j = i + 1; j < 4; j++) {
      if (fabs(temp[j][i]) > fabs(temp[i][i])) {
        swap = j;
      }
    }

    if (swap != i) {
      /* 
         ** Swap rows. */
      for (k = 0; k < 4; k++) {
        t = temp[i][k];
        temp[i][k] = temp[swap][k];
        temp[swap][k] = t;

        t = inverse[i * 4 + k];
        inverse[i * 4 + k] = inverse[swap * 4 + k];
        inverse[swap * 4 + k] = t;
      }
    }
    if (temp[i][i] == 0) {
      /* 
         ** No non-zero pivot.  The matrix is singular, which
         shouldn't ** happen.  This means the user gave us a
         bad matrix. */
      return 0;
    }
    t = temp[i][i];
    for (k = 0; k < 4; k++) {
      temp[i][k] /= t;
      inverse[i * 4 + k] /= t;
    }
    for (j = 0; j < 4; j++) {
      if (j != i) {
        t = temp[j][i];
        for (k = 0; k < 4; k++) {
          temp[j][k] -= temp[i][k] * t;
          inverse[j * 4 + k] -= inverse[i * 4 + k] * t;
        }
      }
    }
  }
  return 1;
}

/*
   ** This is a screwball function.  What it does is the following:
   ** Given screen x and y coordinates, compute the corresponding object space 
   **   x and y coordinates given that the object space z is 0.9 + OFFSETZ.
   ** Since the tops of (most) pieces are at z = 0.9 + OFFSETZ, we use that 
   **   number.
 */
int
computeCoords(int piece, int mousex, int mousey,
  GLfloat * selx, GLfloat * sely)
{
  GLfloat modelMatrix[16];
  GLfloat projMatrix[16];
  GLfloat finalMatrix[16];
  GLfloat in[4];
  GLfloat a, b, c, d;
  GLfloat top, bot;
  GLfloat z;
  GLfloat w;
  GLfloat height;

  if (piece == 0)
    return 0;
  height = zsize[piece] - 0.1 + OFFSETZ;

  glGetFloatv(GL_PROJECTION_MATRIX, projMatrix);
  glGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix);
  multMatrices(modelMatrix, projMatrix, finalMatrix);
  if (!invertMatrix(finalMatrix, finalMatrix))
    return 0;

  in[0] = (2.0 * (mousex - viewport[0]) / viewport[2]) - 1;
  in[1] = (2.0 * ((H - mousey) - viewport[1]) / viewport[3]) - 1;

  a = in[0] * finalMatrix[0 * 4 + 2] +
    in[1] * finalMatrix[1 * 4 + 2] +
    finalMatrix[3 * 4 + 2];
  b = finalMatrix[2 * 4 + 2];
  c = in[0] * finalMatrix[0 * 4 + 3] +
    in[1] * finalMatrix[1 * 4 + 3] +
    finalMatrix[3 * 4 + 3];
  d = finalMatrix[2 * 4 + 3];

  /* 
     ** Ok, now we need to solve for z: **   (a + b z) / (c + d 

     z) = height. ** ("height" is the height in object space we 

     want to solve z for) ** ** ==>  a + b z = height c +
     height d z **      bz - height d z = height c - a ** z =
     (height c - a) / (b - height d) */
  top = height * c - a;
  bot = b - height * d;
  if (bot == 0.0)
    return 0;

  z = top / bot;

  /* 
     ** Ok, no problem. ** Now we solve for x and y.  We know
     that w = c + d z, so we compute it. */
  w = c + d * z;

  /* 
     ** Now for x and y: */
  *selx = (in[0] * finalMatrix[0 * 4 + 0] +
    in[1] * finalMatrix[1 * 4 + 0] +
    z * finalMatrix[2 * 4 + 0] +
    finalMatrix[3 * 4 + 0]) / w - OFFSETX;
  *sely = (in[0] * finalMatrix[0 * 4 + 1] +
    in[1] * finalMatrix[1 * 4 + 1] +
    z * finalMatrix[2 * 4 + 1] +
    finalMatrix[3 * 4 + 1]) / w - OFFSETY;
  return 1;
}

static int selected;
static int selectx, selecty;
static float selstartx, selstarty;

void
grabPiece(int piece, float selx, float sely)
{
  int hit;

  selectx = selx;
  selecty = sely;
  if (selectx < 0 || selecty < 0 || selectx >= WIDTH || selecty >= HEIGHT) {
    return;
  }
  hit = thePuzzle[selecty][selectx];
  if (hit != piece)
    return;
  if (hit) {
    movingPiece = hit;
    while (selectx > 0 && thePuzzle[selecty][selectx - 1] == movingPiece) {
      selectx--;
    }
    while (selecty > 0 && thePuzzle[selecty - 1][selectx] == movingPiece) {
      selecty--;
    }
    move_x = selectx;
    move_y = selecty;
    selected = 1;
    selstartx = selx;
    selstarty = sely;
  } else {
    selected = 0;
  }
  changeState();
}

void
moveSelection(float selx, float sely)
{
  float deltax, deltay;
  int dir;
  Config newpieces;

  if (!selected)
    return;
  deltax = selx - selstartx;
  deltay = sely - selstarty;

  if (fabs(deltax) > fabs(deltay)) {
    deltay = 0;
    if (deltax > 0) {
      if (deltax > 1)
        deltax = 1;
      dir = 2;
    } else {
      if (deltax < -1)
        deltax = -1;
      dir = 0;
    }
  } else {
    deltax = 0;
    if (deltay > 0) {
      if (deltay > 1)
        deltay = 1;
      dir = 3;
    } else {
      if (deltay < -1)
        deltay = -1;
      dir = 1;
    }
  }
  if (canmove(thePuzzle, selectx, selecty, dir, newpieces)) {
    move_x = deltax + selectx;
    move_y = deltay + selecty;
    if (deltax > 0.5) {
      memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
      selectx++;
      selstartx++;
    } else if (deltax < -0.5) {
      memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
      selectx--;
      selstartx--;
    } else if (deltay > 0.5) {
      memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
      selecty++;
      selstarty++;
    } else if (deltay < -0.5) {
      memcpy(thePuzzle, newpieces, HEIGHT * WIDTH);
      selecty--;
      selstarty--;
    }
  } else {
    if (deltay > 0 && thePuzzle[selecty][selectx] == 10 &&
      selectx == 1 && selecty == 3) {
      /* Allow visual movement of solution piece outside of the 

         box */
      move_x = selectx;
      move_y = sely - selstarty + selecty;
    } else {
      move_x = selectx;
      move_y = selecty;
    }
  }
}

void
dropSelection(void)
{
  if (!selected)
    return;
  movingPiece = 0;
  selected = 0;
  changeState();
}

static int left_mouse, middle_mouse;
static int mousex, mousey;
static int solving;
static int spinning;
static float lastquat[4];
static int sel_piece;

static void
Reshape(int width, int height)
{

  W = width;
  H = height;
  glViewport(0, 0, W, H);
  glGetIntegerv(GL_VIEWPORT, viewport);
}

void
toggleSolve(void)
{
    if (solving) {
      freeSolutions();
      solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
      glutSetWindowTitle("glpuzzle");
      movingPiece = 0;
    } else {
      glutChangeToMenuEntry(1, "Stop solving", 1);
      glutSetWindowTitle("Solving...");
      if (solvePuzzle()) {
        solving = 1;
      }
    }
    changeState();
    glutPostRedisplay();
}

void reset(void)
{
    if (solving) {
      freeSolutions();
      solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
      movingPiece = 0;
      changeState();
    }
    glutSetWindowTitle("glpuzzle");
    memcpy(thePuzzle, startConfig, HEIGHT * WIDTH);
    glutPostRedisplay();
}

void
keyboard(unsigned char c, int x, int y)
{
  int piece;

  switch (c) {
  case 27:
    exit(0);
    break;
  case 'D':
  case 'd':
    if (solving) {
      freeSolutions();
      solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
      glutSetWindowTitle("glpuzzle");
      movingPiece = 0;
      changeState();
    }
    piece = selectPiece(x, y);
    if (piece) {
      nukePiece(piece);
    }
    glutPostRedisplay();
    break;
  case 'R':
  case 'r':
    reset();
    break;
  case 'S':
  case 's':
    toggleSolve();
    break;
  case 'b':
  case 'B':
    depth = 1 - depth;
    if (depth) {
      glEnable(GL_DEPTH_TEST);
    } else {
      glDisable(GL_DEPTH_TEST);
    }
    glutPostRedisplay();
    break;
  default:
    break;
  }
}

void
motion(int x, int y)
{
  float selx, sely;

  if (middle_mouse && !left_mouse) {
    if (mousex != x || mousey != y) {
      trackball(lastquat,
        (2.0*mousex - W) / W,
        (H - 2.0*mousey) / H,
        (2.0*x - W) / W,
        (H - 2.0*y) / H);
      spinning = 1;
    } else {
      spinning = 0;
    }
    changeState();
  } else {
    computeCoords(sel_piece, x, y, &selx, &sely);
    moveSelection(selx, sely);
  }
  mousex = x;
  mousey = y;
  glutPostRedisplay();
}

void
mouse(int b, int s, int x, int y)
{
  float selx, sely;

  mousex = x;
  mousey = y;
  curX = x;
  curY = y;
  if (s == GLUT_DOWN) {
    switch (b) {
    case GLUT_LEFT_BUTTON:
      if (solving) {
        freeSolutions();
        solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
        glutSetWindowTitle("glpuzzle");
        movingPiece = 0;
      }
      left_mouse = GL_TRUE;
      sel_piece = selectPiece(mousex, mousey);
      if (computeCoords(sel_piece, mousex, mousey, &selx, &sely)) {
        grabPiece(sel_piece, selx, sely);
      }
      glutPostRedisplay();
      break;
    case GLUT_MIDDLE_BUTTON:
      middle_mouse = GL_TRUE;
      glutPostRedisplay();
      break;
    }
  } else {
    switch (b) {
    case GLUT_LEFT_BUTTON:
      left_mouse = GL_FALSE;
      dropSelection();
      glutPostRedisplay();
      break;
    case GLUT_MIDDLE_BUTTON:
      middle_mouse = GL_FALSE;
      glutPostRedisplay();
      break;
    }
  }
  motion(x, y);
}

void
animate(void)
{
  if (spinning) {
    add_quats(lastquat, curquat, curquat);
  }
  glutPostRedisplay();
  if (solving) {
    if (!continueSolving()) {
      solving = 0;
      glutChangeToMenuEntry(1, "Solving", 1);
      glutSetWindowTitle("glpuzzle");
    }
  }
  if (!solving && !spinning && !visible) {
    glutIdleFunc(NULL);
  }
}

void
changeState(void)
{
  if (visible) {
    if (!solving && !spinning) {
      glutIdleFunc(NULL);
    } else {
      glutIdleFunc(animate);
    }
  } else {
    glutIdleFunc(NULL);
  }
}

void
init(void)
{
  static float lmodel_ambient[] =
  {0.0, 0.0, 0.0, 0.0};
  static float lmodel_twoside[] =
  {GL_FALSE};
  static float lmodel_local[] =
  {GL_FALSE};
  static float light0_ambient[] =
  {0.1, 0.1, 0.1, 1.0};
  static float light0_diffuse[] =
  {1.0, 1.0, 1.0, 0.0};
  static float light0_position[] =
  {0.8660254, 0.5, 1, 0};
  static float light0_specular[] =
  {0.0, 0.0, 0.0, 0.0};
  static float bevel_mat_ambient[] =
  {0.0, 0.0, 0.0, 1.0};
  static float bevel_mat_shininess[] =
  {40.0};
  static float bevel_mat_specular[] =
  {0.0, 0.0, 0.0, 0.0};
  static float bevel_mat_diffuse[] =
  {1.0, 0.0, 0.0, 0.0};

  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
  glEnable(GL_DEPTH_TEST);
  glClearDepth(1.0);

  glClearColor(0.5, 0.5, 0.5, 0.0);
  glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
  glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
  glEnable(GL_LIGHT0);

  glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, lmodel_local);
  glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
  glEnable(GL_LIGHTING);

  glMaterialfv(GL_FRONT, GL_AMBIENT, bevel_mat_ambient);
  glMaterialfv(GL_FRONT, GL_SHININESS, bevel_mat_shininess);
  glMaterialfv(GL_FRONT, GL_SPECULAR, bevel_mat_specular);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, bevel_mat_diffuse);

  glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
  glEnable(GL_COLOR_MATERIAL);
  glShadeModel(GL_FLAT);

  trackball(curquat, 0.0, 0.0, 0.0, 0.0);
  srandom(time(NULL));
}

static void
Usage(void)
{
  printf("Usage: puzzle [-s]\n");
  printf("   -s:  Run in single buffered mode\n");
  exit(-1);
}

void
visibility(int v)
{
  if (v == GLUT_VISIBLE) {
    visible = 1;
  } else {
    visible = 0;
  }
  changeState();
}

void
menu(int choice)
{
   switch(choice) {
   case 1:
      toggleSolve();
      break;
   case 2:
      reset();
      break;
   case 3:
      exit(0);
      break;
   }
}

int
main(int argc, char **argv)
{
  long i;

  glutInit(&argc, argv);
  for (i = 1; i < argc; i++) {
    if (argv[i][0] == '-') {
      switch (argv[i][1]) {
      case 's':
        doubleBuffer = 0;
        break;
      default:
        Usage();
      }
    } else {
      Usage();
    }
  }

  glutInitWindowSize(W, H);
  if (doubleBuffer) {
    glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
  } else {
    glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_SINGLE | GLUT_MULTISAMPLE);
  }

  glutCreateWindow("glpuzzle");

  init();

  glGetIntegerv(GL_VIEWPORT, viewport);

  printf("\n");
  printf("r   Reset puzzle\n");
  printf("s   Solve puzzle (may take a few seconds to compute)\n");
  printf("d   Destroy a piece - makes the puzzle easier\n");
  printf("b   Toggles the depth buffer on and off\n");
  printf("\n");
  printf("Left mouse moves pieces\n");
  printf("Middle mouse spins the puzzle\n");
  printf("Right mouse has menu\n");

  glutReshapeFunc(Reshape);
  glutDisplayFunc(redraw);
  glutKeyboardFunc(keyboard);
  glutMotionFunc(motion);
  glutMouseFunc(mouse);
  glutVisibilityFunc(visibility);
  glutCreateMenu(menu);
  glutAddMenuEntry("Solve", 1);
  glutAddMenuEntry("Reset", 2);
  glutAddMenuEntry("Quit", 3);
  glutAttachMenu(GLUT_RIGHT_BUTTON);
  glutMainLoop();
  return 0;             /* ANSI C requires main to return int. */
}

More info. on selection/picking is here.


Animation

simpleanim.c - simple animation
simpleanim_doublebuffer.exe
simpleanim_singlebuffer.exe
view code


// code based on example from 
// http://www.site.uottawa.ca/~nbobic/csi4130/Animation.htm


/** This program demonstrates the use of GLUT to create a window
  * in which we will use OpenGL drawing commands and to add a
  * display callback function to this window. We will also show
  * the use of the idle callback function for animation
  */
/*@{*/
 
#include 
#include 
#include "glut.h"
 
 
/** the angle of rotation of our object.
  */
int angle = 0;
 
 
/** display callback function.
  * This function is called everytime a display event is generated.
  * It clears the screen and redraws the cube rotated by "angle" about
  * the y axis.
  */
void display (void)
{
  glClear (GL_COLOR_BUFFER_BIT);     /* clear the opengl area */
 
  glPushMatrix();
  glRotatef(-20,1,0,0);
  glRotatef(angle,0,1,0);             /* Do the rotation */
  glutWireCube(0.9);                  /* Draw the cube */
  glutSwapBuffers();
  glPopMatrix();
}
 
 
/** Animation function used as the idle callback function.
  * This function Increases the rotation angle and then calls for
  * another display event to be signalled.
  */
void animate (void)
{
  angle += 2;
  glutPostRedisplay();               /* Signal for display event */
 
}
 
 
/** main function.
  * This function initialises the GLUT graphics library, and creates a
  * window. It then enters the event loop waiting for display events.
  */
int main (int argc, char **argv)
{
  glutInit (&argc, argv);             /* Initialise glut */
  glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE);     /* Initialise the display mode */
 
  glutCreateWindow ("simple animation demo");         /* create your window */
 
  glutDisplayFunc(display);           /* Set up the display callback */

  /**********************/
  glutIdleFunc(animate);              /* Set up the animation function */
  /**********************/
  
  glutMainLoop ();                    /* Enter the event processing loop */
}
 
/*@}*/


animate.c - another simple animation
animate.exe
view code

/************************************************************
 Add Opengl32.lib glu32.lib glut32.lib to
   Project->Setting->Link->General->Object/Library Modules.
*************************************************************/

// Source: http://www.site.uottawa.ca/~nbobic/csi4130


#include "glut.h"
#include 


#define M_PI (4*atan(1))
/* animation step */
int step;


void circle( GLint res, GLfloat radius ){

	int i;

	glBegin(GL_POLYGON);
	for (i=0;i 1.0)
		glOrtho(0.0,100.0*ar,0.0,100.0,10.0,-10.0);
	else
		glOrtho(0.0,100.0,0.0,100.0/ar,10.0,-10.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}


void animate(int value){
	/* stop after 30 steps */
	if(value == 30)
	  {
	    // return;
	    value=0;
	    step=0;
	  }

	glutPostRedisplay();
	step++;
	/* call animate again in 200 mili-Secs */
	glutTimerFunc(200,animate,step);
}

void menuControl(int value){

	switch (value){
	case 1:
		/* call animate in 200 mili-Secs */
		glutTimerFunc(200,animate,step);
		break;
	case 2:
		step = 0;
		/* call animate in 200 mili-Secs */
		glutTimerFunc(200,animate,step);
		break;
	case 101:
		exit(1);
		break;
	}
}



int main(int argc,char **argv){

	glutInit(&argc,argv);

	/* window definition and creation */
	/* the viewport is the same as window coords */
	glutInitWindowSize(800,800);
	glutInitWindowPosition(50,50);
	glutCreateWindow("Animation: Flickering");

	/* the reshape function */
	glutReshapeFunc(reshapeWin);

	/* the display function */
  	glutDisplayFunc(displayWin);

	glutCreateMenu(menuControl);
	glutAddMenuEntry("Start",1);
	glutAddMenuEntry("Restart",2);
	glutAddMenuEntry("Exit",101);
	glutAttachMenu(GLUT_RIGHT_BUTTON);

	glutMainLoop();

	return 0;
}


gears.c - gears
gears.exe
view code


pendulum.c - pendulum
pendulum.exe
view code


origami.c - origami
origami.exe
view code


/* Copyright (c) Mark J. Kilgard, 1994. */

/* This program is freely distributable without licensing fees 
   and is provided without guarantee or warrantee expressed or 
   implied. This program is -not- in the public domain. */

#include 
#include 
#include 

#define GL_EXT_polygon_offset 0 /* be safe */

/* Uses EXT_polygon_offset extension if available to better
   render the fold outlines. */

#if GL_EXT_polygon_offset
int polygon_offset;
#endif

enum {
  FLAT,                 /* completely flat sheet of paper */
  FLAP1,                /* left flap being folded in */
  FLAP2,                /* right flap being folded int */
  CENTER2,              /* right side folded up at center */
  WING2,                /* right wing folded down */
  CENTER1,              /* left side folded up at center */
  WING1,                /* left wing folded down */
  FOLDED                /* fully folded paper airplane */
} States;

int motion = 1;
int spinning = 1;
int state = FLAT;
int click = 0;
int delay = 0;
int direction;
float flap1_angle = 0;
float flap2_angle = 0;
float center1_angle = 0;
float center2_angle = 0;
float wing1_angle = 0;
float wing2_angle = 0;

/**

These correspond to the polygons for the paper sections:

  +----------+----------+
  |         /|\         |
  |  2     / | \    3   |
  |       /  |  \       |
  +------/   |   \------+
  |     /|   |   |\     |
  | 1  / |   |   | \ 4  |
  |   /  |   |   |  \   |
  |  /   |   |   |   \  |
  | /    | 5 | 6 |    \ |
  |/     |   |   |     \|
  +      |   |   |      +
  |  7   |   |   |  8   |
  |      |   |   |      |
  |      |   |   |      |
  |      |   |   |      |
  |      |   |   |      |
  |      |   |   |      |
  |      |   |   |      |
  +------+---+---+------+

*/

typedef GLfloat Point[2];

Point poly1[] =
{
  {-1, 0},
  {-1 / 3.0, 2 / 3.0},
  {-1, 2 / 3.0}
};

Point poly2[] =
{
  {-1, 1},
  {-1, 2 / 3.0},
  {-1 / 3.0, 2 / 3.0},
  {0, 1}
};

Point poly3[] =
{
  {0, 1},
  {1, 1},
  {1, 2 / 3.0},
  {1 / 3.0, 2 / 3.0}
};

Point poly4[] =
{
  {1 / 3.0, 2 / 3.0},
  {1, 2 / 3.0},
  {1, 0}
};

Point poly5[] =
{
  {-1 / 3.0, 2 / 3.0},
  {0, 1},
  {0, -1.5},
  {-1 / 3.0, -1.5}
};

Point poly6[] =
{
  {0, 1},
  {1 / 3.0, 2 / 3.0},
  {1 / 3.0, -1.5},
  {0, -1.5}
};

Point poly7[] =
{
  {-1, 0},
  {-1 / 3.0, 2 / 3.0},
  {-1 / 3.0, -1.5},
  {-1, -1.5}
};

Point poly8[] =
{
  {1, 0},
  {1 / 3.0, 2 / 3.0},
  {1 / 3.0, -1.5},
  {1, -1.5}
};

void
polydlist(int dlist, int num, Point points[])
{
  int i;

  glNewList(dlist, GL_COMPILE);
  glBegin(GL_POLYGON);
  for (i = 0; i < num; i++) {
    glVertex2fv(&points[i][0]);
  }
  glEnd();
  glEndList();
}

void
idle(void)
{
  if (spinning)
    click++;
  switch (state) {
  case FLAT:
    delay++;
    if (delay >= 80) {
      delay = 0;
      state = FLAP1;
      glutSetWindowTitle("origami (folding)");
      direction = 1;
    }
    break;
  case FLAP1:
    flap1_angle += 2 * direction;
    if (flap1_angle >= 180) {
      state = FLAP2;
    } else if (flap1_angle <= 0) {
      state = FLAT;
    }
    break;
  case FLAP2:
    flap2_angle += 2 * direction;
    if (flap2_angle >= 180) {
      state = CENTER2;
    } else if (flap2_angle <= 0) {
      state = FLAP1;
    }
    break;
  case CENTER2:
    center2_angle += 2 * direction;
    if (center2_angle >= 84) {
      state = WING2;
    } else if (center2_angle <= 0) {
      state = FLAP2;
    }
    break;
  case WING2:
    wing2_angle += 2 * direction;
    if (wing2_angle >= 84) {
      state = CENTER1;
    } else if (wing2_angle <= 0) {
      state = CENTER2;
    }
    break;
  case CENTER1:
    center1_angle += 2 * direction;
    if (center1_angle >= 84) {
      state = WING1;
    } else if (center1_angle <= 0) {
      state = WING2;
    }
    break;
  case WING1:
    wing1_angle += 2 * direction;
    if (wing1_angle >= 84) {
      state = FOLDED;
    } else if (wing1_angle <= 0) {
      state = CENTER1;
    }
    break;
  case FOLDED:
    delay++;
    if (delay >= 80) {
      delay = 0;
      glutSetWindowTitle("origami (unfolding)");
      direction = -1;
      state = WING1;
    }
    break;
  }
  glutPostRedisplay();
}

void
draw_folded_plane(void)
{
  /* *INDENT-OFF* */
  glPushMatrix();
    glRotatef(click, 0, 0, 1);
    glRotatef(click / 5.0, 0, 1, 0);
    glTranslatef(0, .25, 0);
    glPushMatrix();
      glRotatef(center1_angle, 0, 1, 0);
      glPushMatrix();
        glTranslatef(-.5, .5, 0);
        glRotatef(flap1_angle, 1, 1, 0);
        glTranslatef(.5, -.5, 0);
        glCallList(2);
      glPopMatrix();
      glCallList(5);

      glPushMatrix();
        glTranslatef(-1 / 3.0, 0, 0);
        glRotatef(-wing1_angle, 0, 1, 0);
        glTranslatef(1 / 3.0, 0, 0);

        glCallList(7);
        glPushMatrix();
          glTranslatef(-.5, .5, 0);
          glRotatef(flap1_angle, 1, 1, 0);
          glTranslatef(.5, -.5, 0);
          glCallList(1);
        glPopMatrix();
      glPopMatrix();
    glPopMatrix();

    glPushMatrix();
      glRotatef(-center2_angle, 0, 1, 0);
      glPushMatrix();
        glTranslatef(.5, .5, 0);
        glRotatef(-flap2_angle, -1, 1, 0);
        glTranslatef(-.5, -.5, 0);
        glCallList(3);
      glPopMatrix();
      glCallList(6);

      glPushMatrix();
        glTranslatef(1 / 3.0, 0, 0);
        glRotatef(wing2_angle, 0, 1, 0);
        glTranslatef(-1 / 3.0, 0, 0);

        glCallList(8);
        glPushMatrix();
          glTranslatef(.5, .5, 0);
          glRotatef(-flap2_angle, -1, 1, 0);
          glTranslatef(-.5, -.5, 0);
          glCallList(4);
        glPopMatrix();
      glPopMatrix();
    glPopMatrix();
  glPopMatrix();
  /* *INDENT-ON* */

}

void
display(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  glColor3ub(67, 205, 128);
#if GL_EXT_polygon_offset
  if (polygon_offset) {
    glPolygonOffsetEXT(0.5, 0.0);
    glEnable(GL_POLYGON_OFFSET_EXT);
  }
#endif
  draw_folded_plane();
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  glColor3ub(255, 255, 255);
#if GL_EXT_polygon_offset
  if (polygon_offset) {
    glPolygonOffsetEXT(0.0, 0.0);
    /* XXX a bug in the unpatched IRIX 5.3 OpenGL posts
       GL_INVALID_ENUM when GL_POLYGON_OFFSET_EXT is disabled;
       please ignore it. */
    glDisable(GL_POLYGON_OFFSET_EXT);
  } else {
    glPushMatrix();
    glTranslatef(0, 0, .05);
  }
#else
  glPushMatrix();
  glTranslatef(0, 0, .05);
#endif
  draw_folded_plane();
#if GL_EXT_polygon_offset
  if (!polygon_offset) {
    glPopMatrix();
  }
#else
  glPopMatrix();
#endif
  glutSwapBuffers();
}

void
visible(int state)
{
  if (state == GLUT_VISIBLE) {
    if (motion)
      glutIdleFunc(idle);
  } else {
    glutIdleFunc(NULL);
  }
}

void
menu(int value)
{
  switch (value) {
  case 1:
    direction = -direction;
    if (direction > 0) {
      glutSetWindowTitle("origami (folding)");
    } else {
      glutSetWindowTitle("origami (unfolding)");
    }
    break;
  case 2:
    motion = 1 - motion;
    if (motion) {
      glutIdleFunc(idle);
    } else {
      glutIdleFunc(NULL);
    }
    break;
  case 3:
    spinning = 1 - spinning;
    break;
  case 666:
    exit(0);
  }
}

int
main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
  glutCreateWindow("origami");
  glutDisplayFunc(display);
  glutVisibilityFunc(visible);
  glClearColor(.488, .617, .75, 1.0);
  glMatrixMode(GL_PROJECTION);
  gluPerspective(40.0, 1.0, 0.1, 10.0);
  glMatrixMode(GL_MODELVIEW);
  gluLookAt(0, 0, 5.5,
    0, 0, 0,
    0, 1, 0);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glLineWidth(2.0);
  polydlist(1, sizeof(poly1) / sizeof(Point), poly1);
  polydlist(2, sizeof(poly2) / sizeof(Point), poly2);
  polydlist(3, sizeof(poly3) / sizeof(Point), poly3);
  polydlist(4, sizeof(poly4) / sizeof(Point), poly4);
  polydlist(5, sizeof(poly5) / sizeof(Point), poly5);
  polydlist(6, sizeof(poly6) / sizeof(Point), poly6);
  polydlist(7, sizeof(poly7) / sizeof(Point), poly7);
  polydlist(8, sizeof(poly8) / sizeof(Point), poly8);
  glutCreateMenu(menu);
  glutAddMenuEntry("Reverse direction", 1);
  glutAddMenuEntry("Toggle motion", 2);
  glutAddMenuEntry("Toggle spinning", 3);
  glutAddMenuEntry("Quit", 666);
  glutAttachMenu(GLUT_RIGHT_BUTTON);
#if GL_EXT_polygon_offset
  polygon_offset = glutExtensionSupported("GL_EXT_polygon_offset");
#endif
  glutMainLoop();
  return 0;             /* ANSI C requires main to return int. */
}


noof.c - 'noof'!
noof.exe
view code


/* XXX Very crufty code follows. */

#include 
#include "glut.h"

#include 
#ifndef _WIN32
#include 
#else
#define random rand
#define srandom srand
#endif
/* Some  files do not define M_PI... */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#include 
#include 

/* For portability... */
#undef fcos
#undef fsin
#define fcos  cos
#define fsin  sin

/* --- shape parameters def'n --- */
#define N_SHAPES 7
float pos[N_SHAPES * 3];
float dir[N_SHAPES * 3];
float acc[N_SHAPES * 3];
float col[N_SHAPES * 3];
float hsv[N_SHAPES * 3];
float hpr[N_SHAPES * 3];
float ang[N_SHAPES];
float spn[N_SHAPES];
float sca[N_SHAPES];
float geep[N_SHAPES];
float peep[N_SHAPES];
float speedsq[N_SHAPES];
int blad[N_SHAPES];

float ht, wd;

void
initshapes(int i)
{
  int k;
  float f;

  /* random init of pos, dir, color */
  for (k = i * 3; k <= i * 3 + 2; k++) {
    f = random() / 2147483647.0;
    pos[k] = f;
    f = random() / 2147483647.0;
    f = (f - 0.5) * 0.05;
    dir[k] = f;
    f = random() / 2147483647.0;
    f = (f - 0.5) * 0.0002;
    acc[k] = f;
    f = random() / 2147483647.0;
    col[k] = f;
  }

  speedsq[i] = dir[i * 3] * dir[i * 3] + dir[i * 3 + 1] * dir[i * 3 + 1];
  f = random() / 2147483647.0;
  blad[i] = 2 + (int) (f * 17.0);
  f = random() / 2147483647.0;
  ang[i] = f;
  f = random() / 2147483647.0;
  spn[i] = (f - 0.5) * 40.0 / (10 + blad[i]);
  f = random() / 2147483647.0;
  sca[i] = (f * 0.1 + 0.08);
  dir[i * 3] *= sca[i];
  dir[i * 3 + 1] *= sca[i];

  f = random() / 2147483647.0;
  hsv[i * 3] = f * 360.0;

  f = random() / 2147483647.0;
  hsv[i * 3 + 1] = f * 0.6 + 0.4;

  f = random() / 2147483647.0;
  hsv[i * 3 + 2] = f * 0.7 + 0.3;

  f = random() / 2147483647.0;
  hpr[i * 3] = f * 0.005 * 360.0;
  f = random() / 2147483647.0;
  hpr[i * 3 + 1] = f * 0.03;
  f = random() / 2147483647.0;
  hpr[i * 3 + 2] = f * 0.02;

  geep[i] = 0;
  f = random() / 2147483647.0;
  peep[i] = 0.01 + f * 0.2;
}

int tko = 0;

float bladeratio[] =
{
  /* nblades = 2..7 */
  0.0, 0.0, 3.00000, 1.73205, 1.00000, 0.72654, 0.57735, 0.48157,
  /* 8..13 */
  0.41421, 0.36397, 0.19076, 0.29363, 0.26795, 0.24648,
  /* 14..19 */
  0.22824, 0.21256, 0.19891, 0.18693, 0.17633, 0.16687,
};

void
drawleaf(int l)
{

  int b, blades;
  float x, y;
  float wobble;

  blades = blad[l];

  y = 0.10 * fsin(geep[l] * M_PI / 180.0) + 0.099 * fsin(geep[l] * 5.12 * M_PI / 180.0);
  if (y < 0)
    y = -y;
  x = 0.15 * fcos(geep[l] * M_PI / 180.0) + 0.149 * fcos(geep[l] * 5.12 * M_PI / 180.0);
  if (x < 0.0)
    x = 0.0 - x;
  if (y < 0.001 && x > 0.000002 && ((tko & 0x1) == 0)) {
    initshapes(l);      /* let it become reborn as something
                           else */
    tko++;
    return;
  } {
    float w1 = fsin(geep[l] * 15.3 * M_PI / 180.0);
    wobble = 3.0 + 2.00 * fsin(geep[l] * 0.4 * M_PI / 180.0) + 3.94261 * w1;
  }

  /**
  if(blades == 2) if (y > 3.000*x) y = x*3.000;
  if(blades == 3) if (y > 1.732*x) y = x*1.732;
  if(blades == 4) if (y >       x) y = x;
  if(blades == 5) if (y > 0.726*x) y = x*0.726;
  if(blades == 6) if (y > 0.577*x) y = x*0.577;
  if(blades == 7) if (y > 0.481*x) y = x*0.481;
  if(blades == 8) if (y > 0.414*x) y = x*0.414;
  */
  if (y > x * bladeratio[blades])
    y = x * bladeratio[blades];

  for (b = 0; b < blades; b++) {
    glPushMatrix();
    glTranslatef(pos[l * 3], pos[l * 3 + 1], pos[l * 3 + 2]);
    glRotatef(ang[l] + b * (360.0 / blades), 0.0, 0.0, 1.0);
    glScalef(wobble * sca[l], wobble * sca[l], wobble * sca[l]);
    /**
    if(tko & 0x40000) glColor3f(col[l*3], col[l*3+1], col[l*3+2]); 
    else
    */
    glColor4ub(0, 0, 0, 0x60);

    /* constrain geep cooridinates here XXX */
    glEnable(GL_BLEND);

    glBegin(GL_TRIANGLE_STRIP);
    glVertex2f(x * sca[l], 0.0);
    glVertex2f(x, y);
    glVertex2f(x, -y);  /* C */
    glVertex2f(0.3, 0.0);  /* D */
    glEnd();

    /**
    if(tko++ & 0x40000) glColor3f(0,0,0);
    else
    */
    glColor3f(col[l * 3], col[l * 3 + 1], col[l * 3 + 2]);
    glBegin(GL_LINE_LOOP);
    glVertex2f(x * sca[l], 0.0);
    glVertex2f(x, y);
    glVertex2f(0.3, 0.0);  /* D */
    glVertex2f(x, -y);  /* C */
    glEnd();
    glDisable(GL_BLEND);

    glPopMatrix();
  }
}

void
motionUpdate(int t)
{
  if (pos[t * 3] < -sca[t] * wd && dir[t * 3] < 0.0) {
    dir[t * 3] = -dir[t * 3];
  /**
  acc[t*3+1] += 0.8*acc[t*3];
  acc[t*3] = -0.8*acc[t*3];
  */
  } else if (pos[t * 3] > (1 + sca[t]) * wd && dir[t * 3] > 0.0) {
    dir[t * 3] = -dir[t * 3];
    /**
    acc[t*3+1] += 0.8*acc[t*3];
    acc[t*3] = -0.8*acc[t*3];
    */
  } else if (pos[t * 3 + 1] < -sca[t] * ht && dir[t * 3 + 1] < 0.0) {
    dir[t * 3 + 1] = -dir[t * 3 + 1];
    /**
    acc[t*3] += 0.8*acc[t*3+1];
    acc[t*3+1] = -0.8*acc[t*3+1];
    */
  } else if (pos[t * 3 + 1] > (1 + sca[t]) * ht && dir[t * 3 + 1] > 0.0) {
    dir[t * 3 + 1] = -dir[t * 3 + 1];
    /**
    acc[t*3] += 0.8*acc[t*3+1];
    acc[t*3+1] = -0.8*acc[t*3+1];
    */
  }

  pos[t * 3] += dir[t * 3];
  pos[t * 3 + 1] += dir[t * 3 + 1];
  /**
  dir[t*3]   += acc[t*3];
  dir[t*3+1] += acc[t*3+1];
  */
  ang[t] += spn[t];
  geep[t] += peep[t];
  if (geep[t] > 360 * 5.0)
    geep[t] -= 360 * 5.0;
  if (ang[t] < 0.0) {
    ang[t] += 360.0;
  }
  if (ang[t] > 360.0) {
    ang[t] -= 360.0;
  }
}

void
colorUpdate(int i)
{
  if (hsv[i * 3 + 1] <= 0.5 && hpr[i * 3 + 1] < 0.0)
    hpr[i * 3 + 1] = -hpr[i * 3 + 1];  /* adjust s */
  if (hsv[i * 3 + 1] >= 1.0 && hpr[i * 3 + 1] > 0.0)
    hpr[i * 3 + 1] = -hpr[i * 3 + 1];  /* adjust s */
  if (hsv[i * 3 + 2] <= 0.4 && hpr[i * 3 + 2] < 0.0)
    hpr[i * 3 + 2] = -hpr[i * 3 + 2];  /* adjust s */
  if (hsv[i * 3 + 2] >= 1.0 && hpr[i * 3 + 2] > 0.0)
    hpr[i * 3 + 2] = -hpr[i * 3 + 2];  /* adjust s */

  hsv[i * 3] += hpr[i * 3];
  hsv[i * 3 + 1] += hpr[i * 3 + 1];
  hsv[i * 3 + 2] += hpr[i * 3 + 2];

  /* --- hsv -> rgb --- */
#define H(hhh) hhh[i*3  ]
#define S(hhh) hhh[i*3+1]
#define V(hhh) hhh[i*3+2]

#define R(hhh) hhh[i*3  ]
#define G(hhh) hhh[i*3+1]
#define B(hhh) hhh[i*3+2]

  if (V(hsv) < 0.0)
    V(hsv) = 0.0;
  if (V(hsv) > 1.0)
    V(hsv) = 1.0;
  if (S(hsv) <= 0.0) {
    R(col) = V(hsv);
    G(col) = V(hsv);
    B(col) = V(hsv);
  } else {
    float f, h, p, q, t, v;
    int hi;

    while (H(hsv) < 0.0)
      H(hsv) += 360.0;
    while (H(hsv) >= 360.0)
      H(hsv) -= 360.0;

    if (S(hsv) < 0.0)
      S(hsv) = 0.0;
    if (S(hsv) > 1.0)
      S(hsv) = 1.0;

    h = H(hsv) / 60.0;
    hi = (int) (h);
    f = h - hi;
    v = V(hsv);
    p = V(hsv) * (1 - S(hsv));
    q = V(hsv) * (1 - S(hsv) * f);
    t = V(hsv) * (1 - S(hsv) * (1 - f));

    if (hi <= 0) {
      R(col) = v;
      G(col) = t;
      B(col) = p;
    } else if (hi == 1) {
      R(col) = q;
      G(col) = v;
      B(col) = p;
    } else if (hi == 2) {
      R(col) = p;
      G(col) = v;
      B(col) = t;
    } else if (hi == 3) {
      R(col) = p;
      G(col) = q;
      B(col) = v;
    } else if (hi == 4) {
      R(col) = t;
      G(col) = p;
      B(col) = v;
    } else {
      R(col) = v;
      G(col) = p;
      B(col) = q;
    }
  }
}

void
gravity(float fx)
{
  int a, b;

  for (a = 0; a < N_SHAPES; a++) {
    for (b = 0; b < a; b++) {
      float t, d2;

      t = pos[b * 3] - pos[a * 3];
      d2 = t * t;
      t = pos[b * 3 + 1] - pos[a * 3 + 1];
      d2 += t * t;
      if (d2 < 0.000001)
        d2 = 0.00001;
      if (d2 < 0.1) {

        float v0, v1, z;
        v0 = pos[b * 3] - pos[a * 3];
        v1 = pos[b * 3 + 1] - pos[a * 3 + 1];

        z = 0.00000001 * fx / (d2);

        dir[a * 3] += v0 * z * sca[b];
        dir[b * 3] += -v0 * z * sca[a];
        dir[a * 3 + 1] += v1 * z * sca[b];
        dir[b * 3 + 1] += -v1 * z * sca[a];

      }
    }
    /** apply brakes
    if(dir[a*3]*dir[a*3] + dir[a*3+1]*dir[a*3+1]
      > 0.0001) {
      dir[a*3] *= 0.9;
      dir[a*3+1] *= 0.9;
    }
    */
  }
}
void
oneFrame(void)
{
  int i;

  /**
  if((random() & 0xff) == 0x34){
    glClear(GL_COLOR_BUFFER_BIT);
  }

  if((tko & 0x1f) == 0x1f){
    glEnable(GL_BLEND);
    glColor4f(0.0, 0.0, 0.0, 0.09);
    glRectf(0.0, 0.0, wd, ht);
    glDisable(GL_BLEND);
#ifdef __sgi
    sginap(0);
#endif
  }
  */
  gravity(-2.0);
  for (i = 0; i < N_SHAPES; i++) {
    motionUpdate(i);
#ifdef __sgi
    sginap(0);
#endif
    colorUpdate(i);
#ifdef __sgi
    sginap(0);
#endif
    drawleaf(i);
#ifdef __sgi
    sginap(0);
#endif

  }
  glFlush();
}

void
display(void)
{
  glClear(GL_COLOR_BUFFER_BIT);
}

void
myReshape(int w, int h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if (w <= h) {
    wd = 1.0;
    ht = (GLfloat) h / (GLfloat) w;
    glOrtho(0.0, 1.0,
      0.0, 1.0 * (GLfloat) h / (GLfloat) w,
      -16.0, 4.0);
  } else {
    wd = (GLfloat) w / (GLfloat) h;
    ht = 1.0;
    glOrtho(0.0, 1.0 * (GLfloat) w / (GLfloat) h,
      0.0, 1.0,
      -16.0, 4.0);
  }
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void
visibility(int status)
{
  if (status == GLUT_VISIBLE) {
    glutIdleFunc(oneFrame);
  } else {
    glutIdleFunc(NULL);
  }

}

void
myinit(void)
{
  int i;
  srandom(getpid());
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glEnable(GL_LINE_SMOOTH);
  glShadeModel(GL_FLAT);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  for (i = 0; i < N_SHAPES; i++)
    initshapes(i);
  myReshape(200, 200);
}

/* ARGSUSED1 */
void
keys(unsigned char c, int x, int y)
{

  if (c == 0x1b)
    exit(0);            /* escape */
}

int
main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize(300, 300);
  glutCreateWindow(argv[0]);

  myinit();
  glutReshapeFunc(myReshape);
  glutDisplayFunc(display);
  glutKeyboardFunc(keys);
  glutVisibilityFunc(visibility);
  glutIdleFunc(oneFrame);
  glutPostRedisplay();
  glutMainLoop();
  return 0;             /* ANSI C requires main to return int. */
}


text3d.c - 3D text
text3d.exe
view code

/* Text3d by Robert J. Doyle, Jr., Naval Research Laboratory, Washington, DC. */
#include 
#include 
#include 
#include 
#ifndef _WIN32
#include 
#endif
#include "glut.h"

typedef enum {RESERVED, M_SIDE, M_EDGE, M_WHOLE, O_SIDE, O_EDGE, O_WHOLE, 
T_SIDE, T_EDGE, T_WHOLE, H_SIDE, H_EDGE, H_WHOLE,REPEAT_SIDE, REPEAT_EDGE, REPEAT1, 
REPEAT2_SIDE, REPEAT2_EDGE,REPEAT2, REPEAT3_SIDE, REPEAT3_EDGE,REPEAT3, 
REPEAT4_SIDE, REPEAT4_EDGE,REPEAT4} displayLists;

GLfloat sideColor[] = {0.0, 0.0, 0.5, 1.0};
GLfloat edgeColor[] = {0.7, 0.7, 0.0, 1.0};
GLfloat shininess[] = {128.0};
GLfloat mat_specular[] = {0.7, 0.7, 0.7, 1.0};

GLfloat width = 0.0;
GLfloat width2 = 2.0;

GLfloat letterM[][3] = 
{ 
    {-3.125000, 0.000000, 0.000000},
    {-3.125000, 6.208000, 0.000000},
    {-1.233000, 6.208000, 0.000000},
    {0.003000, 1.484000, 0.000000},
    {1.223000, 6.208000, 0.000000},
    {3.123000, 6.208000, 0.000000},
    {3.123000, 0.000000, 0.000000},
    {1.923000, 0.000000, 0.000000},
    {1.923000, 5.010000, 0.000000},
    {0.659000, 0.000000, 0.000000},
    {-0.649000, 0.000000, 0.000000},
    {-1.925000, 5.010000, 0.000000},
    {-1.925000, 0.000000, 0.000000}

};

GLfloat letterO[][3] = 
{ 
    {-3.038000, 3.102000, 0.000000},
    {-2.974000, 3.874000, 0.000000},
    {-2.827000, 4.440000, 0.000000},
    {-2.802000, 4.508000, 0.000000},
    {-2.544000, 5.042000, 0.000000},
    {-2.502000, 5.110000, 0.000000},
    {-2.223000, 5.479000, 0.000000},
    {-2.132000, 5.576000, 0.000000},
    {-1.784000, 5.869000, 0.000000},
    {-1.678000, 5.940000, 0.000000},
    {-1.260000, 6.155000, 0.000000},
    {-1.148000, 6.198000, 0.000000},
    {-0.677000, 6.321000, 0.000000},
    {-0.638000, 6.328000, 0.000000},
    {-0.002000, 6.378000, 0.000000},
    {0.634000, 6.328000, 0.000000},
    {1.107000, 6.210000, 0.000000},
    {1.144000, 6.198000, 0.000000},
    {1.570000, 6.002000, 0.000000},
    {1.674000, 5.940000, 0.000000},
    {2.038000, 5.661000, 0.000000},
    {2.128000, 5.576000, 0.000000},
    {2.428000, 5.217000, 0.000000},
    {2.504000, 5.104000, 0.000000},
    {2.762000, 4.598000, 0.000000},
    {2.798000, 4.508000, 0.000000},
    {2.960000, 3.913000, 0.000000},
    {2.970000, 3.862000, 0.000000},
    {3.034000, 3.102000, 0.000000},
    {2.970000, 2.342000, 0.000000},
    {2.815000, 1.745000, 0.000000},
    {2.798000, 1.696000, 0.000000},
    {2.554000, 1.182000, 0.000000},
    {2.504000, 1.100000, 0.000000},
    {2.221000, 0.726000, 0.000000},
    {2.128000, 0.628000, 0.000000},
    {1.776000, 0.332000, 0.000000},
    {1.674000, 0.264000, 0.000000},
    {1.256000, 0.049000, 0.000000},
    {1.144000, 0.006000, 0.000000},
    {0.672000, -0.117000, 0.000000},
    {0.634000, -0.124000, 0.000000},
    {-0.002000, -0.174000, 0.000000},
    {-0.638000, -0.124000, 0.000000},
    {-1.112000, -0.006000, 0.000000},
    {-1.148000, 0.006000, 0.000000},
    {-1.576000, 0.202000, 0.000000},
    {-1.678000, 0.264000, 0.000000},
    {-2.041000, 0.540000, 0.000000},
    {-2.132000, 0.628000, 0.000000},
    {-2.430000, 0.983000, 0.000000},
    {-2.502000, 1.094000, 0.000000},
    {-2.773000, 1.622000, 0.000000},
    {-2.802000, 1.696000, 0.000000},
    {-2.962000, 2.258000, 0.000000},
    {-2.974000, 2.330000, 0.000000},
    {-1.736000, 3.102000, 10000.0},
    {-1.710000, 3.578000, 0.000000},
    {-1.644000, 3.934000, 0.000000},
    {-1.503000, 4.328000, 0.000000},
    {-1.494000, 4.346000, 0.000000},
    {-1.352000, 4.593000, 0.000000},
    {-1.306000, 4.656000, 0.000000},
    {-1.120000, 4.857000, 0.000000},
    {-1.040000, 4.926000, 0.000000},
    {-0.825000, 5.067000, 0.000000},
    {-0.726000, 5.116000, 0.000000},
    {-0.480000, 5.200000, 0.000000},
    {-0.402000, 5.218000, 0.000000},
    {-0.041000, 5.257000, 0.000000},
    {-0.002000, 5.258000, 0.000000},
    {0.361000, 5.227000, 0.000000},
    {0.400000, 5.220000, 0.000000},
    {0.650000, 5.147000, 0.000000},
    {0.726000, 5.116000, 0.000000},
    {0.950000, 4.990000, 0.000000},
    {1.038000, 4.926000, 0.000000},
    {1.239000, 4.736000, 0.000000},
    {1.306000, 4.656000, 0.000000},
    {1.462000, 4.413000, 0.000000},
    {1.498000, 4.342000, 0.000000},
    {1.635000, 3.964000, 0.000000},
    {1.644000, 3.934000, 0.000000},
    {1.710000, 3.568000, 0.000000},
    {1.736000, 3.102000, 0.000000},
    {1.710000, 2.636000, 0.000000},
    {1.642000, 2.268000, 0.000000},
    {1.508000, 1.886000, 0.000000},
    {1.496000, 1.860000, 0.000000},
    {1.351000, 1.610000, 0.000000},
    {1.304000, 1.546000, 0.000000},
    {1.115000, 1.343000, 0.000000},
    {1.036000, 1.276000, 0.000000},
    {0.823000, 1.135000, 0.000000},
    {0.724000, 1.086000, 0.000000},
    {0.480000, 1.001000, 0.000000},
    {0.400000, 0.984000, 0.000000},
    {0.035000, 0.946000, 0.000000},
    {-0.002000, 0.946000, 0.000000},
    {-0.368000, 0.979000, 0.000000},
    {-0.402000, 0.986000, 0.000000},
    {-0.653000, 1.057000, 0.000000},
    {-0.726000, 1.088000, 0.000000},
    {-0.952000, 1.213000, 0.000000},
    {-1.040000, 1.278000, 0.000000},
    {-1.240000, 1.467000, 0.000000},
    {-1.306000, 1.548000, 0.000000},
    {-1.460000, 1.788000, 0.000000},
    {-1.494000, 1.858000, 0.000000},
    {-1.639000, 2.251000, 0.000000},
    {-1.644000, 2.270000, 0.000000},
    {-1.710000, 2.626000, 0.000000}
};

GLfloat letterT[][3] = 
{
    {-0.640000, 0.000000, 0.000000},
    {-0.640000, 5.104000, 0.000000},
    {-2.476000, 5.104000, 0.000000},
    {-2.476000, 6.208000, 0.000000},
    {2.476000, 6.208000, 0.000000},
    {2.476000, 5.104000, 0.000000},
    {0.640000, 5.104000, 0.000000},
    {0.640000, 0.000000, 0.000000}
};

GLfloat letterH[][3] = 
{
    {-2.570000, 0.000000, 0.000000},
    {-2.570000, 6.208000, 0.000000},
    {-1.282000, 6.208000, 0.000000},
    {-1.282000, 3.900000, 0.000000},
    {1.280000, 3.900000, 0.000000},
    {1.280000, 6.208000, 0.000000},
    {2.568000, 6.208000, 0.000000},
    {2.568000, 0.000000, 0.000000},
    {1.280000, 0.000000, 0.000000},
    {1.280000, 2.760000, 0.000000},
    {-1.282000, 2.760000, 0.000000},
    {-1.282000, 0.000000, 0.000000}
};

/*  Initialize light source and lighting.
 */


static void checkErrors(void)
{
  GLenum error;
  while ((error = glGetError()) != GL_NO_ERROR) {
    fprintf(stderr, "Error: %s\n", (char *) gluErrorString(error));
  }
}

void myinit(void)
{
	int count1 =  sizeof(letterM) / (3 * sizeof(GLfloat)); 
	int count2 =  sizeof(letterO) / (3 * sizeof(GLfloat));
	int count3 =  sizeof(letterT) / (3 * sizeof(GLfloat)); 
	int count4 =  sizeof(letterH) / (3 * sizeof(GLfloat));

	int i;
	
    GLfloat light_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
/*	light_position is NOT default value	*/
    GLfloat light_position[] = { -1.0, -1.0, 1.0, 0.0 };

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glDepthFunc(GL_LESS);
    glEnable(GL_DEPTH_TEST);

	glDrawBuffer(GL_FRONT_AND_BACK);
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);
	glClear(GL_ACCUM_BUFFER_BIT);
	glDrawBuffer(GL_BACK);	

/* Zero position of text */

    		for(i = 0; i < count1; i++) {
	    		letterM[i][1] = letterM[i][1] - 3.175;
	    	}
    		for(i = 0; i < count2; i++) {
	    		letterO[i][1] = letterO[i][1] - 3.175;
			}
    		for(i = 0; i < count3; i++) {
	    		letterT[i][1] = letterT[i][1] - 3.175;
			} 
    		for(i = 0; i < count4; i++) {
	    		letterH[i][1] = letterH[i][1] - 3.175;
			} 
}


/* Mark Kilgard's tessellation code from the "dino" demos. */
void extrudeSolidFromPolygon(GLfloat data[][3], unsigned int dataSize,
		GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
{
	GLdouble vertex[3], dx, dy, len;
	int i, k;
	int flag = 0;
	int count = dataSize / (3 * sizeof(GLfloat));
    static GLUtriangulatorObj *tobj = NULL;

    if (tobj == NULL) {
    	tobj = gluNewTess();
    	
    	gluTessCallback(tobj, GLU_BEGIN, glBegin);
    	gluTessCallback(tobj, GLU_VERTEX, glVertex3fv);
    	gluTessCallback(tobj, GLU_END, glEnd);
    }
    glNewList(side, GL_COMPILE);
    	glShadeModel(GL_SMOOTH);
    	gluBeginPolygon(tobj);
    		for(i = 0; i < count; i++) {
    			/* This detects a new contour from a large number placed in
    			the unused z coordinate of the vertex where the new contour 
    			starts. See the coordinates for letterO, above. The coordinate 
    			must be reset below for additional calls. */

    			if (data[i][2] > 1000.0) {
    				data[i][2] = 0.0;
    				flag = 1; k = i;
    				gluNextContour(tobj, GLU_INTERIOR); 
    			}
    			
    			vertex[0] = data[i][0];
    			vertex[1] = data[i][1];
    			vertex[2] = 0.0;
    			gluTessVertex(tobj, vertex, data[i]);
    		}
    	gluEndPolygon(tobj);
    glEndList();
	
				/* Reset coordinate for new calls. */
				if (flag == 1) {
				data[k][2] = 10000.0;
				flag = 0;
				}
	glNewList(edge, GL_COMPILE);
		glBegin(GL_QUAD_STRIP);
		for(i = 0; i <= count; i++) {
			glVertex3f(data[i % count][0], data[i % count][1], 0.0);
			glVertex3f(data[i % count][0], data[i % count][1], thickness);
			/* Normals */
			dx = data[(i+ 1) % count][1] - data[i % count][1];
			dy = data[i % count][0] - data[(i + 1) % count][0];
			len = sqrt(dx * dx + dy * dy);
			glNormal3f(dx / len, dy / len, 0.0);
		}
		glEnd();
	glEndList();
	
	glNewList(whole, GL_COMPILE);
		glFrontFace(GL_CW);

		glMaterialfv(GL_FRONT, GL_DIFFUSE, edgeColor);
		glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);				

		glCallList(edge);
		glNormal3f(0.0, 0.0, -1.0); 
		glCallList(side);
		glPushMatrix();
			glTranslatef(0.0, 0.0, thickness);
			glFrontFace(GL_CCW);
			glNormal3f(0.0, 0.0, 1.0);

		glMaterialfv(GL_FRONT, GL_DIFFUSE, sideColor);
		glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);		
		
			glCallList(side);
		glPopMatrix();
	glEndList();
}

void repeat(int j)
{
	if(j == 2){			
		glPushMatrix();
			glTranslatef((31 * -0.34) , 9.3, -9.6);
			glCallList(REPEAT1);
		glPopMatrix();				
	}
	if(j == 3){			
		glPushMatrix();
			glTranslatef(31 * -0.34, 9.3, -9.6);
			glCallList(REPEAT1);
		glPopMatrix();
		glPushMatrix();
			glTranslatef(31 * -.09, 9.3, -9.6);
			glCallList(REPEAT2);
		glPopMatrix();	
	}
	if(j == 4){			
		glPushMatrix();
			glTranslatef(31 * -0.34, 9.3, -9.6);
			glCallList(REPEAT1);
		glPopMatrix();
		glPushMatrix();
			glTranslatef(31 * -.09, 9.3, -9.6);
			glCallList(REPEAT2);
		glPopMatrix();	
		glPushMatrix();
			glTranslatef(31 * 0.12, 9.3, -9.6);
			glCallList(REPEAT3);
		glPopMatrix();
	}	
}

void display(void)
{
	int i, j;
	GLfloat xPos = -0.34;
	glLoadIdentity();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		
	glTranslatef(0.0, 0.0, -10.0);

	extrudeSolidFromPolygon(letterM, sizeof(letterM), width2, REPEAT_SIDE, 
		REPEAT_EDGE, REPEAT1);
	extrudeSolidFromPolygon(letterO, sizeof(letterO), width2, REPEAT2_SIDE, 
		REPEAT2_EDGE, REPEAT2);
	extrudeSolidFromPolygon(letterT, sizeof(letterT), width2, REPEAT3_SIDE, 
		REPEAT3_EDGE, REPEAT3);
	extrudeSolidFromPolygon(letterH, sizeof(letterH), width2, REPEAT4_SIDE, 
		REPEAT4_EDGE, REPEAT4);			

	for(j = 1; j < 5; j++){ 
		width = 0.0;
checkErrors();
		for(i = 0; i < 10; i++){ 

			glPushMatrix();
				repeat(j);
			glPopMatrix();
			
			glPushMatrix();
				glRotatef(90.0, 0.0, 1.0, 0.0); 
				if(j == 1){
					extrudeSolidFromPolygon(letterM, sizeof(letterM), width, M_SIDE, 
						M_EDGE, M_WHOLE);
					glCallList(M_WHOLE);					
				}
				if(j == 2){
					extrudeSolidFromPolygon(letterO, sizeof(letterO), width, O_SIDE, 
						O_EDGE, O_WHOLE);
					glCallList(O_WHOLE);
				}				
				if(j == 3){
					extrudeSolidFromPolygon(letterT, sizeof(letterT), width, T_SIDE, 
						T_EDGE, T_WHOLE);
					glCallList(T_WHOLE);
				}
				if(j == 4){
					extrudeSolidFromPolygon(letterH, sizeof(letterH), width, H_SIDE, 
						H_EDGE, H_WHOLE);
					glCallList(H_WHOLE);
				}				
				glutSwapBuffers();
		    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
				width = width + 0.2;
			glPopMatrix();
		}
		for(i = 0; i < 45 ; i++){
				
			glPushMatrix();
				repeat(j);
			glPopMatrix();					
			
			glPushMatrix();					
				glRotatef(90.0 - (2.0 * i), 0.0, 1.0, 0.0);
				if(j == 1){
				glCallList(M_WHOLE);
				}
				if(j == 2){
				glCallList(O_WHOLE);
				}
				if(j == 3){
				glCallList(T_WHOLE);
				}
				if(j == 4){
				glCallList(H_WHOLE);
				}			
				glutSwapBuffers();
		    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		
			glPopMatrix();
		} 
		for(i = 1; i < 32 ; i++){
			
			glPushMatrix();
				repeat(j);
			glPopMatrix();
			
			glPushMatrix();
				glTranslatef(i * xPos, i * 0.3, i * -0.3);
				if(j == 1){
				glCallList(M_WHOLE);
				}
				if(j == 2){
				glCallList(O_WHOLE);
				}
				if(j == 3){
				glCallList(T_WHOLE);
				}
				if(j == 4){
				glCallList(H_WHOLE);
				}		
				glutSwapBuffers();
				glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);				
			glPopMatrix();
		}				

		if(j == 1){
			xPos = xPos + 0.25;
		}
		else{
			xPos = xPos + 0.21;
		}
	}
	glFlush();
}

void myReshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
  glFrustum(-7.0, 7.0, -7.0, 7.0, 6.0, 20.0);
/*    if (w <= h) 
	glOrtho (-7.0, 7.0, -7.0*(GLfloat)h/(GLfloat)w, 
	    7.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else 
	glOrtho (-7.0*(GLfloat)w/(GLfloat)h, 
	    7.0*(GLfloat)w/(GLfloat)h, -7.0, 7.0, -10.0, 10.0); */
    glMatrixMode(GL_MODELVIEW);
}

/*  Main Loop
 *  Open window with initial window size, title bar, 
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutCreateWindow ("text3d");

/*glCullFace(GL_FRONT);*/
/*glEnable(GL_CULL_FACE);*/

    myinit();
    glutReshapeFunc (myReshape);
    glutDisplayFunc (display);
    glutMainLoop();
    return 0;             /* ANSI C requires main to return int. */
}


camzoom.c - camera zooming
camzoom.exe