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