dinoshade.c
Go to the documentation of this file.
00001 
00002 /* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
00003 
00004 /* This program is freely distributable without licensing fees 
00005    and is provided without guarantee or warrantee expressed or 
00006    implied. This program is -not- in the public domain. */
00007 
00008 /* Example for PC game developers to show how to *combine* texturing,
00009    reflections, and projected shadows all in real-time with OpenGL.
00010    Robust reflections use stenciling.  Robust projected shadows
00011    use both stenciling and polygon offset.  PC game programmers
00012    should realize that neither stenciling nor polygon offset are 
00013    supported by Direct3D, so these real-time rendering algorithms
00014    are only really viable with OpenGL. 
00015    
00016    The program has modes for disabling the stenciling and polygon
00017    offset uses.  It is worth running this example with these features
00018    toggled off so you can see the sort of artifacts that result.
00019    
00020    Notice that the floor texturing, reflections, and shadowing
00021    all co-exist properly. */
00022 
00023 /* When you run this program:  Left mouse button controls the
00024    view.  Middle mouse button controls light position (left &
00025    right rotates light around dino; up & down moves light
00026    position up and down).  Right mouse button pops up menu. */
00027 
00028 /* Check out the comments in the "redraw" routine to see how the
00029    reflection blending and surface stenciling is done.  You can
00030    also see in "redraw" how the projected shadows are rendered,
00031 
00032    including the use of stenciling and polygon offset. */
00033 
00034 /* This program is derived from glutdino.c */
00035 
00036 /* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
00037 
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <math.h>       /* for cos(), sin(), and sqrt() */
00042 #include <GL/glut.h>    /* OpenGL Utility Toolkit header */
00043 
00044 /* Some <math.h> files do not define M_PI... */
00045 #ifndef M_PI
00046 #define M_PI 3.14159265
00047 #endif
00048 
00049 /* Variable controlling various rendering modes. */
00050 static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1;
00051 static int renderShadow = 1, renderDinosaur = 1, renderReflection = 1;
00052 static int linearFiltering = 0, useMipmaps = 0, useTexture = 1;
00053 static int reportSpeed = 0;
00054 static int animation = 1;
00055 static GLboolean lightSwitch = GL_TRUE;
00056 static int directionalLight = 1;
00057 static int forceExtension = 0;
00058 
00059 /* Time varying or user-controled variables. */
00060 static float jump = 0.0;
00061 static float lightAngle = 0.0, lightHeight = 20;
00062 GLfloat angle = -150;   /* in degrees */
00063 GLfloat angle2 = 30;   /* in degrees */
00064 
00065 int moving, startx, starty;
00066 int lightMoving = 0, lightStartX, lightStartY;
00067 
00068 enum {
00069   MISSING, EXTENSION, ONE_DOT_ONE
00070 };
00071 int polygonOffsetVersion;
00072 
00073 static GLdouble bodyWidth = 3.0;
00074 /* *INDENT-OFF* */
00075 static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
00076   {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
00077   {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
00078   {1, 2} };
00079 static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
00080   {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
00081   {13, 9}, {11, 11}, {9, 11} };
00082 static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
00083   {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
00084 static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
00085   {9.6, 15.25}, {9, 15.25} };
00086 static GLfloat lightPosition[4];
00087 static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
00088 static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0};
00089 /* *INDENT-ON* */
00090 
00091 /* Nice floor texture tiling pattern. */
00092 static char *circles[] = {
00093   "....xxxx........",
00094   "..xxxxxxxx......",
00095   ".xxxxxxxxxx.....",
00096   ".xxx....xxx.....",
00097   "xxx......xxx....",
00098   "xxx......xxx....",
00099   "xxx......xxx....",
00100   "xxx......xxx....",
00101   ".xxx....xxx.....",
00102   ".xxxxxxxxxx.....",
00103   "..xxxxxxxx......",
00104   "....xxxx........",
00105   "................",
00106   "................",
00107   "................",
00108   "................",
00109 };
00110 
00111 static void
00112 makeFloorTexture(void)
00113 {
00114   GLubyte floorTexture[16][16][3];
00115   GLubyte *loc;
00116   int s, t;
00117 
00118   /* Setup RGB image for the texture. */
00119   loc = (GLubyte*) floorTexture;
00120   for (t = 0; t < 16; t++) {
00121     for (s = 0; s < 16; s++) {
00122       if (circles[t][s] == 'x') {
00123         /* Nice green. */
00124         loc[0] = 0x1f;
00125         loc[1] = 0x8f;
00126         loc[2] = 0x1f;
00127       } else {
00128         /* Light gray. */
00129         loc[0] = 0xaa;
00130         loc[1] = 0xaa;
00131         loc[2] = 0xaa;
00132       }
00133       loc += 3;
00134     }
00135   }
00136 
00137   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00138 
00139   if (useMipmaps) {
00140     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00141       GL_LINEAR_MIPMAP_LINEAR);
00142     gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
00143       GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
00144   } else {
00145     if (linearFiltering) {
00146       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00147     } else {
00148       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00149     }
00150     glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
00151       GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
00152   }
00153 }
00154 
00155 enum {
00156   X, Y, Z, W
00157 };
00158 enum {
00159   A, B, C, D
00160 };
00161 
00162 /* Create a matrix that will project the desired shadow. */
00163 void
00164 shadowMatrix(GLfloat shadowMat[4][4],
00165   GLfloat groundplane[4],
00166   GLfloat lightpos[4])
00167 {
00168   GLfloat dot;
00169 
00170   /* Find dot product between light position vector and ground plane normal. */
00171   dot = groundplane[X] * lightpos[X] +
00172     groundplane[Y] * lightpos[Y] +
00173     groundplane[Z] * lightpos[Z] +
00174     groundplane[W] * lightpos[W];
00175 
00176   shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
00177   shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
00178   shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
00179   shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
00180 
00181   shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
00182   shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
00183   shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
00184   shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
00185 
00186   shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
00187   shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
00188   shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
00189   shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
00190 
00191   shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
00192   shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
00193   shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
00194   shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
00195 
00196 }
00197 
00198 /* Find the plane equation given 3 points. */
00199 void
00200 findPlane(GLfloat plane[4],
00201   GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
00202 {
00203   GLfloat vec0[3], vec1[3];
00204 
00205   /* Need 2 vectors to find cross product. */
00206   vec0[X] = v1[X] - v0[X];
00207   vec0[Y] = v1[Y] - v0[Y];
00208   vec0[Z] = v1[Z] - v0[Z];
00209 
00210   vec1[X] = v2[X] - v0[X];
00211   vec1[Y] = v2[Y] - v0[Y];
00212   vec1[Z] = v2[Z] - v0[Z];
00213 
00214   /* find cross product to get A, B, and C of plane equation */
00215   plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
00216   plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
00217   plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
00218 
00219   plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
00220 }
00221 
00222 void
00223 extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
00224   GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
00225 {
00226   static GLUtriangulatorObj *tobj = NULL;
00227   GLdouble vertex[3], dx, dy, len;
00228   int i;
00229   int count = (int) (dataSize / (2 * sizeof(GLfloat)));
00230 
00231   if (tobj == NULL) {
00232     tobj = gluNewTess();  /* create and initialize a GLU
00233                              polygon tesselation object */
00234     gluTessCallback(tobj, GLU_BEGIN, glBegin);
00235     gluTessCallback(tobj, GLU_VERTEX, glVertex2fv);  /* semi-tricky */
00236     gluTessCallback(tobj, GLU_END, glEnd);
00237   }
00238   glNewList(side, GL_COMPILE);
00239   glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
00240                                tessellation */
00241   gluBeginPolygon(tobj);
00242   for (i = 0; i < count; i++) {
00243     vertex[0] = data[i][0];
00244     vertex[1] = data[i][1];
00245     vertex[2] = 0;
00246     gluTessVertex(tobj, vertex, data[i]);
00247   }
00248   gluEndPolygon(tobj);
00249   glEndList();
00250   glNewList(edge, GL_COMPILE);
00251   glShadeModel(GL_FLAT);  /* flat shade keeps angular hands
00252                              from being "smoothed" */
00253   glBegin(GL_QUAD_STRIP);
00254   for (i = 0; i <= count; i++) {
00255     /* mod function handles closing the edge */
00256     glVertex3f(data[i % count][0], data[i % count][1], 0.0);
00257     glVertex3f(data[i % count][0], data[i % count][1], thickness);
00258     /* Calculate a unit normal by dividing by Euclidean
00259        distance. We * could be lazy and use
00260        glEnable(GL_NORMALIZE) so we could pass in * arbitrary
00261        normals for a very slight performance hit. */
00262     dx = data[(i + 1) % count][1] - data[i % count][1];
00263     dy = data[i % count][0] - data[(i + 1) % count][0];
00264     len = sqrt(dx * dx + dy * dy);
00265     glNormal3f(dx / len, dy / len, 0.0);
00266   }
00267   glEnd();
00268   glEndList();
00269   glNewList(whole, GL_COMPILE);
00270   glFrontFace(GL_CW);
00271   glCallList(edge);
00272   glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
00273   glCallList(side);
00274   glPushMatrix();
00275   glTranslatef(0.0, 0.0, thickness);
00276   glFrontFace(GL_CCW);
00277   glNormal3f(0.0, 0.0, 1.0);  /* opposite normal for other side */
00278   glCallList(side);
00279   glPopMatrix();
00280   glEndList();
00281 }
00282 
00283 /* Enumerants for refering to display lists. */
00284 typedef enum {
00285   RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
00286   LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE
00287 } displayLists;
00288 
00289 static void
00290 makeDinosaur(void)
00291 {
00292   extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
00293     BODY_SIDE, BODY_EDGE, BODY_WHOLE);
00294   extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
00295     ARM_SIDE, ARM_EDGE, ARM_WHOLE);
00296   extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
00297     LEG_SIDE, LEG_EDGE, LEG_WHOLE);
00298   extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
00299     EYE_SIDE, EYE_EDGE, EYE_WHOLE);
00300 }
00301 
00302 static void
00303 drawDinosaur(void)
00304 
00305 {
00306   glPushMatrix();
00307   /* Translate the dinosaur to be at (0,8,0). */
00308   glTranslatef(-8, 0, -bodyWidth / 2);
00309   glTranslatef(0.0, jump, 0.0);
00310   glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
00311   glCallList(BODY_WHOLE);
00312   glTranslatef(0.0, 0.0, bodyWidth);
00313   glCallList(ARM_WHOLE);
00314   glCallList(LEG_WHOLE);
00315   glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
00316   glCallList(ARM_WHOLE);
00317   glTranslatef(0.0, 0.0, -bodyWidth / 4);
00318   glCallList(LEG_WHOLE);
00319   glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1);
00320   glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
00321   glCallList(EYE_WHOLE);
00322   glPopMatrix();
00323 }
00324 
00325 static GLfloat floorVertices[4][3] = {
00326   { -20.0, 0.0, 20.0 },
00327   { 20.0, 0.0, 20.0 },
00328   { 20.0, 0.0, -20.0 },
00329   { -20.0, 0.0, -20.0 },
00330 };
00331 
00332 /* Draw a floor (possibly textured). */
00333 static void
00334 drawFloor(void)
00335 {
00336   glDisable(GL_LIGHTING);
00337 
00338   if (useTexture) {
00339     glEnable(GL_TEXTURE_2D);
00340   }
00341 
00342   glBegin(GL_QUADS);
00343     glTexCoord2f(0.0, 0.0);
00344     glVertex3fv(floorVertices[0]);
00345     glTexCoord2f(0.0, 16.0);
00346     glVertex3fv(floorVertices[1]);
00347     glTexCoord2f(16.0, 16.0);
00348     glVertex3fv(floorVertices[2]);
00349     glTexCoord2f(16.0, 0.0);
00350     glVertex3fv(floorVertices[3]);
00351   glEnd();
00352 
00353   if (useTexture) {
00354     glDisable(GL_TEXTURE_2D);
00355   }
00356 
00357   glEnable(GL_LIGHTING);
00358 }
00359 
00360 static GLfloat floorPlane[4];
00361 static GLfloat floorShadow[4][4];
00362 
00363 static void
00364 redraw(void)
00365 {
00366   int start, end;
00367 
00368   if (reportSpeed) {
00369     start = glutGet(GLUT_ELAPSED_TIME);
00370   }
00371 
00372   /* Clear; default stencil clears to zero. */
00373   if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow)) {
00374     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00375   } else {
00376     /* Avoid clearing stencil when not using it. */
00377     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00378   }
00379 
00380   /* Reposition the light source. */
00381   lightPosition[0] = 12*cos(lightAngle);
00382   lightPosition[1] = lightHeight;
00383   lightPosition[2] = 12*sin(lightAngle);
00384   if (directionalLight) {
00385     lightPosition[3] = 0.0;
00386   } else {
00387     lightPosition[3] = 1.0;
00388   }
00389 
00390   shadowMatrix(floorShadow, floorPlane, lightPosition);
00391 
00392   glPushMatrix();
00393     /* Perform scene rotations based on user mouse input. */
00394     glRotatef(angle2, 1.0, 0.0, 0.0);
00395     glRotatef(angle, 0.0, 1.0, 0.0);
00396      
00397     /* Tell GL new light source position. */
00398     glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
00399 
00400     if (renderReflection) {
00401       if (stencilReflection) {
00402         /* We can eliminate the visual "artifact" of seeing the "flipped"
00403            dinosaur underneath the floor by using stencil.  The idea is
00404            draw the floor without color or depth update but so that 
00405            a stencil value of one is where the floor will be.  Later when
00406            rendering the dinosaur reflection, we will only update pixels
00407            with a stencil value of 1 to make sure the reflection only
00408            lives on the floor, not below the floor. */
00409 
00410         /* Don't update color or depth. */
00411         glDisable(GL_DEPTH_TEST);
00412         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
00413 
00414         /* Draw 1 into the stencil buffer. */
00415         glEnable(GL_STENCIL_TEST);
00416         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
00417         glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
00418 
00419         /* Now render floor; floor pixels just get their stencil set to 1. */
00420         drawFloor();
00421 
00422         /* Re-enable update of color and depth. */ 
00423         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
00424         glEnable(GL_DEPTH_TEST);
00425 
00426         /* Now, only render where stencil is set to 1. */
00427         glStencilFunc(GL_EQUAL, 1, 0xffffffff);  /* draw if ==1 */
00428         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
00429       }
00430 
00431       glPushMatrix();
00432 
00433         /* The critical reflection step: Reflect dinosaur through the floor
00434            (the Y=0 plane) to make a relection. */
00435         glScalef(1.0, -1.0, 1.0);
00436 
00437         /* Reflect the light position. */
00438         glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
00439 
00440         /* To avoid our normals getting reversed and hence botched lighting
00441            on the reflection, turn on normalize.  */
00442         glEnable(GL_NORMALIZE);
00443         glCullFace(GL_FRONT);
00444 
00445         /* Draw the reflected dinosaur. */
00446         drawDinosaur();
00447 
00448         /* Disable noramlize again and re-enable back face culling. */
00449         glDisable(GL_NORMALIZE);
00450         glCullFace(GL_BACK);
00451 
00452       glPopMatrix();
00453 
00454       /* Switch back to the unreflected light position. */
00455       glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
00456 
00457       if (stencilReflection) {
00458         glDisable(GL_STENCIL_TEST);
00459       }
00460     }
00461 
00462     /* Back face culling will get used to only draw either the top or the
00463        bottom floor.  This let's us get a floor with two distinct
00464        appearances.  The top floor surface is reflective and kind of red.
00465        The bottom floor surface is not reflective and blue. */
00466 
00467     /* Draw "bottom" of floor in blue. */
00468     glFrontFace(GL_CW);  /* Switch face orientation. */
00469     glColor4f(0.1, 0.1, 0.7, 1.0);
00470     drawFloor();
00471     glFrontFace(GL_CCW);
00472 
00473     if (renderShadow) {
00474       if (stencilShadow) {
00475         /* Draw the floor with stencil value 3.  This helps us only 
00476            draw the shadow once per floor pixel (and only on the
00477            floor pixels). */
00478         glEnable(GL_STENCIL_TEST);
00479         glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
00480         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
00481       }
00482     }
00483 
00484     /* Draw "top" of floor.  Use blending to blend in reflection. */
00485     glEnable(GL_BLEND);
00486     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00487     glColor4f(0.7, 0.0, 0.0, 0.3);
00488     glColor4f(1.0, 1.0, 1.0, 0.3);
00489     drawFloor();
00490     glDisable(GL_BLEND);
00491 
00492     if (renderDinosaur) {
00493       /* Draw "actual" dinosaur, not its reflection. */
00494       drawDinosaur();
00495     }
00496 
00497     if (renderShadow) {
00498 
00499       /* Render the projected shadow. */
00500 
00501       if (stencilShadow) {
00502 
00503         /* Now, only render where stencil is set above 2 (ie, 3 where
00504            the top floor is).  Update stencil with 2 where the shadow
00505            gets drawn so we don't redraw (and accidently reblend) the
00506            shadow). */
00507         glStencilFunc(GL_LESS, 2, 0xffffffff);  /* draw if ==1 */
00508         glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
00509       }
00510 
00511       /* To eliminate depth buffer artifacts, we use polygon offset
00512          to raise the depth of the projected shadow slightly so
00513          that it does not depth buffer alias with the floor. */
00514       if (offsetShadow) {
00515         switch (polygonOffsetVersion) {
00516         case EXTENSION:
00517 #ifdef GL_EXT_polygon_offset
00518           glEnable(GL_POLYGON_OFFSET_EXT);
00519           break;
00520 #endif
00521 #ifdef GL_VERSION_1_1
00522         case ONE_DOT_ONE:
00523           glEnable(GL_POLYGON_OFFSET_FILL);
00524           break;
00525 #endif
00526         case MISSING:
00527           /* Oh well. */
00528           break;
00529         }
00530       }
00531 
00532       /* Render 50% black shadow color on top of whatever the
00533          floor appareance is. */
00534       glEnable(GL_BLEND);
00535       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00536       glDisable(GL_LIGHTING);  /* Force the 50% black. */
00537       glColor4f(0.0, 0.0, 0.0, 0.5);
00538 
00539       glPushMatrix();
00540         /* Project the shadow. */
00541         glMultMatrixf((GLfloat *) floorShadow);
00542         drawDinosaur();
00543       glPopMatrix();
00544 
00545       glDisable(GL_BLEND);
00546       glEnable(GL_LIGHTING);
00547 
00548       if (offsetShadow) {
00549         switch (polygonOffsetVersion) {
00550 #ifdef GL_EXT_polygon_offset
00551         case EXTENSION:
00552           glDisable(GL_POLYGON_OFFSET_EXT);
00553           break;
00554 #endif
00555 #ifdef GL_VERSION_1_1
00556         case ONE_DOT_ONE:
00557           glDisable(GL_POLYGON_OFFSET_FILL);
00558           break;
00559 #endif
00560         case MISSING:
00561           /* Oh well. */
00562           break;
00563         }
00564       }
00565       if (stencilShadow) {
00566         glDisable(GL_STENCIL_TEST);
00567       }
00568     }
00569 
00570     glPushMatrix();
00571     glDisable(GL_LIGHTING);
00572     glColor3f(1.0, 1.0, 0.0);
00573     if (directionalLight) {
00574       /* Draw an arrowhead. */
00575       glDisable(GL_CULL_FACE);
00576       glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
00577       glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0);
00578       glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1);
00579       glBegin(GL_TRIANGLE_FAN);
00580         glVertex3f(0, 0, 0);
00581         glVertex3f(2, 1, 1);
00582         glVertex3f(2, -1, 1);
00583         glVertex3f(2, -1, -1);
00584         glVertex3f(2, 1, -1);
00585         glVertex3f(2, 1, 1);
00586       glEnd();
00587       /* Draw a white line from light direction. */
00588       glColor3f(1.0, 1.0, 1.0);
00589       glBegin(GL_LINES);
00590         glVertex3f(0, 0, 0);
00591         glVertex3f(5, 0, 0);
00592       glEnd();
00593       glEnable(GL_CULL_FACE);
00594     } else {
00595       /* Draw a yellow ball at the light source. */
00596       glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
00597       glutSolidSphere(1.0, 5, 5);
00598     }
00599     glEnable(GL_LIGHTING);
00600     glPopMatrix();
00601 
00602   glPopMatrix();
00603 
00604   if (reportSpeed) {
00605     glFinish();
00606     end = glutGet(GLUT_ELAPSED_TIME);
00607     printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
00608   }
00609 
00610   glutSwapBuffers();
00611 }
00612 
00613 /* ARGSUSED2 */
00614 static void
00615 mouse(int button, int state, int x, int y)
00616 {
00617   if (button == GLUT_LEFT_BUTTON) {
00618     if (state == GLUT_DOWN) {
00619       moving = 1;
00620       startx = x;
00621       starty = y;
00622     }
00623     if (state == GLUT_UP) {
00624       moving = 0;
00625     }
00626   }
00627   if (button == GLUT_MIDDLE_BUTTON) {
00628     if (state == GLUT_DOWN) {
00629       lightMoving = 1;
00630       lightStartX = x;
00631       lightStartY = y;
00632     }
00633     if (state == GLUT_UP) {
00634       lightMoving = 0;
00635     }
00636   }
00637 }
00638 
00639 /* ARGSUSED1 */
00640 static void
00641 motion(int x, int y)
00642 {
00643   if (moving) {
00644     angle = angle + (x - startx);
00645     angle2 = angle2 + (y - starty);
00646     startx = x;
00647     starty = y;
00648     glutPostRedisplay();
00649   }
00650   if (lightMoving) {
00651     lightAngle += (x - lightStartX)/40.0;
00652     lightHeight += (lightStartY - y)/20.0;
00653     lightStartX = x;
00654     lightStartY = y;
00655     glutPostRedisplay();
00656   }
00657 }
00658 
00659 /* Advance time varying state when idle callback registered. */
00660 static void
00661 idle(void)
00662 {
00663   static float time = 0.0;
00664 
00665   time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
00666 
00667   jump = 4.0 * fabs(sin(time)*0.5);
00668   if (!lightMoving) {
00669     lightAngle += 0.03;
00670   }
00671   glutPostRedisplay();
00672 }
00673 
00674 enum {
00675   M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
00676   M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
00677   M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
00678 };
00679 
00680 static void
00681 controlLights(int value)
00682 {
00683   switch (value) {
00684   case M_NONE:
00685     return;
00686   case M_MOTION:
00687     animation = 1 - animation;
00688     if (animation) {
00689       glutIdleFunc(idle);
00690     } else {
00691       glutIdleFunc(NULL);
00692     }
00693     break;
00694   case M_LIGHT:
00695     lightSwitch = !lightSwitch;
00696     if (lightSwitch) {
00697       glEnable(GL_LIGHT0);
00698     } else {
00699       glDisable(GL_LIGHT0);
00700     }
00701     break;
00702   case M_TEXTURE:
00703     useTexture = !useTexture;
00704     break;
00705   case M_SHADOWS:
00706     renderShadow = 1 - renderShadow;
00707     break;
00708   case M_REFLECTION:
00709     renderReflection = 1 - renderReflection;
00710     break;
00711   case M_DINOSAUR:
00712     renderDinosaur = 1 - renderDinosaur;
00713     break;
00714   case M_STENCIL_REFLECTION:
00715     stencilReflection = 1 - stencilReflection;
00716     break;
00717   case M_STENCIL_SHADOW:
00718     stencilShadow = 1 - stencilShadow;
00719     break;
00720   case M_OFFSET_SHADOW:
00721     offsetShadow = 1 - offsetShadow;
00722     break;
00723   case M_POSITIONAL:
00724     directionalLight = 0;
00725     break;
00726   case M_DIRECTIONAL:
00727     directionalLight = 1;
00728     break;
00729   case M_PERFORMANCE:
00730     reportSpeed = 1 - reportSpeed;
00731     break;
00732   }
00733   glutPostRedisplay();
00734 }
00735 
00736 /* When not visible, stop animating.  Restart when visible again. */
00737 static void 
00738 visible(int vis)
00739 {
00740   if (vis == GLUT_VISIBLE) {
00741     if (animation)
00742       glutIdleFunc(idle);
00743   } else {
00744     if (!animation)
00745       glutIdleFunc(NULL);
00746   }
00747 }
00748 
00749 /* Press any key to redraw; good when motion stopped and
00750    performance reporting on. */
00751 /* ARGSUSED */
00752 static void
00753 key(unsigned char c, int x, int y)
00754 {
00755   if (c == 27) {
00756     exit(0);  /* IRIS GLism, Escape quits. */
00757   }
00758   glutPostRedisplay();
00759 }
00760 
00761 /* Press any key to redraw; good when motion stopped and
00762    performance reporting on. */
00763 /* ARGSUSED */
00764 static void
00765 special(int k, int x, int y)
00766 {
00767   glutPostRedisplay();
00768 }
00769 
00770 static int
00771 supportsOneDotOne(void)
00772 {
00773   const char *version;
00774   int major, minor;
00775 
00776   version = (char *) glGetString(GL_VERSION);
00777   if (sscanf(version, "%d.%d", &major, &minor) == 2)
00778     return major >= 1 && minor >= 1;
00779   return 0;            /* OpenGL version string malformed! */
00780 }
00781 
00782 int
00783 main(int argc, char **argv)
00784 {
00785   int i;
00786 
00787   glutInit(&argc, argv);
00788 
00789   for (i=1; i<argc; i++) {
00790     if (!strcmp("-linear", argv[i])) {
00791       linearFiltering = 1;
00792     } else if (!strcmp("-mipmap", argv[i])) {
00793       useMipmaps = 1;
00794     } else if (!strcmp("-ext", argv[i])) {
00795       forceExtension = 1;
00796     }
00797   }
00798 
00799   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL | GLUT_MULTISAMPLE);
00800 
00801 #if 1
00802   /* In GLUT 4.0, you'll be able to do this an be sure to
00803      get 2 bits of stencil if the machine has it for you. */
00804   glutInitDisplayString("samples stencil>=2 rgb double depth");
00805 #endif
00806 
00807   glutCreateWindow("Shadowy Leapin' Lizards");
00808 
00809   if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
00810     printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
00811     exit(1);
00812   }
00813 
00814   /* Register GLUT callbacks. */
00815   glutDisplayFunc(redraw);
00816   glutMouseFunc(mouse);
00817   glutMotionFunc(motion);
00818   glutVisibilityFunc(visible);
00819   glutKeyboardFunc(key);
00820   glutSpecialFunc(special);
00821 
00822   glutCreateMenu(controlLights);
00823 
00824   glutAddMenuEntry("Toggle motion", M_MOTION);
00825   glutAddMenuEntry("-----------------------", M_NONE);
00826   glutAddMenuEntry("Toggle light", M_LIGHT);
00827   glutAddMenuEntry("Toggle texture", M_TEXTURE);
00828   glutAddMenuEntry("Toggle shadows", M_SHADOWS);
00829   glutAddMenuEntry("Toggle reflection", M_REFLECTION);
00830   glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR);
00831   glutAddMenuEntry("-----------------------", M_NONE);
00832   glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
00833   glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
00834   glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
00835   glutAddMenuEntry("----------------------", M_NONE);
00836   glutAddMenuEntry("Positional light", M_POSITIONAL);
00837   glutAddMenuEntry("Directional light", M_DIRECTIONAL);
00838   glutAddMenuEntry("-----------------------", M_NONE);
00839   glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
00840   glutAttachMenu(GLUT_RIGHT_BUTTON);
00841   makeDinosaur();
00842 
00843 #ifdef GL_VERSION_1_1
00844   if (supportsOneDotOne() && !forceExtension) {
00845     polygonOffsetVersion = ONE_DOT_ONE;
00846     glPolygonOffset(-2.0, -1.0);
00847   } else
00848 #endif
00849   {
00850 #ifdef GL_EXT_polygon_offset
00851   /* check for the polygon offset extension */
00852   if (glutExtensionSupported("GL_EXT_polygon_offset")) {
00853     polygonOffsetVersion = EXTENSION;
00854     glPolygonOffsetEXT(-0.1, -0.002);
00855   } else 
00856 #endif
00857     {
00858       polygonOffsetVersion = MISSING;
00859       printf("\ndinoshine: Missing polygon offset.\n");
00860       printf("           Expect shadow depth aliasing artifacts.\n\n");
00861     }
00862   }
00863 
00864   glEnable(GL_CULL_FACE);
00865   glEnable(GL_DEPTH_TEST);
00866   glEnable(GL_TEXTURE_2D);
00867   glLineWidth(3.0);
00868 
00869   glMatrixMode(GL_PROJECTION);
00870   gluPerspective( /* field of view in degree */ 40.0,
00871   /* aspect ratio */ 1.0,
00872     /* Z near */ 20.0, /* Z far */ 100.0);
00873   glMatrixMode(GL_MODELVIEW);
00874   gluLookAt(0.0, 8.0, 60.0,  /* eye is at (0,8,60) */
00875     0.0, 8.0, 0.0,      /* center is at (0,8,0) */
00876     0.0, 1.0, 0.);      /* up is in postivie Y direction */
00877 
00878   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
00879   glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
00880   glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
00881   glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
00882   glEnable(GL_LIGHT0);
00883   glEnable(GL_LIGHTING);
00884 
00885   makeFloorTexture();
00886 
00887   /* Setup floor plane for projected shadow calculations. */
00888   findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
00889 
00890   glutMainLoop();
00891   return 0;             /* ANSI C requires main to return int. */
00892 }


euslisp
Author(s): Toshihiro Matsui
autogenerated on Thu Sep 3 2015 10:36:19