2 /* Copyright (c) Mark J. Kilgard, 1994, 1997. */
\r
4 /* This program is freely distributable without licensing fees
\r
5 and is provided without guarantee or warrantee expressed or
\r
6 implied. This program is -not- in the public domain. */
\r
8 /* Example for PC game developers to show how to *combine* texturing,
\r
9 reflections, and projected shadows all in real-time with OpenGL.
\r
10 Robust reflections use stenciling. Robust projected shadows
\r
11 use both stenciling and polygon offset. PC game programmers
\r
12 should realize that neither stenciling nor polygon offset are
\r
13 supported by Direct3D, so these real-time rendering algorithms
\r
14 are only really viable with OpenGL.
\r
16 The program has modes for disabling the stenciling and polygon
\r
17 offset uses. It is worth running this example with these features
\r
18 toggled off so you can see the sort of artifacts that result.
\r
20 Notice that the floor texturing, reflections, and shadowing
\r
21 all co-exist properly. */
\r
23 /* When you run this program: Left mouse button controls the
\r
24 view. Middle mouse button controls light position (left &
\r
25 right rotates light around dino; up & down moves light
\r
26 position up and down). Right mouse button pops up menu. */
\r
28 /* Check out the comments in the "redraw" routine to see how the
\r
29 reflection blending and surface stenciling is done. You can
\r
30 also see in "redraw" how the projected shadows are rendered,
\r
32 including the use of stenciling and polygon offset. */
\r
34 /* This program is derived from glutdino.c */
\r
36 /* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
\r
41 #include <math.h> /* for cos(), sin(), and sqrt() */
\r
42 #include <GL/glut.h> /* OpenGL Utility Toolkit header */
\r
44 /* Some <math.h> files do not define M_PI... */
\r
46 #define M_PI 3.14159265
\r
49 /* Variable controlling various rendering modes. */
\r
50 static int stencilReflection = 1, stencilShadow = 1, offsetShadow = 1;
\r
51 static int renderShadow = 1, renderDinosaur = 1, renderReflection = 1;
\r
52 static int linearFiltering = 0, useMipmaps = 0, useTexture = 1;
\r
53 static int reportSpeed = 0;
\r
54 static int animation = 1;
\r
55 static GLboolean lightSwitch = GL_TRUE;
\r
56 static int directionalLight = 1;
\r
57 static int forceExtension = 0;
\r
59 /* Time varying or user-controled variables. */
\r
60 static float jump = 0.0;
\r
61 static float lightAngle = 0.0, lightHeight = 20;
\r
62 GLfloat angle = -150; /* in degrees */
\r
63 GLfloat angle2 = 30; /* in degrees */
\r
65 int moving, startx, starty;
\r
66 int lightMoving = 0, lightStartX, lightStartY;
\r
69 MISSING, EXTENSION, ONE_DOT_ONE
\r
71 int polygonOffsetVersion;
\r
73 static GLdouble bodyWidth = 3.0;
\r
75 static GLfloat body[][2] = { {0, 3}, {1, 1}, {5, 1}, {8, 4}, {10, 4}, {11, 5},
\r
76 {11, 11.5}, {13, 12}, {13, 13}, {10, 13.5}, {13, 14}, {13, 15}, {11, 16},
\r
77 {8, 16}, {7, 15}, {7, 13}, {8, 12}, {7, 11}, {6, 6}, {4, 3}, {3, 2},
\r
79 static GLfloat arm[][2] = { {8, 10}, {9, 9}, {10, 9}, {13, 8}, {14, 9}, {16, 9},
\r
80 {15, 9.5}, {16, 10}, {15, 10}, {15.5, 11}, {14.5, 10}, {14, 11}, {14, 10},
\r
81 {13, 9}, {11, 11}, {9, 11} };
\r
82 static GLfloat leg[][2] = { {8, 6}, {8, 4}, {9, 3}, {9, 2}, {8, 1}, {8, 0.5}, {9, 0},
\r
83 {12, 0}, {10, 1}, {10, 2}, {12, 4}, {11, 6}, {10, 7}, {9, 7} };
\r
84 static GLfloat eye[][2] = { {8.75, 15}, {9, 14.7}, {9.6, 14.7}, {10.1, 15},
\r
85 {9.6, 15.25}, {9, 15.25} };
\r
86 static GLfloat lightPosition[4];
\r
87 static GLfloat lightColor[] = {0.8, 1.0, 0.8, 1.0}; /* green-tinted */
\r
88 static GLfloat skinColor[] = {0.1, 1.0, 0.1, 1.0}, eyeColor[] = {1.0, 0.2, 0.2, 1.0};
\r
91 /* Nice floor texture tiling pattern. */
\r
92 static char *circles[] = {
\r
100 "xxx......xxx....",
\r
101 ".xxx....xxx.....",
\r
102 ".xxxxxxxxxx.....",
\r
103 "..xxxxxxxx......",
\r
104 "....xxxx........",
\r
105 "................",
\r
106 "................",
\r
107 "................",
\r
108 "................",
\r
112 makeFloorTexture(void)
\r
114 GLubyte floorTexture[16][16][3];
\r
118 /* Setup RGB image for the texture. */
\r
119 loc = (GLubyte*) floorTexture;
\r
120 for (t = 0; t < 16; t++) {
\r
121 for (s = 0; s < 16; s++) {
\r
122 if (circles[t][s] == 'x') {
\r
137 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
\r
140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
\r
141 GL_LINEAR_MIPMAP_LINEAR);
\r
142 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 16, 16,
\r
143 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
\r
145 if (linearFiltering) {
\r
146 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
\r
148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
\r
150 glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
\r
151 GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
\r
162 /* Create a matrix that will project the desired shadow. */
\r
164 shadowMatrix(GLfloat shadowMat[4][4],
\r
165 GLfloat groundplane[4],
\r
166 GLfloat lightpos[4])
\r
170 /* Find dot product between light position vector and ground plane normal. */
\r
171 dot = groundplane[X] * lightpos[X] +
\r
172 groundplane[Y] * lightpos[Y] +
\r
173 groundplane[Z] * lightpos[Z] +
\r
174 groundplane[W] * lightpos[W];
\r
176 shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
\r
177 shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
\r
178 shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
\r
179 shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
\r
181 shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
\r
182 shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
\r
183 shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
\r
184 shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
\r
186 shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
\r
187 shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
\r
188 shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
\r
189 shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
\r
191 shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
\r
192 shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
\r
193 shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
\r
194 shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
\r
198 /* Find the plane equation given 3 points. */
\r
200 findPlane(GLfloat plane[4],
\r
201 GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
\r
203 GLfloat vec0[3], vec1[3];
\r
205 /* Need 2 vectors to find cross product. */
\r
206 vec0[X] = v1[X] - v0[X];
\r
207 vec0[Y] = v1[Y] - v0[Y];
\r
208 vec0[Z] = v1[Z] - v0[Z];
\r
210 vec1[X] = v2[X] - v0[X];
\r
211 vec1[Y] = v2[Y] - v0[Y];
\r
212 vec1[Z] = v2[Z] - v0[Z];
\r
214 /* find cross product to get A, B, and C of plane equation */
\r
215 plane[A] = vec0[Y] * vec1[Z] - vec0[Z] * vec1[Y];
\r
216 plane[B] = -(vec0[X] * vec1[Z] - vec0[Z] * vec1[X]);
\r
217 plane[C] = vec0[X] * vec1[Y] - vec0[Y] * vec1[X];
\r
219 plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
\r
223 extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
\r
224 GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
\r
226 static GLUtriangulatorObj *tobj = NULL;
\r
227 GLdouble vertex[3], dx, dy, len;
\r
229 int count = (int) (dataSize / (2 * sizeof(GLfloat)));
\r
231 if (tobj == NULL) {
\r
232 tobj = gluNewTess(); /* create and initialize a GLU
\r
233 polygon tesselation object */
\r
234 gluTessCallback(tobj, GLU_BEGIN, glBegin);
\r
235 gluTessCallback(tobj, GLU_VERTEX, glVertex2fv); /* semi-tricky */
\r
236 gluTessCallback(tobj, GLU_END, glEnd);
\r
238 glNewList(side, GL_COMPILE);
\r
239 glShadeModel(GL_SMOOTH); /* smooth minimizes seeing
\r
241 gluBeginPolygon(tobj);
\r
242 for (i = 0; i < count; i++) {
\r
243 vertex[0] = data[i][0];
\r
244 vertex[1] = data[i][1];
\r
246 gluTessVertex(tobj, vertex, data[i]);
\r
248 gluEndPolygon(tobj);
\r
250 glNewList(edge, GL_COMPILE);
\r
251 glShadeModel(GL_FLAT); /* flat shade keeps angular hands
\r
252 from being "smoothed" */
\r
253 glBegin(GL_QUAD_STRIP);
\r
254 for (i = 0; i <= count; i++) {
\r
255 /* mod function handles closing the edge */
\r
256 glVertex3f(data[i % count][0], data[i % count][1], 0.0);
\r
257 glVertex3f(data[i % count][0], data[i % count][1], thickness);
\r
258 /* Calculate a unit normal by dividing by Euclidean
\r
259 distance. We * could be lazy and use
\r
260 glEnable(GL_NORMALIZE) so we could pass in * arbitrary
\r
261 normals for a very slight performance hit. */
\r
262 dx = data[(i + 1) % count][1] - data[i % count][1];
\r
263 dy = data[i % count][0] - data[(i + 1) % count][0];
\r
264 len = sqrt(dx * dx + dy * dy);
\r
265 glNormal3f(dx / len, dy / len, 0.0);
\r
269 glNewList(whole, GL_COMPILE);
\r
270 glFrontFace(GL_CW);
\r
272 glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */
\r
275 glTranslatef(0.0, 0.0, thickness);
\r
276 glFrontFace(GL_CCW);
\r
277 glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */
\r
283 /* Enumerants for refering to display lists. */
\r
285 RESERVED, BODY_SIDE, BODY_EDGE, BODY_WHOLE, ARM_SIDE, ARM_EDGE, ARM_WHOLE,
\r
286 LEG_SIDE, LEG_EDGE, LEG_WHOLE, EYE_SIDE, EYE_EDGE, EYE_WHOLE
\r
292 extrudeSolidFromPolygon(body, sizeof(body), bodyWidth,
\r
293 BODY_SIDE, BODY_EDGE, BODY_WHOLE);
\r
294 extrudeSolidFromPolygon(arm, sizeof(arm), bodyWidth / 4,
\r
295 ARM_SIDE, ARM_EDGE, ARM_WHOLE);
\r
296 extrudeSolidFromPolygon(leg, sizeof(leg), bodyWidth / 2,
\r
297 LEG_SIDE, LEG_EDGE, LEG_WHOLE);
\r
298 extrudeSolidFromPolygon(eye, sizeof(eye), bodyWidth + 0.2,
\r
299 EYE_SIDE, EYE_EDGE, EYE_WHOLE);
\r
307 /* Translate the dinosaur to be at (0,8,0). */
\r
308 glTranslatef(-8, 0, -bodyWidth / 2);
\r
309 glTranslatef(0.0, jump, 0.0);
\r
310 glMaterialfv(GL_FRONT, GL_DIFFUSE, skinColor);
\r
311 glCallList(BODY_WHOLE);
\r
312 glTranslatef(0.0, 0.0, bodyWidth);
\r
313 glCallList(ARM_WHOLE);
\r
314 glCallList(LEG_WHOLE);
\r
315 glTranslatef(0.0, 0.0, -bodyWidth - bodyWidth / 4);
\r
316 glCallList(ARM_WHOLE);
\r
317 glTranslatef(0.0, 0.0, -bodyWidth / 4);
\r
318 glCallList(LEG_WHOLE);
\r
319 glTranslatef(0.0, 0.0, bodyWidth / 2 - 0.1);
\r
320 glMaterialfv(GL_FRONT, GL_DIFFUSE, eyeColor);
\r
321 glCallList(EYE_WHOLE);
\r
325 static GLfloat floorVertices[4][3] = {
\r
326 { -20.0, 0.0, 20.0 },
\r
327 { 20.0, 0.0, 20.0 },
\r
328 { 20.0, 0.0, -20.0 },
\r
329 { -20.0, 0.0, -20.0 },
\r
332 /* Draw a floor (possibly textured). */
\r
336 glDisable(GL_LIGHTING);
\r
339 glEnable(GL_TEXTURE_2D);
\r
343 glTexCoord2f(0.0, 0.0);
\r
344 glVertex3fv(floorVertices[0]);
\r
345 glTexCoord2f(0.0, 16.0);
\r
346 glVertex3fv(floorVertices[1]);
\r
347 glTexCoord2f(16.0, 16.0);
\r
348 glVertex3fv(floorVertices[2]);
\r
349 glTexCoord2f(16.0, 0.0);
\r
350 glVertex3fv(floorVertices[3]);
\r
354 glDisable(GL_TEXTURE_2D);
\r
357 glEnable(GL_LIGHTING);
\r
360 static GLfloat floorPlane[4];
\r
361 static GLfloat floorShadow[4][4];
\r
369 start = glutGet(GLUT_ELAPSED_TIME);
\r
372 /* Clear; default stencil clears to zero. */
\r
373 if ((stencilReflection && renderReflection) || (stencilShadow && renderShadow)) {
\r
374 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
\r
376 /* Avoid clearing stencil when not using it. */
\r
377 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
\r
380 /* Reposition the light source. */
\r
381 lightPosition[0] = 12*cos(lightAngle);
\r
382 lightPosition[1] = lightHeight;
\r
383 lightPosition[2] = 12*sin(lightAngle);
\r
384 if (directionalLight) {
\r
385 lightPosition[3] = 0.0;
\r
387 lightPosition[3] = 1.0;
\r
390 shadowMatrix(floorShadow, floorPlane, lightPosition);
\r
393 /* Perform scene rotations based on user mouse input. */
\r
394 glRotatef(angle2, 1.0, 0.0, 0.0);
\r
395 glRotatef(angle, 0.0, 1.0, 0.0);
\r
397 /* Tell GL new light source position. */
\r
398 glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
\r
400 if (renderReflection) {
\r
401 if (stencilReflection) {
\r
402 /* We can eliminate the visual "artifact" of seeing the "flipped"
\r
403 dinosaur underneath the floor by using stencil. The idea is
\r
404 draw the floor without color or depth update but so that
\r
405 a stencil value of one is where the floor will be. Later when
\r
406 rendering the dinosaur reflection, we will only update pixels
\r
407 with a stencil value of 1 to make sure the reflection only
\r
408 lives on the floor, not below the floor. */
\r
410 /* Don't update color or depth. */
\r
411 glDisable(GL_DEPTH_TEST);
\r
412 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
\r
414 /* Draw 1 into the stencil buffer. */
\r
415 glEnable(GL_STENCIL_TEST);
\r
416 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
\r
417 glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
\r
419 /* Now render floor; floor pixels just get their stencil set to 1. */
\r
422 /* Re-enable update of color and depth. */
\r
423 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
\r
424 glEnable(GL_DEPTH_TEST);
\r
426 /* Now, only render where stencil is set to 1. */
\r
427 glStencilFunc(GL_EQUAL, 1, 0xffffffff); /* draw if ==1 */
\r
428 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
\r
433 /* The critical reflection step: Reflect dinosaur through the floor
\r
434 (the Y=0 plane) to make a relection. */
\r
435 glScalef(1.0, -1.0, 1.0);
\r
437 /* Reflect the light position. */
\r
438 glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
\r
440 /* To avoid our normals getting reversed and hence botched lighting
\r
441 on the reflection, turn on normalize. */
\r
442 glEnable(GL_NORMALIZE);
\r
443 glCullFace(GL_FRONT);
\r
445 /* Draw the reflected dinosaur. */
\r
448 /* Disable noramlize again and re-enable back face culling. */
\r
449 glDisable(GL_NORMALIZE);
\r
450 glCullFace(GL_BACK);
\r
454 /* Switch back to the unreflected light position. */
\r
455 glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
\r
457 if (stencilReflection) {
\r
458 glDisable(GL_STENCIL_TEST);
\r
462 /* Back face culling will get used to only draw either the top or the
\r
463 bottom floor. This let's us get a floor with two distinct
\r
464 appearances. The top floor surface is reflective and kind of red.
\r
465 The bottom floor surface is not reflective and blue. */
\r
467 /* Draw "bottom" of floor in blue. */
\r
468 glFrontFace(GL_CW); /* Switch face orientation. */
\r
469 glColor4f(0.1, 0.1, 0.7, 1.0);
\r
471 glFrontFace(GL_CCW);
\r
473 if (renderShadow) {
\r
474 if (stencilShadow) {
\r
475 /* Draw the floor with stencil value 3. This helps us only
\r
476 draw the shadow once per floor pixel (and only on the
\r
478 glEnable(GL_STENCIL_TEST);
\r
479 glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
\r
480 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
\r
484 /* Draw "top" of floor. Use blending to blend in reflection. */
\r
485 glEnable(GL_BLEND);
\r
486 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
\r
487 glColor4f(0.7, 0.0, 0.0, 0.3);
\r
488 glColor4f(1.0, 1.0, 1.0, 0.3);
\r
490 glDisable(GL_BLEND);
\r
492 if (renderDinosaur) {
\r
493 /* Draw "actual" dinosaur, not its reflection. */
\r
497 if (renderShadow) {
\r
499 /* Render the projected shadow. */
\r
501 if (stencilShadow) {
\r
503 /* Now, only render where stencil is set above 2 (ie, 3 where
\r
504 the top floor is). Update stencil with 2 where the shadow
\r
505 gets drawn so we don't redraw (and accidently reblend) the
\r
507 glStencilFunc(GL_LESS, 2, 0xffffffff); /* draw if ==1 */
\r
508 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
\r
511 /* To eliminate depth buffer artifacts, we use polygon offset
\r
512 to raise the depth of the projected shadow slightly so
\r
513 that it does not depth buffer alias with the floor. */
\r
514 if (offsetShadow) {
\r
515 switch (polygonOffsetVersion) {
\r
517 #ifdef GL_EXT_polygon_offset
\r
518 glEnable(GL_POLYGON_OFFSET_EXT);
\r
521 #ifdef GL_VERSION_1_1
\r
523 glEnable(GL_POLYGON_OFFSET_FILL);
\r
532 /* Render 50% black shadow color on top of whatever the
\r
533 floor appareance is. */
\r
534 glEnable(GL_BLEND);
\r
535 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
\r
536 glDisable(GL_LIGHTING); /* Force the 50% black. */
\r
537 glColor4f(0.0, 0.0, 0.0, 0.5);
\r
540 /* Project the shadow. */
\r
541 glMultMatrixf((GLfloat *) floorShadow);
\r
545 glDisable(GL_BLEND);
\r
546 glEnable(GL_LIGHTING);
\r
548 if (offsetShadow) {
\r
549 switch (polygonOffsetVersion) {
\r
550 #ifdef GL_EXT_polygon_offset
\r
552 glDisable(GL_POLYGON_OFFSET_EXT);
\r
555 #ifdef GL_VERSION_1_1
\r
557 glDisable(GL_POLYGON_OFFSET_FILL);
\r
565 if (stencilShadow) {
\r
566 glDisable(GL_STENCIL_TEST);
\r
571 glDisable(GL_LIGHTING);
\r
572 glColor3f(1.0, 1.0, 0.0);
\r
573 if (directionalLight) {
\r
574 /* Draw an arrowhead. */
\r
575 glDisable(GL_CULL_FACE);
\r
576 glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
\r
577 glRotatef(lightAngle * -180.0 / M_PI, 0, 1, 0);
\r
578 glRotatef(atan(lightHeight/12) * 180.0 / M_PI, 0, 0, 1);
\r
579 glBegin(GL_TRIANGLE_FAN);
\r
580 glVertex3f(0, 0, 0);
\r
581 glVertex3f(2, 1, 1);
\r
582 glVertex3f(2, -1, 1);
\r
583 glVertex3f(2, -1, -1);
\r
584 glVertex3f(2, 1, -1);
\r
585 glVertex3f(2, 1, 1);
\r
587 /* Draw a white line from light direction. */
\r
588 glColor3f(1.0, 1.0, 1.0);
\r
590 glVertex3f(0, 0, 0);
\r
591 glVertex3f(5, 0, 0);
\r
593 glEnable(GL_CULL_FACE);
\r
595 /* Draw a yellow ball at the light source. */
\r
596 glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
\r
597 glutSolidSphere(1.0, 5, 5);
\r
599 glEnable(GL_LIGHTING);
\r
606 end = glutGet(GLUT_ELAPSED_TIME);
\r
607 printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
\r
615 mouse(int button, int state, int x, int y)
\r
617 if (button == GLUT_LEFT_BUTTON) {
\r
618 if (state == GLUT_DOWN) {
\r
623 if (state == GLUT_UP) {
\r
627 if (button == GLUT_MIDDLE_BUTTON) {
\r
628 if (state == GLUT_DOWN) {
\r
633 if (state == GLUT_UP) {
\r
641 motion(int x, int y)
\r
644 angle = angle + (x - startx);
\r
645 angle2 = angle2 + (y - starty);
\r
648 glutPostRedisplay();
\r
651 lightAngle += (x - lightStartX)/40.0;
\r
652 lightHeight += (lightStartY - y)/20.0;
\r
655 glutPostRedisplay();
\r
659 /* Advance time varying state when idle callback registered. */
\r
663 static float time = 0.0;
\r
665 time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
\r
667 jump = 4.0 * fabs(sin(time)*0.5);
\r
668 if (!lightMoving) {
\r
669 lightAngle += 0.03;
\r
671 glutPostRedisplay();
\r
675 M_NONE, M_MOTION, M_LIGHT, M_TEXTURE, M_SHADOWS, M_REFLECTION, M_DINOSAUR,
\r
676 M_STENCIL_REFLECTION, M_STENCIL_SHADOW, M_OFFSET_SHADOW,
\r
677 M_POSITIONAL, M_DIRECTIONAL, M_PERFORMANCE
\r
681 controlLights(int value)
\r
687 animation = 1 - animation;
\r
689 glutIdleFunc(idle);
\r
691 glutIdleFunc(NULL);
\r
695 lightSwitch = !lightSwitch;
\r
697 glEnable(GL_LIGHT0);
\r
699 glDisable(GL_LIGHT0);
\r
703 useTexture = !useTexture;
\r
706 renderShadow = 1 - renderShadow;
\r
709 renderReflection = 1 - renderReflection;
\r
712 renderDinosaur = 1 - renderDinosaur;
\r
714 case M_STENCIL_REFLECTION:
\r
715 stencilReflection = 1 - stencilReflection;
\r
717 case M_STENCIL_SHADOW:
\r
718 stencilShadow = 1 - stencilShadow;
\r
720 case M_OFFSET_SHADOW:
\r
721 offsetShadow = 1 - offsetShadow;
\r
724 directionalLight = 0;
\r
726 case M_DIRECTIONAL:
\r
727 directionalLight = 1;
\r
729 case M_PERFORMANCE:
\r
730 reportSpeed = 1 - reportSpeed;
\r
733 glutPostRedisplay();
\r
736 /* When not visible, stop animating. Restart when visible again. */
\r
740 if (vis == GLUT_VISIBLE) {
\r
742 glutIdleFunc(idle);
\r
745 glutIdleFunc(NULL);
\r
749 /* Press any key to redraw; good when motion stopped and
\r
750 performance reporting on. */
\r
753 key(unsigned char c, int x, int y)
\r
756 exit(0); /* IRIS GLism, Escape quits. */
\r
758 glutPostRedisplay();
\r
761 /* Press any key to redraw; good when motion stopped and
\r
762 performance reporting on. */
\r
765 special(int k, int x, int y)
\r
767 glutPostRedisplay();
\r
771 supportsOneDotOne(void)
\r
773 const char *version;
\r
776 version = (char *) glGetString(GL_VERSION);
\r
777 if (sscanf(version, "%d.%d", &major, &minor) == 2)
\r
778 return major >= 1 && minor >= 1;
\r
779 return 0; /* OpenGL version string malformed! */
\r
783 main(int argc, char **argv)
\r
787 glutInit(&argc, argv);
\r
789 for (i=1; i<argc; i++) {
\r
790 if (!strcmp("-linear", argv[i])) {
\r
791 linearFiltering = 1;
\r
792 } else if (!strcmp("-mipmap", argv[i])) {
\r
794 } else if (!strcmp("-ext", argv[i])) {
\r
795 forceExtension = 1;
\r
799 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL | GLUT_MULTISAMPLE);
\r
802 /* In GLUT 4.0, you'll be able to do this an be sure to
\r
803 get 2 bits of stencil if the machine has it for you. */
\r
804 glutInitDisplayString("samples stencil>=2 rgb double depth");
\r
807 glutCreateWindow("Shadowy Leapin' Lizards");
\r
809 if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
\r
810 printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
\r
814 /* Register GLUT callbacks. */
\r
815 glutDisplayFunc(redraw);
\r
816 glutMouseFunc(mouse);
\r
817 glutMotionFunc(motion);
\r
818 glutVisibilityFunc(visible);
\r
819 glutKeyboardFunc(key);
\r
820 glutSpecialFunc(special);
\r
822 glutCreateMenu(controlLights);
\r
824 glutAddMenuEntry("Toggle motion", M_MOTION);
\r
825 glutAddMenuEntry("-----------------------", M_NONE);
\r
826 glutAddMenuEntry("Toggle light", M_LIGHT);
\r
827 glutAddMenuEntry("Toggle texture", M_TEXTURE);
\r
828 glutAddMenuEntry("Toggle shadows", M_SHADOWS);
\r
829 glutAddMenuEntry("Toggle reflection", M_REFLECTION);
\r
830 glutAddMenuEntry("Toggle dinosaur", M_DINOSAUR);
\r
831 glutAddMenuEntry("-----------------------", M_NONE);
\r
832 glutAddMenuEntry("Toggle reflection stenciling", M_STENCIL_REFLECTION);
\r
833 glutAddMenuEntry("Toggle shadow stenciling", M_STENCIL_SHADOW);
\r
834 glutAddMenuEntry("Toggle shadow offset", M_OFFSET_SHADOW);
\r
835 glutAddMenuEntry("----------------------", M_NONE);
\r
836 glutAddMenuEntry("Positional light", M_POSITIONAL);
\r
837 glutAddMenuEntry("Directional light", M_DIRECTIONAL);
\r
838 glutAddMenuEntry("-----------------------", M_NONE);
\r
839 glutAddMenuEntry("Toggle performance", M_PERFORMANCE);
\r
840 glutAttachMenu(GLUT_RIGHT_BUTTON);
\r
843 #ifdef GL_VERSION_1_1
\r
844 if (supportsOneDotOne() && !forceExtension) {
\r
845 polygonOffsetVersion = ONE_DOT_ONE;
\r
846 glPolygonOffset(-2.0, -1.0);
\r
850 #ifdef GL_EXT_polygon_offset
\r
851 /* check for the polygon offset extension */
\r
852 if (glutExtensionSupported("GL_EXT_polygon_offset")) {
\r
853 polygonOffsetVersion = EXTENSION;
\r
854 glPolygonOffsetEXT(-0.1, -0.002);
\r
858 polygonOffsetVersion = MISSING;
\r
859 printf("\ndinoshine: Missing polygon offset.\n");
\r
860 printf(" Expect shadow depth aliasing artifacts.\n\n");
\r
864 glEnable(GL_CULL_FACE);
\r
865 glEnable(GL_DEPTH_TEST);
\r
866 glEnable(GL_TEXTURE_2D);
\r
869 glMatrixMode(GL_PROJECTION);
\r
870 gluPerspective( /* field of view in degree */ 40.0,
\r
871 /* aspect ratio */ 1.0,
\r
872 /* Z near */ 20.0, /* Z far */ 100.0);
\r
873 glMatrixMode(GL_MODELVIEW);
\r
874 gluLookAt(0.0, 8.0, 60.0, /* eye is at (0,8,60) */
\r
875 0.0, 8.0, 0.0, /* center is at (0,8,0) */
\r
876 0.0, 1.0, 0.); /* up is in postivie Y direction */
\r
878 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
\r
879 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
\r
880 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
\r
881 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
\r
882 glEnable(GL_LIGHT0);
\r
883 glEnable(GL_LIGHTING);
\r
885 makeFloorTexture();
\r
887 /* Setup floor plane for projected shadow calculations. */
\r
888 findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
\r
891 return 0; /* ANSI C requires main to return int. */
\r