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 <GL/glut.h> /* OpenGL Utility Toolkit header */
43 /* Some <math.h> files do not define M_PI... */
45 #define M_PI 3.14159265358979323846
48 /* Variable controlling various rendering modes. */
49 static int stencilReflection
= 1, stencilShadow
= 1, offsetShadow
= 1;
50 static int renderShadow
= 1, renderDinosaur
= 1, renderReflection
= 1;
51 static int linearFiltering
= 0, useMipmaps
= 0, useTexture
= 1;
52 static int reportSpeed
= 0;
53 static int animation
= 1;
54 static GLboolean lightSwitch
= GL_TRUE
;
55 static int directionalLight
= 1;
56 static int forceExtension
= 0;
58 /* Time varying or user-controled variables. */
59 static float jump
= 0.0;
60 static float lightAngle
= 0.0, lightHeight
= 20;
61 GLfloat angle
= -150; /* in degrees */
62 GLfloat angle2
= 30; /* in degrees */
64 int moving
, startx
, starty
;
65 int lightMoving
= 0, lightStartX
, lightStartY
;
68 MISSING
, EXTENSION
, ONE_DOT_ONE
70 int polygonOffsetVersion
;
72 static GLdouble bodyWidth
= 3.0;
74 static GLfloat body
[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
75 {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
76 {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
78 static GLfloat arm
[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
79 {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
80 {13, 9}, {11, 11}, {9, 11} };
81 static GLfloat leg
[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
82 {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
83 static GLfloat eye
[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
84 {9.6, 15.25}, {9, 15.25} };
85 static GLfloat lightPosition
[4];
86 static GLfloat lightColor
[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
87 static GLfloat skinColor
[] = {0.1, 1.0, 0.1, 1.0}, eyeColor
[] = {1.0, 0.2, 0.2, 1.0};
90 /* Nice floor texture tiling pattern. */
91 static char *circles
[] = {
111 makeFloorTexture(void)
113 GLubyte floorTexture
[16][16][3];
117 /* Setup RGB image for the texture. */
118 loc
= (GLubyte
*) floorTexture
;
119 for (t
= 0; t
< 16; t
++) {
120 for (s
= 0; s
< 16; s
++) {
121 if (circles
[t
][s
] == 'x') {
136 glPixelStorei(GL_UNPACK_ALIGNMENT
, 1);
139 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
,
140 GL_LINEAR_MIPMAP_LINEAR
);
141 gluBuild2DMipmaps(GL_TEXTURE_2D
, 3, 16, 16,
142 GL_RGB
, GL_UNSIGNED_BYTE
, floorTexture
);
144 if (linearFiltering
) {
145 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
147 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
149 glTexImage2D(GL_TEXTURE_2D
, 0, 3, 16, 16, 0,
150 GL_RGB
, GL_UNSIGNED_BYTE
, floorTexture
);
161 /* Create a matrix that will project the desired shadow. */
163 shadowMatrix(GLfloat shadowMat
[4][4],
164 GLfloat groundplane
[4],
169 /* Find dot product between light position vector and ground plane normal. */
170 dot
= groundplane
[X
] * lightpos
[X
] +
171 groundplane
[Y
] * lightpos
[Y
] +
172 groundplane
[Z
] * lightpos
[Z
] +
173 groundplane
[W
] * lightpos
[W
];
175 shadowMat
[0][0] = dot
- lightpos
[X
] * groundplane
[X
];
176 shadowMat
[1][0] = 0.f
- lightpos
[X
] * groundplane
[Y
];
177 shadowMat
[2][0] = 0.f
- lightpos
[X
] * groundplane
[Z
];
178 shadowMat
[3][0] = 0.f
- lightpos
[X
] * groundplane
[W
];
180 shadowMat
[X
][1] = 0.f
- lightpos
[Y
] * groundplane
[X
];
181 shadowMat
[1][1] = dot
- lightpos
[Y
] * groundplane
[Y
];
182 shadowMat
[2][1] = 0.f
- lightpos
[Y
] * groundplane
[Z
];
183 shadowMat
[3][1] = 0.f
- lightpos
[Y
] * groundplane
[W
];
185 shadowMat
[X
][2] = 0.f
- lightpos
[Z
] * groundplane
[X
];
186 shadowMat
[1][2] = 0.f
- lightpos
[Z
] * groundplane
[Y
];
187 shadowMat
[2][2] = dot
- lightpos
[Z
] * groundplane
[Z
];
188 shadowMat
[3][2] = 0.f
- lightpos
[Z
] * groundplane
[W
];
190 shadowMat
[X
][3] = 0.f
- lightpos
[W
] * groundplane
[X
];
191 shadowMat
[1][3] = 0.f
- lightpos
[W
] * groundplane
[Y
];
192 shadowMat
[2][3] = 0.f
- lightpos
[W
] * groundplane
[Z
];
193 shadowMat
[3][3] = dot
- lightpos
[W
] * groundplane
[W
];
197 /* Find the plane equation given 3 points. */
199 findPlane(GLfloat plane
[4],
200 GLfloat v0
[3], GLfloat v1
[3], GLfloat v2
[3])
202 GLfloat vec0
[3], vec1
[3];
204 /* Need 2 vectors to find cross product. */
205 vec0
[X
] = v1
[X
] - v0
[X
];
206 vec0
[Y
] = v1
[Y
] - v0
[Y
];
207 vec0
[Z
] = v1
[Z
] - v0
[Z
];
209 vec1
[X
] = v2
[X
] - v0
[X
];
210 vec1
[Y
] = v2
[Y
] - v0
[Y
];
211 vec1
[Z
] = v2
[Z
] - v0
[Z
];
213 /* find cross product to get A, B, and C of plane equation */
214 plane
[A
] = vec0
[Y
] * vec1
[Z
] - vec0
[Z
] * vec1
[Y
];
215 plane
[B
] = -(vec0
[X
] * vec1
[Z
] - vec0
[Z
] * vec1
[X
]);
216 plane
[C
] = vec0
[X
] * vec1
[Y
] - vec0
[Y
] * vec1
[X
];
218 plane
[D
] = -(plane
[A
] * v0
[X
] + plane
[B
] * v0
[Y
] + plane
[C
] * v0
[Z
]);
222 extrudeSolidFromPolygon(GLfloat data
[][2], unsigned int dataSize
,
223 GLdouble thickness
, GLuint side
, GLuint edge
, GLuint whole
)
225 static GLUtriangulatorObj
*tobj
= NULL
;
226 GLdouble vertex
[3], dx
, dy
, len
;
228 int count
= (int) (dataSize
/ (2 * sizeof(GLfloat
)));
231 tobj
= gluNewTess(); /* create and initialize a GLU
232 polygon tesselation object */
233 gluTessCallback(tobj
, GLU_BEGIN
, glBegin
);
234 gluTessCallback(tobj
, GLU_VERTEX
, glVertex2fv
); /* semi-tricky */
235 gluTessCallback(tobj
, GLU_END
, glEnd
);
237 glNewList(side
, GL_COMPILE
);
238 glShadeModel(GL_SMOOTH
); /* smooth minimizes seeing
240 gluBeginPolygon(tobj
);
241 for (i
= 0; i
< count
; i
++) {
242 vertex
[0] = data
[i
][0];
243 vertex
[1] = data
[i
][1];
245 gluTessVertex(tobj
, vertex
, data
[i
]);
249 glNewList(edge
, GL_COMPILE
);
250 glShadeModel(GL_FLAT
); /* flat shade keeps angular hands
251 from being "smoothed" */
252 glBegin(GL_QUAD_STRIP
);
253 for (i
= 0; i
<= count
; i
++) {
254 /* mod function handles closing the edge */
255 glVertex3f(data
[i
% count
][0], data
[i
% count
][1], 0.0);
256 glVertex3f(data
[i
% count
][0], data
[i
% count
][1], thickness
);
257 /* Calculate a unit normal by dividing by Euclidean
258 distance. We * could be lazy and use
259 glEnable(GL_NORMALIZE) so we could pass in * arbitrary
260 normals for a very slight performance hit. */
261 dx
= data
[(i
+ 1) % count
][1] - data
[i
% count
][1];
262 dy
= data
[i
% count
][0] - data
[(i
+ 1) % count
][0];
263 len
= sqrt(dx
* dx
+ dy
* dy
);
264 glNormal3f(dx
/ len
, dy
/ len
, 0.0);
268 glNewList(whole
, GL_COMPILE
);
271 glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */
274 glTranslatef(0.0, 0.0, thickness
);
276 glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */
282 /* Enumerants for refering to display lists. */
284 RESERVED
, BODY_SIDE
, BODY_EDGE
, BODY_WHOLE
, ARM_SIDE
, ARM_EDGE
, ARM_WHOLE
,
285 LEG_SIDE
, LEG_EDGE
, LEG_WHOLE
, EYE_SIDE
, EYE_EDGE
, EYE_WHOLE
291 extrudeSolidFromPolygon(body
, sizeof(body
), bodyWidth
,
292 BODY_SIDE
, BODY_EDGE
, BODY_WHOLE
);
293 extrudeSolidFromPolygon(arm
, sizeof(arm
), bodyWidth
/ 4,
294 ARM_SIDE
, ARM_EDGE
, ARM_WHOLE
);
295 extrudeSolidFromPolygon(leg
, sizeof(leg
), bodyWidth
/ 2,
296 LEG_SIDE
, LEG_EDGE
, LEG_WHOLE
);
297 extrudeSolidFromPolygon(eye
, sizeof(eye
), bodyWidth
+ 0.2,
298 EYE_SIDE
, EYE_EDGE
, EYE_WHOLE
);
306 /* Translate the dinosaur to be at (0,8,0). */
307 glTranslatef(-8, 0, -bodyWidth
/ 2);
308 glTranslatef(0.0, jump
, 0.0);
309 glMaterialfv(GL_FRONT
, GL_DIFFUSE
, skinColor
);
310 glCallList(BODY_WHOLE
);
311 glTranslatef(0.0, 0.0, bodyWidth
);
312 glCallList(ARM_WHOLE
);
313 glCallList(LEG_WHOLE
);
314 glTranslatef(0.0, 0.0, -bodyWidth
- bodyWidth
/ 4);
315 glCallList(ARM_WHOLE
);
316 glTranslatef(0.0, 0.0, -bodyWidth
/ 4);
317 glCallList(LEG_WHOLE
);
318 glTranslatef(0.0, 0.0, bodyWidth
/ 2 - 0.1);
319 glMaterialfv(GL_FRONT
, GL_DIFFUSE
, eyeColor
);
320 glCallList(EYE_WHOLE
);
324 static GLfloat floorVertices
[4][3] = {
325 { -20.0, 0.0, 20.0 },
327 { 20.0, 0.0, -20.0 },
328 { -20.0, 0.0, -20.0 },
331 /* Draw a floor (possibly textured). */
335 glDisable(GL_LIGHTING
);
338 glEnable(GL_TEXTURE_2D
);
342 glTexCoord2f(0.0, 0.0);
343 glVertex3fv(floorVertices
[0]);
344 glTexCoord2f(0.0, 16.0);
345 glVertex3fv(floorVertices
[1]);
346 glTexCoord2f(16.0, 16.0);
347 glVertex3fv(floorVertices
[2]);
348 glTexCoord2f(16.0, 0.0);
349 glVertex3fv(floorVertices
[3]);
353 glDisable(GL_TEXTURE_2D
);
356 glEnable(GL_LIGHTING
);
359 static GLfloat floorPlane
[4];
360 static GLfloat floorShadow
[4][4];
368 start
= glutGet(GLUT_ELAPSED_TIME
);
371 /* Clear; default stencil clears to zero. */
372 if ((stencilReflection
&& renderReflection
) || (stencilShadow
&& renderShadow
)) {
373 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
375 /* Avoid clearing stencil when not using it. */
376 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
379 /* Reposition the light source. */
380 lightPosition
[0] = 12*cos(lightAngle
);
381 lightPosition
[1] = lightHeight
;
382 lightPosition
[2] = 12*sin(lightAngle
);
383 if (directionalLight
) {
384 lightPosition
[3] = 0.0;
386 lightPosition
[3] = 1.0;
389 shadowMatrix(floorShadow
, floorPlane
, lightPosition
);
392 /* Perform scene rotations based on user mouse input. */
393 glRotatef(angle2
, 1.0, 0.0, 0.0);
394 glRotatef(angle
, 0.0, 1.0, 0.0);
396 /* Tell GL new light source position. */
397 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
399 if (renderReflection
) {
400 if (stencilReflection
) {
401 /* We can eliminate the visual "artifact" of seeing the "flipped"
402 dinosaur underneath the floor by using stencil. The idea is
403 draw the floor without color or depth update but so that
404 a stencil value of one is where the floor will be. Later when
405 rendering the dinosaur reflection, we will only update pixels
406 with a stencil value of 1 to make sure the reflection only
407 lives on the floor, not below the floor. */
409 /* Don't update color or depth. */
410 glDisable(GL_DEPTH_TEST
);
411 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
413 /* Draw 1 into the stencil buffer. */
414 glEnable(GL_STENCIL_TEST
);
415 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
416 glStencilFunc(GL_ALWAYS
, 1, 0xffffffff);
418 /* Now render floor; floor pixels just get their stencil set to 1. */
421 /* Re-enable update of color and depth. */
422 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
423 glEnable(GL_DEPTH_TEST
);
425 /* Now, only render where stencil is set to 1. */
426 glStencilFunc(GL_EQUAL
, 1, 0xffffffff); /* draw if ==1 */
427 glStencilOp(GL_KEEP
, GL_KEEP
, GL_KEEP
);
432 /* The critical reflection step: Reflect dinosaur through the floor
433 (the Y=0 plane) to make a relection. */
434 glScalef(1.0, -1.0, 1.0);
436 /* Reflect the light position. */
437 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
439 /* To avoid our normals getting reversed and hence botched lighting
440 on the reflection, turn on normalize. */
441 glEnable(GL_NORMALIZE
);
442 glCullFace(GL_FRONT
);
444 /* Draw the reflected dinosaur. */
447 /* Disable noramlize again and re-enable back face culling. */
448 glDisable(GL_NORMALIZE
);
453 /* Switch back to the unreflected light position. */
454 glLightfv(GL_LIGHT0
, GL_POSITION
, lightPosition
);
456 if (stencilReflection
) {
457 glDisable(GL_STENCIL_TEST
);
461 /* Back face culling will get used to only draw either the top or the
462 bottom floor. This let's us get a floor with two distinct
463 appearances. The top floor surface is reflective and kind of red.
464 The bottom floor surface is not reflective and blue. */
466 /* Draw "bottom" of floor in blue. */
467 glFrontFace(GL_CW
); /* Switch face orientation. */
468 glColor4f(0.1, 0.1, 0.7, 1.0);
474 /* Draw the floor with stencil value 3. This helps us only
475 draw the shadow once per floor pixel (and only on the
477 glEnable(GL_STENCIL_TEST
);
478 glStencilFunc(GL_ALWAYS
, 3, 0xffffffff);
479 glStencilOp(GL_KEEP
, GL_KEEP
, GL_REPLACE
);
483 /* Draw "top" of floor. Use blending to blend in reflection. */
485 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
486 glColor4f(0.7, 0.0, 0.0, 0.3);
487 glColor4f(1.0, 1.0, 1.0, 0.3);
491 if (renderDinosaur
) {
492 /* Draw "actual" dinosaur, not its reflection. */
498 /* Render the projected shadow. */
502 /* Now, only render where stencil is set above 2 (ie, 3 where
503 the top floor is). Update stencil with 2 where the shadow
504 gets drawn so we don't redraw (and accidently reblend) the
506 glStencilFunc(GL_LESS
, 2, 0xffffffff); /* draw if ==1 */
507 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
510 /* To eliminate depth buffer artifacts, we use polygon offset
511 to raise the depth of the projected shadow slightly so
512 that it does not depth buffer alias with the floor. */
514 switch (polygonOffsetVersion
) {
516 #ifdef GL_EXT_polygon_offset
517 glEnable(GL_POLYGON_OFFSET_EXT
);
520 #ifdef GL_VERSION_1_1
522 glEnable(GL_POLYGON_OFFSET_FILL
);
531 /* Render 50% black shadow color on top of whatever the
532 floor appareance is. */
534 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
535 glDisable(GL_LIGHTING
); /* Force the 50% black. */
536 glColor4f(0.0, 0.0, 0.0, 0.5);
539 /* Project the shadow. */
540 glMultMatrixf((GLfloat
*) floorShadow
);
545 glEnable(GL_LIGHTING
);
548 switch (polygonOffsetVersion
) {
549 #ifdef GL_EXT_polygon_offset
551 glDisable(GL_POLYGON_OFFSET_EXT
);
554 #ifdef GL_VERSION_1_1
556 glDisable(GL_POLYGON_OFFSET_FILL
);
565 glDisable(GL_STENCIL_TEST
);
570 glDisable(GL_LIGHTING
);
571 glColor3f(1.0, 1.0, 0.0);
572 if (directionalLight
) {
573 /* Draw an arrowhead. */
574 glDisable(GL_CULL_FACE
);
575 glTranslatef(lightPosition
[0], lightPosition
[1], lightPosition
[2]);
576 glRotatef(lightAngle
* -180.0 / M_PI
, 0, 1, 0);
577 glRotatef(atan(lightHeight
/12) * 180.0 / M_PI
, 0, 0, 1);
578 glBegin(GL_TRIANGLE_FAN
);
581 glVertex3f(2, -1, 1);
582 glVertex3f(2, -1, -1);
583 glVertex3f(2, 1, -1);
586 /* Draw a white line from light direction. */
587 glColor3f(1.0, 1.0, 1.0);
592 glEnable(GL_CULL_FACE
);
594 /* Draw a yellow ball at the light source. */
595 glTranslatef(lightPosition
[0], lightPosition
[1], lightPosition
[2]);
596 glutSolidSphere(1.0, 5, 5);
598 glEnable(GL_LIGHTING
);
605 end
= glutGet(GLUT_ELAPSED_TIME
);
606 printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end
-start
), end
-start
);
614 mouse(int button
, int state
, int x
, int y
)
616 if (button
== GLUT_LEFT_BUTTON
) {
617 if (state
== GLUT_DOWN
) {
622 if (state
== GLUT_UP
) {
626 if (button
== GLUT_MIDDLE_BUTTON
) {
627 if (state
== GLUT_DOWN
) {
632 if (state
== GLUT_UP
) {
643 angle
= angle
+ (x
- startx
);
644 angle2
= angle2
+ (y
- starty
);
650 lightAngle
+= (x
- lightStartX
)/40.0;
651 lightHeight
+= (lightStartY
- y
)/20.0;
658 /* Advance time varying state when idle callback registered. */
662 static float time
= 0.0;
664 time
= glutGet(GLUT_ELAPSED_TIME
) / 500.0;
666 jump
= 4.0 * fabs(sin(time
)*0.5);
674 M_NONE
, M_MOTION
, M_LIGHT
, M_TEXTURE
, M_SHADOWS
, M_REFLECTION
, M_DINOSAUR
,
675 M_STENCIL_REFLECTION
, M_STENCIL_SHADOW
, M_OFFSET_SHADOW
,
676 M_POSITIONAL
, M_DIRECTIONAL
, M_PERFORMANCE
680 controlLights(int value
)
686 animation
= 1 - animation
;
694 lightSwitch
= !lightSwitch
;
698 glDisable(GL_LIGHT0
);
702 useTexture
= !useTexture
;
705 renderShadow
= 1 - renderShadow
;
708 renderReflection
= 1 - renderReflection
;
711 renderDinosaur
= 1 - renderDinosaur
;
713 case M_STENCIL_REFLECTION
:
714 stencilReflection
= 1 - stencilReflection
;
716 case M_STENCIL_SHADOW
:
717 stencilShadow
= 1 - stencilShadow
;
719 case M_OFFSET_SHADOW
:
720 offsetShadow
= 1 - offsetShadow
;
723 directionalLight
= 0;
726 directionalLight
= 1;
729 reportSpeed
= 1 - reportSpeed
;
735 /* When not visible, stop animating. Restart when visible again. */
739 if (vis
== GLUT_VISIBLE
) {
748 /* Press any key to redraw; good when motion stopped and
749 performance reporting on. */
752 key(unsigned char c
, int x
, int y
)
755 exit(0); /* IRIS GLism, Escape quits. */
760 /* Press any key to redraw; good when motion stopped and
761 performance reporting on. */
764 special(int k
, int x
, int y
)
770 supportsOneDotOne(void)
775 version
= (char *) glGetString(GL_VERSION
);
776 if (sscanf(version
, "%d.%d", &major
, &minor
) == 2)
777 return major
>= 1 && minor
>= 1;
778 return 0; /* OpenGL version string malformed! */
782 main(int argc
, char **argv
)
786 glutInit(&argc
, argv
);
788 for (i
=1; i
<argc
; i
++) {
789 if (!strcmp("-linear", argv
[i
])) {
791 } else if (!strcmp("-mipmap", argv
[i
])) {
793 } else if (!strcmp("-ext", argv
[i
])) {
798 glutInitDisplayMode(GLUT_RGB
| GLUT_DOUBLE
| GLUT_DEPTH
| GLUT_STENCIL
);
801 /* In GLUT 4.0, you'll be able to do this an be sure to
802 get 2 bits of stencil if the machine has it for you. */
803 glutInitDisplayString("samples stencil>=2 rgb double depth");
806 glutCreateWindow("Shadowy Leapin' Lizards");
808 if (glutGet(GLUT_WINDOW_STENCIL_SIZE
) <= 1) {
809 printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
813 /* Register GLUT callbacks. */
814 glutDisplayFunc(redraw
);
815 glutMouseFunc(mouse
);
816 glutMotionFunc(motion
);
817 glutVisibilityFunc(visible
);
818 glutKeyboardFunc(key
);
819 glutSpecialFunc(special
);
821 glutCreateMenu(controlLights
);
823 glutAddMenuEntry("Toggle motion", M_MOTION
);
824 glutAddMenuEntry("-----------------------", M_NONE
);
825 glutAddMenuEntry("Toggle light", M_LIGHT
);
826 glutAddMenuEntry("Toggle texture", M_TEXTURE
);
827 glutAddMenuEntry("Toggle shadows", M_SHADOWS
);
828 glutAddMenuEntry("Toggle reflection", M_REFLECTION
);
829 glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR
);
830 glutAddMenuEntry("-----------------------", M_NONE
);
831 glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION
);
832 glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW
);
833 glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW
);
834 glutAddMenuEntry("----------------------", M_NONE
);
835 glutAddMenuEntry("Positional light", M_POSITIONAL
);
836 glutAddMenuEntry("Directional light", M_DIRECTIONAL
);
837 glutAddMenuEntry("-----------------------", M_NONE
);
838 glutAddMenuEntry("Toggle performance", M_PERFORMANCE
);
839 glutAttachMenu(GLUT_RIGHT_BUTTON
);
842 #ifdef GL_VERSION_1_1
843 if (supportsOneDotOne() && !forceExtension
) {
844 polygonOffsetVersion
= ONE_DOT_ONE
;
845 glPolygonOffset(-2.0, -9.0);
849 #ifdef GL_EXT_polygon_offset
850 /* check for the polygon offset extension */
851 if (glutExtensionSupported("GL_EXT_polygon_offset")) {
852 polygonOffsetVersion
= EXTENSION
;
853 glPolygonOffsetEXT(-2.0, -0.002);
857 polygonOffsetVersion
= MISSING
;
858 printf("\ndinoshine: Missing polygon offset.\n");
859 printf(" Expect shadow depth aliasing artifacts.\n\n");
863 glEnable(GL_CULL_FACE
);
864 glEnable(GL_DEPTH_TEST
);
865 glEnable(GL_TEXTURE_2D
);
868 glMatrixMode(GL_PROJECTION
);
869 gluPerspective( /* field of view in degree */ 40.0,
870 /* aspect ratio */ 1.0,
871 /* Z near */ 20.0, /* Z far */ 100.0);
872 glMatrixMode(GL_MODELVIEW
);
873 gluLookAt(0.0, 8.0, 60.0, /* eye is at (0,8,60) */
874 0.0, 8.0, 0.0, /* center is at (0,8,0) */
875 0.0, 1.0, 0.); /* up is in postivie Y direction */
877 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER
, 1);
878 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, lightColor
);
879 glLightf(GL_LIGHT0
, GL_CONSTANT_ATTENUATION
, 0.1);
880 glLightf(GL_LIGHT0
, GL_LINEAR_ATTENUATION
, 0.05);
882 glEnable(GL_LIGHTING
);
886 /* Setup floor plane for projected shadow calculations. */
887 findPlane(floorPlane
, floorVertices
[1], floorVertices
[2], floorVertices
[3]);
890 return 0; /* ANSI C requires main to return int. */