Merge branch 'mesa_7_6_branch'
[mesa.git] / progs / demos / 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 static 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 static 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 static 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 = 0, end = 0;
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 fflush(stdout);
628 }
629
630 glutSwapBuffers();
631 }
632
633 /* ARGSUSED2 */
634 static void
635 mouse(int button, int state, int x, int y)
636 {
637 if (button == GLUT_LEFT_BUTTON) {
638 if (state == GLUT_DOWN) {
639 moving = 1;
640 startx = x;
641 starty = y;
642 }
643 if (state == GLUT_UP) {
644 moving = 0;
645 }
646 }
647 if (button == GLUT_MIDDLE_BUTTON) {
648 if (state == GLUT_DOWN) {
649 lightMoving = 1;
650 lightStartX = x;
651 lightStartY = y;
652 }
653 if (state == GLUT_UP) {
654 lightMoving = 0;
655 }
656 }
657 }
658
659 /* ARGSUSED1 */
660 static void
661 motion(int x, int y)
662 {
663 if (moving) {
664 angle = angle + (x - startx);
665 angle2 = angle2 + (y - starty);
666 startx = x;
667 starty = y;
668 glutPostRedisplay();
669 }
670 if (lightMoving) {
671 lightAngle += (x - lightStartX)/40.0;
672 lightHeight += (lightStartY - y)/20.0;
673 lightStartX = x;
674 lightStartY = y;
675 glutPostRedisplay();
676 }
677 }
678
679 /* Advance time varying state when idle callback registered. */
680 static void
681 idle(void)
682 {
683 static float time = 0.0;
684
685 time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
686
687 jump = 4.0 * fabs(sin(time)*0.5);
688 if (!lightMoving) {
689 lightAngle += 0.03;
690 }
691 glutPostRedisplay();
692 }
693
694 enum {
695 M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
696 M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
697 M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
698 };
699
700 static void
701 controlLights(int value)
702 {
703 switch (value) {
704 case M_NONE:
705 return;
706 case M_MOTION:
707 animation = 1 - animation;
708 if (animation) {
709 glutIdleFunc(idle);
710 } else {
711 glutIdleFunc(NULL);
712 }
713 break;
714 case M_LIGHT:
715 lightSwitch = !lightSwitch;
716 if (lightSwitch) {
717 glEnable(GL_LIGHT0);
718 } else {
719 glDisable(GL_LIGHT0);
720 }
721 break;
722 case M_TEXTURE:
723 useTexture = !useTexture;
724 break;
725 case M_SHADOWS:
726 renderShadow = 1 - renderShadow;
727 break;
728 case M_REFLECTION:
729 renderReflection = 1 - renderReflection;
730 break;
731 case M_DINOSAUR:
732 renderDinosaur = 1 - renderDinosaur;
733 break;
734 case M_STENCIL_REFLECTION:
735 stencilReflection = 1 - stencilReflection;
736 break;
737 case M_STENCIL_SHADOW:
738 stencilShadow = 1 - stencilShadow;
739 break;
740 case M_OFFSET_SHADOW:
741 offsetShadow = 1 - offsetShadow;
742 break;
743 case M_POSITIONAL:
744 directionalLight = 0;
745 break;
746 case M_DIRECTIONAL:
747 directionalLight = 1;
748 break;
749 case M_PERFORMANCE:
750 reportSpeed = 1 - reportSpeed;
751 break;
752 }
753 glutPostRedisplay();
754 }
755
756 /* When not visible, stop animating. Restart when visible again. */
757 static void
758 visible(int vis)
759 {
760 if (vis == GLUT_VISIBLE) {
761 if (animation)
762 glutIdleFunc(idle);
763 } else {
764 if (!animation)
765 glutIdleFunc(NULL);
766 }
767 }
768
769 /* Press any key to redraw; good when motion stopped and
770 performance reporting on. */
771 /* ARGSUSED */
772 static void
773 key(unsigned char c, int x, int y)
774 {
775 if (c == 27) {
776 exit(0); /* IRIS GLism, Escape quits. */
777 }
778 glutPostRedisplay();
779 }
780
781 /* Press any key to redraw; good when motion stopped and
782 performance reporting on. */
783 /* ARGSUSED */
784 static void
785 special(int k, int x, int y)
786 {
787 glutPostRedisplay();
788 }
789
790 static int
791 supportsOneDotOne(void)
792 {
793 const char *version;
794 int major, minor;
795
796 version = (char *) glGetString(GL_VERSION);
797 if (sscanf(version, "%d.%d", &major, &minor) == 2)
798 return major * 10 + minor >= 11;
799 return 0; /* OpenGL version string malformed! */
800 }
801
802 int
803 main(int argc, char **argv)
804 {
805 int i;
806
807 glutInit(&argc, argv);
808
809 for (i=1; i<argc; i++) {
810 if (!strcmp("-linear", argv[i])) {
811 linearFiltering = 1;
812 } else if (!strcmp("-mipmap", argv[i])) {
813 useMipmaps = 1;
814 } else if (!strcmp("-ext", argv[i])) {
815 forceExtension = 1;
816 }
817 }
818
819 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
820
821 #if 0
822 /* In GLUT 4.0, you'll be able to do this an be sure to
823 get 2 bits of stencil if the machine has it for you. */
824 glutInitDisplayString("samples stencil>=2 rgb double depth");
825 #endif
826
827 glutCreateWindow("Shadowy Leapin' Lizards");
828 glewInit();
829
830 if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
831 printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
832 exit(1);
833 }
834
835 /* Register GLUT callbacks. */
836 glutDisplayFunc(redraw);
837 glutMouseFunc(mouse);
838 glutMotionFunc(motion);
839 glutVisibilityFunc(visible);
840 glutKeyboardFunc(key);
841 glutSpecialFunc(special);
842
843 glutCreateMenu(controlLights);
844
845 glutAddMenuEntry("Toggle motion", M_MOTION);
846 glutAddMenuEntry("-----------------------", M_NONE);
847 glutAddMenuEntry("Toggle light", M_LIGHT);
848 glutAddMenuEntry("Toggle texture", M_TEXTURE);
849 glutAddMenuEntry("Toggle shadows", M_SHADOWS);
850 glutAddMenuEntry("Toggle reflection", M_REFLECTION);
851 glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR);
852 glutAddMenuEntry("-----------------------", M_NONE);
853 glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
854 glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
855 glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
856 glutAddMenuEntry("----------------------", M_NONE);
857 glutAddMenuEntry("Positional light", M_POSITIONAL);
858 glutAddMenuEntry("Directional light", M_DIRECTIONAL);
859 glutAddMenuEntry("-----------------------", M_NONE);
860 glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
861 glutAttachMenu(GLUT_RIGHT_BUTTON);
862 makeDinosaur();
863
864 #ifdef GL_VERSION_1_1
865 if (supportsOneDotOne() && !forceExtension) {
866 polygonOffsetVersion = ONE_DOT_ONE;
867 glPolygonOffset(-2.0, -9.0);
868 } else
869 #endif
870 {
871 #ifdef GL_EXT_polygon_offset
872 /* check for the polygon offset extension */
873 if (glutExtensionSupported("GL_EXT_polygon_offset")) {
874 polygonOffsetVersion = EXTENSION;
875 glPolygonOffsetEXT(-2.0, -0.002);
876 } else
877 #endif
878 {
879 polygonOffsetVersion = MISSING;
880 printf("\ndinoshine: Missing polygon offset.\n");
881 printf(" Expect shadow depth aliasing artifacts.\n\n");
882 fflush(stdout);
883 }
884 }
885
886 glEnable(GL_CULL_FACE);
887 glEnable(GL_DEPTH_TEST);
888 glEnable(GL_TEXTURE_2D);
889 glLineWidth(3.0);
890
891 glMatrixMode(GL_PROJECTION);
892 gluPerspective( /* field of view in degree */ 40.0,
893 /* aspect ratio */ 1.0,
894 /* Z near */ 20.0, /* Z far */ 100.0);
895 glMatrixMode(GL_MODELVIEW);
896 gluLookAt(0.0, 8.0, 60.0, /* eye is at (0,8,60) */
897 0.0, 8.0, 0.0, /* center is at (0,8,0) */
898 0.0, 1.0, 0.); /* up is in postivie Y direction */
899
900 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
901 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
902 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
903 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
904 glEnable(GL_LIGHT0);
905 glEnable(GL_LIGHTING);
906
907 makeFloorTexture();
908
909 /* Setup floor plane for projected shadow calculations. */
910 findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
911
912 glutMainLoop();
913 return 0; /* ANSI C requires main to return int. */
914 }