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