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() */
41 #include <stddef.h> /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
45 #define GL_GLEXT_LEGACY
46 #include <GL/glut.h> /* OpenGL Utility Toolkit header */
48 /* Some <math.h> files do not define M_PI... */
50 #define M_PI 3.14159265358979323846
53 /* Variable controlling various rendering modes. */
54 static int stencilReflection
= 1, stencilShadow
= 1, offsetShadow
= 1;
55 static int renderShadow
= 1, renderDinosaur
= 1, renderReflection
= 1;
56 static int linearFiltering
= 0, useMipmaps
= 0, useTexture
= 1;
57 static int reportSpeed
= 0;
58 static int animation
= 1;
59 static GLboolean lightSwitch
= GL_TRUE
;
60 static int directionalLight
= 1;
61 static int forceExtension
= 0;
63 /* Time varying or user-controled variables. */
64 static float jump
= 0.0;
65 static float lightAngle
= 0.0, lightHeight
= 20;
66 GLfloat angle
= -150; /* in degrees */
67 GLfloat angle2
= 30; /* in degrees */
69 int moving
, startx
, starty
;
70 int lightMoving
= 0, lightStartX
, lightStartY
;
73 MISSING
, EXTENSION
, ONE_DOT_ONE
75 int polygonOffsetVersion
;
77 static GLdouble bodyWidth
= 3.0;
79 static GLfloat body
[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
80 {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
81 {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
83 static GLfloat arm
[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
84 {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
85 {13, 9}, {11, 11}, {9, 11} };
86 static GLfloat leg
[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
87 {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
88 static GLfloat eye
[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
89 {9.6, 15.25}, {9, 15.25} };
90 static GLfloat lightPosition
[4];
91 static GLfloat lightColor
[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
92 static GLfloat skinColor
[] = {0.1, 1.0, 0.1, 1.0}, eyeColor
[] = {1.0, 0.2, 0.2, 1.0};
95 /* Nice floor texture tiling pattern. */
96 static char *circles
[] = {
116 makeFloorTexture(void)
118 GLubyte floorTexture
[16][16][3];
122 /* Setup RGB image for the texture. */
123 loc
= (GLubyte
*) floorTexture
;
124 for (t
= 0; t
< 16; t
++) {
125 for (s
= 0; s
< 16; s
++) {
126 if (circles
[t
][s
] == 'x') {
141 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
144 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
,
145 GL_LINEAR_MIPMAP_LINEAR
);
146 gluBuild2DMipmaps(GL_TEXTURE_2D
, 3, 16, 16,
147 GL_RGB
, GL_UNSIGNED_BYTE
, floorTexture
);
149 if (linearFiltering
) {
150 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
152 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
154 glTexImage2D(GL_TEXTURE_2D
, 0, 3, 16, 16, 0,
155 GL_RGB
, GL_UNSIGNED_BYTE
, floorTexture
);
166 /* Create a matrix that will project the desired shadow. */
168 shadowMatrix(GLfloat shadowMat
[4][4],
169 GLfloat groundplane
[4],
174 /* Find dot product between light position vector and ground plane normal. */
175 dot
= groundplane
[X
] * lightpos
[X
] +
176 groundplane
[Y
] * lightpos
[Y
] +
177 groundplane
[Z
] * lightpos
[Z
] +
178 groundplane
[W
] * lightpos
[W
];
180 shadowMat
[0][0] = dot
- lightpos
[X
] * groundplane
[X
];
181 shadowMat
[1][0] = 0.f
- lightpos
[X
] * groundplane
[Y
];
182 shadowMat
[2][0] = 0.f
- lightpos
[X
] * groundplane
[Z
];
183 shadowMat
[3][0] = 0.f
- lightpos
[X
] * groundplane
[W
];
185 shadowMat
[X
][1] = 0.f
- lightpos
[Y
] * groundplane
[X
];
186 shadowMat
[1][1] = dot
- lightpos
[Y
] * groundplane
[Y
];
187 shadowMat
[2][1] = 0.f
- lightpos
[Y
] * groundplane
[Z
];
188 shadowMat
[3][1] = 0.f
- lightpos
[Y
] * groundplane
[W
];
190 shadowMat
[X
][2] = 0.f
- lightpos
[Z
] * groundplane
[X
];
191 shadowMat
[1][2] = 0.f
- lightpos
[Z
] * groundplane
[Y
];
192 shadowMat
[2][2] = dot
- lightpos
[Z
] * groundplane
[Z
];
193 shadowMat
[3][2] = 0.f
- lightpos
[Z
] * groundplane
[W
];
195 shadowMat
[X
][3] = 0.f
- lightpos
[W
] * groundplane
[X
];
196 shadowMat
[1][3] = 0.f
- lightpos
[W
] * groundplane
[Y
];
197 shadowMat
[2][3] = 0.f
- lightpos
[W
] * groundplane
[Z
];
198 shadowMat
[3][3] = dot
- lightpos
[W
] * groundplane
[W
];
202 /* Find the plane equation given 3 points. */
204 findPlane(GLfloat plane
[4],
205 GLfloat v0
[3], GLfloat v1
[3], GLfloat v2
[3])
207 GLfloat vec0
[3], vec1
[3];
209 /* Need 2 vectors to find cross product. */
210 vec0
[X
] = v1
[X
] - v0
[X
];
211 vec0
[Y
] = v1
[Y
] - v0
[Y
];
212 vec0
[Z
] = v1
[Z
] - v0
[Z
];
214 vec1
[X
] = v2
[X
] - v0
[X
];
215 vec1
[Y
] = v2
[Y
] - v0
[Y
];
216 vec1
[Z
] = v2
[Z
] - v0
[Z
];
218 /* find cross product to get A, B, and C of plane equation */
219 plane
[A
] = vec0
[Y
] * vec1
[Z
] - vec0
[Z
] * vec1
[Y
];
220 plane
[B
] = -(vec0
[X
] * vec1
[Z
] - vec0
[Z
] * vec1
[X
]);
221 plane
[C
] = vec0
[X
] * vec1
[Y
] - vec0
[Y
] * vec1
[X
];
223 plane
[D
] = -(plane
[A
] * v0
[X
] + plane
[B
] * v0
[Y
] + plane
[C
] * v0
[Z
]);
227 extrudeSolidFromPolygon(GLfloat data
[][2], unsigned int dataSize
,
228 GLdouble thickness
, GLuint side
, GLuint edge
, GLuint whole
)
230 static GLUtriangulatorObj
*tobj
= NULL
;
231 GLdouble vertex
[3], dx
, dy
, len
;
233 int count
= (int) (dataSize
/ (2 * sizeof(GLfloat
)));
236 tobj
= gluNewTess(); /* create and initialize a GLU
237 polygon tesselation object */
238 gluTessCallback(tobj
, GLU_BEGIN
, glBegin
);
239 gluTessCallback(tobj
, GLU_VERTEX
, glVertex2fv
); /* semi-tricky */
240 gluTessCallback(tobj
, GLU_END
, glEnd
);
242 glNewList(side
, GL_COMPILE
);
243 glShadeModel(GL_SMOOTH
); /* smooth minimizes seeing
245 gluBeginPolygon(tobj
);
246 for (i
= 0; i
< count
; i
++) {
247 vertex
[0] = data
[i
][0];
248 vertex
[1] = data
[i
][1];
250 gluTessVertex(tobj
, vertex
, data
[i
]);
254 glNewList(edge
, GL_COMPILE
);
255 glShadeModel(GL_FLAT
); /* flat shade keeps angular hands
256 from being "smoothed" */
257 glBegin(GL_QUAD_STRIP
);
258 for (i
= 0; i
<= count
; i
++) {
259 #if 1 /* weird, but seems to be legal */
260 /* mod function handles closing the edge */
261 glVertex3f(data
[i
% count
][0], data
[i
% count
][1], 0.0);
262 glVertex3f(data
[i
% count
][0], data
[i
% count
][1], thickness
);
263 /* Calculate a unit normal by dividing by Euclidean
264 distance. We * could be lazy and use
265 glEnable(GL_NORMALIZE) so we could pass in * arbitrary
266 normals for a very slight performance hit. */
267 dx
= data
[(i
+ 1) % count
][1] - data
[i
% count
][1];
268 dy
= data
[i
% count
][0] - data
[(i
+ 1) % count
][0];
269 len
= sqrt(dx
* dx
+ dy
* dy
);
270 glNormal3f(dx
/ len
, dy
/ len
, 0.0);
271 #else /* the nice way of doing it */
272 /* Calculate a unit normal by dividing by Euclidean
273 distance. We * could be lazy and use
274 glEnable(GL_NORMALIZE) so we could pass in * arbitrary
275 normals for a very slight performance hit. */
276 dx
= data
[i
% count
][1] - data
[(i
- 1 + count
) % count
][1];
277 dy
= data
[(i
- 1 + count
) % count
][0] - data
[i
% count
][0];
278 len
= sqrt(dx
* dx
+ dy
* dy
);
279 glNormal3f(dx
/ len
, dy
/ len
, 0.0);
280 /* mod function handles closing the edge */
281 glVertex3f(data
[i
% count
][0], data
[i
% count
][1], 0.0);
282 glVertex3f(data
[i
% count
][0], data
[i
% count
][1], thickness
);
287 glNewList(whole
, GL_COMPILE
);
290 glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */
293 glTranslatef(0.0, 0.0, thickness
);
295 glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */
301 /* Enumerants for refering to display lists. */
303 RESERVED
, BODY_SIDE
, BODY_EDGE
, BODY_WHOLE
, ARM_SIDE
, ARM_EDGE
, ARM_WHOLE
,
304 LEG_SIDE
, LEG_EDGE
, LEG_WHOLE
, EYE_SIDE
, EYE_EDGE
, EYE_WHOLE
310 extrudeSolidFromPolygon(body
, sizeof(body
), bodyWidth
,
311 BODY_SIDE
, BODY_EDGE
, BODY_WHOLE
);
312 extrudeSolidFromPolygon(arm
, sizeof(arm
), bodyWidth
/ 4,
313 ARM_SIDE
, ARM_EDGE
, ARM_WHOLE
);
314 extrudeSolidFromPolygon(leg
, sizeof(leg
), bodyWidth
/ 2,
315 LEG_SIDE
, LEG_EDGE
, LEG_WHOLE
);
316 extrudeSolidFromPolygon(eye
, sizeof(eye
), bodyWidth
+ 0.2,
317 EYE_SIDE
, EYE_EDGE
, EYE_WHOLE
);
325 /* Translate the dinosaur to be at (0,8,0). */
326 glTranslatef(-8, 0, -bodyWidth
/ 2);
327 glTranslatef(0.0, jump
, 0.0);
328 glMaterialfv(GL_FRONT
, GL_DIFFUSE
, skinColor
);
329 glCallList(BODY_WHOLE
);
330 glTranslatef(0.0, 0.0, bodyWidth
);
331 glCallList(ARM_WHOLE
);
332 glCallList(LEG_WHOLE
);
333 glTranslatef(0.0, 0.0, -bodyWidth
- bodyWidth
/ 4);
334 glCallList(ARM_WHOLE
);
335 glTranslatef(0.0, 0.0, -bodyWidth
/ 4);
336 glCallList(LEG_WHOLE
);
337 glTranslatef(0.0, 0.0, bodyWidth
/ 2 - 0.1);
338 glMaterialfv(GL_FRONT
, GL_DIFFUSE
, eyeColor
);
339 glCallList(EYE_WHOLE
);
343 static GLfloat floorVertices
[4][3] = {
344 { -20.0, 0.0, 20.0 },
346 { 20.0, 0.0, -20.0 },
347 { -20.0, 0.0, -20.0 },
350 /* Draw a floor (possibly textured). */
354 glDisable(GL_LIGHTING
);
357 glEnable(GL_TEXTURE_2D
);
361 glTexCoord2f(0.0, 0.0);
362 glVertex3fv(floorVertices
[0]);
363 glTexCoord2f(0.0, 16.0);
364 glVertex3fv(floorVertices
[1]);
365 glTexCoord2f(16.0, 16.0);
366 glVertex3fv(floorVertices
[2]);
367 glTexCoord2f(16.0, 0.0);
368 glVertex3fv(floorVertices
[3]);
372 glDisable(GL_TEXTURE_2D
);
375 glEnable(GL_LIGHTING
);
378 static GLfloat floorPlane
[4];
379 static GLfloat floorShadow
[4][4];
387 start
= glutGet(GLUT_ELAPSED_TIME
);
390 /* Clear; default stencil clears to zero. */
391 if ((stencilReflection
&& renderReflection
) || (stencilShadow
&& renderShadow
)) {
392 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
394 /* Avoid clearing stencil when not using it. */
395 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
398 /* Reposition the light source. */
399 lightPosition
[0] = 12*cos(lightAngle
);
400 lightPosition
[1] = lightHeight
;
401 lightPosition
[2] = 12*sin(lightAngle
);
402 if (directionalLight
) {
403 lightPosition
[3] = 0.0;
405 lightPosition
[3] = 1.0;
408 shadowMatrix(floorShadow
, floorPlane
, lightPosition
);
411 /* Perform scene rotations based on user mouse input. */
412 glRotatef(angle2
, 1.0, 0.0, 0.0);
413 glRotatef(angle
, 0.0, 1.0, 0.0);
415 /* Tell GL new light source position. */
416 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
418 if (renderReflection
) {
419 if (stencilReflection
) {
420 /* We can eliminate the visual "artifact" of seeing the "flipped"
421 dinosaur underneath the floor by using stencil. The idea is
422 draw the floor without color or depth update but so that
423 a stencil value of one is where the floor will be. Later when
424 rendering the dinosaur reflection, we will only update pixels
425 with a stencil value of 1 to make sure the reflection only
426 lives on the floor, not below the floor. */
428 /* Don't update color or depth. */
429 glDisable(GL_DEPTH_TEST
);
430 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
432 /* Draw 1 into the stencil buffer. */
433 glEnable(GL_STENCIL_TEST
);
434 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
435 glStencilFunc(GL_ALWAYS
, 1, 0xffffffff);
437 /* Now render floor; floor pixels just get their stencil set to 1. */
440 /* Re-enable update of color and depth. */
441 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
442 glEnable(GL_DEPTH_TEST
);
444 /* Now, only render where stencil is set to 1. */
445 glStencilFunc(GL_EQUAL
, 1, 0xffffffff); /* draw if ==1 */
446 glStencilOp(GL_KEEP
, GL_KEEP
, GL_KEEP
);
451 /* The critical reflection step: Reflect dinosaur through the floor
452 (the Y=0 plane) to make a relection. */
453 glScalef(1.0, -1.0, 1.0);
455 /* Reflect the light position. */
456 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
458 /* To avoid our normals getting reversed and hence botched lighting
459 on the reflection, turn on normalize. */
460 glEnable(GL_NORMALIZE
);
461 glCullFace(GL_FRONT
);
463 /* Draw the reflected dinosaur. */
466 /* Disable noramlize again and re-enable back face culling. */
467 glDisable(GL_NORMALIZE
);
472 /* Switch back to the unreflected light position. */
473 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
475 if (stencilReflection
) {
476 glDisable(GL_STENCIL_TEST
);
480 /* Back face culling will get used to only draw either the top or the
481 bottom floor. This let's us get a floor with two distinct
482 appearances. The top floor surface is reflective and kind of red.
483 The bottom floor surface is not reflective and blue. */
485 /* Draw "bottom" of floor in blue. */
486 glFrontFace(GL_CW
); /* Switch face orientation. */
487 glColor4f(0.1, 0.1, 0.7, 1.0);
493 /* Draw the floor with stencil value 3. This helps us only
494 draw the shadow once per floor pixel (and only on the
496 glEnable(GL_STENCIL_TEST
);
497 glStencilFunc(GL_ALWAYS
, 3, 0xffffffff);
498 glStencilOp(GL_KEEP
, GL_KEEP
, GL_REPLACE
);
502 /* Draw "top" of floor. Use blending to blend in reflection. */
504 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
505 glColor4f(0.7, 0.0, 0.0, 0.3);
506 glColor4f(1.0, 1.0, 1.0, 0.3);
510 if (renderDinosaur
) {
511 /* Draw "actual" dinosaur, not its reflection. */
517 /* Render the projected shadow. */
521 /* Now, only render where stencil is set above 2 (ie, 3 where
522 the top floor is). Update stencil with 2 where the shadow
523 gets drawn so we don't redraw (and accidently reblend) the
525 glStencilFunc(GL_LESS
, 2, 0xffffffff); /* draw if ==1 */
526 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
529 /* To eliminate depth buffer artifacts, we use polygon offset
530 to raise the depth of the projected shadow slightly so
531 that it does not depth buffer alias with the floor. */
533 switch (polygonOffsetVersion
) {
535 #ifdef GL_EXT_polygon_offset
536 glEnable(GL_POLYGON_OFFSET_EXT
);
539 #ifdef GL_VERSION_1_1
541 glEnable(GL_POLYGON_OFFSET_FILL
);
550 /* Render 50% black shadow color on top of whatever the
551 floor appareance is. */
553 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
554 glDisable(GL_LIGHTING
); /* Force the 50% black. */
555 glColor4f(0.0, 0.0, 0.0, 0.5);
558 /* Project the shadow. */
559 glMultMatrixf((GLfloat
*) floorShadow
);
564 glEnable(GL_LIGHTING
);
567 switch (polygonOffsetVersion
) {
568 #ifdef GL_EXT_polygon_offset
570 glDisable(GL_POLYGON_OFFSET_EXT
);
573 #ifdef GL_VERSION_1_1
575 glDisable(GL_POLYGON_OFFSET_FILL
);
584 glDisable(GL_STENCIL_TEST
);
589 glDisable(GL_LIGHTING
);
590 glColor3f(1.0, 1.0, 0.0);
591 if (directionalLight
) {
592 /* Draw an arrowhead. */
593 glDisable(GL_CULL_FACE
);
594 glTranslatef(lightPosition
[0], lightPosition
[1], lightPosition
[2]);
595 glRotatef(lightAngle
* -180.0 / M_PI
, 0, 1, 0);
596 glRotatef(atan(lightHeight
/12) * 180.0 / M_PI
, 0, 0, 1);
597 glBegin(GL_TRIANGLE_FAN
);
600 glVertex3f(2, -1, 1);
601 glVertex3f(2, -1, -1);
602 glVertex3f(2, 1, -1);
605 /* Draw a white line from light direction. */
606 glColor3f(1.0, 1.0, 1.0);
611 glEnable(GL_CULL_FACE
);
613 /* Draw a yellow ball at the light source. */
614 glTranslatef(lightPosition
[0], lightPosition
[1], lightPosition
[2]);
615 glutSolidSphere(1.0, 5, 5);
617 glEnable(GL_LIGHTING
);
624 end
= glutGet(GLUT_ELAPSED_TIME
);
625 printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end
-start
), end
-start
);
633 mouse(int button
, int state
, int x
, int y
)
635 if (button
== GLUT_LEFT_BUTTON
) {
636 if (state
== GLUT_DOWN
) {
641 if (state
== GLUT_UP
) {
645 if (button
== GLUT_MIDDLE_BUTTON
) {
646 if (state
== GLUT_DOWN
) {
651 if (state
== GLUT_UP
) {
662 angle
= angle
+ (x
- startx
);
663 angle2
= angle2
+ (y
- starty
);
669 lightAngle
+= (x
- lightStartX
)/40.0;
670 lightHeight
+= (lightStartY
- y
)/20.0;
677 /* Advance time varying state when idle callback registered. */
681 static float time
= 0.0;
683 time
= glutGet(GLUT_ELAPSED_TIME
) / 500.0;
685 jump
= 4.0 * fabs(sin(time
)*0.5);
693 M_NONE
, M_MOTION
, M_LIGHT
, M_TEXTURE
, M_SHADOWS
, M_REFLECTION
, M_DINOSAUR
,
694 M_STENCIL_REFLECTION
, M_STENCIL_SHADOW
, M_OFFSET_SHADOW
,
695 M_POSITIONAL
, M_DIRECTIONAL
, M_PERFORMANCE
699 controlLights(int value
)
705 animation
= 1 - animation
;
713 lightSwitch
= !lightSwitch
;
717 glDisable(GL_LIGHT0
);
721 useTexture
= !useTexture
;
724 renderShadow
= 1 - renderShadow
;
727 renderReflection
= 1 - renderReflection
;
730 renderDinosaur
= 1 - renderDinosaur
;
732 case M_STENCIL_REFLECTION
:
733 stencilReflection
= 1 - stencilReflection
;
735 case M_STENCIL_SHADOW
:
736 stencilShadow
= 1 - stencilShadow
;
738 case M_OFFSET_SHADOW
:
739 offsetShadow
= 1 - offsetShadow
;
742 directionalLight
= 0;
745 directionalLight
= 1;
748 reportSpeed
= 1 - reportSpeed
;
754 /* When not visible, stop animating. Restart when visible again. */
758 if (vis
== GLUT_VISIBLE
) {
767 /* Press any key to redraw; good when motion stopped and
768 performance reporting on. */
771 key(unsigned char c
, int x
, int y
)
774 exit(0); /* IRIS GLism, Escape quits. */
779 /* Press any key to redraw; good when motion stopped and
780 performance reporting on. */
783 special(int k
, int x
, int y
)
789 supportsOneDotOne(void)
794 version
= (char *) glGetString(GL_VERSION
);
795 if (sscanf(version
, "%d.%d", &major
, &minor
) == 2)
796 return major
>= 1 && minor
>= 1;
797 return 0; /* OpenGL version string malformed! */
801 main(int argc
, char **argv
)
805 glutInit(&argc
, argv
);
807 for (i
=1; i
<argc
; i
++) {
808 if (!strcmp("-linear", argv
[i
])) {
810 } else if (!strcmp("-mipmap", argv
[i
])) {
812 } else if (!strcmp("-ext", argv
[i
])) {
817 glutInitDisplayMode(GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
| GLUT_STENCIL
);
820 /* In GLUT 4.0, you'll be able to do this an be sure to
821 get 2 bits of stencil if the machine has it for you. */
822 glutInitDisplayString("samples stencil>=2 rgb double depth");
825 glutCreateWindow("Shadowy Leapin' Lizards");
827 if (glutGet(GLUT_WINDOW_STENCIL_SIZE
) <= 1) {
828 printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
832 /* Register GLUT callbacks. */
833 glutDisplayFunc(redraw
);
834 glutMouseFunc(mouse
);
835 glutMotionFunc(motion
);
836 glutVisibilityFunc(visible
);
837 glutKeyboardFunc(key
);
838 glutSpecialFunc(special
);
840 glutCreateMenu(controlLights
);
842 glutAddMenuEntry("Toggle motion", M_MOTION
);
843 glutAddMenuEntry("-----------------------", M_NONE
);
844 glutAddMenuEntry("Toggle light", M_LIGHT
);
845 glutAddMenuEntry("Toggle texture", M_TEXTURE
);
846 glutAddMenuEntry("Toggle shadows", M_SHADOWS
);
847 glutAddMenuEntry("Toggle reflection", M_REFLECTION
);
848 glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR
);
849 glutAddMenuEntry("-----------------------", M_NONE
);
850 glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION
);
851 glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW
);
852 glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW
);
853 glutAddMenuEntry("----------------------", M_NONE
);
854 glutAddMenuEntry("Positional light", M_POSITIONAL
);
855 glutAddMenuEntry("Directional light", M_DIRECTIONAL
);
856 glutAddMenuEntry("-----------------------", M_NONE
);
857 glutAddMenuEntry("Toggle performance", M_PERFORMANCE
);
858 glutAttachMenu(GLUT_RIGHT_BUTTON
);
861 #ifdef GL_VERSION_1_1
862 if (supportsOneDotOne() && !forceExtension
) {
863 polygonOffsetVersion
= ONE_DOT_ONE
;
864 glPolygonOffset(-2.0, -9.0);
868 #ifdef GL_EXT_polygon_offset
869 /* check for the polygon offset extension */
870 if (glutExtensionSupported("GL_EXT_polygon_offset")) {
871 polygonOffsetVersion
= EXTENSION
;
872 glPolygonOffsetEXT(-2.0, -0.002);
876 polygonOffsetVersion
= MISSING
;
877 printf("\ndinoshine: Missing polygon offset.\n");
878 printf(" Expect shadow depth aliasing artifacts.\n\n");
882 glEnable(GL_CULL_FACE
);
883 glEnable(GL_DEPTH_TEST
);
884 glEnable(GL_TEXTURE_2D
);
887 glMatrixMode(GL_PROJECTION
);
888 gluPerspective( /* field of view in degree */ 40.0,
889 /* aspect ratio */ 1.0,
890 /* Z near */ 20.0, /* Z far */ 100.0);
891 glMatrixMode(GL_MODELVIEW
);
892 gluLookAt(0.0, 8.0, 60.0, /* eye is at (0,8,60) */
893 0.0, 8.0, 0.0, /* center is at (0,8,0) */
894 0.0, 1.0, 0.); /* up is in postivie Y direction */
896 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER
, 1);
897 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, lightColor
);
898 glLightf(GL_LIGHT0
, GL_CONSTANT_ATTENUATION
, 0.1);
899 glLightf(GL_LIGHT0
, GL_LINEAR_ATTENUATION
, 0.05);
901 glEnable(GL_LIGHTING
);
905 /* Setup floor plane for projected shadow calculations. */
906 findPlane(floorPlane
, floorVertices
[1], floorVertices
[2], floorVertices
[3]);
909 return 0; /* ANSI C requires main to return int. */