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