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() */
42
# include <stddef.h> /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
44
# include <malloc.h> /* for ptrdiff_t, referenced by GL.h when GL_GLEXT_LEGACY defined */
49
#define GL_GLEXT_LEGACY
50
#include <GL/glut.h> /* OpenGL Utility Toolkit header */
52
/* Some <math.h> files do not define M_PI... */
54
#define M_PI 3.14159265358979323846
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;
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 */
73
int moving, startx, starty;
74
int lightMoving = 0, lightStartX, lightStartY;
77
MISSING, EXTENSION, ONE_DOT_ONE
79
int polygonOffsetVersion;
81
static GLdouble bodyWidth = 3.0;
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},
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};
99
/* Nice floor texture tiling pattern. */
100
static char *circles[] = {
120
makeFloorTexture(void)
122
GLubyte floorTexture[16][16][3];
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') {
145
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
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);
153
if (linearFiltering) {
154
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
156
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
158
glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
159
GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
170
/* Create a matrix that will project the desired shadow. */
172
shadowMatrix(GLfloat shadowMat[4][4],
173
GLfloat groundplane[4],
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];
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];
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];
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];
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];
206
/* Find the plane equation given 3 points. */
208
findPlane(GLfloat plane[4],
209
GLfloat v0[3], GLfloat v1[3], GLfloat v2[3])
211
GLfloat vec0[3], vec1[3];
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];
218
vec1[X] = v2[X] - v0[X];
219
vec1[Y] = v2[Y] - v0[Y];
220
vec1[Z] = v2[Z] - v0[Z];
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];
227
plane[D] = -(plane[A] * v0[X] + plane[B] * v0[Y] + plane[C] * v0[Z]);
231
extrudeSolidFromPolygon(GLfloat data[][2], unsigned int dataSize,
232
GLdouble thickness, GLuint side, GLuint edge, GLuint whole)
234
static GLUtriangulatorObj *tobj = NULL;
235
GLdouble vertex[3], dx, dy, len;
237
int count = (int) (dataSize / (2 * sizeof(GLfloat)));
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);
246
glNewList(side, GL_COMPILE);
247
glShadeModel(GL_SMOOTH); /* smooth minimizes seeing
249
gluBeginPolygon(tobj);
250
for (i = 0; i < count; i++) {
251
vertex[0] = data[i][0];
252
vertex[1] = data[i][1];
254
gluTessVertex(tobj, vertex, data[i]);
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);
291
glNewList(whole, GL_COMPILE);
294
glNormal3f(0.0, 0.0, -1.0); /* constant normal for side */
297
glTranslatef(0.0, 0.0, thickness);
299
glNormal3f(0.0, 0.0, 1.0); /* opposite normal for other side */
305
/* Enumerants for refering to display lists. */
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
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);
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);
347
static GLfloat floorVertices[4][3] = {
348
{ -20.0, 0.0, 20.0 },
350
{ 20.0, 0.0, -20.0 },
351
{ -20.0, 0.0, -20.0 },
354
/* Draw a floor (possibly textured). */
358
glDisable(GL_LIGHTING);
361
glEnable(GL_TEXTURE_2D);
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]);
376
glDisable(GL_TEXTURE_2D);
379
glEnable(GL_LIGHTING);
382
static GLfloat floorPlane[4];
383
static GLfloat floorShadow[4][4];
391
start = glutGet(GLUT_ELAPSED_TIME);
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);
398
/* Avoid clearing stencil when not using it. */
399
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
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;
409
lightPosition[3] = 1.0;
412
shadowMatrix(floorShadow, floorPlane, lightPosition);
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);
419
/* Tell GL new light source position. */
420
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
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. */
432
/* Don't update color or depth. */
433
glDisable(GL_DEPTH_TEST);
434
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
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);
441
/* Now render floor; floor pixels just get their stencil set to 1. */
444
/* Re-enable update of color and depth. */
445
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
446
glEnable(GL_DEPTH_TEST);
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);
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);
459
/* Reflect the light position. */
460
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
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);
467
/* Draw the reflected dinosaur. */
470
/* Disable noramlize again and re-enable back face culling. */
471
glDisable(GL_NORMALIZE);
476
/* Switch back to the unreflected light position. */
477
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
479
if (stencilReflection) {
480
glDisable(GL_STENCIL_TEST);
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. */
489
/* Draw "bottom" of floor in blue. */
490
glFrontFace(GL_CW); /* Switch face orientation. */
491
glColor4f(0.1, 0.1, 0.7, 1.0);
497
/* Draw the floor with stencil value 3. This helps us only
498
draw the shadow once per floor pixel (and only on the
500
glEnable(GL_STENCIL_TEST);
501
glStencilFunc(GL_ALWAYS, 3, 0xffffffff);
502
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
506
/* Draw "top" of floor. Use blending to blend in reflection. */
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);
514
if (renderDinosaur) {
515
/* Draw "actual" dinosaur, not its reflection. */
521
/* Render the projected shadow. */
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
529
glStencilFunc(GL_LESS, 2, 0xffffffff); /* draw if ==1 */
530
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
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. */
537
switch (polygonOffsetVersion) {
539
#ifdef GL_EXT_polygon_offset
540
glEnable(GL_POLYGON_OFFSET_EXT);
543
#ifdef GL_VERSION_1_1
545
glEnable(GL_POLYGON_OFFSET_FILL);
554
/* Render 50% black shadow color on top of whatever the
555
floor appareance is. */
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);
562
/* Project the shadow. */
563
glMultMatrixf((GLfloat *) floorShadow);
568
glEnable(GL_LIGHTING);
571
switch (polygonOffsetVersion) {
572
#ifdef GL_EXT_polygon_offset
574
glDisable(GL_POLYGON_OFFSET_EXT);
577
#ifdef GL_VERSION_1_1
579
glDisable(GL_POLYGON_OFFSET_FILL);
588
glDisable(GL_STENCIL_TEST);
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);
604
glVertex3f(2, -1, 1);
605
glVertex3f(2, -1, -1);
606
glVertex3f(2, 1, -1);
609
/* Draw a white line from light direction. */
610
glColor3f(1.0, 1.0, 1.0);
615
glEnable(GL_CULL_FACE);
617
/* Draw a yellow ball at the light source. */
618
glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
619
glutSolidSphere(1.0, 5, 5);
621
glEnable(GL_LIGHTING);
628
end = glutGet(GLUT_ELAPSED_TIME);
629
printf("Speed %.3g frames/sec (%d ms)\n", 1000.0/(end-start), end-start);
637
mouse(int button, int state, int x, int y)
639
if (button == GLUT_LEFT_BUTTON) {
640
if (state == GLUT_DOWN) {
645
if (state == GLUT_UP) {
649
if (button == GLUT_MIDDLE_BUTTON) {
650
if (state == GLUT_DOWN) {
655
if (state == GLUT_UP) {
666
angle = angle + (x - startx);
667
angle2 = angle2 + (y - starty);
673
lightAngle += (x - lightStartX)/40.0;
674
lightHeight += (lightStartY - y)/20.0;
681
/* Advance time varying state when idle callback registered. */
685
static float time = 0.0;
687
time = glutGet(GLUT_ELAPSED_TIME) / 500.0;
689
jump = 4.0 * fabs(sin(time)*0.5);
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
703
controlLights(int value)
709
animation = 1 - animation;
717
lightSwitch = !lightSwitch;
721
glDisable(GL_LIGHT0);
725
useTexture = !useTexture;
728
renderShadow = 1 - renderShadow;
731
renderReflection = 1 - renderReflection;
734
renderDinosaur = 1 - renderDinosaur;
736
case M_STENCIL_REFLECTION:
737
stencilReflection = 1 - stencilReflection;
739
case M_STENCIL_SHADOW:
740
stencilShadow = 1 - stencilShadow;
742
case M_OFFSET_SHADOW:
743
offsetShadow = 1 - offsetShadow;
746
directionalLight = 0;
749
directionalLight = 1;
752
reportSpeed = 1 - reportSpeed;
758
/* When not visible, stop animating. Restart when visible again. */
762
if (vis == GLUT_VISIBLE) {
771
/* Press any key to redraw; good when motion stopped and
772
performance reporting on. */
775
key(unsigned char c, int x, int y)
778
exit(0); /* IRIS GLism, Escape quits. */
783
/* Press any key to redraw; good when motion stopped and
784
performance reporting on. */
787
special(int k, int x, int y)
793
supportsOneDotOne(void)
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! */
805
main(int argc, char **argv)
809
glutInit(&argc, argv);
811
for (i=1; i<argc; i++) {
812
if (!strcmp("-linear", argv[i])) {
814
} else if (!strcmp("-mipmap", argv[i])) {
816
} else if (!strcmp("-ext", argv[i])) {
821
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
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");
829
glutCreateWindow("Shadowy Leapin' Lizards");
831
if (glutGet(GLUT_WINDOW_STENCIL_SIZE) <= 1) {
832
printf("dinoshade: Sorry, I need at least 2 bits of stencil.\n");
836
/* Register GLUT callbacks. */
837
glutDisplayFunc(redraw);
838
glutMouseFunc(mouse);
839
glutMotionFunc(motion);
840
glutVisibilityFunc(visible);
841
glutKeyboardFunc(key);
842
glutSpecialFunc(special);
844
glutCreateMenu(controlLights);
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);
865
#ifdef GL_VERSION_1_1
866
if (supportsOneDotOne() && !forceExtension) {
867
polygonOffsetVersion = ONE_DOT_ONE;
868
glPolygonOffset(-2.0, -9.0);
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);
880
polygonOffsetVersion = MISSING;
881
printf("\ndinoshine: Missing polygon offset.\n");
882
printf(" Expect shadow depth aliasing artifacts.\n\n");
886
glEnable(GL_CULL_FACE);
887
glEnable(GL_DEPTH_TEST);
888
glEnable(GL_TEXTURE_2D);
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 */
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);
905
glEnable(GL_LIGHTING);
909
/* Setup floor plane for projected shadow calculations. */
910
findPlane(floorPlane, floorVertices[1], floorVertices[2], floorVertices[3]);
913
return 0; /* ANSI C requires main to return int. */