s/DP4/DP3/
[mesa.git] / progs / demos / shadowtex.c
1 /*
2 * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and
3 * GL_ARB_shadow_ambient extensions (or the old SGIX extensions).
4 *
5 * Brian Paul
6 * 19 Feb 2001
7 *
8 * Added GL_EXT_shadow_funcs support on 23 March 2002
9 *
10 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <math.h>
35 #include <GL/glut.h>
36 #include "../util/showbuffer.c"
37
38 #if 0 /* change to 1 if you want to use the old SGIX extensions */
39 #undef GL_ARB_depth_texture
40 #undef GL_ARB_shadow
41 #undef GL_ARB_shadow_ambient
42 #endif
43
44
45 #define DEG_TO_RAD (3.14159 / 180.0)
46
47 static GLint WindowWidth = 450, WindowHeight = 300;
48 static GLfloat Xrot = 15, Yrot = 0, Zrot = 0;
49
50 static GLfloat Red[4] = {1, 0, 0, 1};
51 static GLfloat Green[4] = {0, 1, 0, 1};
52 static GLfloat Blue[4] = {0, 0, 1, 1};
53 static GLfloat Yellow[4] = {1, 1, 0, 1};
54
55 static GLfloat LightDist = 10;
56 static GLfloat LightLatitude = 45.0;
57 static GLfloat LightLongitude = 45.0;
58 static GLfloat LightPos[4];
59 static GLfloat SpotDir[3];
60 static GLfloat SpotAngle = 40.0 * DEG_TO_RAD;
61 static GLfloat ShadowNear = 4.0, ShadowFar = 24.0;
62 static GLint ShadowTexWidth = 256, ShadowTexHeight = 256;
63
64 static GLboolean LinearFilter = GL_FALSE;
65
66 static GLfloat Bias = -0.06;
67
68 static GLboolean Anim = GL_TRUE;
69
70 static GLboolean HaveEXTshadowFuncs = GL_FALSE;
71 static GLint Operator = 0;
72 static const GLenum OperatorFunc[8] = {
73 GL_LEQUAL, GL_LESS, GL_GEQUAL, GL_GREATER,
74 GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER };
75 static const char *OperatorName[8] = {
76 "GL_LEQUAL", "GL_LESS", "GL_GEQUAL", "GL_GREATER",
77 "GL_EQUAL", "GL_NOTEQUAL", "GL_ALWAYS", "GL_NEVER" };
78
79
80 static GLuint DisplayMode;
81 #define SHOW_NORMAL 0
82 #define SHOW_DEPTH_IMAGE 1
83 #define SHOW_DEPTH_MAPPING 2
84 #define SHOW_DISTANCE 3
85
86
87
88 static void
89 DrawScene(void)
90 {
91 GLfloat k = 6;
92 /* sphere */
93 glPushMatrix();
94 glTranslatef(1.6, 2.2, 2.7);
95 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Green);
96 glColor4fv(Green);
97 glutSolidSphere(1.5, 15, 15);
98 glPopMatrix();
99 /* dodecahedron */
100 glPushMatrix();
101 glTranslatef(-2.0, 1.2, 2.1);
102 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Red);
103 glColor4fv(Red);
104 glutSolidDodecahedron();
105 glPopMatrix();
106 /* icosahedron */
107 glPushMatrix();
108 glTranslatef(-0.6, 1.3, -0.5);
109 glScalef(1.5, 1.5, 1.5);
110 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Yellow);
111 glColor4fv(Red);
112 glutSolidIcosahedron();
113 glPopMatrix();
114 /* a plane */
115 glPushMatrix();
116 glTranslatef(0, -1.1, 0);
117 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blue);
118 glColor4fv(Blue);
119 glNormal3f(0, 1, 0);
120 glBegin(GL_POLYGON);
121 glVertex3f(-k, 0, -k);
122 glVertex3f( k, 0, -k);
123 glVertex3f( k, 0, k);
124 glVertex3f(-k, 0, k);
125 glEnd();
126 glPopMatrix();
127 }
128
129
130 /*
131 * Load the GL_TEXTURE matrix with the projection from the light
132 * source's point of view.
133 */
134 static void
135 MakeShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3],
136 GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar)
137 {
138 GLfloat d;
139
140 glMatrixMode(GL_TEXTURE);
141 glLoadIdentity();
142 glTranslatef(0.5, 0.5, 0.5 + Bias);
143 glScalef(0.5, 0.5, 0.5);
144 d = shadowNear * tan(spotAngle);
145 glFrustum(-d, d, -d, d, shadowNear, shadowFar);
146 gluLookAt(lightPos[0], lightPos[1], lightPos[2],
147 lightPos[0] + spotDir[0],
148 lightPos[1] + spotDir[1],
149 lightPos[2] + spotDir[2],
150 0, 1, 0);
151 glMatrixMode(GL_MODELVIEW);
152 }
153
154
155 static void
156 EnableIdentityTexgen(void)
157 {
158 /* texgen so that texcoord = vertex coord */
159 static GLfloat sPlane[4] = { 1, 0, 0, 0 };
160 static GLfloat tPlane[4] = { 0, 1, 0, 0 };
161 static GLfloat rPlane[4] = { 0, 0, 1, 0 };
162 static GLfloat qPlane[4] = { 0, 0, 0, 1 };
163
164 glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
165 glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
166 glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
167 glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane);
168 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
169 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
170 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
171 glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
172
173 glEnable(GL_TEXTURE_GEN_S);
174 glEnable(GL_TEXTURE_GEN_T);
175 glEnable(GL_TEXTURE_GEN_R);
176 glEnable(GL_TEXTURE_GEN_Q);
177 }
178
179
180 /*
181 * Setup 1-D texgen so that the distance from the light source, between
182 * the near and far planes maps to s=0 and s=1. When we draw the scene,
183 * the grayness will indicate the fragment's distance from the light
184 * source.
185 */
186 static void
187 EnableDistanceTexgen(const GLfloat lightPos[4], const GLfloat lightDir[3],
188 GLfloat lightNear, GLfloat lightFar)
189 {
190 GLfloat m, d;
191 GLfloat sPlane[4];
192 GLfloat nearPoint[3];
193
194 m = sqrt(lightDir[0] * lightDir[0] +
195 lightDir[1] * lightDir[1] +
196 lightDir[2] * lightDir[2]);
197
198 d = lightFar - lightNear;
199
200 /* nearPoint = point on light direction vector which intersects the
201 * near plane of the light frustum.
202 */
203 nearPoint[0] = LightPos[0] + lightDir[0] / m * lightNear;
204 nearPoint[1] = LightPos[1] + lightDir[1] / m * lightNear;
205 nearPoint[2] = LightPos[2] + lightDir[2] / m * lightNear;
206
207 sPlane[0] = lightDir[0] / d / m;
208 sPlane[1] = lightDir[1] / d / m;
209 sPlane[2] = lightDir[2] / d / m;
210 sPlane[3] = -(sPlane[0] * nearPoint[0]
211 + sPlane[1] * nearPoint[1]
212 + sPlane[2] * nearPoint[2]);
213
214 glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
215 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
216 glEnable(GL_TEXTURE_GEN_S);
217 }
218
219
220 static void
221 DisableTexgen(void)
222 {
223 glDisable(GL_TEXTURE_GEN_S);
224 glDisable(GL_TEXTURE_GEN_T);
225 glDisable(GL_TEXTURE_GEN_R);
226 glDisable(GL_TEXTURE_GEN_Q);
227 }
228
229
230 static void
231 ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude,
232 GLfloat pos[4], GLfloat dir[3])
233 {
234
235 pos[0] = dist * sin(longitude * DEG_TO_RAD);
236 pos[1] = dist * sin(latitude * DEG_TO_RAD);
237 pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD);
238 pos[3] = 1;
239 dir[0] = -pos[0];
240 dir[1] = -pos[1];
241 dir[2] = -pos[2];
242 }
243
244
245 static void
246 Display(void)
247 {
248 GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight;
249 GLfloat d;
250 GLenum error;
251
252 ComputeLightPos(LightDist, LightLatitude, LightLongitude,
253 LightPos, SpotDir);
254 /*
255 * Step 1: render scene from point of view of the light source
256 */
257 /* compute frustum to enclose spot light cone */
258 d = ShadowNear * tan(SpotAngle);
259 glMatrixMode(GL_PROJECTION);
260 glLoadIdentity();
261 glFrustum(-d, d, -d, d, ShadowNear, ShadowFar);
262 glMatrixMode(GL_MODELVIEW);
263 glLoadIdentity();
264 gluLookAt(LightPos[0], LightPos[1], LightPos[2], /* from */
265 0, 0, 0, /* target */
266 0, 1, 0); /* up */
267
268 glViewport(0, 0, ShadowTexWidth, ShadowTexHeight);
269 glClear(GL_DEPTH_BUFFER_BIT);
270 DrawScene();
271
272 /*
273 * Step 2: copy depth buffer into texture map
274 */
275 if (DisplayMode == SHOW_DEPTH_MAPPING) {
276 /* load depth image as gray-scale luminance texture */
277 GLfloat *depth = (GLfloat *) malloc(ShadowTexWidth * ShadowTexHeight
278 * sizeof(GLfloat));
279 if (depth) {
280 glReadPixels(0, 0, ShadowTexWidth, ShadowTexHeight,
281 GL_DEPTH_COMPONENT, GL_FLOAT, depth);
282 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
283 ShadowTexWidth, ShadowTexHeight, 0,
284 GL_LUMINANCE, GL_FLOAT, depth);
285 free(depth);
286 }
287 }
288 else {
289 /* The normal shadow case */
290 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
291 0, 0, ShadowTexWidth, ShadowTexHeight, 0);
292 }
293
294 /*
295 * Step 3: render scene from point of view of the camera
296 */
297 glViewport(0, 0, WindowWidth, WindowHeight);
298 if (DisplayMode == SHOW_DEPTH_IMAGE) {
299 ShowDepthBuffer(WindowWidth, WindowHeight, 0, 1);
300 }
301 else {
302 glMatrixMode(GL_PROJECTION);
303 glLoadIdentity();
304 glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0);
305 glMatrixMode(GL_MODELVIEW);
306 glLoadIdentity();
307 glTranslatef(0.0, 0.0, -22.0);
308 glRotatef(Xrot, 1, 0, 0);
309 glRotatef(Yrot, 0, 1, 0);
310 glRotatef(Zrot, 0, 0, 1);
311 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
312 glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
313 if (LinearFilter) {
314 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
315 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
316 }
317 else {
318 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
319 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
320 }
321 if (DisplayMode == SHOW_DEPTH_MAPPING) {
322 #if defined(GL_ARB_shadow)
323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
324 #elif defined(GL_SGIX_shadow)
325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_FALSE);
326 #endif
327 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
328 glEnable(GL_TEXTURE_2D);
329 MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
330 EnableIdentityTexgen();
331 }
332 else if (DisplayMode == SHOW_DISTANCE) {
333 glMatrixMode(GL_TEXTURE);
334 glLoadIdentity();
335 glMatrixMode(GL_MODELVIEW);
336 EnableDistanceTexgen(LightPos, SpotDir, ShadowNear+Bias, ShadowFar);
337 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
338 glEnable(GL_TEXTURE_1D);
339 }
340 else {
341 assert(DisplayMode == SHOW_NORMAL);
342 #if defined(GL_ARB_shadow)
343 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
344 GL_COMPARE_R_TO_TEXTURE_ARB);
345 #elif defined(GL_SGIX_shadow)
346 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_TRUE);
347 #endif
348 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
349 glEnable(GL_TEXTURE_2D);
350 MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
351 EnableIdentityTexgen();
352 }
353 DrawScene();
354 DisableTexgen();
355 glDisable(GL_TEXTURE_1D);
356 glDisable(GL_TEXTURE_2D);
357 }
358
359 glutSwapBuffers();
360
361 error = glGetError();
362 if (error) {
363 printf("GL Error: %s\n", (char *) gluErrorString(error));
364 }
365 }
366
367
368 static void
369 Reshape(int width, int height)
370 {
371 WindowWidth = width;
372 WindowHeight = height;
373 if (width >= 512 && height >= 512) {
374 ShadowTexWidth = ShadowTexHeight = 512;
375 }
376 else if (width >= 256 && height >= 256) {
377 ShadowTexWidth = ShadowTexHeight = 256;
378 }
379 else {
380 ShadowTexWidth = ShadowTexHeight = 128;
381 }
382 printf("Using %d x %d depth texture\n", ShadowTexWidth, ShadowTexHeight);
383 }
384
385
386 static void
387 Idle(void)
388 {
389 Yrot += 5.0;
390 /*LightLongitude -= 5.0;*/
391 glutPostRedisplay();
392 }
393
394
395 static void
396 Key(unsigned char key, int x, int y)
397 {
398 const GLfloat step = 3.0;
399 (void) x;
400 (void) y;
401 switch (key) {
402 case 'a':
403 Anim = !Anim;
404 if (Anim)
405 glutIdleFunc(Idle);
406 else
407 glutIdleFunc(NULL);
408 break;
409 case 'b':
410 Bias -= 0.01;
411 printf("Bias %g\n", Bias);
412 break;
413 case 'B':
414 Bias += 0.01;
415 printf("Bias %g\n", Bias);
416 break;
417 case 'd':
418 DisplayMode = SHOW_DISTANCE;
419 break;
420 case 'f':
421 LinearFilter = !LinearFilter;
422 printf("%s filtering\n", LinearFilter ? "Bilinear" : "Nearest");
423 break;
424 case 'i':
425 DisplayMode = SHOW_DEPTH_IMAGE;
426 break;
427 case 'm':
428 DisplayMode = SHOW_DEPTH_MAPPING;
429 break;
430 case 'n':
431 case ' ':
432 DisplayMode = SHOW_NORMAL;
433 break;
434 case 'o':
435 if (HaveEXTshadowFuncs) {
436 Operator++;
437 if (Operator >= 8)
438 Operator = 0;
439 printf("Operator: %s\n", OperatorName[Operator]);
440 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB,
441 OperatorFunc[Operator]);
442 }
443 break;
444 case 'z':
445 Zrot -= step;
446 break;
447 case 'Z':
448 Zrot += step;
449 break;
450 case 27:
451 exit(0);
452 break;
453 }
454 glutPostRedisplay();
455 }
456
457
458 static void
459 SpecialKey(int key, int x, int y)
460 {
461 const GLfloat step = 3.0;
462 const int mod = glutGetModifiers();
463 (void) x;
464 (void) y;
465 switch (key) {
466 case GLUT_KEY_UP:
467 if (mod)
468 LightLatitude += step;
469 else
470 Xrot += step;
471 break;
472 case GLUT_KEY_DOWN:
473 if (mod)
474 LightLatitude -= step;
475 else
476 Xrot -= step;
477 break;
478 case GLUT_KEY_LEFT:
479 if (mod)
480 LightLongitude += step;
481 else
482 Yrot += step;
483 break;
484 case GLUT_KEY_RIGHT:
485 if (mod)
486 LightLongitude -= step;
487 else
488 Yrot -= step;
489 break;
490 }
491 glutPostRedisplay();
492 }
493
494
495 static void
496 Init(void)
497 {
498 #if defined(GL_ARB_depth_texture) && defined(GL_ARB_shadow)
499 if (!glutExtensionSupported("GL_ARB_depth_texture") ||
500 !glutExtensionSupported("GL_ARB_shadow")) {
501 printf("Sorry, this demo requires the GL_ARB_depth_texture and GL_ARB_shadow extensions\n");
502 exit(1);
503 }
504 printf("Using GL_ARB_depth_texture and GL_ARB_shadow\n");
505 #elif defined(GL_SGIX_depth_texture) && defined(GL_SGIX_shadow)
506 if (!glutExtensionSupported("GL_SGIX_depth_texture") ||
507 !glutExtensionSupported("GL_SGIX_shadow")) {
508 printf("Sorry, this demo requires the GL_SGIX_depth_texture and GL_SGIX_shadow extensions\n");
509 exit(1);
510 }
511 printf("Using GL_SGIX_depth_texture and GL_SGIX_shadow\n");
512 #endif
513 HaveEXTshadowFuncs = glutExtensionSupported("GL_EXT_shadow_funcs");
514
515 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
516 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
517 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
518 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
519 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
520 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
521 #if defined(GL_ARB_shadow)
522 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
523 GL_COMPARE_R_TO_TEXTURE_ARB);
524 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
525 #elif defined(GL_SGIX_shadow)
526 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_OPERATOR_SGIX,
527 GL_TEXTURE_LEQUAL_R_SGIX);
528 #endif
529
530 #if defined(GL_ARB_shadow_ambient)
531 if (glutExtensionSupported("GL_ARB_shadow_ambient")) {
532 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 0.3);
533 printf("and GL_ARB_shadow_ambient\n");
534 }
535 #elif defined(GL_SGIX_shadow_ambient)
536 if (glutExtensionSupported("GL_SGIX_shadow_ambient")) {
537 glTexParameterf(GL_TEXTURE_2D, GL_SHADOW_AMBIENT_SGIX, 0.3);
538 printf("and GL_SGIX_shadow_ambient\n");
539 }
540 #endif
541
542 /* setup 1-D grayscale texture image for SHOW_DISTANCE mode */
543 {
544 GLuint i;
545 GLubyte image[256];
546 for (i = 0; i < 256; i++)
547 image[i] = i;
548 glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE,
549 256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
550 }
551
552 glEnable(GL_DEPTH_TEST);
553 glEnable(GL_LIGHTING);
554 glEnable(GL_LIGHT0);
555 }
556
557
558 static void
559 PrintHelp(void)
560 {
561 printf("Keys:\n");
562 printf(" a = toggle animation\n");
563 printf(" i = show depth texture image\n");
564 printf(" m = show depth texture mapping\n");
565 printf(" d = show fragment distance from light source\n");
566 printf(" n = show normal, shadowed image\n");
567 printf(" f = toggle nearest/bilinear texture filtering\n");
568 printf(" b/B = decrease/increase shadow map Z bias\n");
569 printf(" cursor keys = rotate scene\n");
570 printf(" <shift> + cursor keys = rotate light source\n");
571 if (HaveEXTshadowFuncs)
572 printf(" o = cycle through comparison modes\n");
573 }
574
575
576 int
577 main(int argc, char *argv[])
578 {
579 glutInit(&argc, argv);
580 glutInitWindowPosition(0, 0);
581 glutInitWindowSize(WindowWidth, WindowHeight);
582 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
583 glutCreateWindow(argv[0]);
584 glutReshapeFunc(Reshape);
585 glutKeyboardFunc(Key);
586 glutSpecialFunc(SpecialKey);
587 glutDisplayFunc(Display);
588 if (Anim)
589 glutIdleFunc(Idle);
590 Init();
591 PrintHelp();
592 glutMainLoop();
593 return 0;
594 }