Home | History | Annotate | Line # | Download | only in demos
      1 
      2 /* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
      3 
      4 /* This program is freely distributable without licensing fees
      5    and is provided without guarantee or warrantee expressed or
      6    implied. This program is -not- in the public domain. */
      7 
      8 /* Example for PC game developers to show how to *combine* texturing,
      9    reflections, and projected shadows all in real-time with OpenGL.
     10    Robust reflections use stenciling.  Robust projected shadows
     11    use both stenciling and polygon offset.  PC game programmers
     12    should realize that neither stenciling nor polygon offset are
     13    supported by Direct3D, so these real-time rendering algorithms
     14    are only really viable with OpenGL.
     15 
     16    The program has modes for disabling the stenciling and polygon
     17    offset uses.  It is worth running this example with these features
     18    toggled off so you can see the sort of artifacts that result.
     19 
     20    Notice that the floor texturing, reflections, and shadowing
     21    all co-exist properly. */
     22 
     23 /* When you run this program:  Left mouse button controls the
     24    view.  Middle mouse button controls light position (left &
     25    right rotates light around dino; up & down moves light
     26    position up and down).  Right mouse button pops up menu. */
     27 
     28 /* Check out the comments in the "redraw" routine to see how the
     29    reflection blending and surface stenciling is done.  You can
     30    also see in "redraw" how the projected shadows are rendered,
     31    including the use of stenciling and polygon offset. */
     32 
     33 /* This program is derived from glutdino.c */
     34 
     35 /* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
     36 
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <math.h>       /* for cos(), sin(), and sqrt() */
     41 #include <stddef.h>	/* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
     42 #ifdef _WIN32
     43 #include <windows.h>
     44 #endif
     45 #define GL_GLEXT_LEGACY
     46 #include <GL/glew.h>    /* OpenGL Utility Toolkit header */
     47 #include "glut_wrap.h"    /* OpenGL Utility Toolkit header */
     48 
     49 /* Some <math.h> files do not define M_PI... */
     50 #ifndef M_PI
     51 #define M_PI 3.14159265358979323846
     52 #endif
     53 
     54 /* Variable controlling various rendering modes. */
     55 static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1;
     56 static int renderShadow = 1, renderDinosaur = 1, renderReflection = 1;
     57 static int linearFiltering = 0, useMipmaps = 0, useTexture = 1;
     58 static int reportSpeed = 0;
     59 static int animation = 1;
     60 static GLboolean lightSwitch = GL_TRUE;
     61 static int directionalLight = 1;
     62 static int forceExtension = 0;
     63 
     64 /* Time varying or user-controled variables. */
     65 static float jump = 0.0;
     66 static float lightAngle = 0.0, lightHeight = 20;
     67 GLfloat angle = -150;   /* in degrees */
     68 GLfloat angle2 = 30;   /* in degrees */
     69 
     70 int moving, startx, starty;
     71 int lightMoving = 0, lightStartX, lightStartY;
     72 
     73 enum {
     74   MISSING, EXTENSION, ONE_DOT_ONE
     75 };
     76 int polygonOffsetVersion;
     77 
     78 static GLdouble bodyWidth = 3.0;
     79 /* *INDENT-OFF* */
     80 static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
     81   {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
     82   {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
     83   {1, 2} };
     84 static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
     85   {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
     86   {13, 9}, {11, 11}, {9, 11} };
     87 static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
     88   {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
     89 static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
     90   {9.6, 15.25}, {9, 15.25} };
     91 static GLfloat lightPosition[4];
     92 static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
     93 static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0};
     94 /* *INDENT-ON* */
     95 
     96 /* Nice floor texture tiling pattern. */
     97 static char *circles[] = {
     98   "....xxxx........",
     99   "..xxxxxxxx......",
    100   ".xxxxxxxxxx.....",
    101   ".xxx....xxx.....",
    102   "xxx......xxx....",
    103   "xxx......xxx....",
    104   "xxx......xxx....",
    105   "xxx......xxx....",
    106   ".xxx....xxx.....",
    107   ".xxxxxxxxxx.....",
    108   "..xxxxxxxx......",
    109   "....xxxx........",
    110   "................",
    111   "................",
    112   "................",
    113   "................",
    114 };
    115 
    116 static void
    117 makeFloorTexture(void)
    118 {
    119   GLubyte floorTexture[16][16][3];
    120   GLubyte *loc;
    121   int s, t;
    122 
    123   /* Setup RGB image for the texture. */
    124   loc = (GLubyte*) floorTexture;
    125   for (t = 0; t < 16; t++) {
    126     for (s = 0; s < 16; s++) {
    127       if (circles[t][s] == 'x') {
    128 	/* Nice green. */
    129         loc[0] = 0x1f;
    130         loc[1] = 0x8f;
    131         loc[2] = 0x1f;
    132       } else {
    133 	/* Light gray. */
    134         loc[0] = 0xaa;
    135         loc[1] = 0xaa;
    136         loc[2] = 0xaa;
    137       }
    138       loc += 3;
    139     }
    140   }
    141 
    142   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    143 
    144   if (useMipmaps) {
    145     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    146       GL_LINEAR_MIPMAP_LINEAR);
    147     gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
    148       GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
    149   } else {
    150     if (linearFiltering) {
    151       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    152     } else {
    153       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    154     }
    155     glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
    156       GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
    157   }
    158 }
    159 
    160 enum {
    161   X, Y, Z, W
    162 };
    163 enum {
    164   A, B, C, D
    165 };
    166 
    167 /* Create a matrix that will project the desired shadow. */
    168 static void
    169 shadowMatrix(GLfloat shadowMat[4][4],
    170   GLfloat groundplane[4],
    171   GLfloat lightpos[4])
    172 {
    173   GLfloat dot;
    174 
    175   /* Find dot product between light position vector and ground plane normal. */
    176   dot = groundplane[X] * lightpos[X] +
    177     groundplane[Y] * lightpos[Y] +
    178     groundplane[Z] * lightpos[Z] +
    179     groundplane[W] * lightpos[W];
    180 
    181   shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
    182   shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
    183   shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
    184   shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
    185 
    186   shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
    187   shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
    188   shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
    189   shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
    190 
    191   shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
    192   shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
    193   shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
    194   shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
    195 
    196   shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
    197   shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
    198   shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
    199   shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
    200 
    201 }
    202 
    203 /* Find the plane equation given 3 points. */
    204 static void
    205 findPlane(GLfloat plane[4],
    206   GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
    207 {
    208   GLfloat vec0[3], vec1[3];
    209 
    210   /* Need 2 vectors to find cross product. */
    211   vec0[X] = v1[X] - v0[X];
    212   vec0[Y] = v1[Y] - v0[Y];
    213   vec0[Z] = v1[Z] - v0[Z];
    214 
    215   vec1[X] = v2[X] - v0[X];
    216   vec1[Y] = v2[Y] - v0[Y];
    217   vec1[Z] = v2[Z] - v0[Z];
    218 
    219   /* find cross product to get A, B, and C of plane equation */
    220   plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
    221   plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
    222   plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
    223 
    224   plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
    225 }
    226 
    227 static void
    228 extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
    229   GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
    230 {
    231   static GLUtriangulatorObj *tobj = NULL;
    232   GLdouble vertex[3], dx, dy, len;
    233   int i;
    234   int count = (int) (dataSize / (2 * sizeof(GLfloat)));
    235 
    236   if (tobj == NULL) {
    237     tobj = gluNewTess();  /* create and initialize a GLU
    238                              polygon tesselation object */
    239     gluTessCallback(tobj, GLU_BEGIN, glBegin);
    240     gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);  /* semi-tricky */
    241     gluTessCallback(tobj, GLU_END, glEnd);
    242   }
    243   glNewList(side, GL_COMPILE);
    244   glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
    245                                tessellation */
    246   gluBeginPolygon(tobj);
    247   for (i = 0; i < count; i++) {
    248     vertex[0] = data[i][0];
    249     vertex[1] = data[i][1];
    250     vertex[2] = 0;
    251     gluTessVertex(tobj, vertex, data[i]);
    252   }
    253   gluEndPolygon(tobj);
    254   glEndList();
    255   glNewList(edge, GL_COMPILE);
    256   glShadeModel(GL_FLAT);  /* flat shade keeps angular hands
    257                              from being "smoothed" */
    258   glBegin(GL_QUAD_STRIP);
    259   for (i = 0; i <= count; i++) {
    260 #if 1 /* weird, but seems to be legal */
    261     /* mod function handles closing the edge */
    262     glVertex3f(data[i % count][0], data[i % count][1], 0.0);
    263     glVertex3f(data[i % count][0], data[i % count][1], thickness);
    264     /* Calculate a unit normal by dividing by Euclidean
    265        distance. We * could be lazy and use
    266        glEnable(GL_NORMALIZE) so we could pass in * arbitrary
    267        normals for a very slight performance hit. */
    268     dx = data[(i + 1) % count][1] - data[i % count][1];
    269     dy = data[i % count][0] - data[(i + 1) % count][0];
    270     len = sqrt(dx * dx + dy * dy);
    271     glNormal3f(dx / len, dy / len, 0.0);
    272 #else /* the nice way of doing it */
    273     /* Calculate a unit normal by dividing by Euclidean
    274        distance. We * could be lazy and use
    275        glEnable(GL_NORMALIZE) so we could pass in * arbitrary
    276        normals for a very slight performance hit. */
    277     dx = data[i % count][1] - data[(i - 1 + count) % count][1];
    278     dy = data[(i - 1 + count) % count][0] - data[i % count][0];
    279     len = sqrt(dx * dx + dy * dy);
    280     glNormal3f(dx / len, dy / len, 0.0);
    281     /* mod function handles closing the edge */
    282     glVertex3f(data[i % count][0], data[i % count][1], 0.0);
    283     glVertex3f(data[i % count][0], data[i % count][1], thickness);
    284 #endif
    285   }
    286   glEnd();
    287   glEndList();
    288   glNewList(whole, GL_COMPILE);
    289   glFrontFace(GL_CW);
    290   glCallList(edge);
    291   glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
    292   glCallList(side);
    293   glPushMatrix();
    294   glTranslatef(0.0, 0.0, thickness);
    295   glFrontFace(GL_CCW);
    296   glNormal3f(0.0, 0.0, 1.0);  /* opposite normal for other side */
    297   glCallList(side);
    298   glPopMatrix();
    299   glEndList();
    300 }
    301 
    302 /* Enumerants for refering to display lists. */
    303 typedef enum {
    304   RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
    305   LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE
    306 } displayLists;
    307 
    308 static void
    309 makeDinosaur(void)
    310 {
    311   extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
    312     BODY_SIDE, BODY_EDGE, BODY_WHOLE);
    313   extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
    314     ARM_SIDE, ARM_EDGE, ARM_WHOLE);
    315   extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
    316     LEG_SIDE, LEG_EDGE, LEG_WHOLE);
    317   extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
    318     EYE_SIDE, EYE_EDGE, EYE_WHOLE);
    319 }
    320 
    321 static void
    322 drawDinosaur(void)
    323 
    324 {
    325   glPushMatrix();
    326   /* Translate the dinosaur to be at (0,8,0). */
    327   glTranslatef(-8, 0, -bodyWidth / 2);
    328   glTranslatef(0.0, jump, 0.0);
    329   glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
    330   glCallList(BODY_WHOLE);
    331   glTranslatef(0.0, 0.0, bodyWidth);
    332   glCallList(ARM_WHOLE);
    333   glCallList(LEG_WHOLE);
    334   glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
    335   glCallList(ARM_WHOLE);
    336   glTranslatef(0.0, 0.0, -bodyWidth / 4);
    337   glCallList(LEG_WHOLE);
    338   glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1);
    339   glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
    340   glCallList(EYE_WHOLE);
    341   glPopMatrix();
    342 }
    343 
    344 static GLfloat floorVertices[4][3] = {
    345   { -20.0, 0.0, 20.0 },
    346   { 20.0, 0.0, 20.0 },
    347   { 20.0, 0.0, -20.0 },
    348   { -20.0, 0.0, -20.0 },
    349 };
    350 
    351 /* Draw a floor (possibly textured). */
    352 static void
    353 drawFloor(void)
    354 {
    355   glDisable(GL_LIGHTING);
    356 
    357   if (useTexture) {
    358     glEnable(GL_TEXTURE_2D);
    359   }
    360 
    361   glBegin(GL_QUADS);
    362     glTexCoord2f(0.0, 0.0);
    363     glVertex3fv(floorVertices[0]);
    364     glTexCoord2f(0.0, 16.0);
    365     glVertex3fv(floorVertices[1]);
    366     glTexCoord2f(16.0, 16.0);
    367     glVertex3fv(floorVertices[2]);
    368     glTexCoord2f(16.0, 0.0);
    369     glVertex3fv(floorVertices[3]);
    370   glEnd();
    371 
    372   if (useTexture) {
    373     glDisable(GL_TEXTURE_2D);
    374   }
    375 
    376   glEnable(GL_LIGHTING);
    377 }
    378 
    379 static GLfloat floorPlane[4];
    380 static GLfloat floorShadow[4][4];
    381 
    382 static void
    383 redraw(void)
    384 {
    385   int start = 0, end = 0;
    386 
    387   if (reportSpeed) {
    388     start = glutGet(GLUT_ELAPSED_TIME);
    389   }
    390 
    391   /* Clear; default stencil clears to zero. */
    392   if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow)) {
    393     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    394   } else {
    395     /* Avoid clearing stencil when not using it. */
    396     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    397   }
    398 
    399   /* Reposition the light source. */
    400   lightPosition[0] = 12*cos(lightAngle);
    401   lightPosition[1] = lightHeight;
    402   lightPosition[2] = 12*sin(lightAngle);
    403   if (directionalLight) {
    404     lightPosition[3] = 0.0;
    405   } else {
    406     lightPosition[3] = 1.0;
    407   }
    408 
    409   shadowMatrix(floorShadow, floorPlane, lightPosition);
    410 
    411   glPushMatrix();
    412     /* Perform scene rotations based on user mouse input. */
    413     glRotatef(angle2, 1.0, 0.0, 0.0);
    414     glRotatef(angle, 0.0, 1.0, 0.0);
    415 
    416     /* Tell GL new light source position. */
    417     glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    418 
    419     if (renderReflection) {
    420       if (stencilReflection) {
    421         /* We can eliminate the visual "artifact" of seeing the "flipped"
    422   	   dinosaur underneath the floor by using stencil.  The idea is
    423 	   draw the floor without color or depth update but so that
    424 	   a stencil value of one is where the floor will be.  Later when
    425 	   rendering the dinosaur reflection, we will only update pixels
    426 	   with a stencil value of 1 to make sure the reflection only
    427 	   lives on the floor, not below the floor. */
    428 
    429         /* Don't update color or depth. */
    430         glDisable(GL_DEPTH_TEST);
    431         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    432 
    433         /* Draw 1 into the stencil buffer. */
    434         glEnable(GL_STENCIL_TEST);
    435         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
    436         glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
    437 
    438         /* Now render floor; floor pixels just get their stencil set to 1. */
    439         drawFloor();
    440 
    441         /* Re-enable update of color and depth. */
    442         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    443         glEnable(GL_DEPTH_TEST);
    444 
    445         /* Now, only render where stencil is set to 1. */
    446         glStencilFunc(GL_EQUAL, 1, 0xffffffff);  /* draw if ==1 */
    447         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    448       }
    449 
    450       glPushMatrix();
    451 
    452         /* The critical reflection step: Reflect dinosaur through the floor
    453            (the Y=0 plane) to make a relection. */
    454         glScalef(1.0, -1.0, 1.0);
    455 
    456 	/* Reflect the light position. */
    457         glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    458 
    459         /* To avoid our normals getting reversed and hence botched lighting
    460 	   on the reflection, turn on normalize.  */
    461         glEnable(GL_NORMALIZE);
    462         glCullFace(GL_FRONT);
    463 
    464         /* Draw the reflected dinosaur. */
    465         drawDinosaur();
    466 
    467         /* Disable noramlize again and re-enable back face culling. */
    468         glDisable(GL_NORMALIZE);
    469         glCullFace(GL_BACK);
    470 
    471       glPopMatrix();
    472 
    473       /* Switch back to the unreflected light position. */
    474       glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    475 
    476       if (stencilReflection) {
    477         glDisable(GL_STENCIL_TEST);
    478       }
    479     }
    480 
    481     /* Back face culling will get used to only draw either the top or the
    482        bottom floor.  This let's us get a floor with two distinct
    483        appearances.  The top floor surface is reflective and kind of red.
    484        The bottom floor surface is not reflective and blue. */
    485 
    486     /* Draw "bottom" of floor in blue. */
    487     glFrontFace(GL_CW);  /* Switch face orientation. */
    488     glColor4f(0.1, 0.1, 0.7, 1.0);
    489     drawFloor();
    490     glFrontFace(GL_CCW);
    491 
    492     if (renderShadow) {
    493       if (stencilShadow) {
    494 	/* Draw the floor with stencil value 3.  This helps us only
    495 	   draw the shadow once per floor pixel (and only on the
    496 	   floor pixels). */
    497         glEnable(GL_STENCIL_TEST);
    498         glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
    499         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    500       }
    501     }
    502 
    503     /* Draw "top" of floor.  Use blending to blend in reflection. */
    504     glEnable(GL_BLEND);
    505     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    506     glColor4f(0.7, 0.0, 0.0, 0.3);
    507     glColor4f(1.0, 1.0, 1.0, 0.3);
    508     drawFloor();
    509     glDisable(GL_BLEND);
    510 
    511     if (renderDinosaur) {
    512       /* Draw "actual" dinosaur, not its reflection. */
    513       drawDinosaur();
    514     }
    515 
    516     if (renderShadow) {
    517 
    518       /* Render the projected shadow. */
    519 
    520       if (stencilShadow) {
    521 
    522         /* Now, only render where stencil is set above 2 (ie, 3 where
    523 	   the top floor is).  Update stencil with 2 where the shadow
    524 	   gets drawn so we don't redraw (and accidently reblend) the
    525 	   shadow). */
    526         glStencilFunc(GL_LESS, 2, 0xffffffff);  /* draw if ==1 */
    527         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
    528       }
    529 
    530       /* To eliminate depth buffer artifacts, we use polygon offset
    531 	 to raise the depth of the projected shadow slightly so
    532 	 that it does not depth buffer alias with the floor. */
    533       if (offsetShadow) {
    534 	switch (polygonOffsetVersion) {
    535 	case EXTENSION:
    536 #ifdef GL_EXT_polygon_offset
    537 	  glEnable(GL_POLYGON_OFFSET_EXT);
    538 	  break;
    539 #endif
    540 #ifdef GL_VERSION_1_1
    541 	case ONE_DOT_ONE:
    542           glEnable(GL_POLYGON_OFFSET_FILL);
    543 	  break;
    544 #endif
    545 	case MISSING:
    546 	  /* Oh well. */
    547 	  break;
    548 	}
    549       }
    550 
    551       /* Render 50% black shadow color on top of whatever the
    552          floor appareance is. */
    553       glEnable(GL_BLEND);
    554       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    555       glDisable(GL_LIGHTING);  /* Force the 50% black. */
    556       glColor4f(0.0, 0.0, 0.0, 0.5);
    557 
    558       glPushMatrix();
    559 	/* Project the shadow. */
    560         glMultMatrixf((GLfloat *) floorShadow);
    561         drawDinosaur();
    562       glPopMatrix();
    563 
    564       glDisable(GL_BLEND);
    565       glEnable(GL_LIGHTING);
    566 
    567       if (offsetShadow) {
    568 	switch (polygonOffsetVersion) {
    569 #ifdef GL_EXT_polygon_offset
    570 	case EXTENSION:
    571 	  glDisable(GL_POLYGON_OFFSET_EXT);
    572 	  break;
    573 #endif
    574 #ifdef GL_VERSION_1_1
    575 	case ONE_DOT_ONE:
    576           glDisable(GL_POLYGON_OFFSET_FILL);
    577 	  break;
    578 #endif
    579 	case MISSING:
    580 	  /* Oh well. */
    581 	  break;
    582 	}
    583       }
    584       if (stencilShadow) {
    585         glDisable(GL_STENCIL_TEST);
    586       }
    587     }
    588 
    589     glPushMatrix();
    590     glDisable(GL_LIGHTING);
    591     glColor3f(1.0, 1.0, 0.0);
    592     if (directionalLight) {
    593       /* Draw an arrowhead. */
    594       glDisable(GL_CULL_FACE);
    595       glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
    596       glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0);
    597       glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1);
    598       glBegin(GL_TRIANGLE_FAN);
    599 	glVertex3f(0, 0, 0);
    600 	glVertex3f(2, 1, 1);
    601 	glVertex3f(2, -1, 1);
    602 	glVertex3f(2, -1, -1);
    603 	glVertex3f(2, 1, -1);
    604 	glVertex3f(2, 1, 1);
    605       glEnd();
    606       /* Draw a white line from light direction. */
    607       glColor3f(1.0, 1.0, 1.0);
    608       glBegin(GL_LINES);
    609 	glVertex3f(0, 0, 0);
    610 	glVertex3f(5, 0, 0);
    611       glEnd();
    612       glEnable(GL_CULL_FACE);
    613     } else {
    614       /* Draw a yellow ball at the light source. */
    615       glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
    616       glutSolidSphere(1.0, 5, 5);
    617     }
    618     glEnable(GL_LIGHTING);
    619     glPopMatrix();
    620 
    621   glPopMatrix();
    622 
    623   if (reportSpeed) {
    624     glFinish();
    625     end = glutGet(GLUT_ELAPSED_TIME);
    626     printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
    627     fflush(stdout);
    628   }
    629 
    630   glutSwapBuffers();
    631 }
    632 
    633 /* ARGSUSED2 */
    634 static void
    635 mouse(int button, int state, int x, int y)
    636 {
    637   if (button == GLUT_LEFT_BUTTON) {
    638     if (state == GLUT_DOWN) {
    639       moving = 1;
    640       startx = x;
    641       starty = y;
    642     }
    643     if (state == GLUT_UP) {
    644       moving = 0;
    645     }
    646   }
    647   if (button == GLUT_MIDDLE_BUTTON) {
    648     if (state == GLUT_DOWN) {
    649       lightMoving = 1;
    650       lightStartX = x;
    651       lightStartY = y;
    652     }
    653     if (state == GLUT_UP) {
    654       lightMoving = 0;
    655     }
    656   }
    657 }
    658 
    659 /* ARGSUSED1 */
    660 static void
    661 motion(int x, int y)
    662 {
    663   if (moving) {
    664     angle = angle + (x - startx);
    665     angle2 = angle2 + (y - starty);
    666     startx = x;
    667     starty = y;
    668     glutPostRedisplay();
    669   }
    670   if (lightMoving) {
    671     lightAngle += (x - lightStartX)/40.0;
    672     lightHeight += (lightStartY - y)/20.0;
    673     lightStartX = x;
    674     lightStartY = y;
    675     glutPostRedisplay();
    676   }
    677 }
    678 
    679 /* Advance time varying state when idle callback registered. */
    680 static void
    681 idle(void)
    682 {
    683   static float time = 0.0;
    684 
    685   time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
    686 
    687   jump = 4.0 * fabs(sin(time)*0.5);
    688   if (!lightMoving) {
    689     lightAngle += 0.03;
    690   }
    691   glutPostRedisplay();
    692 }
    693 
    694 enum {
    695   M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
    696   M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
    697   M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
    698 };
    699 
    700 static void
    701 controlLights(int value)
    702 {
    703   switch (value) {
    704   case M_NONE:
    705     return;
    706   case M_MOTION:
    707     animation = 1 - animation;
    708     if (animation) {
    709       glutIdleFunc(idle);
    710     } else {
    711       glutIdleFunc(NULL);
    712     }
    713     break;
    714   case M_LIGHT:
    715     lightSwitch = !lightSwitch;
    716     if (lightSwitch) {
    717       glEnable(GL_LIGHT0);
    718     } else {
    719       glDisable(GL_LIGHT0);
    720     }
    721     break;
    722   case M_TEXTURE:
    723     useTexture = !useTexture;
    724     break;
    725   case M_SHADOWS:
    726     renderShadow = 1 - renderShadow;
    727     break;
    728   case M_REFLECTION:
    729     renderReflection = 1 - renderReflection;
    730     break;
    731   case M_DINOSAUR:
    732     renderDinosaur = 1 - renderDinosaur;
    733     break;
    734   case M_STENCIL_REFLECTION:
    735     stencilReflection = 1 - stencilReflection;
    736     break;
    737   case M_STENCIL_SHADOW:
    738     stencilShadow = 1 - stencilShadow;
    739     break;
    740   case M_OFFSET_SHADOW:
    741     offsetShadow = 1 - offsetShadow;
    742     break;
    743   case M_POSITIONAL:
    744     directionalLight = 0;
    745     break;
    746   case M_DIRECTIONAL:
    747     directionalLight = 1;
    748     break;
    749   case M_PERFORMANCE:
    750     reportSpeed = 1 - reportSpeed;
    751     break;
    752   }
    753   glutPostRedisplay();
    754 }
    755 
    756 /* When not visible, stop animating.  Restart when visible again. */
    757 static void
    758 visible(int vis)
    759 {
    760   if (vis == GLUT_VISIBLE) {
    761     if (animation)
    762       glutIdleFunc(idle);
    763   } else {
    764     if (!animation)
    765       glutIdleFunc(NULL);
    766   }
    767 }
    768 
    769 /* Press any key to redraw; good when motion stopped and
    770    performance reporting on. */
    771 /* ARGSUSED */
    772 static void
    773 key(unsigned char c, int x, int y)
    774 {
    775   if (c == 27) {
    776     exit(0);  /* IRIS GLism, Escape quits. */
    777   }
    778   glutPostRedisplay();
    779 }
    780 
    781 /* Press any key to redraw; good when motion stopped and
    782    performance reporting on. */
    783 /* ARGSUSED */
    784 static void
    785 special(int k, int x, int y)
    786 {
    787   glutPostRedisplay();
    788 }
    789 
    790 int
    791 main(int argc, char **argv)
    792 {
    793   int i;
    794 
    795   glutInit(&argc, argv);
    796 
    797   for (i=1; i<argc; i++) {
    798     if (!strcmp("-linear", argv[i])) {
    799       linearFiltering = 1;
    800     } else if (!strcmp("-mipmap", argv[i])) {
    801       useMipmaps = 1;
    802     } else if (!strcmp("-ext", argv[i])) {
    803       forceExtension = 1;
    804     }
    805   }
    806 
    807   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
    808 
    809 #if 0
    810   /* In GLUT 4.0, you'll be able to do this an be sure to
    811      get 2 bits of stencil if the machine has it for you. */
    812   glutInitDisplayString("samples stencil>=2 rgb double depth");
    813 #endif
    814 
    815   glutCreateWindow("Shadowy Leapin' Lizards");
    816   glewInit();
    817 
    818   if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
    819     printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
    820     exit(1);
    821   }
    822 
    823   /* Register GLUT callbacks. */
    824   glutDisplayFunc(redraw);
    825   glutMouseFunc(mouse);
    826   glutMotionFunc(motion);
    827   glutVisibilityFunc(visible);
    828   glutKeyboardFunc(key);
    829   glutSpecialFunc(special);
    830 
    831   glutCreateMenu(controlLights);
    832 
    833   glutAddMenuEntry("Toggle motion", M_MOTION);
    834   glutAddMenuEntry("-----------------------", M_NONE);
    835   glutAddMenuEntry("Toggle light", M_LIGHT);
    836   glutAddMenuEntry("Toggle texture", M_TEXTURE);
    837   glutAddMenuEntry("Toggle shadows", M_SHADOWS);
    838   glutAddMenuEntry("Toggle reflection", M_REFLECTION);
    839   glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR);
    840   glutAddMenuEntry("-----------------------", M_NONE);
    841   glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
    842   glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
    843   glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
    844   glutAddMenuEntry("----------------------", M_NONE);
    845   glutAddMenuEntry("Positional light", M_POSITIONAL);
    846   glutAddMenuEntry("Directional light", M_DIRECTIONAL);
    847   glutAddMenuEntry("-----------------------", M_NONE);
    848   glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
    849   glutAttachMenu(GLUT_RIGHT_BUTTON);
    850   makeDinosaur();
    851 
    852 #ifdef GL_VERSION_1_1
    853   if (GLEW_VERSION_1_1 && !forceExtension) {
    854     polygonOffsetVersion = ONE_DOT_ONE;
    855     glPolygonOffset(-2.0, -9.0);
    856   } else
    857 #endif
    858   {
    859 #ifdef GL_EXT_polygon_offset
    860   /* check for the polygon offset extension */
    861   if (GLEW_EXT_polygon_offset) {
    862     polygonOffsetVersion = EXTENSION;
    863     glPolygonOffsetEXT(-2.0, -0.002);
    864   } else
    865 #endif
    866     {
    867       polygonOffsetVersion = MISSING;
    868       printf("\ndinoshine: Missing polygon offset.\n");
    869       printf("           Expect shadow depth aliasing artifacts.\n\n");
    870       fflush(stdout);
    871     }
    872   }
    873 
    874   glEnable(GL_CULL_FACE);
    875   glEnable(GL_DEPTH_TEST);
    876   glEnable(GL_TEXTURE_2D);
    877   glLineWidth(3.0);
    878 
    879   glMatrixMode(GL_PROJECTION);
    880   gluPerspective( /* field of view in degree */ 40.0,
    881   /* aspect ratio */ 1.0,
    882     /* Z near */ 20.0, /* Z far */ 100.0);
    883   glMatrixMode(GL_MODELVIEW);
    884   gluLookAt(0.0, 8.0, 60.0,  /* eye is at (0,8,60) */
    885     0.0, 8.0, 0.0,      /* center is at (0,8,0) */
    886     0.0, 1.0, 0.);      /* up is in postivie Y direction */
    887 
    888   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
    889   glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
    890   glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
    891   glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
    892   glEnable(GL_LIGHT0);
    893   glEnable(GL_LIGHTING);
    894 
    895   makeFloorTexture();
    896 
    897   /* Setup floor plane for projected shadow calculations. */
    898   findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
    899 
    900   glutMainLoop();
    901   return 0;             /* ANSI C requires main to return int. */
    902 }
    903