~ubuntu-branches/ubuntu/quantal/mesa/quantal

« back to all changes in this revision

Viewing changes to progs/tests/dinoshade.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2007-02-21 12:44:07 UTC
  • mfrom: (1.2.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 22.
  • Revision ID: james.westby@ubuntu.com-20070221124407-rgcacs32mycrtadl
ImportĀ upstreamĀ versionĀ 6.5.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
/* Copyright (c) Mark J. Kilgard, 1994, 1997.  */
3
 
 
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. */
7
 
 
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. 
15
 
   
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.
19
 
   
20
 
   Notice that the floor texturing, reflections, and shadowing
21
 
   all co-exist properly. */
22
 
 
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. */
27
 
 
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. */
32
 
 
33
 
/* This program is derived from glutdino.c */
34
 
 
35
 
/* Compile: cc -o dinoshade dinoshade.c -lglut -lGLU -lGL -lXmu -lXext -lX11 -lm */
36
 
 
37
 
#include <stdio.h>
38
 
#include <stdlib.h>
39
 
#include <string.h>
40
 
#include <math.h>       /* for cos(), sin(), and sqrt() */
41
 
#ifdef __VMS
42
 
# include <stddef.h>    /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
43
 
#else
44
 
# include <malloc.h>    /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
45
 
#endif
46
 
#ifdef _WIN32
47
 
#include <windows.h>
48
 
#endif
49
 
#define GL_GLEXT_LEGACY
50
 
#include <GL/glut.h>    /* OpenGL Utility Toolkit header */
51
 
 
52
 
/* Some <math.h> files do not define M_PI... */
53
 
#ifndef M_PI
54
 
#define M_PI 3.14159265358979323846
55
 
#endif
56
 
 
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;
66
 
 
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 */
72
 
 
73
 
int moving, startx, starty;
74
 
int lightMoving = 0, lightStartX, lightStartY;
75
 
 
76
 
enum {
77
 
  MISSING, EXTENSION, ONE_DOT_ONE
78
 
};
79
 
int polygonOffsetVersion;
80
 
 
81
 
static GLdouble bodyWidth = 3.0;
82
 
/* *INDENT-OFF* */
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},
86
 
  {1, 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};
97
 
/* *INDENT-ON* */
98
 
 
99
 
/* Nice floor texture tiling pattern. */
100
 
static char *circles[] = {
101
 
  "....xxxx........",
102
 
  "..xxxxxxxx......",
103
 
  ".xxxxxxxxxx.....",
104
 
  ".xxx....xxx.....",
105
 
  "xxx......xxx....",
106
 
  "xxx......xxx....",
107
 
  "xxx......xxx....",
108
 
  "xxx......xxx....",
109
 
  ".xxx....xxx.....",
110
 
  ".xxxxxxxxxx.....",
111
 
  "..xxxxxxxx......",
112
 
  "....xxxx........",
113
 
  "................",
114
 
  "................",
115
 
  "................",
116
 
  "................",
117
 
};
118
 
 
119
 
static void
120
 
makeFloorTexture(void)
121
 
{
122
 
  GLubyte floorTexture[16][16][3];
123
 
  GLubyte *loc;
124
 
  int s, t;
125
 
 
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') {
131
 
        /* Nice green. */
132
 
        loc[0] = 0x1f;
133
 
        loc[1] = 0x8f;
134
 
        loc[2] = 0x1f;
135
 
      } else {
136
 
        /* Light gray. */
137
 
        loc[0] = 0xaa;
138
 
        loc[1] = 0xaa;
139
 
        loc[2] = 0xaa;
140
 
      }
141
 
      loc += 3;
142
 
    }
143
 
  }
144
 
 
145
 
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
146
 
 
147
 
  if (useMipmaps) {
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);
152
 
  } else {
153
 
    if (linearFiltering) {
154
 
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
155
 
    } else {
156
 
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
157
 
    }
158
 
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
159
 
      GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
160
 
  }
161
 
}
162
 
 
163
 
enum {
164
 
  X, Y, Z, W
165
 
};
166
 
enum {
167
 
  A, B, C, D
168
 
};
169
 
 
170
 
/* Create a matrix that will project the desired shadow. */
171
 
void
172
 
shadowMatrix(GLfloat shadowMat[4][4],
173
 
  GLfloat groundplane[4],
174
 
  GLfloat lightpos[4])
175
 
{
176
 
  GLfloat dot;
177
 
 
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];
183
 
 
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];
188
 
 
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];
193
 
 
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];
198
 
 
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];
203
 
 
204
 
}
205
 
 
206
 
/* Find the plane equation given 3 points. */
207
 
void
208
 
findPlane(GLfloat plane[4],
209
 
  GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
210
 
{
211
 
  GLfloat vec0[3], vec1[3];
212
 
 
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];
217
 
 
218
 
  vec1[X] = v2[X] - v0[X];
219
 
  vec1[Y] = v2[Y] - v0[Y];
220
 
  vec1[Z] = v2[Z] - v0[Z];
221
 
 
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];
226
 
 
227
 
  plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
228
 
}
229
 
 
230
 
void
231
 
extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
232
 
  GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
233
 
{
234
 
  static GLUtriangulatorObj *tobj = NULL;
235
 
  GLdouble vertex[3], dx, dy, len;
236
 
  int i;
237
 
  int count = (int) (dataSize / (2 * sizeof(GLfloat)));
238
 
 
239
 
  if (tobj == NULL) {
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);
245
 
  }
246
 
  glNewList(side, GL_COMPILE);
247
 
  glShadeModel(GL_SMOOTH);  /* smooth minimizes seeing
248
 
                               tessellation */
249
 
  gluBeginPolygon(tobj);
250
 
  for (i = 0; i < count; i++) {
251
 
    vertex[0] = data[i][0];
252
 
    vertex[1] = data[i][1];
253
 
    vertex[2] = 0;
254
 
    gluTessVertex(tobj, vertex, data[i]);
255
 
  }
256
 
  gluEndPolygon(tobj);
257
 
  glEndList();
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);
287
 
#endif
288
 
  }
289
 
  glEnd();
290
 
  glEndList();
291
 
  glNewList(whole, GL_COMPILE);
292
 
  glFrontFace(GL_CW);
293
 
  glCallList(edge);
294
 
  glNormal3f(0.0, 0.0, -1.0);  /* constant normal for side */
295
 
  glCallList(side);
296
 
  glPushMatrix();
297
 
  glTranslatef(0.0, 0.0, thickness);
298
 
  glFrontFace(GL_CCW);
299
 
  glNormal3f(0.0, 0.0, 1.0);  /* opposite normal for other side */
300
 
  glCallList(side);
301
 
  glPopMatrix();
302
 
  glEndList();
303
 
}
304
 
 
305
 
/* Enumerants for refering to display lists. */
306
 
typedef enum {
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
309
 
} displayLists;
310
 
 
311
 
static void
312
 
makeDinosaur(void)
313
 
{
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);
322
 
}
323
 
 
324
 
static void
325
 
drawDinosaur(void)
326
 
 
327
 
{
328
 
  glPushMatrix();
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);
344
 
  glPopMatrix();
345
 
}
346
 
 
347
 
static GLfloat floorVertices[4][3] = {
348
 
  { -20.0, 0.0, 20.0 },
349
 
  { 20.0, 0.0, 20.0 },
350
 
  { 20.0, 0.0, -20.0 },
351
 
  { -20.0, 0.0, -20.0 },
352
 
};
353
 
 
354
 
/* Draw a floor (possibly textured). */
355
 
static void
356
 
drawFloor(void)
357
 
{
358
 
  glDisable(GL_LIGHTING);
359
 
 
360
 
  if (useTexture) {
361
 
    glEnable(GL_TEXTURE_2D);
362
 
  }
363
 
 
364
 
  glBegin(GL_QUADS);
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]);
373
 
  glEnd();
374
 
 
375
 
  if (useTexture) {
376
 
    glDisable(GL_TEXTURE_2D);
377
 
  }
378
 
 
379
 
  glEnable(GL_LIGHTING);
380
 
}
381
 
 
382
 
static GLfloat floorPlane[4];
383
 
static GLfloat floorShadow[4][4];
384
 
 
385
 
static void
386
 
redraw(void)
387
 
{
388
 
  int start, end;
389
 
 
390
 
  if (reportSpeed) {
391
 
    start = glutGet(GLUT_ELAPSED_TIME);
392
 
  }
393
 
 
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);
397
 
  } else {
398
 
    /* Avoid clearing stencil when not using it. */
399
 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
400
 
  }
401
 
 
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;
408
 
  } else {
409
 
    lightPosition[3] = 1.0;
410
 
  }
411
 
 
412
 
  shadowMatrix(floorShadow, floorPlane, lightPosition);
413
 
 
414
 
  glPushMatrix();
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);
418
 
     
419
 
    /* Tell GL new light source position. */
420
 
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
421
 
 
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. */
431
 
 
432
 
        /* Don't update color or depth. */
433
 
        glDisable(GL_DEPTH_TEST);
434
 
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
435
 
 
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);
440
 
 
441
 
        /* Now render floor; floor pixels just get their stencil set to 1. */
442
 
        drawFloor();
443
 
 
444
 
        /* Re-enable update of color and depth. */ 
445
 
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
446
 
        glEnable(GL_DEPTH_TEST);
447
 
 
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);
451
 
      }
452
 
 
453
 
      glPushMatrix();
454
 
 
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);
458
 
 
459
 
        /* Reflect the light position. */
460
 
        glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
461
 
 
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);
466
 
 
467
 
        /* Draw the reflected dinosaur. */
468
 
        drawDinosaur();
469
 
 
470
 
        /* Disable noramlize again and re-enable back face culling. */
471
 
        glDisable(GL_NORMALIZE);
472
 
        glCullFace(GL_BACK);
473
 
 
474
 
      glPopMatrix();
475
 
 
476
 
      /* Switch back to the unreflected light position. */
477
 
      glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
478
 
 
479
 
      if (stencilReflection) {
480
 
        glDisable(GL_STENCIL_TEST);
481
 
      }
482
 
    }
483
 
 
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. */
488
 
 
489
 
    /* Draw "bottom" of floor in blue. */
490
 
    glFrontFace(GL_CW);  /* Switch face orientation. */
491
 
    glColor4f(0.1, 0.1, 0.7, 1.0);
492
 
    drawFloor();
493
 
    glFrontFace(GL_CCW);
494
 
 
495
 
    if (renderShadow) {
496
 
      if (stencilShadow) {
497
 
        /* Draw the floor with stencil value 3.  This helps us only 
498
 
           draw the shadow once per floor pixel (and only on the
499
 
           floor pixels). */
500
 
        glEnable(GL_STENCIL_TEST);
501
 
        glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
502
 
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
503
 
      }
504
 
    }
505
 
 
506
 
    /* Draw "top" of floor.  Use blending to blend in reflection. */
507
 
    glEnable(GL_BLEND);
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);
511
 
    drawFloor();
512
 
    glDisable(GL_BLEND);
513
 
 
514
 
    if (renderDinosaur) {
515
 
      /* Draw "actual" dinosaur, not its reflection. */
516
 
      drawDinosaur();
517
 
    }
518
 
 
519
 
    if (renderShadow) {
520
 
 
521
 
      /* Render the projected shadow. */
522
 
 
523
 
      if (stencilShadow) {
524
 
 
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
528
 
           shadow). */
529
 
        glStencilFunc(GL_LESS, 2, 0xffffffff);  /* draw if ==1 */
530
 
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
531
 
      }
532
 
 
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. */
536
 
      if (offsetShadow) {
537
 
        switch (polygonOffsetVersion) {
538
 
        case EXTENSION:
539
 
#ifdef GL_EXT_polygon_offset
540
 
          glEnable(GL_POLYGON_OFFSET_EXT);
541
 
          break;
542
 
#endif
543
 
#ifdef GL_VERSION_1_1
544
 
        case ONE_DOT_ONE:
545
 
          glEnable(GL_POLYGON_OFFSET_FILL);
546
 
          break;
547
 
#endif
548
 
        case MISSING:
549
 
          /* Oh well. */
550
 
          break;
551
 
        }
552
 
      }
553
 
 
554
 
      /* Render 50% black shadow color on top of whatever the
555
 
         floor appareance is. */
556
 
      glEnable(GL_BLEND);
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);
560
 
 
561
 
      glPushMatrix();
562
 
        /* Project the shadow. */
563
 
        glMultMatrixf((GLfloat *) floorShadow);
564
 
        drawDinosaur();
565
 
      glPopMatrix();
566
 
 
567
 
      glDisable(GL_BLEND);
568
 
      glEnable(GL_LIGHTING);
569
 
 
570
 
      if (offsetShadow) {
571
 
        switch (polygonOffsetVersion) {
572
 
#ifdef GL_EXT_polygon_offset
573
 
        case EXTENSION:
574
 
          glDisable(GL_POLYGON_OFFSET_EXT);
575
 
          break;
576
 
#endif
577
 
#ifdef GL_VERSION_1_1
578
 
        case ONE_DOT_ONE:
579
 
          glDisable(GL_POLYGON_OFFSET_FILL);
580
 
          break;
581
 
#endif
582
 
        case MISSING:
583
 
          /* Oh well. */
584
 
          break;
585
 
        }
586
 
      }
587
 
      if (stencilShadow) {
588
 
        glDisable(GL_STENCIL_TEST);
589
 
      }
590
 
    }
591
 
 
592
 
    glPushMatrix();
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);
602
 
        glVertex3f(0, 0, 0);
603
 
        glVertex3f(2, 1, 1);
604
 
        glVertex3f(2, -1, 1);
605
 
        glVertex3f(2, -1, -1);
606
 
        glVertex3f(2, 1, -1);
607
 
        glVertex3f(2, 1, 1);
608
 
      glEnd();
609
 
      /* Draw a white line from light direction. */
610
 
      glColor3f(1.0, 1.0, 1.0);
611
 
      glBegin(GL_LINES);
612
 
        glVertex3f(0, 0, 0);
613
 
        glVertex3f(5, 0, 0);
614
 
      glEnd();
615
 
      glEnable(GL_CULL_FACE);
616
 
    } else {
617
 
      /* Draw a yellow ball at the light source. */
618
 
      glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
619
 
      glutSolidSphere(1.0, 5, 5);
620
 
    }
621
 
    glEnable(GL_LIGHTING);
622
 
    glPopMatrix();
623
 
 
624
 
  glPopMatrix();
625
 
 
626
 
  if (reportSpeed) {
627
 
    glFinish();
628
 
    end = glutGet(GLUT_ELAPSED_TIME);
629
 
    printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
630
 
  }
631
 
 
632
 
  glutSwapBuffers();
633
 
}
634
 
 
635
 
/* ARGSUSED2 */
636
 
static void
637
 
mouse(int button, int state, int x, int y)
638
 
{
639
 
  if (button == GLUT_LEFT_BUTTON) {
640
 
    if (state == GLUT_DOWN) {
641
 
      moving = 1;
642
 
      startx = x;
643
 
      starty = y;
644
 
    }
645
 
    if (state == GLUT_UP) {
646
 
      moving = 0;
647
 
    }
648
 
  }
649
 
  if (button == GLUT_MIDDLE_BUTTON) {
650
 
    if (state == GLUT_DOWN) {
651
 
      lightMoving = 1;
652
 
      lightStartX = x;
653
 
      lightStartY = y;
654
 
    }
655
 
    if (state == GLUT_UP) {
656
 
      lightMoving = 0;
657
 
    }
658
 
  }
659
 
}
660
 
 
661
 
/* ARGSUSED1 */
662
 
static void
663
 
motion(int x, int y)
664
 
{
665
 
  if (moving) {
666
 
    angle = angle + (x - startx);
667
 
    angle2 = angle2 + (y - starty);
668
 
    startx = x;
669
 
    starty = y;
670
 
    glutPostRedisplay();
671
 
  }
672
 
  if (lightMoving) {
673
 
    lightAngle += (x - lightStartX)/40.0;
674
 
    lightHeight += (lightStartY - y)/20.0;
675
 
    lightStartX = x;
676
 
    lightStartY = y;
677
 
    glutPostRedisplay();
678
 
  }
679
 
}
680
 
 
681
 
/* Advance time varying state when idle callback registered. */
682
 
static void
683
 
idle(void)
684
 
{
685
 
  static float time = 0.0;
686
 
 
687
 
  time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
688
 
 
689
 
  jump = 4.0 * fabs(sin(time)*0.5);
690
 
  if (!lightMoving) {
691
 
    lightAngle += 0.03;
692
 
  }
693
 
  glutPostRedisplay();
694
 
}
695
 
 
696
 
enum {
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
700
 
};
701
 
 
702
 
static void
703
 
controlLights(int value)
704
 
{
705
 
  switch (value) {
706
 
  case M_NONE:
707
 
    return;
708
 
  case M_MOTION:
709
 
    animation = 1 - animation;
710
 
    if (animation) {
711
 
      glutIdleFunc(idle);
712
 
    } else {
713
 
      glutIdleFunc(NULL);
714
 
    }
715
 
    break;
716
 
  case M_LIGHT:
717
 
    lightSwitch = !lightSwitch;
718
 
    if (lightSwitch) {
719
 
      glEnable(GL_LIGHT0);
720
 
    } else {
721
 
      glDisable(GL_LIGHT0);
722
 
    }
723
 
    break;
724
 
  case M_TEXTURE:
725
 
    useTexture = !useTexture;
726
 
    break;
727
 
  case M_SHADOWS:
728
 
    renderShadow = 1 - renderShadow;
729
 
    break;
730
 
  case M_REFLECTION:
731
 
    renderReflection = 1 - renderReflection;
732
 
    break;
733
 
  case M_DINOSAUR:
734
 
    renderDinosaur = 1 - renderDinosaur;
735
 
    break;
736
 
  case M_STENCIL_REFLECTION:
737
 
    stencilReflection = 1 - stencilReflection;
738
 
    break;
739
 
  case M_STENCIL_SHADOW:
740
 
    stencilShadow = 1 - stencilShadow;
741
 
    break;
742
 
  case M_OFFSET_SHADOW:
743
 
    offsetShadow = 1 - offsetShadow;
744
 
    break;
745
 
  case M_POSITIONAL:
746
 
    directionalLight = 0;
747
 
    break;
748
 
  case M_DIRECTIONAL:
749
 
    directionalLight = 1;
750
 
    break;
751
 
  case M_PERFORMANCE:
752
 
    reportSpeed = 1 - reportSpeed;
753
 
    break;
754
 
  }
755
 
  glutPostRedisplay();
756
 
}
757
 
 
758
 
/* When not visible, stop animating.  Restart when visible again. */
759
 
static void 
760
 
visible(int vis)
761
 
{
762
 
  if (vis == GLUT_VISIBLE) {
763
 
    if (animation)
764
 
      glutIdleFunc(idle);
765
 
  } else {
766
 
    if (!animation)
767
 
      glutIdleFunc(NULL);
768
 
  }
769
 
}
770
 
 
771
 
/* Press any key to redraw; good when motion stopped and
772
 
   performance reporting on. */
773
 
/* ARGSUSED */
774
 
static void
775
 
key(unsigned char c, int x, int y)
776
 
{
777
 
  if (c == 27) {
778
 
    exit(0);  /* IRIS GLism, Escape quits. */
779
 
  }
780
 
  glutPostRedisplay();
781
 
}
782
 
 
783
 
/* Press any key to redraw; good when motion stopped and
784
 
   performance reporting on. */
785
 
/* ARGSUSED */
786
 
static void
787
 
special(int k, int x, int y)
788
 
{
789
 
  glutPostRedisplay();
790
 
}
791
 
 
792
 
static int
793
 
supportsOneDotOne(void)
794
 
{
795
 
  const char *version;
796
 
  int major, minor;
797
 
 
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! */
802
 
}
803
 
 
804
 
int
805
 
main(int argc, char **argv)
806
 
{
807
 
  int i;
808
 
 
809
 
  glutInit(&argc, argv);
810
 
 
811
 
  for (i=1; i<argc; i++) {
812
 
    if (!strcmp("-linear", argv[i])) {
813
 
      linearFiltering = 1;
814
 
    } else if (!strcmp("-mipmap", argv[i])) {
815
 
      useMipmaps = 1;
816
 
    } else if (!strcmp("-ext", argv[i])) {
817
 
      forceExtension = 1;
818
 
    }
819
 
  }
820
 
 
821
 
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
822
 
 
823
 
#if 0
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");
827
 
#endif
828
 
 
829
 
  glutCreateWindow("Shadowy Leapin' Lizards");
830
 
 
831
 
  if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
832
 
    printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
833
 
    exit(1);
834
 
  }
835
 
 
836
 
  /* Register GLUT callbacks. */
837
 
  glutDisplayFunc(redraw);
838
 
  glutMouseFunc(mouse);
839
 
  glutMotionFunc(motion);
840
 
  glutVisibilityFunc(visible);
841
 
  glutKeyboardFunc(key);
842
 
  glutSpecialFunc(special);
843
 
 
844
 
  glutCreateMenu(controlLights);
845
 
 
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);
863
 
  makeDinosaur();
864
 
 
865
 
#ifdef GL_VERSION_1_1
866
 
  if (supportsOneDotOne() && !forceExtension) {
867
 
    polygonOffsetVersion = ONE_DOT_ONE;
868
 
    glPolygonOffset(-2.0, -9.0);
869
 
  } else
870
 
#endif
871
 
  {
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);
877
 
  } else 
878
 
#endif
879
 
    {
880
 
      polygonOffsetVersion = MISSING;
881
 
      printf("\ndinoshine: Missing polygon offset.\n");
882
 
      printf("           Expect shadow depth aliasing artifacts.\n\n");
883
 
    }
884
 
  }
885
 
 
886
 
  glEnable(GL_CULL_FACE);
887
 
  glEnable(GL_DEPTH_TEST);
888
 
  glEnable(GL_TEXTURE_2D);
889
 
  glLineWidth(3.0);
890
 
 
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 */
899
 
 
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);
904
 
  glEnable(GL_LIGHT0);
905
 
  glEnable(GL_LIGHTING);
906
 
 
907
 
  makeFloorTexture();
908
 
 
909
 
  /* Setup floor plane for projected shadow calculations. */
910
 
  findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
911
 
 
912
 
  glutMainLoop();
913
 
  return 0;             /* ANSI C requires main to return int. */
914
 
}