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

UCC git Repository :: git.ucc.asn.au