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/glew.h> /* OpenGL Utility Toolkit header */
47 #include <GL/glut.h> /* OpenGL Utility Toolkit header */
49 /* Some <math.h> files do not define M_PI... */
51 #define M_PI 3.14159265358979323846
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;
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 */
70 int moving
, startx
, starty
;
71 int lightMoving
= 0, lightStartX
, lightStartY
;
74 MISSING
, EXTENSION
, ONE_DOT_ONE
76 int polygonOffsetVersion
;
78 static GLdouble bodyWidth
= 3.0;
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},
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};
96 /* Nice floor texture tiling pattern. */
97 static char *circles
[] = {
117 makeFloorTexture(void)
119 GLubyte floorTexture
[16][16][3];
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') {
142 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
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
);
150 if (linearFiltering
) {
151 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
153 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
155 glTexImage2D(GL_TEXTURE_2D
, 0, 3, 16, 16, 0,
156 GL_RGB
, GL_UNSIGNED_BYTE
, floorTexture
);
167 /* Create a matrix that will project the desired shadow. */
169 shadowMatrix(GLfloat shadowMat
[4][4],
170 GLfloat groundplane
[4],
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
];
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
];
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
];
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
];
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
];
203 /* Find the plane equation given 3 points. */
205 findPlane(GLfloat plane
[4],
206 GLfloat v0
[3], GLfloat v1
[3], GLfloat v2
[3])
208 GLfloat vec0
[3], vec1
[3];
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
];
215 vec1
[X
] = v2
[X
] - v0
[X
];
216 vec1
[Y
] = v2
[Y
] - v0
[Y
];
217 vec1
[Z
] = v2
[Z
] - v0
[Z
];
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
];
224 plane
[D
] = -(plane
[A
] * v0
[X
] + plane
[B
] * v0
[Y
] + plane
[C
] * v0
[Z
]);
228 extrudeSolidFromPolygon(GLfloat data
[][2], unsigned int dataSize
,
229 GLdouble thickness
, GLuint side
, GLuint edge
, GLuint whole
)
231 static GLUtriangulatorObj
*tobj
= NULL
;
232 GLdouble vertex
[3], dx
, dy
, len
;
234 int count
= (int) (dataSize
/ (2 * sizeof(GLfloat
)));
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
);
243 glNewList(side
, GL_COMPILE
);
244 glShadeModel(GL_SMOOTH
); /* smooth minimizes seeing
246 gluBeginPolygon(tobj
);
247 for (i
= 0; i
< count
; i
++) {
248 vertex
[0] = data
[i
][0];
249 vertex
[1] = data
[i
][1];
251 gluTessVertex(tobj
, vertex
, data
[i
]);
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
);
288 glNewList(whole
, GL_COMPILE
);
291 glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */
294 glTranslatef(0.0, 0.0, thickness
);
296 glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */
302 /* Enumerants for refering to display lists. */
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
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
);
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
);
344 static GLfloat floorVertices
[4][3] = {
345 { -20.0, 0.0, 20.0 },
347 { 20.0, 0.0, -20.0 },
348 { -20.0, 0.0, -20.0 },
351 /* Draw a floor (possibly textured). */
355 glDisable(GL_LIGHTING
);
358 glEnable(GL_TEXTURE_2D
);
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]);
373 glDisable(GL_TEXTURE_2D
);
376 glEnable(GL_LIGHTING
);
379 static GLfloat floorPlane
[4];
380 static GLfloat floorShadow
[4][4];
388 start
= glutGet(GLUT_ELAPSED_TIME
);
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
);
395 /* Avoid clearing stencil when not using it. */
396 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
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;
406 lightPosition
[3] = 1.0;
409 shadowMatrix(floorShadow
, floorPlane
, lightPosition
);
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);
416 /* Tell GL new light source position. */
417 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
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. */
429 /* Don't update color or depth. */
430 glDisable(GL_DEPTH_TEST
);
431 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
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);
438 /* Now render floor; floor pixels just get their stencil set to 1. */
441 /* Re-enable update of color and depth. */
442 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
443 glEnable(GL_DEPTH_TEST
);
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
);
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);
456 /* Reflect the light position. */
457 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
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
);
464 /* Draw the reflected dinosaur. */
467 /* Disable noramlize again and re-enable back face culling. */
468 glDisable(GL_NORMALIZE
);
473 /* Switch back to the unreflected light position. */
474 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
476 if (stencilReflection
) {
477 glDisable(GL_STENCIL_TEST
);
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. */
486 /* Draw "bottom" of floor in blue. */
487 glFrontFace(GL_CW
); /* Switch face orientation. */
488 glColor4f(0.1, 0.1, 0.7, 1.0);
494 /* Draw the floor with stencil value 3. This helps us only
495 draw the shadow once per floor pixel (and only on the
497 glEnable(GL_STENCIL_TEST
);
498 glStencilFunc(GL_ALWAYS
, 3, 0xffffffff);
499 glStencilOp(GL_KEEP
, GL_KEEP
, GL_REPLACE
);
503 /* Draw "top" of floor. Use blending to blend in reflection. */
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);
511 if (renderDinosaur
) {
512 /* Draw "actual" dinosaur, not its reflection. */
518 /* Render the projected shadow. */
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
526 glStencilFunc(GL_LESS
, 2, 0xffffffff); /* draw if ==1 */
527 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
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. */
534 switch (polygonOffsetVersion
) {
536 #ifdef GL_EXT_polygon_offset
537 glEnable(GL_POLYGON_OFFSET_EXT
);
540 #ifdef GL_VERSION_1_1
542 glEnable(GL_POLYGON_OFFSET_FILL
);
551 /* Render 50% black shadow color on top of whatever the
552 floor appareance is. */
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);
559 /* Project the shadow. */
560 glMultMatrixf((GLfloat
*) floorShadow
);
565 glEnable(GL_LIGHTING
);
568 switch (polygonOffsetVersion
) {
569 #ifdef GL_EXT_polygon_offset
571 glDisable(GL_POLYGON_OFFSET_EXT
);
574 #ifdef GL_VERSION_1_1
576 glDisable(GL_POLYGON_OFFSET_FILL
);
585 glDisable(GL_STENCIL_TEST
);
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
);
601 glVertex3f(2, -1, 1);
602 glVertex3f(2, -1, -1);
603 glVertex3f(2, 1, -1);
606 /* Draw a white line from light direction. */
607 glColor3f(1.0, 1.0, 1.0);
612 glEnable(GL_CULL_FACE
);
614 /* Draw a yellow ball at the light source. */
615 glTranslatef(lightPosition
[0], lightPosition
[1], lightPosition
[2]);
616 glutSolidSphere(1.0, 5, 5);
618 glEnable(GL_LIGHTING
);
625 end
= glutGet(GLUT_ELAPSED_TIME
);
626 printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end
-start
), end
-start
);
634 mouse(int button
, int state
, int x
, int y
)
636 if (button
== GLUT_LEFT_BUTTON
) {
637 if (state
== GLUT_DOWN
) {
642 if (state
== GLUT_UP
) {
646 if (button
== GLUT_MIDDLE_BUTTON
) {
647 if (state
== GLUT_DOWN
) {
652 if (state
== GLUT_UP
) {
663 angle
= angle
+ (x
- startx
);
664 angle2
= angle2
+ (y
- starty
);
670 lightAngle
+= (x
- lightStartX
)/40.0;
671 lightHeight
+= (lightStartY
- y
)/20.0;
678 /* Advance time varying state when idle callback registered. */
682 static float time
= 0.0;
684 time
= glutGet(GLUT_ELAPSED_TIME
) / 500.0;
686 jump
= 4.0 * fabs(sin(time
)*0.5);
694 M_NONE
, M_MOTION
, M_LIGHT
, M_TEXTURE
, M_SHADOWS
, M_REFLECTION
, M_DINOSAUR
,
695 M_STENCIL_REFLECTION
, M_STENCIL_SHADOW
, M_OFFSET_SHADOW
,
696 M_POSITIONAL
, M_DIRECTIONAL
, M_PERFORMANCE
700 controlLights(int value
)
706 animation
= 1 - animation
;
714 lightSwitch
= !lightSwitch
;
718 glDisable(GL_LIGHT0
);
722 useTexture
= !useTexture
;
725 renderShadow
= 1 - renderShadow
;
728 renderReflection
= 1 - renderReflection
;
731 renderDinosaur
= 1 - renderDinosaur
;
733 case M_STENCIL_REFLECTION
:
734 stencilReflection
= 1 - stencilReflection
;
736 case M_STENCIL_SHADOW
:
737 stencilShadow
= 1 - stencilShadow
;
739 case M_OFFSET_SHADOW
:
740 offsetShadow
= 1 - offsetShadow
;
743 directionalLight
= 0;
746 directionalLight
= 1;
749 reportSpeed
= 1 - reportSpeed
;
755 /* When not visible, stop animating. Restart when visible again. */
759 if (vis
== GLUT_VISIBLE
) {
768 /* Press any key to redraw; good when motion stopped and
769 performance reporting on. */
772 key(unsigned char c
, int x
, int y
)
775 exit(0); /* IRIS GLism, Escape quits. */
780 /* Press any key to redraw; good when motion stopped and
781 performance reporting on. */
784 special(int k
, int x
, int y
)
790 supportsOneDotOne(void)
795 version
= (char *) glGetString(GL_VERSION
);
796 if (sscanf(version
, "%d.%d", &major
, &minor
) == 2)
797 return major
* 10 + minor
>= 11;
798 return 0; /* OpenGL version string malformed! */
802 main(int argc
, char **argv
)
806 glutInit(&argc
, argv
);
808 for (i
=1; i
<argc
; i
++) {
809 if (!strcmp("-linear", argv
[i
])) {
811 } else if (!strcmp("-mipmap", argv
[i
])) {
813 } else if (!strcmp("-ext", argv
[i
])) {
818 glutInitDisplayMode(GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
| GLUT_STENCIL
);
821 /* In GLUT 4.0, you'll be able to do this an be sure to
822 get 2 bits of stencil if the machine has it for you. */
823 glutInitDisplayString("samples stencil>=2 rgb double depth");
826 glutCreateWindow("Shadowy Leapin' Lizards");
829 if (glutGet(GLUT_WINDOW_STENCIL_SIZE
) <= 1) {
830 printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
834 /* Register GLUT callbacks. */
835 glutDisplayFunc(redraw
);
836 glutMouseFunc(mouse
);
837 glutMotionFunc(motion
);
838 glutVisibilityFunc(visible
);
839 glutKeyboardFunc(key
);
840 glutSpecialFunc(special
);
842 glutCreateMenu(controlLights
);
844 glutAddMenuEntry("Toggle motion", M_MOTION
);
845 glutAddMenuEntry("-----------------------", M_NONE
);
846 glutAddMenuEntry("Toggle light", M_LIGHT
);
847 glutAddMenuEntry("Toggle texture", M_TEXTURE
);
848 glutAddMenuEntry("Toggle shadows", M_SHADOWS
);
849 glutAddMenuEntry("Toggle reflection", M_REFLECTION
);
850 glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR
);
851 glutAddMenuEntry("-----------------------", M_NONE
);
852 glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION
);
853 glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW
);
854 glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW
);
855 glutAddMenuEntry("----------------------", M_NONE
);
856 glutAddMenuEntry("Positional light", M_POSITIONAL
);
857 glutAddMenuEntry("Directional light", M_DIRECTIONAL
);
858 glutAddMenuEntry("-----------------------", M_NONE
);
859 glutAddMenuEntry("Toggle performance", M_PERFORMANCE
);
860 glutAttachMenu(GLUT_RIGHT_BUTTON
);
863 #ifdef GL_VERSION_1_1
864 if (supportsOneDotOne() && !forceExtension
) {
865 polygonOffsetVersion
= ONE_DOT_ONE
;
866 glPolygonOffset(-2.0, -9.0);
870 #ifdef GL_EXT_polygon_offset
871 /* check for the polygon offset extension */
872 if (glutExtensionSupported("GL_EXT_polygon_offset")) {
873 polygonOffsetVersion
= EXTENSION
;
874 glPolygonOffsetEXT(-2.0, -0.002);
878 polygonOffsetVersion
= MISSING
;
879 printf("\ndinoshine: Missing polygon offset.\n");
880 printf(" Expect shadow depth aliasing artifacts.\n\n");
884 glEnable(GL_CULL_FACE
);
885 glEnable(GL_DEPTH_TEST
);
886 glEnable(GL_TEXTURE_2D
);
889 glMatrixMode(GL_PROJECTION
);
890 gluPerspective( /* field of view in degree */ 40.0,
891 /* aspect ratio */ 1.0,
892 /* Z near */ 20.0, /* Z far */ 100.0);
893 glMatrixMode(GL_MODELVIEW
);
894 gluLookAt(0.0, 8.0, 60.0, /* eye is at (0,8,60) */
895 0.0, 8.0, 0.0, /* center is at (0,8,0) */
896 0.0, 1.0, 0.); /* up is in postivie Y direction */
898 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER
, 1);
899 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, lightColor
);
900 glLightf(GL_LIGHT0
, GL_CONSTANT_ATTENUATION
, 0.1);
901 glLightf(GL_LIGHT0
, GL_LINEAR_ATTENUATION
, 0.05);
903 glEnable(GL_LIGHTING
);
907 /* Setup floor plane for projected shadow calculations. */
908 findPlane(floorPlane
, floorVertices
[1], floorVertices
[2], floorVertices
[3]);
911 return 0; /* ANSI C requires main to return int. */