shadowfun.c
上传用户:xk288cn
上传日期:2007-05-28
资源大小:4876k
文件大小:32k
源码类别:

GIS编程

开发平台:

Visual C++

  1. /* Copyright (c) Mark J. Kilgard, 1997.  */
  2. /* This program is freely distributable without licensing fees  and is
  3.    provided without guarantee or warrantee expressed or  implied. This
  4.    program is -not- in the public domain. */
  5. /* Unix compile line:  cc -o shadowfun shadowfun.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
  6. /* THIS PROGRAM REQUIRES GLU 1.2. If you have IRIX 5.3, you need patch 1449
  7.    (the IRIX 5.3 GLU 1.2 functionality patch) or its successor to use this
  8.    program.  GLU 1.2 is standard on IRIX 6.2 and later. */
  9. /* This program demonstrates a light source and object of arbitrary geometry
  10.    casing a shadow on arbitary geometry.  The program uses OpenGL's feedback, 
  11.    stencil, and boundary tessellation support. */
  12. #include <assert.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <math.h>
  16. #include <GL/glut.h>
  17. #ifdef GLU_VERSION_1_2
  18. /* Win32 calling conventions. */
  19. #ifndef CALLBACK
  20. #define CALLBACK
  21. #endif
  22. /* Some <math.h> files do not define M_PI... */
  23. #ifndef M_PI
  24. #define M_PI 3.14159265358979323846
  25. #endif
  26. const float uniquePassThroughValue = 34567.0;
  27. #define SmallerOf(a,b) ((a) < (b) ? (a) : (b))
  28. int stencilBits;
  29. /* Display list names. */
  30. enum {
  31.   /* Display lists should start at 1, not 0. */
  32.   DL_BALL = 1, DL_CONE, DL_LIGHT, DL_SHADOW_VOLUME, DL_SPHERE,
  33.   DL_ICO, DL_TORUS, DL_CUBE, DL_SHADOW_VOLUME_TOP, DL_BASE_SHADOW_VOLUME
  34. };
  35. /* Menu option names. */
  36. enum {
  37.   /* Important for objectMaxRadius array that the shape enums appear first in
  38.      this list. */
  39.   M_TORUS, M_CUBE, M_SPHERE, M_ICO, M_DOUBLE_TORUS, M_ANGLE, M_BOUNDARY,
  40.   M_NO_SHADOW, M_NO_LIGHT, M_FRONT_VOLUME, M_BACK_VOLUME, M_SHADOW,
  41.   M_LIGHT_SOURCE_VIEW, M_NORMAL_VIEW, M_SPIN, M_SWING, M_STOP
  42. };
  43. /* Coordinates. */
  44. enum {
  45.   X, Y, Z
  46. };
  47. const int TEXDIM = 64;
  48. int shape;
  49. GLfloat maxRadius;
  50. int renderMode = M_SHADOW;
  51. int view = M_NORMAL_VIEW;
  52. int renderBoundary = 0;
  53. GLfloat angle = 0.0;
  54. int frontFace = 1;
  55. int rotatingObject = 1;
  56. int swingingLight = 1;
  57. float swingTime = M_PI / 2.0;
  58. GLfloat lightDiffuse[4] =
  59. {1.0, 0.0, 0.0, 1.0};
  60. GLfloat lightPos[4] =
  61. {60.0, 50.0, -350.0, 1.0};
  62. GLfloat objectPos[4] =
  63. {40.0, 30.0, -360.0, 1.0};
  64. GLfloat sceneEyePos[4] =
  65. {0.0, 0.0, 0.0, 0.0};
  66. struct VertexHolder {
  67.   struct VertexHolder *next;
  68.   GLfloat v[2];
  69. };
  70. typedef struct _ShadowVolumeMemoryPool {
  71.   /* Reference count because ShadowVolumeMemoryPool's can be shared between
  72.      multiple ShadowVolumeState's. */
  73.   int refcnt;
  74.   GLUtesselator *tess;
  75.   GLfloat viewScale;
  76.   /* Memory used for GLU tessellator combine callbacks. */
  77.   GLfloat *combineList;
  78.   int combineListSize;
  79.   int combineNext;
  80.   struct VertexHolder *excessList;
  81. } ShadowVolumeMemoryPool;
  82. typedef struct _ShadowVolumeState {
  83.   ShadowVolumeMemoryPool *pool;
  84.   GLfloat shadowProjectionDistance;
  85.   GLfloat extentScale;
  86.   /* Scratch variables used during GLU tessellator callbacks. */
  87.   int saveFirst;
  88.   GLfloat *firstVertex;
  89. } ShadowVolumeState;
  90. ShadowVolumeState *svs;
  91. static void CALLBACK
  92. begin(GLenum type, void *shadowVolumeState)
  93. {
  94.   ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
  95.   assert(type == GL_LINE_LOOP);
  96.   if (renderBoundary) {
  97.     glBegin(type);
  98.   } else {
  99.     svs->saveFirst = 1;
  100.     glBegin(GL_TRIANGLE_FAN);
  101.     glColor3f(0, 1, 0);
  102.     glVertex3f(0.0, 0.0, 0.0);
  103.   }
  104. }
  105. static void CALLBACK
  106. vertex(void *data, void *shadowVolumeState)
  107. {
  108.   ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
  109.   GLfloat *v = data;
  110.   if (renderBoundary) {
  111.     glVertex2fv(v);
  112.   } else {
  113.     if (svs->saveFirst) {
  114.       svs->firstVertex = v;
  115.       svs->saveFirst = 0;
  116.     }
  117.     glColor3f(0, 0, 1);
  118.     glVertex3f(svs->extentScale * v[X], svs->extentScale * v[Y],
  119.       svs->shadowProjectionDistance);
  120.   }
  121. }
  122. static void CALLBACK
  123. end(void *shadowVolumeState)
  124. {
  125.   ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
  126.   if (!renderBoundary) {
  127.     glColor3f(0, 0, 1);
  128.     glVertex3f(svs->extentScale * svs->firstVertex[X], svs->extentScale * svs->firstVertex[Y],
  129.       svs->shadowProjectionDistance);
  130.   }
  131.   glEnd();
  132. }
  133. static void
  134. freeExcessList(ShadowVolumeMemoryPool * pool)
  135. {
  136.   struct VertexHolder *holder, *next;
  137.   holder = pool->excessList;
  138.   while (holder) {
  139.     next = holder->next;
  140.     free(holder);
  141.     holder = next;
  142.   }
  143.   pool->excessList = NULL;
  144. }
  145. /* ARGSUSED1 */
  146. static void CALLBACK
  147. combine(GLdouble coords[3], void *d[4], GLfloat w[4], void **dataOut, void *shadowVolumeState)
  148. {
  149.   ShadowVolumeState *svs = (ShadowVolumeState *) shadowVolumeState;
  150.   ShadowVolumeMemoryPool *pool = svs->pool;
  151.   struct VertexHolder *holder;
  152.   GLfloat *newCoords;
  153.   if (pool->combineNext >= pool->combineListSize) {
  154.     holder = (struct VertexHolder *) malloc(sizeof(struct VertexHolder));
  155.     holder->next = pool->excessList;
  156.     pool->excessList = holder;
  157.     newCoords = holder->v;
  158.   } else {
  159.     newCoords = &pool->combineList[pool->combineNext * 2];
  160.   }
  161.   newCoords[0] = coords[0];
  162.   newCoords[1] = coords[1];
  163.   *dataOut = newCoords;
  164.   pool->combineNext++;
  165. }
  166. static void CALLBACK
  167. error(GLenum errno)
  168. {
  169.   printf("ERROR: %sn", gluErrorString(errno));
  170. }
  171. static void
  172. processFeedback(GLint size, GLfloat * buffer, ShadowVolumeState * svs)
  173. {
  174.   ShadowVolumeMemoryPool *pool = svs->pool;
  175.   GLfloat *loc, *end, *eyeLoc;
  176.   GLdouble v[3];
  177.   int token, nvertices, i;
  178.   GLfloat passThroughToken;
  179.   int watchingForEyePos;
  180.   if (pool->combineNext > pool->combineListSize) {
  181.     freeExcessList(pool);
  182.     pool->combineListSize = pool->combineNext;
  183.     pool->combineList = realloc(pool->combineList, sizeof(GLfloat) * 2 * pool->combineListSize);
  184.   }
  185.   pool->combineNext = 0;
  186.   watchingForEyePos = 0;
  187.   eyeLoc = NULL;
  188.   glColor3f(1, 1, 1);
  189.   gluTessBeginPolygon(pool->tess, svs);
  190.   loc = buffer;
  191.   end = buffer + size;
  192.   while (loc < end) {
  193.     token = *loc;
  194.     loc++;
  195.     switch (token) {
  196.     case GL_POLYGON_TOKEN:
  197.       nvertices = *loc;
  198.       loc++;
  199.       assert(nvertices >= 3);
  200.       gluTessBeginContour(pool->tess);
  201.       for (i = 0; i < nvertices; i++) {
  202.         v[0] = loc[0];
  203.         v[1] = loc[1];
  204.         v[2] = 0.0;
  205.         gluTessVertex(pool->tess, v, loc);
  206.         loc += 2;
  207.       }
  208.       gluTessEndContour(pool->tess);
  209.       break;
  210.     case GL_PASS_THROUGH_TOKEN:
  211.       passThroughToken = *loc;
  212.       if (passThroughToken == uniquePassThroughValue) {
  213.         watchingForEyePos = !watchingForEyePos;
  214.       } else {
  215.         /* Ignore everything else. */
  216.         fprintf(stderr, "ERROR: Unexpected feedback token 0x%x (%d).n", token, token);
  217.       }
  218.       loc++;
  219.       break;
  220.     case GL_POINT_TOKEN:
  221.       if (watchingForEyePos) {
  222.         fprintf(stderr, "WARNING: Eye point possibly within the shadow volume.n");
  223.         fprintf(stderr, "         Program should be improved to handle this.n");
  224.         /* XXX Write code to handle this case.  You would need to determine
  225.            if the point was instead any of the returned boundary polyons.
  226.            Once you found that you were really in the clipping volume, then I
  227.            haven't quite thought about what you do. */
  228.         eyeLoc = loc;
  229.         watchingForEyePos = 0;
  230.       } else {
  231.         /* Ignore everything else. */
  232.         fprintf(stderr, "ERROR: Unexpected feedback token 0x%x (%d).n",
  233.           token, token);
  234.       }
  235.       loc += 2;
  236.       break;
  237.     default:
  238.       /* Ignore everything else. */
  239.       fprintf(stderr, "ERROR: Unexpected feedback token 0x%x (%d).n",
  240.         token, token);
  241.     }
  242.   }
  243.   gluTessEndPolygon(pool->tess);
  244.   if (eyeLoc && renderBoundary) {
  245.     glColor3f(0, 1, 0);
  246.     glPointSize(7.0);
  247.     glBegin(GL_POINTS);
  248.     glVertex2fv(eyeLoc);
  249.     glEnd();
  250.   }
  251. }
  252. /* Three element vector dot product. */
  253. static GLfloat
  254. vdot(const GLfloat * v1, const GLfloat * v2)
  255. {
  256.   return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  257. }
  258. /* Three element vector cross product. */
  259. static void
  260. vcross(const GLfloat * v1, const GLfloat * v2, GLfloat * cross)
  261. {
  262.   assert(v1 != cross && v2 != cross);
  263.   cross[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
  264.   cross[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
  265.   cross[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]);
  266. }
  267. void
  268. svsFreeShadowVolumeState(ShadowVolumeState * svs)
  269. {
  270.   if (svs->pool) {
  271.     svs->pool->refcnt--;
  272.     if (svs->pool->refcnt == 0) {
  273.       if (svs->pool->excessList) {
  274.         freeExcessList(svs->pool);
  275.       }
  276.       if (svs->pool->combineList) {
  277.         free(svs->pool->combineList);
  278.       }
  279.       if (svs->pool->tess) {
  280.         gluDeleteTess(svs->pool->tess);
  281.       }
  282.       free(svs->pool);
  283.     }
  284.   }
  285.   free(svs);
  286. }
  287. ShadowVolumeState *
  288. svsCreateShadowVolumeState(GLfloat shadowProjectionDistance,
  289.   ShadowVolumeState * shareSVS)
  290. {
  291.   ShadowVolumeState *svs;
  292.   ShadowVolumeMemoryPool *pool;
  293.   GLUtesselator *tess;
  294.   svs = (ShadowVolumeState *) malloc(sizeof(ShadowVolumeState));
  295.   if (svs == NULL) {
  296.     return NULL;
  297.   }
  298.   svs->pool = NULL;
  299.   if (shareSVS == NULL) {
  300.     pool = (ShadowVolumeMemoryPool *) malloc(sizeof(ShadowVolumeMemoryPool));
  301.     if (pool == NULL) {
  302.       svsFreeShadowVolumeState(svs);
  303.       return NULL;
  304.     }
  305.     pool->refcnt = 1;
  306.     pool->excessList = NULL;
  307.     pool->combineList = NULL;
  308.     pool->combineListSize = 0;
  309.     pool->combineNext = 0;
  310.     pool->tess = NULL;
  311.     svs->pool = pool;
  312.     tess = gluNewTess();
  313.     if (tess == NULL) {
  314.       svsFreeShadowVolumeState(svs);
  315.       return NULL;
  316.     }
  317.     gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
  318.     gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
  319.     gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (void (CALLBACK*)()) begin);
  320.     gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (void (CALLBACK*)()) vertex);
  321.     gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (void (CALLBACK*)()) combine);
  322.     gluTessCallback(tess, GLU_TESS_END_DATA, (void (CALLBACK*)()) end);
  323.     gluTessCallback(tess, GLU_TESS_ERROR, (void (CALLBACK*)()) error);
  324.     pool->tess = tess;
  325.   } else {
  326.     pool = shareSVS->pool;
  327.     pool->refcnt++;
  328.   }
  329.   svs->pool = pool;
  330.   svs->shadowProjectionDistance = shadowProjectionDistance;
  331.   return svs;
  332. }
  333. int
  334. svsGenerateShadowVolume(ShadowVolumeState * svs,
  335.   void (*renderFunc) (void), int feedbackBufferSizeGuess,
  336.   GLfloat maxRadius,
  337.   GLfloat lightPos[3], GLfloat objectPos[3], GLfloat eyePos[3])
  338. {
  339.   static GLfloat unit[3] =
  340.   {0.0, 0.0, 1.0};
  341.   static GLfloat *feedbackBuffer = NULL;
  342.   static int bufferSize = 0;
  343.   GLfloat axis[3], lightDelta[3], eyeDelta[3];
  344.   GLfloat nnear, ffar;  /* Avoid Intel C keywords.  Grumble. */
  345.   GLfloat lightDistance, eyeDistance, angle, fieldOfViewRatio, fieldOfViewAngle,
  346.     topScale, viewScale;
  347.   GLint returned;
  348.   if (svs->pool->viewScale == 0.0) {
  349.     GLfloat maxViewSize[2];
  350.     glGetFloatv(GL_MAX_VIEWPORT_DIMS, maxViewSize);
  351.     printf("max viewport = %gx%gn", maxViewSize[0], maxViewSize[1]);
  352.     svs->pool->viewScale = SmallerOf(maxViewSize[0], maxViewSize[1]) / 2.0;
  353.   }
  354.   viewScale = svs->pool->viewScale;
  355.   if (bufferSize > feedbackBufferSizeGuess) {
  356.     feedbackBufferSizeGuess = bufferSize;
  357.   }
  358.   /* Calculate the light's distance from the object being shadowed. */
  359.   lightDelta[X] = objectPos[X] - lightPos[X];
  360.   lightDelta[Y] = objectPos[Y] - lightPos[Y];
  361.   lightDelta[Z] = objectPos[Z] - lightPos[Z];
  362.   lightDistance = sqrt(lightDelta[X] * lightDelta[X] +
  363.     lightDelta[Y] * lightDelta[Y] + lightDelta[Z] * lightDelta[Z]);
  364.   /* Determine the appropriate field of view.  We want to use as narrow a
  365.      field of view as possible to not waste resolution, but not narrower than
  366.      the object.  Add 50% extra slop. */
  367.   fieldOfViewRatio = maxRadius / lightDistance;
  368.   if (fieldOfViewRatio > 0.99) {
  369.     fprintf(stderr, "WARNING: Clamping FOV to 164 degrees for determining shadow boundary.n");
  370.     fprintf(stderr, "         Light distance = %g, object maxmium radius = %gn",
  371.       lightDistance, maxRadius);
  372.     /* 2*asin(0.99) ~= 164 degrees. */
  373.     fieldOfViewRatio = 0.99;
  374.   }
  375.   /* Pre-compute scaling factors for the near and far extent of the shadow
  376.      volume. */
  377.   svs->extentScale = svs->shadowProjectionDistance * fieldOfViewRatio / viewScale;
  378.   glMatrixMode(GL_PROJECTION);
  379.   glPushMatrix();
  380.   glLoadIdentity();
  381.   nnear = 0.5 * (lightDistance - maxRadius);
  382.   if (nnear < 0.0001) {
  383.     fprintf(stderr, "WARNING: Clamping near clip plane to 0.0001 because light source too near.n");
  384.     fprintf(stderr, "         Light distance = %g, object maxmium radius = %gn",
  385.       lightDistance, maxRadius);
  386.     nnear = 0.0001;
  387.   }
  388.   ffar = 2.0 * (lightDistance + maxRadius);
  389.   if (eyePos) {
  390.     eyeDelta[X] = eyePos[X] - lightPos[X];
  391.     eyeDelta[Y] = eyePos[Y] - lightPos[Y];
  392.     eyeDelta[Z] = eyePos[Z] - lightPos[Z];
  393.     eyeDistance = 1.05 * sqrt(eyeDelta[X] * eyeDelta[X] + eyeDelta[Y] * eyeDelta[Y] + eyeDelta[Z] * eyeDelta[Z]);
  394.     if (eyeDistance > ffar) {
  395.       ffar = eyeDistance;
  396.     }
  397.   }
  398.   fieldOfViewAngle = 2.0 * asin(fieldOfViewRatio) * 180 / M_PI;
  399.   gluPerspective(fieldOfViewAngle, 1.0, nnear, ffar);
  400.   glMatrixMode(GL_MODELVIEW);
  401.   glPushMatrix();
  402.   glLoadIdentity();
  403.   /* XXX Need to update "up vector".  Degenerate when light directly above or 
  404.      below the object. */
  405.   gluLookAt(lightPos[X], lightPos[Y], lightPos[Z],
  406.     objectPos[X], objectPos[Y], objectPos[Z],
  407.     0.0, 1.0, 0.0);     /* up is in positive Y direction */
  408.   glPushAttrib(GL_VIEWPORT_BIT);
  409.   glViewport(-viewScale, -viewScale, 2 * viewScale, 2 * viewScale);
  410. doFeedback:
  411.   /* XXX Careful, some systems still don't understand realloc of NULL. */
  412.   if (bufferSize < feedbackBufferSizeGuess) {
  413.     bufferSize = feedbackBufferSizeGuess;
  414.     /* XXX Add 32 words of slop (an extra cache line) to end for buggy
  415.        hardware that uses DMA to return feedback results but that sometimes
  416.        overrun the buffer.  Yuck. */
  417.     feedbackBuffer = realloc(feedbackBuffer, bufferSize * sizeof(GLfloat) + 32 * 4);
  418.   }
  419.   glFeedbackBuffer(bufferSize, GL_2D, feedbackBuffer);
  420.   (void) glRenderMode(GL_FEEDBACK);
  421.   (*renderFunc) ();
  422.   /* Render the eye position.  The eye position is "bracketed" by unique pass 
  423.      through tokens.  These bracketing pass through tokens let us determine
  424.      if the eye position was clipped or not.  This helps us determine whether 
  425.      the eye position is possibly within the shadow volume or not.  If the
  426.      point is clipped, the eye position is not in the shadow volume.  If the
  427.      point is not clipped, a more complicated test is necessary to determine
  428.      if the eye position is really in the shadow volume or not.  See
  429.      processFeedback. */
  430.   if (eyePos) {
  431.     glPassThrough(uniquePassThroughValue);
  432.     glBegin(GL_POINTS);
  433.     glVertex3fv(eyePos);
  434.     glEnd();
  435.     glPassThrough(uniquePassThroughValue);
  436.   }
  437.   returned = glRenderMode(GL_RENDER);
  438. #if 0
  439.   if (returned == -1) {
  440. #else
  441.   /* XXX RealityEngine workaround. */
  442.   if (returned == -1 || returned == feedbackBufferSizeGuess) {
  443. #endif
  444.     feedbackBufferSizeGuess = feedbackBufferSizeGuess + (feedbackBufferSizeGuess >> 1);
  445.     goto doFeedback;    /* Try again with larger feedback buffer. */
  446.   }
  447.   glMatrixMode(GL_PROJECTION);
  448.   glPopMatrix();
  449.   glMatrixMode(GL_MODELVIEW);
  450.   glPopMatrix();
  451.   glPopAttrib();        /* Restore viewport. */
  452.   if (renderBoundary) {
  453.     glMatrixMode(GL_PROJECTION);
  454.     glLoadIdentity();
  455.     gluOrtho2D(-viewScale, viewScale, -viewScale, viewScale);
  456.     glMatrixMode(GL_MODELVIEW);
  457.     glLoadIdentity();
  458.     processFeedback(returned, feedbackBuffer, svs);
  459.   } else {
  460.     glNewList(DL_BASE_SHADOW_VOLUME, GL_COMPILE);
  461.     vcross(unit, lightDelta, axis);
  462.     angle = acos(vdot(unit, lightDelta) / lightDistance) * 180.0 / M_PI;
  463.     glRotatef(angle, axis[X], axis[Y], axis[Z]);
  464.     processFeedback(returned, feedbackBuffer, svs);
  465.     glEndList();
  466.     glNewList(DL_SHADOW_VOLUME, GL_COMPILE);
  467.     glPushMatrix();
  468.     glTranslatef(lightPos[X], lightPos[Y], lightPos[Z]);
  469.     glCallList(DL_BASE_SHADOW_VOLUME);
  470.     glPopMatrix();
  471.     glEndList();
  472.     glNewList(DL_SHADOW_VOLUME_TOP, GL_COMPILE);
  473.     glPushMatrix();
  474.     glTranslatef(lightPos[X], lightPos[Y], lightPos[Z]);
  475.     topScale = (lightDistance + maxRadius) / svs->shadowProjectionDistance;
  476.     glScalef(topScale, topScale, topScale);
  477.     glCallList(DL_BASE_SHADOW_VOLUME);
  478.     glPopMatrix();
  479.     glEndList();
  480.   }
  481.   return returned;
  482. }
  483. GLfloat objectMaxRadius[] =
  484. {
  485.   8.0 + 2.0,            /* M_TORUS */
  486.   12.0 / 2.0 * 1.142,   /* M_CUBE */
  487.   8.0,                  /* M_SPHERE */
  488.   8.0,                  /* M_ICO */
  489.   8.0 + 2.0,            /* M_DOUBLE_TORUS */
  490. };
  491. void
  492. renderShadowingObject(void)
  493. {
  494.   static int torusList = 0, cubeList = 0, sphereList = 0, icoList = 0;
  495.   glPushMatrix();
  496.   glTranslatef(objectPos[X], objectPos[Y], objectPos[Z]);
  497.   glRotatef(angle, 1.0, 0.3, 0.0);
  498.   switch (shape) {
  499.   case M_TORUS:
  500.     if (torusList) {
  501.       glCallList(torusList);
  502.     } else {
  503.       torusList = DL_TORUS;
  504.       glNewList(torusList, GL_COMPILE_AND_EXECUTE);
  505.       glutSolidTorus(2.0, 8.0, 8, 15);
  506.       glEndList();
  507.     }
  508.     break;
  509.   case M_CUBE:
  510.     if (cubeList) {
  511.       glCallList(cubeList);
  512.     } else {
  513.       cubeList = DL_CUBE;
  514.       glNewList(cubeList, GL_COMPILE_AND_EXECUTE);
  515.       glutSolidCube(12.0);
  516.       glEndList();
  517.     }
  518.     break;
  519.   case M_SPHERE:
  520.     if (sphereList) {
  521.       glCallList(sphereList);
  522.     } else {
  523.       sphereList = DL_SPHERE;
  524.       glNewList(sphereList, GL_COMPILE_AND_EXECUTE);
  525.       glutSolidSphere(8.0, 10, 10);
  526.       glEndList();
  527.     }
  528.     break;
  529.   case M_ICO:
  530.     if (icoList) {
  531.       glCallList(icoList);
  532.     } else {
  533.       icoList = DL_ICO;
  534.       glNewList(icoList, GL_COMPILE_AND_EXECUTE);
  535.       glEnable(GL_NORMALIZE);
  536.       glPushMatrix();
  537.       glScalef(8.0, 8.0, 8.0);
  538.       glutSolidIcosahedron();
  539.       glPopMatrix();
  540.       glDisable(GL_NORMALIZE);
  541.       glEndList();
  542.     }
  543.     break;
  544.   case M_DOUBLE_TORUS:
  545.     if (torusList) {
  546.       glCallList(torusList);
  547.     } else {
  548.       torusList = DL_TORUS;
  549.       glNewList(torusList, GL_COMPILE_AND_EXECUTE);
  550.       glutSolidTorus(2.0, 8.0, 8, 15);
  551.       glEndList();
  552.     }
  553.     glRotatef(90, 0, 1, 0);
  554.     glCallList(torusList);
  555.     break;
  556.   }
  557.   glPopMatrix();
  558. }
  559. void
  560. sphere(void)
  561. {
  562.   glPushMatrix();
  563.   glTranslatef(60.0, -50.0, -400.0);
  564.   glCallList(DL_BALL);
  565.   glPopMatrix();
  566. }
  567. void
  568. cone(void)
  569. {
  570.   glPushMatrix();
  571.   glTranslatef(-40.0, -40.0, -400.0);
  572.   glCallList(DL_CONE);
  573.   glPopMatrix();
  574. }
  575. void
  576. scene(void)
  577. {
  578.   /* material properties for objects in scene */
  579.   static GLfloat wall_mat[] =
  580.   {1.0, 1.0, 1.0, 1.0};
  581.   static GLfloat shad_mat[] =
  582.   {1.0, 0.1, 0.1, 1.0};
  583.   glMatrixMode(GL_PROJECTION);
  584.   glLoadIdentity();
  585.   if (view == M_LIGHT_SOURCE_VIEW) {
  586.     gluPerspective(45.0, 1.0, 0.5, 600.0);
  587.   } else {
  588.     gluPerspective(33.0, 1.0, 10.0, 600.0);
  589.   }
  590.   glMatrixMode(GL_MODELVIEW);
  591.   glLoadIdentity();
  592.   if (view == M_LIGHT_SOURCE_VIEW) {
  593.     gluLookAt(lightPos[X], lightPos[Y], lightPos[Z],
  594.       objectPos[X], objectPos[Y], objectPos[Z],
  595.       0.0, 1.0, 0.);    /* up is in positive Y direction */
  596.   } else {
  597.     gluLookAt(0.0, 0.0, 0.0,
  598.       0.0, 0.0, -100.0,
  599.       0.0, 1.0, 0.);    /* up is in positive Y direction */
  600.   }
  601.   /* Place light 0 in the right place. */
  602.   glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
  603.   /* Note: wall verticies are ordered so they are all front facing this lets
  604.      me do back face culling to speed things up.  */
  605.   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, wall_mat);
  606.   /* Floor with checkerboard texture.  */
  607.   glEnable(GL_TEXTURE_2D);
  608.   glColor3f(0, 0, 0);
  609.   /* Since we want to turn texturing on for floor only, we have to make floor
  610.      a separate glBegin()/glEnd() sequence. You can't turn texturing on and
  611.      off between begin and end calls */
  612.   glBegin(GL_QUADS);
  613.   glNormal3f(0.0, 1.0, 0.0);
  614.   glTexCoord2i(0, 0);
  615.   glVertex3f(-100.0, -100.0, -320.0);
  616.   glTexCoord2i(4, 0);
  617.   glVertex3f(100.0, -100.0, -320.0);
  618.   glTexCoord2i(4, 4);
  619.   glVertex3f(100.0, -100.0, -520.0);
  620.   glTexCoord2i(0, 4);
  621.   glVertex3f(-100.0, -100.0, -520.0);
  622.   glEnd();
  623.   glDisable(GL_TEXTURE_2D);
  624.   /* Walls. */
  625.   glBegin(GL_QUADS);
  626.   /* Left wall. */
  627.   glNormal3f(1.0, 0.0, 0.0);
  628.   glVertex3f(-100.0, -100.0, -320.0);
  629.   glVertex3f(-100.0, -100.0, -520.0);
  630.   glVertex3f(-100.0, 100.0, -520.0);
  631.   glVertex3f(-100.0, 100.0, -320.0);
  632.   /* Right wall. */
  633.   glNormal3f(-1.0, 0.0, 0.0);
  634.   glVertex3f(100.0, -100.0, -320.0);
  635.   glVertex3f(100.0, 100.0, -320.0);
  636.   glVertex3f(100.0, 100.0, -520.0);
  637.   glVertex3f(100.0, -100.0, -520.0);
  638.   /* Ceiling. */
  639.   glNormal3f(0.0, -1.0, 0.0);
  640.   glVertex3f(-100.0, 100.0, -320.0);
  641.   glVertex3f(-100.0, 100.0, -520.0);
  642.   glVertex3f(100.0, 100.0, -520.0);
  643.   glVertex3f(100.0, 100.0, -320.0);
  644.   /* Back wall. */
  645.   glNormal3f(0.0, 0.0, 1.0);
  646.   glVertex3f(-100.0, -100.0, -520.0);
  647.   glVertex3f(100.0, -100.0, -520.0);
  648.   glVertex3f(100.0, 100.0, -520.0);
  649.   glVertex3f(-100.0, 100.0, -520.0);
  650.   glEnd();
  651.   cone();
  652.   sphere();
  653.   glPushMatrix();
  654.   glTranslatef(lightPos[X], lightPos[Y], lightPos[Z]);
  655.   glCallList(DL_LIGHT);
  656.   glPopMatrix();
  657.   /* Draw shadowing object. */
  658.   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, shad_mat);
  659.   renderShadowingObject();
  660. }
  661. void
  662. generateShadowVolume(void)
  663. {
  664.   GLfloat *eyePos;
  665.   if (view == M_LIGHT_SOURCE_VIEW) {
  666.     eyePos = lightPos;
  667.   } else {
  668.     eyePos = sceneEyePos;
  669.   }
  670.   /* XXX The 2048 feedbackBufferGuessSize is large enough to
  671.      workaround the Octane/Impact bug where if the feedback
  672.      buffer is under 2048 entries, a buggy hardware feedback
  673.      path is used.  2048 forces the (bug free) software path.
  674.      This bug is fixed in IRIX 6.5. */
  675.   svsGenerateShadowVolume(svs, renderShadowingObject, 2048, maxRadius,
  676.     lightPos, objectPos, eyePos);
  677. }
  678. void
  679. display(void)
  680. {
  681.   if (renderBoundary) {
  682.     glClear(GL_COLOR_BUFFER_BIT);
  683.     glDisable(GL_LIGHTING);
  684.     glDisable(GL_DEPTH_TEST);
  685.     generateShadowVolume();
  686.     glEnable(GL_LIGHTING);
  687.   } else {
  688.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  689.     switch (renderMode) {
  690.     case M_NO_SHADOW:
  691.       glEnable(GL_DEPTH_TEST);
  692.       glEnable(GL_LIGHT0);
  693.       scene();
  694.       break;
  695.     case M_NO_LIGHT:
  696.       /* Render scene without the light source enabled (conceptually, the
  697.          entire scene is "in the shadow"). */
  698.       glEnable(GL_DEPTH_TEST);
  699.       glDisable(GL_LIGHT0);
  700.       scene();
  701.       break;
  702.     case M_FRONT_VOLUME:
  703.     case M_BACK_VOLUME:
  704.       generateShadowVolume();
  705.       glEnable(GL_DEPTH_TEST);
  706.       glEnable(GL_LIGHT0);
  707.       scene();
  708.       if (frontFace) {
  709.         glFrontFace(GL_CW);
  710.       } else {
  711.         glFrontFace(GL_CCW);
  712.       }
  713.       glCallList(DL_SHADOW_VOLUME);
  714.       glFrontFace(GL_CCW);
  715.       break;
  716.     case M_SHADOW:
  717.       /* Construct DL_SHADOW_VOLUME display list for the scene's current
  718.          shadow volume. */
  719.       generateShadowVolume();
  720.       /* 1st scene pass. */
  721.       glEnable(GL_DEPTH_TEST);
  722.       glEnable(GL_LIGHT0);
  723.       scene();
  724.       /* 1st shadow volume pass:  Enable stencil to increment the stencil
  725.          value of pixels that pass the depth test when drawing the front
  726.          facing polygons of the shadow volume.  Do not update the depth
  727.          buffer while rendering the shadow volume. */
  728.       glDisable(GL_LIGHTING);
  729.       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  730.       glEnable(GL_STENCIL_TEST);
  731.       glDepthMask(GL_FALSE);
  732.       glStencilFunc(GL_ALWAYS, 0, 0);
  733.       glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
  734.       glCullFace(GL_FRONT);
  735.       glCallList(DL_SHADOW_VOLUME);
  736.       /* 2nd shadow volume pass:  Now, draw the back facing polygons of the
  737.          shadow volume except decrement pixels that pass the depth test.
  738.          Again, do not update the depth buffer. */
  739.       glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);
  740.       glCullFace(GL_BACK);
  741.       glCallList(DL_SHADOW_VOLUME);
  742.       glDisable(GL_CULL_FACE);
  743.       glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
  744.       glCallList(DL_SHADOW_VOLUME_TOP);
  745.       glEnable(GL_CULL_FACE);
  746.       /* Now, pixels that lie within the shadow volume are tagged with a one
  747.          stencil value.  Empty shadowed regions of the shadow volume get
  748.          incremented, then decremented, to resolve to a net zero stencil
  749.          value. */
  750.       /* 2nd scene pass (render shadowed region):  Re-enable update of the
  751.          depth and color buffer (use GL_LEQUAL for depth buffer so we can
  752.          over-write depth values again with color.  Switch back to backface
  753.          culling and disable the light source.  Only update pixels with a
  754.          stencil value of one (shadowed). */
  755.       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  756. #if 0
  757.       glDepthFunc(GL_EQUAL);
  758. #else
  759.       glDepthMask(GL_TRUE);
  760.       glDepthFunc(GL_LEQUAL);
  761. #endif
  762.       glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
  763.       glStencilFunc(GL_EQUAL, 1, 1);
  764.       glDisable(GL_LIGHT0);
  765.       glEnable(GL_LIGHTING);
  766.       scene();
  767.       /* Put state back to sane modes. */
  768.       glDepthMask(GL_TRUE);
  769.       glDepthFunc(GL_LESS);
  770.       glDisable(GL_STENCIL_TEST);
  771.       break;
  772.     }
  773.   }
  774.   glutSwapBuffers();
  775. }
  776. void
  777. idle(void)
  778. {
  779.   if (rotatingObject) {
  780.     angle += 10.0;
  781.   }
  782.   if (swingingLight) {
  783.     swingTime += 0.05;
  784.     lightPos[X] += 2 * cos(swingTime);
  785.   }
  786.   glutPostRedisplay();
  787. }
  788. void
  789. menu(int value)
  790. {
  791.   switch (value) {
  792.   case M_TORUS:
  793.   case M_CUBE:
  794.   case M_SPHERE:
  795.   case M_ICO:
  796.   case M_DOUBLE_TORUS:
  797.     shape = value;
  798.     maxRadius = objectMaxRadius[value];
  799.     glutPostRedisplay();
  800.     break;
  801.   case M_ANGLE:
  802.     angle += 10.0;
  803.     glutPostRedisplay();
  804.     break;
  805.   case M_BOUNDARY:
  806.     renderBoundary = 1;
  807.     glutPostRedisplay();
  808.     break;
  809.   case M_FRONT_VOLUME:
  810.   case M_BACK_VOLUME:
  811.     frontFace = (value == M_FRONT_VOLUME);
  812.     /* FALLTHROUGH */
  813.   case M_NO_SHADOW:
  814.   case M_NO_LIGHT:
  815.   case M_SHADOW:
  816.     renderBoundary = 0;
  817.     renderMode = value;
  818.     glutPostRedisplay();
  819.     break;
  820.   case M_LIGHT_SOURCE_VIEW:
  821.   case M_NORMAL_VIEW:
  822.     view = value;
  823.     glutPostRedisplay();
  824.     break;
  825.   case M_STOP:
  826.     swingingLight = 0;
  827.     rotatingObject = 0;
  828.     glutIdleFunc(NULL);
  829.     break;
  830.   case M_SPIN:
  831.     rotatingObject = 1;
  832.     glutIdleFunc(idle);
  833.     break;
  834.   case M_SWING:
  835.     swingingLight = 1;
  836.     glutIdleFunc(idle);
  837.     break;
  838.   case 666:
  839.     svsFreeShadowVolumeState(svs);
  840.     exit(0);
  841.     /* NOTREACHED */
  842.     break;
  843.   }
  844. }
  845. void
  846. visible(int vis)
  847. {
  848.   if (vis == GLUT_VISIBLE && swingingLight && rotatingObject) {
  849.     glutIdleFunc(idle);
  850.   } else {
  851.     glutIdleFunc(NULL);
  852.   }
  853. }
  854. /* ARGSUSED1 */
  855. void
  856. key(unsigned char c, int x, int y)
  857. {
  858.   switch (c) {
  859.   case 27:             /* Escape. */
  860.     svsFreeShadowVolumeState(svs);
  861.     exit(0);
  862.     break;
  863.   case 13:             /* Return. */
  864.     swingingLight = !swingingLight;
  865.     swingTime = M_PI / 2.0;
  866.     break;
  867.   case ' ':            /* Space. */
  868.     if (rotatingObject || swingingLight) {
  869.       rotatingObject = 0;
  870.       swingingLight = 0;
  871.       glutIdleFunc(NULL);
  872.     } else {
  873.       rotatingObject = 1;
  874.       swingingLight = 1;
  875.       glutIdleFunc(idle);
  876.     }
  877.     break;
  878.   }
  879. }
  880. /* ARGSUSED1 */
  881. void
  882. special(int key, int x, int y)
  883. {
  884.   switch (key) {
  885.   case GLUT_KEY_HOME:
  886.     frontFace = !frontFace;
  887.     glutPostRedisplay();
  888.     break;
  889.   case GLUT_KEY_UP:
  890.     lightPos[Y] += 10.0;
  891.     glutPostRedisplay();
  892.     break;
  893.   case GLUT_KEY_DOWN:
  894.     lightPos[Y] -= 10.0;
  895.     glutPostRedisplay();
  896.     break;
  897.   case GLUT_KEY_PAGE_UP:
  898.     lightPos[Z] += 10.0;
  899.     glutPostRedisplay();
  900.     break;
  901.   case GLUT_KEY_PAGE_DOWN:
  902.     lightPos[Z] -= 10.0;
  903.     glutPostRedisplay();
  904.     break;
  905.   case GLUT_KEY_RIGHT:
  906.     lightPos[X] += 10.0;
  907.     glutPostRedisplay();
  908.     break;
  909.   case GLUT_KEY_LEFT:
  910.     lightPos[X] -= 10.0;
  911.     glutPostRedisplay();
  912.     break;
  913.   }
  914. }
  915. /* Create a single component checkboard texture map. */
  916. GLfloat *
  917. makeTexture(int maxs, int maxt)
  918. {
  919.   int s, t;
  920.   static GLfloat *texture;
  921.   texture = (GLfloat *) malloc(maxs * maxt * sizeof(GLfloat));
  922.   for (t = 0; t < maxt; t++) {
  923.     for (s = 0; s < maxs; s++) {
  924.       texture[s + maxs * t] = ((s >> 4) & 0x1) ^ ((t >> 4) & 0x1);
  925.     }
  926.   }
  927.   return texture;
  928. }
  929. void
  930. initScene(void)
  931. {
  932.   GLfloat *tex;
  933.   GLUquadricObj *qobj;
  934.   static GLfloat sphere_mat[] =
  935.   {1.0, 0.5, 0.0, 1.0};
  936.   static GLfloat cone_mat[] =
  937.   {0.0, 0.5, 1.0, 1.0};
  938.   /* Turn on features. */
  939.   glEnable(GL_DEPTH_TEST);
  940.   glEnable(GL_LIGHTING);
  941.   glEnable(GL_LIGHT0);
  942.   glEnable(GL_CULL_FACE);
  943.   /* remove back faces to speed things up */
  944.   glCullFace(GL_BACK);
  945.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  946.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  947.   /* Make display lists for sphere and cone; for efficiency. */
  948.   qobj = gluNewQuadric();
  949.   glNewList(DL_BALL, GL_COMPILE);
  950.   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, sphere_mat);
  951.   gluSphere(qobj, 20.0, 20, 20);
  952.   glEndList();
  953.   glNewList(DL_CONE, GL_COMPILE);
  954.   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, cone_mat);
  955.   glRotatef(-90.0, 1.0, 0.0, 0.0);
  956.   gluDisk(qobj, 0.0, 20.0, 20, 1);
  957.   gluCylinder(qobj, 20.0, 0.0, 60.0, 20, 20);
  958.   glEndList();
  959.   glNewList(DL_LIGHT, GL_COMPILE);
  960.   glDisable(GL_LIGHTING);
  961.   glColor3f(0.9, 0.9, 0.6);
  962.   gluSphere(qobj, 5.0, 20, 20);
  963.   glEnable(GL_LIGHTING);
  964.   glEndList();
  965.   gluDeleteQuadric(qobj);
  966.   /* load pattern for current 2d texture */
  967.   tex = makeTexture(TEXDIM, TEXDIM);
  968.   glTexImage2D(GL_TEXTURE_2D, 0, 1, TEXDIM, TEXDIM, 0, GL_RED, GL_FLOAT, tex);
  969.   free(tex);
  970. }
  971. int
  972. main(int argc, char **argv)
  973. {
  974.   int shapeMenu, viewMenu, actionMenu, renderModeMenu;
  975.   svs = svsCreateShadowVolumeState(1000.0, NULL);
  976.   glutInitDisplayString("stencil>=1 rgb double depth samples");
  977.   glutInit(&argc, argv);
  978.   glutCreateWindow("shadowfun");
  979.   stencilBits = glutGet(GLUT_WINDOW_STENCIL_SIZE);
  980.   printf("bits of stencil = %dn", stencilBits);
  981.   glutDisplayFunc(display);
  982.   glutVisibilityFunc(visible);
  983.   glutSpecialFunc(special);
  984.   glutKeyboardFunc(key);
  985.   initScene();
  986.   shapeMenu = glutCreateMenu(menu);
  987.   glutAddMenuEntry("Torus", M_TORUS);
  988.   glutAddMenuEntry("Cube", M_CUBE);
  989.   glutAddMenuEntry("Sphere", M_SPHERE);
  990.   glutAddMenuEntry("Icosahedron", M_ICO);
  991.   glutAddMenuEntry("Double Torus", M_DOUBLE_TORUS);
  992.   viewMenu = glutCreateMenu(menu);
  993.   glutAddMenuEntry("Normal view", M_NORMAL_VIEW);
  994.   glutAddMenuEntry("Light source view", M_LIGHT_SOURCE_VIEW);
  995.   renderModeMenu = glutCreateMenu(menu);
  996.   glutAddMenuEntry("With shadow", M_SHADOW);
  997.   glutAddMenuEntry("With front shadow volume", M_FRONT_VOLUME);
  998.   glutAddMenuEntry("With back shadow volume", M_BACK_VOLUME);
  999.   glutAddMenuEntry("Without shadow", M_NO_SHADOW);
  1000.   glutAddMenuEntry("Without light", M_NO_LIGHT);
  1001.   glutAddMenuEntry("2D shadow boundary", M_BOUNDARY);
  1002.   actionMenu = glutCreateMenu(menu);
  1003.   glutAddMenuEntry("Spin object", M_SPIN);
  1004.   glutAddMenuEntry("Swing light", M_SWING);
  1005.   glutAddMenuEntry("Stop", M_STOP);
  1006.   glutCreateMenu(menu);
  1007.   glutAddSubMenu("Object shape", shapeMenu);
  1008.   glutAddSubMenu("Viewpoint", viewMenu);
  1009.   glutAddSubMenu("Render mode", renderModeMenu);
  1010.   glutAddSubMenu("Action", actionMenu);
  1011.   glutAddMenuEntry("Step rotate", M_ANGLE);
  1012.   glutAddMenuEntry("Quit", 666);
  1013.   glutAttachMenu(GLUT_RIGHT_BUTTON);
  1014.   menu(M_DOUBLE_TORUS);
  1015.   glutMainLoop();
  1016.   return 0;             /* ANSI C requires main to return int. */
  1017. }
  1018. #else
  1019. int main(int argc, char** argv)
  1020. {
  1021.   fprintf(stderr, "This program requires the new tesselator API in GLU 1.2.n");
  1022.   fprintf(stderr, "Your GLU library does not support this new interface, sorry.n");
  1023.   return 0;
  1024. }
  1025. #endif  /* GLU_VERSION_1_2 */