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