demos: move tests/dinoshade.c to demos/
authorBrian Paul <brianp@vmware.com>
Sat, 18 Apr 2009 19:08:48 +0000 (13:08 -0600)
committerBrian Paul <brianp@vmware.com>
Sat, 18 Apr 2009 19:08:48 +0000 (13:08 -0600)
progs/demos/Makefile
progs/demos/dinoshade.c [new file with mode: 0644]
progs/tests/Makefile
progs/tests/dinoshade.c [deleted file]

index 2fe407972d7556894161a6abd0d323b79cd60fce..8febc14d410fa734747eb4325efcd84fa571b075 100644 (file)
@@ -19,6 +19,7 @@ PROGS = \
        clearspd \
        copypix \
        cubemap \
+       dinoshade \
        drawpix \
        engine \
        fbo_firecube \
diff --git a/progs/demos/dinoshade.c b/progs/demos/dinoshade.c
new file mode 100644 (file)
index 0000000..451da2e
--- /dev/null
@@ -0,0 +1,912 @@
+
+/* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
+
+/* 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. */
+
+/* Example for PC game developers to show how to *combine* texturing,
+   reflections, and projected shadows all in real-time with OpenGL.
+   Robust reflections use stenciling.  Robust projected shadows
+   use both stenciling and polygon offset.  PC game programmers
+   should realize that neither stenciling nor polygon offset are 
+   supported by Direct3D, so these real-time rendering algorithms
+   are only really viable with OpenGL. 
+   
+   The program has modes for disabling the stenciling and polygon
+   offset uses.  It is worth running this example with these features
+   toggled off so you can see the sort of artifacts that result.
+   
+   Notice that the floor texturing, reflections, and shadowing
+   all co-exist properly. */
+
+/* When you run this program:  Left mouse button controls the
+   view.  Middle mouse button controls light position (left &
+   right rotates light around dino; up & down moves light
+   position up and down).  Right mouse button pops up menu. */
+
+/* Check out the comments in the "redraw" routine to see how the
+   reflection blending and surface stenciling is done.  You can
+   also see in "redraw" how the projected shadows are rendered,
+   including the use of stenciling and polygon offset. */
+
+/* This program is derived from glutdino.c */
+
+/* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>       /* for cos(), sin(), and sqrt() */
+#include <stddef.h>    /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#define GL_GLEXT_LEGACY
+#include <GL/glew.h>    /* OpenGL Utility Toolkit header */
+#include <GL/glut.h>    /* OpenGL Utility Toolkit header */
+
+/* Some <math.h> files do not define M_PI... */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+/* Variable controlling various rendering modes. */
+static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1;
+static int renderShadow = 1, renderDinosaur = 1, renderReflection = 1;
+static int linearFiltering = 0, useMipmaps = 0, useTexture = 1;
+static int reportSpeed = 0;
+static int animation = 1;
+static GLboolean lightSwitch = GL_TRUE;
+static int directionalLight = 1;
+static int forceExtension = 0;
+
+/* Time varying or user-controled variables. */
+static float jump = 0.0;
+static float lightAngle = 0.0, lightHeight = 20;
+GLfloat angle = -150;   /* in degrees */
+GLfloat angle2 = 30;   /* in degrees */
+
+int moving, startx, starty;
+int lightMoving = 0, lightStartX, lightStartY;
+
+enum {
+  MISSING, EXTENSION, ONE_DOT_ONE
+};
+int polygonOffsetVersion;
+
+static GLdouble bodyWidth = 3.0;
+/* *INDENT-OFF* */
+static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
+  {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
+  {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
+  {1, 2} };
+static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
+  {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
+  {13, 9}, {11, 11}, {9, 11} };
+static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
+  {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
+static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
+  {9.6, 15.25}, {9, 15.25} };
+static GLfloat lightPosition[4];
+static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
+static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0};
+/* *INDENT-ON* */
+
+/* Nice floor texture tiling pattern. */
+static char *circles[] = {
+  "....xxxx........",
+  "..xxxxxxxx......",
+  ".xxxxxxxxxx.....",
+  ".xxx....xxx.....",
+  "xxx......xxx....",
+  "xxx......xxx....",
+  "xxx......xxx....",
+  "xxx......xxx....",
+  ".xxx....xxx.....",
+  ".xxxxxxxxxx.....",
+  "..xxxxxxxx......",
+  "....xxxx........",
+  "................",
+  "................",
+  "................",
+  "................",
+};
+
+static void
+makeFloorTexture(void)
+{
+  GLubyte floorTexture[16][16][3];
+  GLubyte *loc;
+  int s, t;
+
+  /* Setup RGB image for the texture. */
+  loc = (GLubyte*) floorTexture;
+  for (t = 0; t < 16; t++) {
+    for (s = 0; s < 16; s++) {
+      if (circles[t][s] == 'x') {
+       /* Nice green. */
+        loc[0] = 0x1f;
+        loc[1] = 0x8f;
+        loc[2] = 0x1f;
+      } else {
+       /* Light gray. */
+        loc[0] = 0xaa;
+        loc[1] = 0xaa;
+        loc[2] = 0xaa;
+      }
+      loc += 3;
+    }
+  }
+
+  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+  if (useMipmaps) {
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+      GL_LINEAR_MIPMAP_LINEAR);
+    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
+      GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
+  } else {
+    if (linearFiltering) {
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    } else {
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    }
+    glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
+      GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
+  }
+}
+
+enum {
+  X, Y, Z, W
+};
+enum {
+  A, B, C, D
+};
+
+/* Create a matrix that will project the desired shadow. */
+static void
+shadowMatrix(GLfloat shadowMat[4][4],
+  GLfloat groundplane[4],
+  GLfloat lightpos[4])
+{
+  GLfloat dot;
+
+  /* Find dot product between light position vector and ground plane normal. */
+  dot = groundplane[X] * lightpos[X] +
+    groundplane[Y] * lightpos[Y] +
+    groundplane[Z] * lightpos[Z] +
+    groundplane[W] * lightpos[W];
+
+  shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
+  shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
+  shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
+  shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
+
+  shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
+  shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
+  shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
+  shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
+
+  shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
+  shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
+  shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
+  shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
+
+  shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
+  shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
+  shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
+  shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
+
+}
+
+/* Find the plane equation given 3 points. */
+static void
+findPlane(GLfloat plane[4],
+  GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
+{
+  GLfloat vec0[3], vec1[3];
+
+  /* Need 2 vectors to find cross product. */
+  vec0[X] = v1[X] - v0[X];
+  vec0[Y] = v1[Y] - v0[Y];
+  vec0[Z] = v1[Z] - v0[Z];
+
+  vec1[X] = v2[X] - v0[X];
+  vec1[Y] = v2[Y] - v0[Y];
+  vec1[Z] = v2[Z] - v0[Z];
+
+  /* find cross product to get A, B, and C of plane equation */
+  plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
+  plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
+  plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
+
+  plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
+}
+
+static void
+extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
+  GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
+{
+  static GLUtriangulatorObj *tobj = NULL;
+  GLdouble vertex[3], dx, dy, len;
+  int i;
+  int count = (int) (dataSize / (2 * sizeof(GLfloat)));
+
+  if (tobj == NULL) {
+    tobj = gluNewTess();  /* create and initialize a GLU
+                             polygon tesselation object */
+    gluTessCallback(tobj, GLU_BEGIN, glBegin);
+    gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);  /* semi-tricky */
+    gluTessCallback(tobj, GLU_END, glEnd);
+  }
+  glNewList(side, GL_COMPILE);
+  glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
+                               tessellation */
+  gluBeginPolygon(tobj);
+  for (i = 0; i < count; i++) {
+    vertex[0] = data[i][0];
+    vertex[1] = data[i][1];
+    vertex[2] = 0;
+    gluTessVertex(tobj, vertex, data[i]);
+  }
+  gluEndPolygon(tobj);
+  glEndList();
+  glNewList(edge, GL_COMPILE);
+  glShadeModel(GL_FLAT);  /* flat shade keeps angular hands
+                             from being "smoothed" */
+  glBegin(GL_QUAD_STRIP);
+  for (i = 0; i <= count; i++) {
+#if 1 /* weird, but seems to be legal */
+    /* mod function handles closing the edge */
+    glVertex3f(data[i % count][0], data[i % count][1], 0.0);
+    glVertex3f(data[i % count][0], data[i % count][1], thickness);
+    /* Calculate a unit normal by dividing by Euclidean
+       distance. We * could be lazy and use
+       glEnable(GL_NORMALIZE) so we could pass in * arbitrary
+       normals for a very slight performance hit. */
+    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);
+#else /* the nice way of doing it */
+    /* Calculate a unit normal by dividing by Euclidean
+       distance. We * could be lazy and use
+       glEnable(GL_NORMALIZE) so we could pass in * arbitrary
+       normals for a very slight performance hit. */
+    dx = data[i % count][1] - data[(i - 1 + count) % count][1];
+    dy = data[(i - 1 + count) % count][0] - data[i % count][0];
+    len = sqrt(dx * dx + dy * dy);
+    glNormal3f(dx / len, dy / len, 0.0);
+    /* mod function handles closing the edge */
+    glVertex3f(data[i % count][0], data[i % count][1], 0.0);
+    glVertex3f(data[i % count][0], data[i % count][1], thickness);
+#endif
+  }
+  glEnd();
+  glEndList();
+  glNewList(whole, GL_COMPILE);
+  glFrontFace(GL_CW);
+  glCallList(edge);
+  glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
+  glCallList(side);
+  glPushMatrix();
+  glTranslatef(0.0, 0.0, thickness);
+  glFrontFace(GL_CCW);
+  glNormal3f(0.0, 0.0, 1.0);  /* opposite normal for other side */
+  glCallList(side);
+  glPopMatrix();
+  glEndList();
+}
+
+/* Enumerants for refering to display lists. */
+typedef enum {
+  RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
+  LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE
+} displayLists;
+
+static void
+makeDinosaur(void)
+{
+  extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
+    BODY_SIDE, BODY_EDGE, BODY_WHOLE);
+  extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
+    ARM_SIDE, ARM_EDGE, ARM_WHOLE);
+  extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
+    LEG_SIDE, LEG_EDGE, LEG_WHOLE);
+  extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
+    EYE_SIDE, EYE_EDGE, EYE_WHOLE);
+}
+
+static void
+drawDinosaur(void)
+
+{
+  glPushMatrix();
+  /* Translate the dinosaur to be at (0,8,0). */
+  glTranslatef(-8, 0, -bodyWidth / 2);
+  glTranslatef(0.0, jump, 0.0);
+  glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
+  glCallList(BODY_WHOLE);
+  glTranslatef(0.0, 0.0, bodyWidth);
+  glCallList(ARM_WHOLE);
+  glCallList(LEG_WHOLE);
+  glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
+  glCallList(ARM_WHOLE);
+  glTranslatef(0.0, 0.0, -bodyWidth / 4);
+  glCallList(LEG_WHOLE);
+  glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1);
+  glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
+  glCallList(EYE_WHOLE);
+  glPopMatrix();
+}
+
+static GLfloat floorVertices[4][3] = {
+  { -20.0, 0.0, 20.0 },
+  { 20.0, 0.0, 20.0 },
+  { 20.0, 0.0, -20.0 },
+  { -20.0, 0.0, -20.0 },
+};
+
+/* Draw a floor (possibly textured). */
+static void
+drawFloor(void)
+{
+  glDisable(GL_LIGHTING);
+
+  if (useTexture) {
+    glEnable(GL_TEXTURE_2D);
+  }
+
+  glBegin(GL_QUADS);
+    glTexCoord2f(0.0, 0.0);
+    glVertex3fv(floorVertices[0]);
+    glTexCoord2f(0.0, 16.0);
+    glVertex3fv(floorVertices[1]);
+    glTexCoord2f(16.0, 16.0);
+    glVertex3fv(floorVertices[2]);
+    glTexCoord2f(16.0, 0.0);
+    glVertex3fv(floorVertices[3]);
+  glEnd();
+
+  if (useTexture) {
+    glDisable(GL_TEXTURE_2D);
+  }
+
+  glEnable(GL_LIGHTING);
+}
+
+static GLfloat floorPlane[4];
+static GLfloat floorShadow[4][4];
+
+static void
+redraw(void)
+{
+  int start, end;
+
+  if (reportSpeed) {
+    start = glutGet(GLUT_ELAPSED_TIME);
+  }
+
+  /* Clear; default stencil clears to zero. */
+  if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow)) {
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+  } else {
+    /* Avoid clearing stencil when not using it. */
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  }
+
+  /* Reposition the light source. */
+  lightPosition[0] = 12*cos(lightAngle);
+  lightPosition[1] = lightHeight;
+  lightPosition[2] = 12*sin(lightAngle);
+  if (directionalLight) {
+    lightPosition[3] = 0.0;
+  } else {
+    lightPosition[3] = 1.0;
+  }
+
+  shadowMatrix(floorShadow, floorPlane, lightPosition);
+
+  glPushMatrix();
+    /* Perform scene rotations based on user mouse input. */
+    glRotatef(angle2, 1.0, 0.0, 0.0);
+    glRotatef(angle, 0.0, 1.0, 0.0);
+     
+    /* Tell GL new light source position. */
+    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
+
+    if (renderReflection) {
+      if (stencilReflection) {
+        /* We can eliminate the visual "artifact" of seeing the "flipped"
+          dinosaur underneath the floor by using stencil.  The idea is
+          draw the floor without color or depth update but so that 
+          a stencil value of one is where the floor will be.  Later when
+          rendering the dinosaur reflection, we will only update pixels
+          with a stencil value of 1 to make sure the reflection only
+          lives on the floor, not below the floor. */
+
+        /* Don't update color or depth. */
+        glDisable(GL_DEPTH_TEST);
+        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+
+        /* Draw 1 into the stencil buffer. */
+        glEnable(GL_STENCIL_TEST);
+        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+        glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
+
+        /* Now render floor; floor pixels just get their stencil set to 1. */
+        drawFloor();
+
+        /* Re-enable update of color and depth. */ 
+        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+        glEnable(GL_DEPTH_TEST);
+
+        /* Now, only render where stencil is set to 1. */
+        glStencilFunc(GL_EQUAL, 1, 0xffffffff);  /* draw if ==1 */
+        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+      }
+
+      glPushMatrix();
+
+        /* The critical reflection step: Reflect dinosaur through the floor
+           (the Y=0 plane) to make a relection. */
+        glScalef(1.0, -1.0, 1.0);
+
+       /* Reflect the light position. */
+        glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
+
+        /* To avoid our normals getting reversed and hence botched lighting
+          on the reflection, turn on normalize.  */
+        glEnable(GL_NORMALIZE);
+        glCullFace(GL_FRONT);
+
+        /* Draw the reflected dinosaur. */
+        drawDinosaur();
+
+        /* Disable noramlize again and re-enable back face culling. */
+        glDisable(GL_NORMALIZE);
+        glCullFace(GL_BACK);
+
+      glPopMatrix();
+
+      /* Switch back to the unreflected light position. */
+      glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
+
+      if (stencilReflection) {
+        glDisable(GL_STENCIL_TEST);
+      }
+    }
+
+    /* Back face culling will get used to only draw either the top or the
+       bottom floor.  This let's us get a floor with two distinct
+       appearances.  The top floor surface is reflective and kind of red.
+       The bottom floor surface is not reflective and blue. */
+
+    /* Draw "bottom" of floor in blue. */
+    glFrontFace(GL_CW);  /* Switch face orientation. */
+    glColor4f(0.1, 0.1, 0.7, 1.0);
+    drawFloor();
+    glFrontFace(GL_CCW);
+
+    if (renderShadow) {
+      if (stencilShadow) {
+       /* Draw the floor with stencil value 3.  This helps us only 
+          draw the shadow once per floor pixel (and only on the
+          floor pixels). */
+        glEnable(GL_STENCIL_TEST);
+        glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
+        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+      }
+    }
+
+    /* Draw "top" of floor.  Use blending to blend in reflection. */
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glColor4f(0.7, 0.0, 0.0, 0.3);
+    glColor4f(1.0, 1.0, 1.0, 0.3);
+    drawFloor();
+    glDisable(GL_BLEND);
+
+    if (renderDinosaur) {
+      /* Draw "actual" dinosaur, not its reflection. */
+      drawDinosaur();
+    }
+
+    if (renderShadow) {
+
+      /* Render the projected shadow. */
+
+      if (stencilShadow) {
+
+        /* Now, only render where stencil is set above 2 (ie, 3 where
+          the top floor is).  Update stencil with 2 where the shadow
+          gets drawn so we don't redraw (and accidently reblend) the
+          shadow). */
+        glStencilFunc(GL_LESS, 2, 0xffffffff);  /* draw if ==1 */
+        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+      }
+
+      /* To eliminate depth buffer artifacts, we use polygon offset
+        to raise the depth of the projected shadow slightly so
+        that it does not depth buffer alias with the floor. */
+      if (offsetShadow) {
+       switch (polygonOffsetVersion) {
+       case EXTENSION:
+#ifdef GL_EXT_polygon_offset
+         glEnable(GL_POLYGON_OFFSET_EXT);
+         break;
+#endif
+#ifdef GL_VERSION_1_1
+       case ONE_DOT_ONE:
+          glEnable(GL_POLYGON_OFFSET_FILL);
+         break;
+#endif
+       case MISSING:
+         /* Oh well. */
+         break;
+       }
+      }
+
+      /* Render 50% black shadow color on top of whatever the
+         floor appareance is. */
+      glEnable(GL_BLEND);
+      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glDisable(GL_LIGHTING);  /* Force the 50% black. */
+      glColor4f(0.0, 0.0, 0.0, 0.5);
+
+      glPushMatrix();
+       /* Project the shadow. */
+        glMultMatrixf((GLfloat *) floorShadow);
+        drawDinosaur();
+      glPopMatrix();
+
+      glDisable(GL_BLEND);
+      glEnable(GL_LIGHTING);
+
+      if (offsetShadow) {
+       switch (polygonOffsetVersion) {
+#ifdef GL_EXT_polygon_offset
+       case EXTENSION:
+         glDisable(GL_POLYGON_OFFSET_EXT);
+         break;
+#endif
+#ifdef GL_VERSION_1_1
+       case ONE_DOT_ONE:
+          glDisable(GL_POLYGON_OFFSET_FILL);
+         break;
+#endif
+       case MISSING:
+         /* Oh well. */
+         break;
+       }
+      }
+      if (stencilShadow) {
+        glDisable(GL_STENCIL_TEST);
+      }
+    }
+
+    glPushMatrix();
+    glDisable(GL_LIGHTING);
+    glColor3f(1.0, 1.0, 0.0);
+    if (directionalLight) {
+      /* Draw an arrowhead. */
+      glDisable(GL_CULL_FACE);
+      glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
+      glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0);
+      glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1);
+      glBegin(GL_TRIANGLE_FAN);
+       glVertex3f(0, 0, 0);
+       glVertex3f(2, 1, 1);
+       glVertex3f(2, -1, 1);
+       glVertex3f(2, -1, -1);
+       glVertex3f(2, 1, -1);
+       glVertex3f(2, 1, 1);
+      glEnd();
+      /* Draw a white line from light direction. */
+      glColor3f(1.0, 1.0, 1.0);
+      glBegin(GL_LINES);
+       glVertex3f(0, 0, 0);
+       glVertex3f(5, 0, 0);
+      glEnd();
+      glEnable(GL_CULL_FACE);
+    } else {
+      /* Draw a yellow ball at the light source. */
+      glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
+      glutSolidSphere(1.0, 5, 5);
+    }
+    glEnable(GL_LIGHTING);
+    glPopMatrix();
+
+  glPopMatrix();
+
+  if (reportSpeed) {
+    glFinish();
+    end = glutGet(GLUT_ELAPSED_TIME);
+    printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
+  }
+
+  glutSwapBuffers();
+}
+
+/* ARGSUSED2 */
+static void
+mouse(int button, int state, int x, int y)
+{
+  if (button == GLUT_LEFT_BUTTON) {
+    if (state == GLUT_DOWN) {
+      moving = 1;
+      startx = x;
+      starty = y;
+    }
+    if (state == GLUT_UP) {
+      moving = 0;
+    }
+  }
+  if (button == GLUT_MIDDLE_BUTTON) {
+    if (state == GLUT_DOWN) {
+      lightMoving = 1;
+      lightStartX = x;
+      lightStartY = y;
+    }
+    if (state == GLUT_UP) {
+      lightMoving = 0;
+    }
+  }
+}
+
+/* ARGSUSED1 */
+static void
+motion(int x, int y)
+{
+  if (moving) {
+    angle = angle + (x - startx);
+    angle2 = angle2 + (y - starty);
+    startx = x;
+    starty = y;
+    glutPostRedisplay();
+  }
+  if (lightMoving) {
+    lightAngle += (x - lightStartX)/40.0;
+    lightHeight += (lightStartY - y)/20.0;
+    lightStartX = x;
+    lightStartY = y;
+    glutPostRedisplay();
+  }
+}
+
+/* Advance time varying state when idle callback registered. */
+static void
+idle(void)
+{
+  static float time = 0.0;
+
+  time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
+
+  jump = 4.0 * fabs(sin(time)*0.5);
+  if (!lightMoving) {
+    lightAngle += 0.03;
+  }
+  glutPostRedisplay();
+}
+
+enum {
+  M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
+  M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
+  M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
+};
+
+static void
+controlLights(int value)
+{
+  switch (value) {
+  case M_NONE:
+    return;
+  case M_MOTION:
+    animation = 1 - animation;
+    if (animation) {
+      glutIdleFunc(idle);
+    } else {
+      glutIdleFunc(NULL);
+    }
+    break;
+  case M_LIGHT:
+    lightSwitch = !lightSwitch;
+    if (lightSwitch) {
+      glEnable(GL_LIGHT0);
+    } else {
+      glDisable(GL_LIGHT0);
+    }
+    break;
+  case M_TEXTURE:
+    useTexture = !useTexture;
+    break;
+  case M_SHADOWS:
+    renderShadow = 1 - renderShadow;
+    break;
+  case M_REFLECTION:
+    renderReflection = 1 - renderReflection;
+    break;
+  case M_DINOSAUR:
+    renderDinosaur = 1 - renderDinosaur;
+    break;
+  case M_STENCIL_REFLECTION:
+    stencilReflection = 1 - stencilReflection;
+    break;
+  case M_STENCIL_SHADOW:
+    stencilShadow = 1 - stencilShadow;
+    break;
+  case M_OFFSET_SHADOW:
+    offsetShadow = 1 - offsetShadow;
+    break;
+  case M_POSITIONAL:
+    directionalLight = 0;
+    break;
+  case M_DIRECTIONAL:
+    directionalLight = 1;
+    break;
+  case M_PERFORMANCE:
+    reportSpeed = 1 - reportSpeed;
+    break;
+  }
+  glutPostRedisplay();
+}
+
+/* When not visible, stop animating.  Restart when visible again. */
+static void 
+visible(int vis)
+{
+  if (vis == GLUT_VISIBLE) {
+    if (animation)
+      glutIdleFunc(idle);
+  } else {
+    if (!animation)
+      glutIdleFunc(NULL);
+  }
+}
+
+/* Press any key to redraw; good when motion stopped and
+   performance reporting on. */
+/* ARGSUSED */
+static void
+key(unsigned char c, int x, int y)
+{
+  if (c == 27) {
+    exit(0);  /* IRIS GLism, Escape quits. */
+  }
+  glutPostRedisplay();
+}
+
+/* Press any key to redraw; good when motion stopped and
+   performance reporting on. */
+/* ARGSUSED */
+static void
+special(int k, int x, int y)
+{
+  glutPostRedisplay();
+}
+
+static int
+supportsOneDotOne(void)
+{
+  const char *version;
+  int major, minor;
+
+  version = (char *) glGetString(GL_VERSION);
+  if (sscanf(version, "%d.%d", &major, &minor) == 2)
+    return major * 10 + minor >= 11;
+  return 0;            /* OpenGL version string malformed! */
+}
+
+int
+main(int argc, char **argv)
+{
+  int i;
+
+  glutInit(&argc, argv);
+
+  for (i=1; i<argc; i++) {
+    if (!strcmp("-linear", argv[i])) {
+      linearFiltering = 1;
+    } else if (!strcmp("-mipmap", argv[i])) {
+      useMipmaps = 1;
+    } else if (!strcmp("-ext", argv[i])) {
+      forceExtension = 1;
+    }
+  }
+
+  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
+
+#if 0
+  /* In GLUT 4.0, you'll be able to do this an be sure to
+     get 2 bits of stencil if the machine has it for you. */
+  glutInitDisplayString("samples stencil>=2 rgb double depth");
+#endif
+
+  glutCreateWindow("Shadowy Leapin' Lizards");
+  glewInit();
+
+  if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
+    printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
+    exit(1);
+  }
+
+  /* Register GLUT callbacks. */
+  glutDisplayFunc(redraw);
+  glutMouseFunc(mouse);
+  glutMotionFunc(motion);
+  glutVisibilityFunc(visible);
+  glutKeyboardFunc(key);
+  glutSpecialFunc(special);
+
+  glutCreateMenu(controlLights);
+
+  glutAddMenuEntry("Toggle motion", M_MOTION);
+  glutAddMenuEntry("-----------------------", M_NONE);
+  glutAddMenuEntry("Toggle light", M_LIGHT);
+  glutAddMenuEntry("Toggle texture", M_TEXTURE);
+  glutAddMenuEntry("Toggle shadows", M_SHADOWS);
+  glutAddMenuEntry("Toggle reflection", M_REFLECTION);
+  glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR);
+  glutAddMenuEntry("-----------------------", M_NONE);
+  glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
+  glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
+  glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
+  glutAddMenuEntry("----------------------", M_NONE);
+  glutAddMenuEntry("Positional light", M_POSITIONAL);
+  glutAddMenuEntry("Directional light", M_DIRECTIONAL);
+  glutAddMenuEntry("-----------------------", M_NONE);
+  glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
+  glutAttachMenu(GLUT_RIGHT_BUTTON);
+  makeDinosaur();
+
+#ifdef GL_VERSION_1_1
+  if (supportsOneDotOne() && !forceExtension) {
+    polygonOffsetVersion = ONE_DOT_ONE;
+    glPolygonOffset(-2.0, -9.0);
+  } else
+#endif
+  {
+#ifdef GL_EXT_polygon_offset
+  /* check for the polygon offset extension */
+  if (glutExtensionSupported("GL_EXT_polygon_offset")) {
+    polygonOffsetVersion = EXTENSION;
+    glPolygonOffsetEXT(-2.0, -0.002);
+  } else 
+#endif
+    {
+      polygonOffsetVersion = MISSING;
+      printf("\ndinoshine: Missing polygon offset.\n");
+      printf("           Expect shadow depth aliasing artifacts.\n\n");
+    }
+  }
+
+  glEnable(GL_CULL_FACE);
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_TEXTURE_2D);
+  glLineWidth(3.0);
+
+  glMatrixMode(GL_PROJECTION);
+  gluPerspective( /* field of view in degree */ 40.0,
+  /* aspect ratio */ 1.0,
+    /* Z near */ 20.0, /* Z far */ 100.0);
+  glMatrixMode(GL_MODELVIEW);
+  gluLookAt(0.0, 8.0, 60.0,  /* eye is at (0,8,60) */
+    0.0, 8.0, 0.0,      /* center is at (0,8,0) */
+    0.0, 1.0, 0.);      /* up is in postivie Y direction */
+
+  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
+  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
+  glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
+  glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
+  glEnable(GL_LIGHT0);
+  glEnable(GL_LIGHTING);
+
+  makeFloorTexture();
+
+  /* Setup floor plane for projected shadow calculations. */
+  findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
+
+  glutMainLoop();
+  return 0;             /* ANSI C requires main to return int. */
+}
index 24275fdc2f152d03e4a547e163fb5b0717f76741..e5da72a3e4b07a7fade48255d30685f29a5454c4 100644 (file)
@@ -37,7 +37,6 @@ SOURCES = \
        copypixrate.c \
        crossbar.c \
        cva.c \
-       dinoshade.c \
        drawbuffers.c \
        exactrast.c \
        floattex.c \
diff --git a/progs/tests/dinoshade.c b/progs/tests/dinoshade.c
deleted file mode 100644 (file)
index fb7c3f4..0000000
+++ /dev/null
@@ -1,912 +0,0 @@
-
-/* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
-
-/* 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. */
-
-/* Example for PC game developers to show how to *combine* texturing,
-   reflections, and projected shadows all in real-time with OpenGL.
-   Robust reflections use stenciling.  Robust projected shadows
-   use both stenciling and polygon offset.  PC game programmers
-   should realize that neither stenciling nor polygon offset are 
-   supported by Direct3D, so these real-time rendering algorithms
-   are only really viable with OpenGL. 
-   
-   The program has modes for disabling the stenciling and polygon
-   offset uses.  It is worth running this example with these features
-   toggled off so you can see the sort of artifacts that result.
-   
-   Notice that the floor texturing, reflections, and shadowing
-   all co-exist properly. */
-
-/* When you run this program:  Left mouse button controls the
-   view.  Middle mouse button controls light position (left &
-   right rotates light around dino; up & down moves light
-   position up and down).  Right mouse button pops up menu. */
-
-/* Check out the comments in the "redraw" routine to see how the
-   reflection blending and surface stenciling is done.  You can
-   also see in "redraw" how the projected shadows are rendered,
-   including the use of stenciling and polygon offset. */
-
-/* This program is derived from glutdino.c */
-
-/* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>       /* for cos(), sin(), and sqrt() */
-#include <stddef.h>    /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
-#ifdef _WIN32
-#include <windows.h>
-#endif
-#define GL_GLEXT_LEGACY
-#include <GL/glew.h>    /* OpenGL Utility Toolkit header */
-#include <GL/glut.h>    /* OpenGL Utility Toolkit header */
-
-/* Some <math.h> files do not define M_PI... */
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
-/* Variable controlling various rendering modes. */
-static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1;
-static int renderShadow = 1, renderDinosaur = 1, renderReflection = 1;
-static int linearFiltering = 0, useMipmaps = 0, useTexture = 1;
-static int reportSpeed = 0;
-static int animation = 1;
-static GLboolean lightSwitch = GL_TRUE;
-static int directionalLight = 1;
-static int forceExtension = 0;
-
-/* Time varying or user-controled variables. */
-static float jump = 0.0;
-static float lightAngle = 0.0, lightHeight = 20;
-GLfloat angle = -150;   /* in degrees */
-GLfloat angle2 = 30;   /* in degrees */
-
-int moving, startx, starty;
-int lightMoving = 0, lightStartX, lightStartY;
-
-enum {
-  MISSING, EXTENSION, ONE_DOT_ONE
-};
-int polygonOffsetVersion;
-
-static GLdouble bodyWidth = 3.0;
-/* *INDENT-OFF* */
-static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
-  {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
-  {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
-  {1, 2} };
-static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
-  {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
-  {13, 9}, {11, 11}, {9, 11} };
-static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
-  {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
-static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
-  {9.6, 15.25}, {9, 15.25} };
-static GLfloat lightPosition[4];
-static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
-static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0};
-/* *INDENT-ON* */
-
-/* Nice floor texture tiling pattern. */
-static char *circles[] = {
-  "....xxxx........",
-  "..xxxxxxxx......",
-  ".xxxxxxxxxx.....",
-  ".xxx....xxx.....",
-  "xxx......xxx....",
-  "xxx......xxx....",
-  "xxx......xxx....",
-  "xxx......xxx....",
-  ".xxx....xxx.....",
-  ".xxxxxxxxxx.....",
-  "..xxxxxxxx......",
-  "....xxxx........",
-  "................",
-  "................",
-  "................",
-  "................",
-};
-
-static void
-makeFloorTexture(void)
-{
-  GLubyte floorTexture[16][16][3];
-  GLubyte *loc;
-  int s, t;
-
-  /* Setup RGB image for the texture. */
-  loc = (GLubyte*) floorTexture;
-  for (t = 0; t < 16; t++) {
-    for (s = 0; s < 16; s++) {
-      if (circles[t][s] == 'x') {
-       /* Nice green. */
-        loc[0] = 0x1f;
-        loc[1] = 0x8f;
-        loc[2] = 0x1f;
-      } else {
-       /* Light gray. */
-        loc[0] = 0xaa;
-        loc[1] = 0xaa;
-        loc[2] = 0xaa;
-      }
-      loc += 3;
-    }
-  }
-
-  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
-  if (useMipmaps) {
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
-      GL_LINEAR_MIPMAP_LINEAR);
-    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
-      GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
-  } else {
-    if (linearFiltering) {
-      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    } else {
-      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    }
-    glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
-      GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
-  }
-}
-
-enum {
-  X, Y, Z, W
-};
-enum {
-  A, B, C, D
-};
-
-/* Create a matrix that will project the desired shadow. */
-void
-shadowMatrix(GLfloat shadowMat[4][4],
-  GLfloat groundplane[4],
-  GLfloat lightpos[4])
-{
-  GLfloat dot;
-
-  /* Find dot product between light position vector and ground plane normal. */
-  dot = groundplane[X] * lightpos[X] +
-    groundplane[Y] * lightpos[Y] +
-    groundplane[Z] * lightpos[Z] +
-    groundplane[W] * lightpos[W];
-
-  shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
-  shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
-  shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
-  shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
-
-  shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
-  shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
-  shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
-  shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
-
-  shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
-  shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
-  shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
-  shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
-
-  shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
-  shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
-  shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
-  shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
-
-}
-
-/* Find the plane equation given 3 points. */
-void
-findPlane(GLfloat plane[4],
-  GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
-{
-  GLfloat vec0[3], vec1[3];
-
-  /* Need 2 vectors to find cross product. */
-  vec0[X] = v1[X] - v0[X];
-  vec0[Y] = v1[Y] - v0[Y];
-  vec0[Z] = v1[Z] - v0[Z];
-
-  vec1[X] = v2[X] - v0[X];
-  vec1[Y] = v2[Y] - v0[Y];
-  vec1[Z] = v2[Z] - v0[Z];
-
-  /* find cross product to get A, B, and C of plane equation */
-  plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
-  plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
-  plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
-
-  plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
-}
-
-void
-extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
-  GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
-{
-  static GLUtriangulatorObj *tobj = NULL;
-  GLdouble vertex[3], dx, dy, len;
-  int i;
-  int count = (int) (dataSize / (2 * sizeof(GLfloat)));
-
-  if (tobj == NULL) {
-    tobj = gluNewTess();  /* create and initialize a GLU
-                             polygon tesselation object */
-    gluTessCallback(tobj, GLU_BEGIN, glBegin);
-    gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);  /* semi-tricky */
-    gluTessCallback(tobj, GLU_END, glEnd);
-  }
-  glNewList(side, GL_COMPILE);
-  glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
-                               tessellation */
-  gluBeginPolygon(tobj);
-  for (i = 0; i < count; i++) {
-    vertex[0] = data[i][0];
-    vertex[1] = data[i][1];
-    vertex[2] = 0;
-    gluTessVertex(tobj, vertex, data[i]);
-  }
-  gluEndPolygon(tobj);
-  glEndList();
-  glNewList(edge, GL_COMPILE);
-  glShadeModel(GL_FLAT);  /* flat shade keeps angular hands
-                             from being "smoothed" */
-  glBegin(GL_QUAD_STRIP);
-  for (i = 0; i <= count; i++) {
-#if 1 /* weird, but seems to be legal */
-    /* mod function handles closing the edge */
-    glVertex3f(data[i % count][0], data[i % count][1], 0.0);
-    glVertex3f(data[i % count][0], data[i % count][1], thickness);
-    /* Calculate a unit normal by dividing by Euclidean
-       distance. We * could be lazy and use
-       glEnable(GL_NORMALIZE) so we could pass in * arbitrary
-       normals for a very slight performance hit. */
-    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);
-#else /* the nice way of doing it */
-    /* Calculate a unit normal by dividing by Euclidean
-       distance. We * could be lazy and use
-       glEnable(GL_NORMALIZE) so we could pass in * arbitrary
-       normals for a very slight performance hit. */
-    dx = data[i % count][1] - data[(i - 1 + count) % count][1];
-    dy = data[(i - 1 + count) % count][0] - data[i % count][0];
-    len = sqrt(dx * dx + dy * dy);
-    glNormal3f(dx / len, dy / len, 0.0);
-    /* mod function handles closing the edge */
-    glVertex3f(data[i % count][0], data[i % count][1], 0.0);
-    glVertex3f(data[i % count][0], data[i % count][1], thickness);
-#endif
-  }
-  glEnd();
-  glEndList();
-  glNewList(whole, GL_COMPILE);
-  glFrontFace(GL_CW);
-  glCallList(edge);
-  glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
-  glCallList(side);
-  glPushMatrix();
-  glTranslatef(0.0, 0.0, thickness);
-  glFrontFace(GL_CCW);
-  glNormal3f(0.0, 0.0, 1.0);  /* opposite normal for other side */
-  glCallList(side);
-  glPopMatrix();
-  glEndList();
-}
-
-/* Enumerants for refering to display lists. */
-typedef enum {
-  RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
-  LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE
-} displayLists;
-
-static void
-makeDinosaur(void)
-{
-  extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
-    BODY_SIDE, BODY_EDGE, BODY_WHOLE);
-  extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
-    ARM_SIDE, ARM_EDGE, ARM_WHOLE);
-  extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
-    LEG_SIDE, LEG_EDGE, LEG_WHOLE);
-  extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
-    EYE_SIDE, EYE_EDGE, EYE_WHOLE);
-}
-
-static void
-drawDinosaur(void)
-
-{
-  glPushMatrix();
-  /* Translate the dinosaur to be at (0,8,0). */
-  glTranslatef(-8, 0, -bodyWidth / 2);
-  glTranslatef(0.0, jump, 0.0);
-  glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
-  glCallList(BODY_WHOLE);
-  glTranslatef(0.0, 0.0, bodyWidth);
-  glCallList(ARM_WHOLE);
-  glCallList(LEG_WHOLE);
-  glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
-  glCallList(ARM_WHOLE);
-  glTranslatef(0.0, 0.0, -bodyWidth / 4);
-  glCallList(LEG_WHOLE);
-  glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1);
-  glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
-  glCallList(EYE_WHOLE);
-  glPopMatrix();
-}
-
-static GLfloat floorVertices[4][3] = {
-  { -20.0, 0.0, 20.0 },
-  { 20.0, 0.0, 20.0 },
-  { 20.0, 0.0, -20.0 },
-  { -20.0, 0.0, -20.0 },
-};
-
-/* Draw a floor (possibly textured). */
-static void
-drawFloor(void)
-{
-  glDisable(GL_LIGHTING);
-
-  if (useTexture) {
-    glEnable(GL_TEXTURE_2D);
-  }
-
-  glBegin(GL_QUADS);
-    glTexCoord2f(0.0, 0.0);
-    glVertex3fv(floorVertices[0]);
-    glTexCoord2f(0.0, 16.0);
-    glVertex3fv(floorVertices[1]);
-    glTexCoord2f(16.0, 16.0);
-    glVertex3fv(floorVertices[2]);
-    glTexCoord2f(16.0, 0.0);
-    glVertex3fv(floorVertices[3]);
-  glEnd();
-
-  if (useTexture) {
-    glDisable(GL_TEXTURE_2D);
-  }
-
-  glEnable(GL_LIGHTING);
-}
-
-static GLfloat floorPlane[4];
-static GLfloat floorShadow[4][4];
-
-static void
-redraw(void)
-{
-  int start, end;
-
-  if (reportSpeed) {
-    start = glutGet(GLUT_ELAPSED_TIME);
-  }
-
-  /* Clear; default stencil clears to zero. */
-  if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow)) {
-    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-  } else {
-    /* Avoid clearing stencil when not using it. */
-    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-  }
-
-  /* Reposition the light source. */
-  lightPosition[0] = 12*cos(lightAngle);
-  lightPosition[1] = lightHeight;
-  lightPosition[2] = 12*sin(lightAngle);
-  if (directionalLight) {
-    lightPosition[3] = 0.0;
-  } else {
-    lightPosition[3] = 1.0;
-  }
-
-  shadowMatrix(floorShadow, floorPlane, lightPosition);
-
-  glPushMatrix();
-    /* Perform scene rotations based on user mouse input. */
-    glRotatef(angle2, 1.0, 0.0, 0.0);
-    glRotatef(angle, 0.0, 1.0, 0.0);
-     
-    /* Tell GL new light source position. */
-    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
-
-    if (renderReflection) {
-      if (stencilReflection) {
-        /* We can eliminate the visual "artifact" of seeing the "flipped"
-          dinosaur underneath the floor by using stencil.  The idea is
-          draw the floor without color or depth update but so that 
-          a stencil value of one is where the floor will be.  Later when
-          rendering the dinosaur reflection, we will only update pixels
-          with a stencil value of 1 to make sure the reflection only
-          lives on the floor, not below the floor. */
-
-        /* Don't update color or depth. */
-        glDisable(GL_DEPTH_TEST);
-        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-
-        /* Draw 1 into the stencil buffer. */
-        glEnable(GL_STENCIL_TEST);
-        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
-        glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
-
-        /* Now render floor; floor pixels just get their stencil set to 1. */
-        drawFloor();
-
-        /* Re-enable update of color and depth. */ 
-        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-        glEnable(GL_DEPTH_TEST);
-
-        /* Now, only render where stencil is set to 1. */
-        glStencilFunc(GL_EQUAL, 1, 0xffffffff);  /* draw if ==1 */
-        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-      }
-
-      glPushMatrix();
-
-        /* The critical reflection step: Reflect dinosaur through the floor
-           (the Y=0 plane) to make a relection. */
-        glScalef(1.0, -1.0, 1.0);
-
-       /* Reflect the light position. */
-        glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
-
-        /* To avoid our normals getting reversed and hence botched lighting
-          on the reflection, turn on normalize.  */
-        glEnable(GL_NORMALIZE);
-        glCullFace(GL_FRONT);
-
-        /* Draw the reflected dinosaur. */
-        drawDinosaur();
-
-        /* Disable noramlize again and re-enable back face culling. */
-        glDisable(GL_NORMALIZE);
-        glCullFace(GL_BACK);
-
-      glPopMatrix();
-
-      /* Switch back to the unreflected light position. */
-      glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
-
-      if (stencilReflection) {
-        glDisable(GL_STENCIL_TEST);
-      }
-    }
-
-    /* Back face culling will get used to only draw either the top or the
-       bottom floor.  This let's us get a floor with two distinct
-       appearances.  The top floor surface is reflective and kind of red.
-       The bottom floor surface is not reflective and blue. */
-
-    /* Draw "bottom" of floor in blue. */
-    glFrontFace(GL_CW);  /* Switch face orientation. */
-    glColor4f(0.1, 0.1, 0.7, 1.0);
-    drawFloor();
-    glFrontFace(GL_CCW);
-
-    if (renderShadow) {
-      if (stencilShadow) {
-       /* Draw the floor with stencil value 3.  This helps us only 
-          draw the shadow once per floor pixel (and only on the
-          floor pixels). */
-        glEnable(GL_STENCIL_TEST);
-        glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
-        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
-      }
-    }
-
-    /* Draw "top" of floor.  Use blending to blend in reflection. */
-    glEnable(GL_BLEND);
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glColor4f(0.7, 0.0, 0.0, 0.3);
-    glColor4f(1.0, 1.0, 1.0, 0.3);
-    drawFloor();
-    glDisable(GL_BLEND);
-
-    if (renderDinosaur) {
-      /* Draw "actual" dinosaur, not its reflection. */
-      drawDinosaur();
-    }
-
-    if (renderShadow) {
-
-      /* Render the projected shadow. */
-
-      if (stencilShadow) {
-
-        /* Now, only render where stencil is set above 2 (ie, 3 where
-          the top floor is).  Update stencil with 2 where the shadow
-          gets drawn so we don't redraw (and accidently reblend) the
-          shadow). */
-        glStencilFunc(GL_LESS, 2, 0xffffffff);  /* draw if ==1 */
-        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
-      }
-
-      /* To eliminate depth buffer artifacts, we use polygon offset
-        to raise the depth of the projected shadow slightly so
-        that it does not depth buffer alias with the floor. */
-      if (offsetShadow) {
-       switch (polygonOffsetVersion) {
-       case EXTENSION:
-#ifdef GL_EXT_polygon_offset
-         glEnable(GL_POLYGON_OFFSET_EXT);
-         break;
-#endif
-#ifdef GL_VERSION_1_1
-       case ONE_DOT_ONE:
-          glEnable(GL_POLYGON_OFFSET_FILL);
-         break;
-#endif
-       case MISSING:
-         /* Oh well. */
-         break;
-       }
-      }
-
-      /* Render 50% black shadow color on top of whatever the
-         floor appareance is. */
-      glEnable(GL_BLEND);
-      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-      glDisable(GL_LIGHTING);  /* Force the 50% black. */
-      glColor4f(0.0, 0.0, 0.0, 0.5);
-
-      glPushMatrix();
-       /* Project the shadow. */
-        glMultMatrixf((GLfloat *) floorShadow);
-        drawDinosaur();
-      glPopMatrix();
-
-      glDisable(GL_BLEND);
-      glEnable(GL_LIGHTING);
-
-      if (offsetShadow) {
-       switch (polygonOffsetVersion) {
-#ifdef GL_EXT_polygon_offset
-       case EXTENSION:
-         glDisable(GL_POLYGON_OFFSET_EXT);
-         break;
-#endif
-#ifdef GL_VERSION_1_1
-       case ONE_DOT_ONE:
-          glDisable(GL_POLYGON_OFFSET_FILL);
-         break;
-#endif
-       case MISSING:
-         /* Oh well. */
-         break;
-       }
-      }
-      if (stencilShadow) {
-        glDisable(GL_STENCIL_TEST);
-      }
-    }
-
-    glPushMatrix();
-    glDisable(GL_LIGHTING);
-    glColor3f(1.0, 1.0, 0.0);
-    if (directionalLight) {
-      /* Draw an arrowhead. */
-      glDisable(GL_CULL_FACE);
-      glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
-      glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0);
-      glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1);
-      glBegin(GL_TRIANGLE_FAN);
-       glVertex3f(0, 0, 0);
-       glVertex3f(2, 1, 1);
-       glVertex3f(2, -1, 1);
-       glVertex3f(2, -1, -1);
-       glVertex3f(2, 1, -1);
-       glVertex3f(2, 1, 1);
-      glEnd();
-      /* Draw a white line from light direction. */
-      glColor3f(1.0, 1.0, 1.0);
-      glBegin(GL_LINES);
-       glVertex3f(0, 0, 0);
-       glVertex3f(5, 0, 0);
-      glEnd();
-      glEnable(GL_CULL_FACE);
-    } else {
-      /* Draw a yellow ball at the light source. */
-      glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
-      glutSolidSphere(1.0, 5, 5);
-    }
-    glEnable(GL_LIGHTING);
-    glPopMatrix();
-
-  glPopMatrix();
-
-  if (reportSpeed) {
-    glFinish();
-    end = glutGet(GLUT_ELAPSED_TIME);
-    printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
-  }
-
-  glutSwapBuffers();
-}
-
-/* ARGSUSED2 */
-static void
-mouse(int button, int state, int x, int y)
-{
-  if (button == GLUT_LEFT_BUTTON) {
-    if (state == GLUT_DOWN) {
-      moving = 1;
-      startx = x;
-      starty = y;
-    }
-    if (state == GLUT_UP) {
-      moving = 0;
-    }
-  }
-  if (button == GLUT_MIDDLE_BUTTON) {
-    if (state == GLUT_DOWN) {
-      lightMoving = 1;
-      lightStartX = x;
-      lightStartY = y;
-    }
-    if (state == GLUT_UP) {
-      lightMoving = 0;
-    }
-  }
-}
-
-/* ARGSUSED1 */
-static void
-motion(int x, int y)
-{
-  if (moving) {
-    angle = angle + (x - startx);
-    angle2 = angle2 + (y - starty);
-    startx = x;
-    starty = y;
-    glutPostRedisplay();
-  }
-  if (lightMoving) {
-    lightAngle += (x - lightStartX)/40.0;
-    lightHeight += (lightStartY - y)/20.0;
-    lightStartX = x;
-    lightStartY = y;
-    glutPostRedisplay();
-  }
-}
-
-/* Advance time varying state when idle callback registered. */
-static void
-idle(void)
-{
-  static float time = 0.0;
-
-  time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
-
-  jump = 4.0 * fabs(sin(time)*0.5);
-  if (!lightMoving) {
-    lightAngle += 0.03;
-  }
-  glutPostRedisplay();
-}
-
-enum {
-  M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
-  M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
-  M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
-};
-
-static void
-controlLights(int value)
-{
-  switch (value) {
-  case M_NONE:
-    return;
-  case M_MOTION:
-    animation = 1 - animation;
-    if (animation) {
-      glutIdleFunc(idle);
-    } else {
-      glutIdleFunc(NULL);
-    }
-    break;
-  case M_LIGHT:
-    lightSwitch = !lightSwitch;
-    if (lightSwitch) {
-      glEnable(GL_LIGHT0);
-    } else {
-      glDisable(GL_LIGHT0);
-    }
-    break;
-  case M_TEXTURE:
-    useTexture = !useTexture;
-    break;
-  case M_SHADOWS:
-    renderShadow = 1 - renderShadow;
-    break;
-  case M_REFLECTION:
-    renderReflection = 1 - renderReflection;
-    break;
-  case M_DINOSAUR:
-    renderDinosaur = 1 - renderDinosaur;
-    break;
-  case M_STENCIL_REFLECTION:
-    stencilReflection = 1 - stencilReflection;
-    break;
-  case M_STENCIL_SHADOW:
-    stencilShadow = 1 - stencilShadow;
-    break;
-  case M_OFFSET_SHADOW:
-    offsetShadow = 1 - offsetShadow;
-    break;
-  case M_POSITIONAL:
-    directionalLight = 0;
-    break;
-  case M_DIRECTIONAL:
-    directionalLight = 1;
-    break;
-  case M_PERFORMANCE:
-    reportSpeed = 1 - reportSpeed;
-    break;
-  }
-  glutPostRedisplay();
-}
-
-/* When not visible, stop animating.  Restart when visible again. */
-static void 
-visible(int vis)
-{
-  if (vis == GLUT_VISIBLE) {
-    if (animation)
-      glutIdleFunc(idle);
-  } else {
-    if (!animation)
-      glutIdleFunc(NULL);
-  }
-}
-
-/* Press any key to redraw; good when motion stopped and
-   performance reporting on. */
-/* ARGSUSED */
-static void
-key(unsigned char c, int x, int y)
-{
-  if (c == 27) {
-    exit(0);  /* IRIS GLism, Escape quits. */
-  }
-  glutPostRedisplay();
-}
-
-/* Press any key to redraw; good when motion stopped and
-   performance reporting on. */
-/* ARGSUSED */
-static void
-special(int k, int x, int y)
-{
-  glutPostRedisplay();
-}
-
-static int
-supportsOneDotOne(void)
-{
-  const char *version;
-  int major, minor;
-
-  version = (char *) glGetString(GL_VERSION);
-  if (sscanf(version, "%d.%d", &major, &minor) == 2)
-    return major * 10 + minor >= 11;
-  return 0;            /* OpenGL version string malformed! */
-}
-
-int
-main(int argc, char **argv)
-{
-  int i;
-
-  glutInit(&argc, argv);
-
-  for (i=1; i<argc; i++) {
-    if (!strcmp("-linear", argv[i])) {
-      linearFiltering = 1;
-    } else if (!strcmp("-mipmap", argv[i])) {
-      useMipmaps = 1;
-    } else if (!strcmp("-ext", argv[i])) {
-      forceExtension = 1;
-    }
-  }
-
-  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
-
-#if 0
-  /* In GLUT 4.0, you'll be able to do this an be sure to
-     get 2 bits of stencil if the machine has it for you. */
-  glutInitDisplayString("samples stencil>=2 rgb double depth");
-#endif
-
-  glutCreateWindow("Shadowy Leapin' Lizards");
-  glewInit();
-
-  if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
-    printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
-    exit(1);
-  }
-
-  /* Register GLUT callbacks. */
-  glutDisplayFunc(redraw);
-  glutMouseFunc(mouse);
-  glutMotionFunc(motion);
-  glutVisibilityFunc(visible);
-  glutKeyboardFunc(key);
-  glutSpecialFunc(special);
-
-  glutCreateMenu(controlLights);
-
-  glutAddMenuEntry("Toggle motion", M_MOTION);
-  glutAddMenuEntry("-----------------------", M_NONE);
-  glutAddMenuEntry("Toggle light", M_LIGHT);
-  glutAddMenuEntry("Toggle texture", M_TEXTURE);
-  glutAddMenuEntry("Toggle shadows", M_SHADOWS);
-  glutAddMenuEntry("Toggle reflection", M_REFLECTION);
-  glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR);
-  glutAddMenuEntry("-----------------------", M_NONE);
-  glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
-  glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
-  glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
-  glutAddMenuEntry("----------------------", M_NONE);
-  glutAddMenuEntry("Positional light", M_POSITIONAL);
-  glutAddMenuEntry("Directional light", M_DIRECTIONAL);
-  glutAddMenuEntry("-----------------------", M_NONE);
-  glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
-  glutAttachMenu(GLUT_RIGHT_BUTTON);
-  makeDinosaur();
-
-#ifdef GL_VERSION_1_1
-  if (supportsOneDotOne() && !forceExtension) {
-    polygonOffsetVersion = ONE_DOT_ONE;
-    glPolygonOffset(-2.0, -9.0);
-  } else
-#endif
-  {
-#ifdef GL_EXT_polygon_offset
-  /* check for the polygon offset extension */
-  if (glutExtensionSupported("GL_EXT_polygon_offset")) {
-    polygonOffsetVersion = EXTENSION;
-    glPolygonOffsetEXT(-2.0, -0.002);
-  } else 
-#endif
-    {
-      polygonOffsetVersion = MISSING;
-      printf("\ndinoshine: Missing polygon offset.\n");
-      printf("           Expect shadow depth aliasing artifacts.\n\n");
-    }
-  }
-
-  glEnable(GL_CULL_FACE);
-  glEnable(GL_DEPTH_TEST);
-  glEnable(GL_TEXTURE_2D);
-  glLineWidth(3.0);
-
-  glMatrixMode(GL_PROJECTION);
-  gluPerspective( /* field of view in degree */ 40.0,
-  /* aspect ratio */ 1.0,
-    /* Z near */ 20.0, /* Z far */ 100.0);
-  glMatrixMode(GL_MODELVIEW);
-  gluLookAt(0.0, 8.0, 60.0,  /* eye is at (0,8,60) */
-    0.0, 8.0, 0.0,      /* center is at (0,8,0) */
-    0.0, 1.0, 0.);      /* up is in postivie Y direction */
-
-  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
-  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
-  glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
-  glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
-  glEnable(GL_LIGHT0);
-  glEnable(GL_LIGHTING);
-
-  makeFloorTexture();
-
-  /* Setup floor plane for projected shadow calculations. */
-  findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
-
-  glutMainLoop();
-  return 0;             /* ANSI C requires main to return int. */
-}