2 /* Copyright (c) Mark J. Kilgard, 1994, 1997. */
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. */
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.
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.
20 Notice that the floor texturing, reflections, and shadowing
21 all co-exist properly. */
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. */
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. */
33 /* This program is derived from glutdino.c */
35 /* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
40 #include <math.h> /* for cos(), sin(), and sqrt() */
42 # include <stddef.h> /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
44 # include <malloc.h> /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
49 #define GL_GLEXT_LEGACY
50 #include <GL/glut.h> /* OpenGL Utility Toolkit header */
52 /* Some <math.h> files do not define M_PI... */
54 #define M_PI 3.14159265358979323846
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;
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 */
73 int moving
, startx
, starty
;
74 int lightMoving
= 0, lightStartX
, lightStartY
;
77 MISSING
, EXTENSION
, ONE_DOT_ONE
79 int polygonOffsetVersion
;
81 static GLdouble bodyWidth
= 3.0;
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},
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};
99 /* Nice floor texture tiling pattern. */
100 static char *circles
[] = {
120 makeFloorTexture(void)
122 GLubyte floorTexture
[16][16][3];
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') {
145 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
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
);
153 if (linearFiltering
) {
154 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
156 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
158 glTexImage2D(GL_TEXTURE_2D
, 0, 3, 16, 16, 0,
159 GL_RGB
, GL_UNSIGNED_BYTE
, floorTexture
);
170 /* Create a matrix that will project the desired shadow. */
172 shadowMatrix(GLfloat shadowMat
[4][4],
173 GLfloat groundplane
[4],
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
];
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
];
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
];
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
];
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
];
206 /* Find the plane equation given 3 points. */
208 findPlane(GLfloat plane
[4],
209 GLfloat v0
[3], GLfloat v1
[3], GLfloat v2
[3])
211 GLfloat vec0
[3], vec1
[3];
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
];
218 vec1
[X
] = v2
[X
] - v0
[X
];
219 vec1
[Y
] = v2
[Y
] - v0
[Y
];
220 vec1
[Z
] = v2
[Z
] - v0
[Z
];
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
];
227 plane
[D
] = -(plane
[A
] * v0
[X
] + plane
[B
] * v0
[Y
] + plane
[C
] * v0
[Z
]);
231 extrudeSolidFromPolygon(GLfloat data
[][2], unsigned int dataSize
,
232 GLdouble thickness
, GLuint side
, GLuint edge
, GLuint whole
)
234 static GLUtriangulatorObj
*tobj
= NULL
;
235 GLdouble vertex
[3], dx
, dy
, len
;
237 int count
= (int) (dataSize
/ (2 * sizeof(GLfloat
)));
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
);
246 glNewList(side
, GL_COMPILE
);
247 glShadeModel(GL_SMOOTH
); /* smooth minimizes seeing
249 gluBeginPolygon(tobj
);
250 for (i
= 0; i
< count
; i
++) {
251 vertex
[0] = data
[i
][0];
252 vertex
[1] = data
[i
][1];
254 gluTessVertex(tobj
, vertex
, data
[i
]);
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
);
291 glNewList(whole
, GL_COMPILE
);
294 glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */
297 glTranslatef(0.0, 0.0, thickness
);
299 glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */
305 /* Enumerants for refering to display lists. */
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
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
);
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
);
347 static GLfloat floorVertices
[4][3] = {
348 { -20.0, 0.0, 20.0 },
350 { 20.0, 0.0, -20.0 },
351 { -20.0, 0.0, -20.0 },
354 /* Draw a floor (possibly textured). */
358 glDisable(GL_LIGHTING
);
361 glEnable(GL_TEXTURE_2D
);
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]);
376 glDisable(GL_TEXTURE_2D
);
379 glEnable(GL_LIGHTING
);
382 static GLfloat floorPlane
[4];
383 static GLfloat floorShadow
[4][4];
391 start
= glutGet(GLUT_ELAPSED_TIME
);
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
);
398 /* Avoid clearing stencil when not using it. */
399 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
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;
409 lightPosition
[3] = 1.0;
412 shadowMatrix(floorShadow
, floorPlane
, lightPosition
);
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);
419 /* Tell GL new light source position. */
420 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
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. */
432 /* Don't update color or depth. */
433 glDisable(GL_DEPTH_TEST
);
434 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
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);
441 /* Now render floor; floor pixels just get their stencil set to 1. */
444 /* Re-enable update of color and depth. */
445 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
446 glEnable(GL_DEPTH_TEST
);
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
);
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);
459 /* Reflect the light position. */
460 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
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
);
467 /* Draw the reflected dinosaur. */
470 /* Disable noramlize again and re-enable back face culling. */
471 glDisable(GL_NORMALIZE
);
476 /* Switch back to the unreflected light position. */
477 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
479 if (stencilReflection
) {
480 glDisable(GL_STENCIL_TEST
);
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. */
489 /* Draw "bottom" of floor in blue. */
490 glFrontFace(GL_CW
); /* Switch face orientation. */
491 glColor4f(0.1, 0.1, 0.7, 1.0);
497 /* Draw the floor with stencil value 3. This helps us only
498 draw the shadow once per floor pixel (and only on the
500 glEnable(GL_STENCIL_TEST
);
501 glStencilFunc(GL_ALWAYS
, 3, 0xffffffff);
502 glStencilOp(GL_KEEP
, GL_KEEP
, GL_REPLACE
);
506 /* Draw "top" of floor. Use blending to blend in reflection. */
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);
514 if (renderDinosaur
) {
515 /* Draw "actual" dinosaur, not its reflection. */
521 /* Render the projected shadow. */
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
529 glStencilFunc(GL_LESS
, 2, 0xffffffff); /* draw if ==1 */
530 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
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. */
537 switch (polygonOffsetVersion
) {
539 #ifdef GL_EXT_polygon_offset
540 glEnable(GL_POLYGON_OFFSET_EXT
);
543 #ifdef GL_VERSION_1_1
545 glEnable(GL_POLYGON_OFFSET_FILL
);
554 /* Render 50% black shadow color on top of whatever the
555 floor appareance is. */
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);
562 /* Project the shadow. */
563 glMultMatrixf((GLfloat
*) floorShadow
);
568 glEnable(GL_LIGHTING
);
571 switch (polygonOffsetVersion
) {
572 #ifdef GL_EXT_polygon_offset
574 glDisable(GL_POLYGON_OFFSET_EXT
);
577 #ifdef GL_VERSION_1_1
579 glDisable(GL_POLYGON_OFFSET_FILL
);
588 glDisable(GL_STENCIL_TEST
);
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
);
604 glVertex3f(2, -1, 1);
605 glVertex3f(2, -1, -1);
606 glVertex3f(2, 1, -1);
609 /* Draw a white line from light direction. */
610 glColor3f(1.0, 1.0, 1.0);
615 glEnable(GL_CULL_FACE
);
617 /* Draw a yellow ball at the light source. */
618 glTranslatef(lightPosition
[0], lightPosition
[1], lightPosition
[2]);
619 glutSolidSphere(1.0, 5, 5);
621 glEnable(GL_LIGHTING
);
628 end
= glutGet(GLUT_ELAPSED_TIME
);
629 printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end
-start
), end
-start
);
637 mouse(int button
, int state
, int x
, int y
)
639 if (button
== GLUT_LEFT_BUTTON
) {
640 if (state
== GLUT_DOWN
) {
645 if (state
== GLUT_UP
) {
649 if (button
== GLUT_MIDDLE_BUTTON
) {
650 if (state
== GLUT_DOWN
) {
655 if (state
== GLUT_UP
) {
666 angle
= angle
+ (x
- startx
);
667 angle2
= angle2
+ (y
- starty
);
673 lightAngle
+= (x
- lightStartX
)/40.0;
674 lightHeight
+= (lightStartY
- y
)/20.0;
681 /* Advance time varying state when idle callback registered. */
685 static float time
= 0.0;
687 time
= glutGet(GLUT_ELAPSED_TIME
) / 500.0;
689 jump
= 4.0 * fabs(sin(time
)*0.5);
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
703 controlLights(int value
)
709 animation
= 1 - animation
;
717 lightSwitch
= !lightSwitch
;
721 glDisable(GL_LIGHT0
);
725 useTexture
= !useTexture
;
728 renderShadow
= 1 - renderShadow
;
731 renderReflection
= 1 - renderReflection
;
734 renderDinosaur
= 1 - renderDinosaur
;
736 case M_STENCIL_REFLECTION
:
737 stencilReflection
= 1 - stencilReflection
;
739 case M_STENCIL_SHADOW
:
740 stencilShadow
= 1 - stencilShadow
;
742 case M_OFFSET_SHADOW
:
743 offsetShadow
= 1 - offsetShadow
;
746 directionalLight
= 0;
749 directionalLight
= 1;
752 reportSpeed
= 1 - reportSpeed
;
758 /* When not visible, stop animating. Restart when visible again. */
762 if (vis
== GLUT_VISIBLE
) {
771 /* Press any key to redraw; good when motion stopped and
772 performance reporting on. */
775 key(unsigned char c
, int x
, int y
)
778 exit(0); /* IRIS GLism, Escape quits. */
783 /* Press any key to redraw; good when motion stopped and
784 performance reporting on. */
787 special(int k
, int x
, int y
)
793 supportsOneDotOne(void)
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! */
805 main(int argc
, char **argv
)
809 glutInit(&argc
, argv
);
811 for (i
=1; i
<argc
; i
++) {
812 if (!strcmp("-linear", argv
[i
])) {
814 } else if (!strcmp("-mipmap", argv
[i
])) {
816 } else if (!strcmp("-ext", argv
[i
])) {
821 glutInitDisplayMode(GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
| GLUT_STENCIL
);
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");
829 glutCreateWindow("Shadowy Leapin' Lizards");
831 if (glutGet(GLUT_WINDOW_STENCIL_SIZE
) <= 1) {
832 printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
836 /* Register GLUT callbacks. */
837 glutDisplayFunc(redraw
);
838 glutMouseFunc(mouse
);
839 glutMotionFunc(motion
);
840 glutVisibilityFunc(visible
);
841 glutKeyboardFunc(key
);
842 glutSpecialFunc(special
);
844 glutCreateMenu(controlLights
);
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
);
865 #ifdef GL_VERSION_1_1
866 if (supportsOneDotOne() && !forceExtension
) {
867 polygonOffsetVersion
= ONE_DOT_ONE
;
868 glPolygonOffset(-2.0, -9.0);
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);
880 polygonOffsetVersion
= MISSING
;
881 printf("\ndinoshine: Missing polygon offset.\n");
882 printf(" Expect shadow depth aliasing artifacts.\n\n");
886 glEnable(GL_CULL_FACE
);
887 glEnable(GL_DEPTH_TEST
);
888 glEnable(GL_TEXTURE_2D
);
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 */
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);
905 glEnable(GL_LIGHTING
);
909 /* Setup floor plane for projected shadow calculations. */
910 findPlane(floorPlane
, floorVertices
[1], floorVertices
[2], floorVertices
[3]);
913 return 0; /* ANSI C requires main to return int. */