aboutsummaryrefslogtreecommitdiff
path: root/progs/demos/shadowtex.c
diff options
context:
space:
mode:
Diffstat (limited to 'progs/demos/shadowtex.c')
-rw-r--r--progs/demos/shadowtex.c600
1 files changed, 600 insertions, 0 deletions
diff --git a/progs/demos/shadowtex.c b/progs/demos/shadowtex.c
new file mode 100644
index 000000000..e918751a7
--- /dev/null
+++ b/progs/demos/shadowtex.c
@@ -0,0 +1,600 @@
+/*
+ * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and
+ * GL_ARB_shadow_ambient extensions (or the old SGIX extensions).
+ *
+ * Brian Paul
+ * 19 Feb 2001
+ *
+ * Added GL_EXT_shadow_funcs support on 23 March 2002
+ *
+ * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <GL/glut.h>
+#include "showbuffer.h"
+
+#if 0 /* change to 1 if you want to use the old SGIX extensions */
+#undef GL_ARB_depth_texture
+#undef GL_ARB_shadow
+#undef GL_ARB_shadow_ambient
+#endif
+
+
+#define DEG_TO_RAD (3.14159 / 180.0)
+
+static GLint WindowWidth = 450, WindowHeight = 300;
+static GLfloat Xrot = 15, Yrot = 0, Zrot = 0;
+
+static GLfloat Red[4] = {1, 0, 0, 1};
+static GLfloat Green[4] = {0, 1, 0, 1};
+static GLfloat Blue[4] = {0, 0, 1, 1};
+static GLfloat Yellow[4] = {1, 1, 0, 1};
+
+static GLfloat LightDist = 10;
+static GLfloat LightLatitude = 45.0;
+static GLfloat LightLongitude = 45.0;
+static GLfloat LightPos[4];
+static GLfloat SpotDir[3];
+static GLfloat SpotAngle = 40.0 * DEG_TO_RAD;
+static GLfloat ShadowNear = 4.0, ShadowFar = 24.0;
+static GLint ShadowTexWidth = 256, ShadowTexHeight = 256;
+
+static GLboolean LinearFilter = GL_FALSE;
+
+static GLfloat Bias = -0.06;
+
+static GLboolean Anim = GL_TRUE;
+
+static GLboolean HaveEXTshadowFuncs = GL_FALSE;
+static GLint Operator = 0;
+static const GLenum OperatorFunc[8] = {
+ GL_LEQUAL, GL_LESS, GL_GEQUAL, GL_GREATER,
+ GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER };
+static const char *OperatorName[8] = {
+ "GL_LEQUAL", "GL_LESS", "GL_GEQUAL", "GL_GREATER",
+ "GL_EQUAL", "GL_NOTEQUAL", "GL_ALWAYS", "GL_NEVER" };
+
+
+static GLuint DisplayMode;
+#define SHOW_NORMAL 0
+#define SHOW_DEPTH_IMAGE 1
+#define SHOW_DEPTH_MAPPING 2
+#define SHOW_DISTANCE 3
+
+
+
+static void
+DrawScene(void)
+{
+ GLfloat k = 6;
+ /* sphere */
+ glPushMatrix();
+ glTranslatef(1.6, 2.2, 2.7);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Green);
+ glColor4fv(Green);
+ glutSolidSphere(1.5, 15, 15);
+ glPopMatrix();
+ /* dodecahedron */
+ glPushMatrix();
+ glTranslatef(-2.0, 1.2, 2.1);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Red);
+ glColor4fv(Red);
+ glutSolidDodecahedron();
+ glPopMatrix();
+ /* icosahedron */
+ glPushMatrix();
+ glTranslatef(-0.6, 1.3, -0.5);
+ glScalef(1.5, 1.5, 1.5);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Yellow);
+ glColor4fv(Red);
+ glutSolidIcosahedron();
+ glPopMatrix();
+ /* a plane */
+ glPushMatrix();
+ glTranslatef(0, -1.1, 0);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Blue);
+ glColor4fv(Blue);
+ glNormal3f(0, 1, 0);
+ glBegin(GL_POLYGON);
+ glVertex3f(-k, 0, -k);
+ glVertex3f( k, 0, -k);
+ glVertex3f( k, 0, k);
+ glVertex3f(-k, 0, k);
+ glEnd();
+ glPopMatrix();
+}
+
+
+/*
+ * Load the GL_TEXTURE matrix with the projection from the light
+ * source's point of view.
+ */
+static void
+MakeShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3],
+ GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar)
+{
+ GLfloat d;
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(0.5, 0.5, 0.5 + Bias);
+ glScalef(0.5, 0.5, 0.5);
+ d = shadowNear * tan(spotAngle);
+ glFrustum(-d, d, -d, d, shadowNear, shadowFar);
+ gluLookAt(lightPos[0], lightPos[1], lightPos[2],
+ lightPos[0] + spotDir[0],
+ lightPos[1] + spotDir[1],
+ lightPos[2] + spotDir[2],
+ 0, 1, 0);
+ glMatrixMode(GL_MODELVIEW);
+}
+
+
+static void
+EnableIdentityTexgen(void)
+{
+ /* texgen so that texcoord = vertex coord */
+ static GLfloat sPlane[4] = { 1, 0, 0, 0 };
+ static GLfloat tPlane[4] = { 0, 1, 0, 0 };
+ static GLfloat rPlane[4] = { 0, 0, 1, 0 };
+ static GLfloat qPlane[4] = { 0, 0, 0, 1 };
+
+ glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
+ glTexGenfv(GL_T, GL_EYE_PLANE, tPlane);
+ glTexGenfv(GL_R, GL_EYE_PLANE, rPlane);
+ glTexGenfv(GL_Q, GL_EYE_PLANE, qPlane);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_GEN_R);
+ glEnable(GL_TEXTURE_GEN_Q);
+}
+
+
+/*
+ * Setup 1-D texgen so that the distance from the light source, between
+ * the near and far planes maps to s=0 and s=1. When we draw the scene,
+ * the grayness will indicate the fragment's distance from the light
+ * source.
+ */
+static void
+EnableDistanceTexgen(const GLfloat lightPos[4], const GLfloat lightDir[3],
+ GLfloat lightNear, GLfloat lightFar)
+{
+ GLfloat m, d;
+ GLfloat sPlane[4];
+ GLfloat nearPoint[3];
+
+ m = sqrt(lightDir[0] * lightDir[0] +
+ lightDir[1] * lightDir[1] +
+ lightDir[2] * lightDir[2]);
+
+ d = lightFar - lightNear;
+
+ /* nearPoint = point on light direction vector which intersects the
+ * near plane of the light frustum.
+ */
+ nearPoint[0] = LightPos[0] + lightDir[0] / m * lightNear;
+ nearPoint[1] = LightPos[1] + lightDir[1] / m * lightNear;
+ nearPoint[2] = LightPos[2] + lightDir[2] / m * lightNear;
+
+ sPlane[0] = lightDir[0] / d / m;
+ sPlane[1] = lightDir[1] / d / m;
+ sPlane[2] = lightDir[2] / d / m;
+ sPlane[3] = -(sPlane[0] * nearPoint[0]
+ + sPlane[1] * nearPoint[1]
+ + sPlane[2] * nearPoint[2]);
+
+ glTexGenfv(GL_S, GL_EYE_PLANE, sPlane);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+ glEnable(GL_TEXTURE_GEN_S);
+}
+
+
+static void
+DisableTexgen(void)
+{
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glDisable(GL_TEXTURE_GEN_R);
+ glDisable(GL_TEXTURE_GEN_Q);
+}
+
+
+static void
+ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude,
+ GLfloat pos[4], GLfloat dir[3])
+{
+
+ pos[0] = dist * sin(longitude * DEG_TO_RAD);
+ pos[1] = dist * sin(latitude * DEG_TO_RAD);
+ pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD);
+ pos[3] = 1;
+ dir[0] = -pos[0];
+ dir[1] = -pos[1];
+ dir[2] = -pos[2];
+}
+
+
+static void
+Display(void)
+{
+ GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight;
+ GLfloat d;
+ GLenum error;
+
+ ComputeLightPos(LightDist, LightLatitude, LightLongitude,
+ LightPos, SpotDir);
+ /*
+ * Step 1: render scene from point of view of the light source
+ */
+ /* compute frustum to enclose spot light cone */
+ d = ShadowNear * tan(SpotAngle);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustum(-d, d, -d, d, ShadowNear, ShadowFar);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt(LightPos[0], LightPos[1], LightPos[2], /* from */
+ 0, 0, 0, /* target */
+ 0, 1, 0); /* up */
+
+ glViewport(0, 0, ShadowTexWidth, ShadowTexHeight);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ DrawScene();
+
+ /*
+ * Step 2: copy depth buffer into texture map
+ */
+ if (DisplayMode == SHOW_DEPTH_MAPPING) {
+ /* load depth image as gray-scale luminance texture */
+ GLfloat *depth = (GLfloat *) malloc(ShadowTexWidth * ShadowTexHeight
+ * sizeof(GLfloat));
+ if (depth) {
+ glReadPixels(0, 0, ShadowTexWidth, ShadowTexHeight,
+ GL_DEPTH_COMPONENT, GL_FLOAT, depth);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
+ ShadowTexWidth, ShadowTexHeight, 0,
+ GL_LUMINANCE, GL_FLOAT, depth);
+ free(depth);
+ }
+ }
+ else {
+ /* The normal shadow case */
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
+ 0, 0, ShadowTexWidth, ShadowTexHeight, 0);
+ }
+
+ /*
+ * Step 3: render scene from point of view of the camera
+ */
+ glViewport(0, 0, WindowWidth, WindowHeight);
+ if (DisplayMode == SHOW_DEPTH_IMAGE) {
+ ShowDepthBuffer(WindowWidth, WindowHeight, 0, 1);
+ }
+ else {
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0.0, 0.0, -22.0);
+ glRotatef(Xrot, 1, 0, 0);
+ glRotatef(Yrot, 0, 1, 0);
+ glRotatef(Zrot, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
+ if (LinearFilter) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+ else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+ if (DisplayMode == SHOW_DEPTH_MAPPING) {
+#if defined(GL_ARB_shadow)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
+#elif defined(GL_SGIX_shadow)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_FALSE);
+#endif
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glEnable(GL_TEXTURE_2D);
+ MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
+ EnableIdentityTexgen();
+ }
+ else if (DisplayMode == SHOW_DISTANCE) {
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ EnableDistanceTexgen(LightPos, SpotDir, ShadowNear+Bias, ShadowFar);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glEnable(GL_TEXTURE_1D);
+ }
+ else {
+ assert(DisplayMode == SHOW_NORMAL);
+#if defined(GL_ARB_shadow)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
+ GL_COMPARE_R_TO_TEXTURE_ARB);
+#elif defined(GL_SGIX_shadow)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_TRUE);
+#endif
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glEnable(GL_TEXTURE_2D);
+ MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
+ EnableIdentityTexgen();
+ }
+ DrawScene();
+ DisableTexgen();
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ glutSwapBuffers();
+
+ error = glGetError();
+ if (error) {
+ printf("GL Error: %s\n", (char *) gluErrorString(error));
+ }
+}
+
+
+static void
+Reshape(int width, int height)
+{
+ WindowWidth = width;
+ WindowHeight = height;
+ if (width >= 512 && height >= 512) {
+ ShadowTexWidth = ShadowTexHeight = 512;
+ }
+ else if (width >= 256 && height >= 256) {
+ ShadowTexWidth = ShadowTexHeight = 256;
+ }
+ else {
+ ShadowTexWidth = ShadowTexHeight = 128;
+ }
+ printf("Using %d x %d depth texture\n", ShadowTexWidth, ShadowTexHeight);
+}
+
+
+static void
+Idle(void)
+{
+ static double t0 = -1.;
+ double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
+ if (t0 < 0.0)
+ t0 = t;
+ dt = t - t0;
+ t0 = t;
+ Yrot += 75.0 * dt;
+ /*LightLongitude -= 5.0;*/
+ glutPostRedisplay();
+}
+
+
+static void
+Key(unsigned char key, int x, int y)
+{
+ const GLfloat step = 3.0;
+ (void) x;
+ (void) y;
+ switch (key) {
+ case 'a':
+ Anim = !Anim;
+ if (Anim)
+ glutIdleFunc(Idle);
+ else
+ glutIdleFunc(NULL);
+ break;
+ case 'b':
+ Bias -= 0.01;
+ printf("Bias %g\n", Bias);
+ break;
+ case 'B':
+ Bias += 0.01;
+ printf("Bias %g\n", Bias);
+ break;
+ case 'd':
+ DisplayMode = SHOW_DISTANCE;
+ break;
+ case 'f':
+ LinearFilter = !LinearFilter;
+ printf("%s filtering\n", LinearFilter ? "Bilinear" : "Nearest");
+ break;
+ case 'i':
+ DisplayMode = SHOW_DEPTH_IMAGE;
+ break;
+ case 'm':
+ DisplayMode = SHOW_DEPTH_MAPPING;
+ break;
+ case 'n':
+ case ' ':
+ DisplayMode = SHOW_NORMAL;
+ break;
+ case 'o':
+ if (HaveEXTshadowFuncs) {
+ Operator++;
+ if (Operator >= 8)
+ Operator = 0;
+ printf("Operator: %s\n", OperatorName[Operator]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB,
+ OperatorFunc[Operator]);
+ }
+ break;
+ case 'z':
+ Zrot -= step;
+ break;
+ case 'Z':
+ Zrot += step;
+ break;
+ case 27:
+ exit(0);
+ break;
+ }
+ glutPostRedisplay();
+}
+
+
+static void
+SpecialKey(int key, int x, int y)
+{
+ const GLfloat step = 3.0;
+ const int mod = glutGetModifiers();
+ (void) x;
+ (void) y;
+ switch (key) {
+ case GLUT_KEY_UP:
+ if (mod)
+ LightLatitude += step;
+ else
+ Xrot += step;
+ break;
+ case GLUT_KEY_DOWN:
+ if (mod)
+ LightLatitude -= step;
+ else
+ Xrot -= step;
+ break;
+ case GLUT_KEY_LEFT:
+ if (mod)
+ LightLongitude += step;
+ else
+ Yrot += step;
+ break;
+ case GLUT_KEY_RIGHT:
+ if (mod)
+ LightLongitude -= step;
+ else
+ Yrot -= step;
+ break;
+ }
+ glutPostRedisplay();
+}
+
+
+static void
+Init(void)
+{
+#if defined(GL_ARB_depth_texture) && defined(GL_ARB_shadow)
+ if (!glutExtensionSupported("GL_ARB_depth_texture") ||
+ !glutExtensionSupported("GL_ARB_shadow")) {
+ printf("Sorry, this demo requires the GL_ARB_depth_texture and GL_ARB_shadow extensions\n");
+ exit(1);
+ }
+ printf("Using GL_ARB_depth_texture and GL_ARB_shadow\n");
+#elif defined(GL_SGIX_depth_texture) && defined(GL_SGIX_shadow)
+ if (!glutExtensionSupported("GL_SGIX_depth_texture") ||
+ !glutExtensionSupported("GL_SGIX_shadow")) {
+ printf("Sorry, this demo requires the GL_SGIX_depth_texture and GL_SGIX_shadow extensions\n");
+ exit(1);
+ }
+ printf("Using GL_SGIX_depth_texture and GL_SGIX_shadow\n");
+#endif
+ HaveEXTshadowFuncs = glutExtensionSupported("GL_EXT_shadow_funcs");
+
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+#if defined(GL_ARB_shadow)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
+ GL_COMPARE_R_TO_TEXTURE_ARB);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
+#elif defined(GL_SGIX_shadow)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_OPERATOR_SGIX,
+ GL_TEXTURE_LEQUAL_R_SGIX);
+#endif
+
+#if defined(GL_ARB_shadow_ambient)
+ if (glutExtensionSupported("GL_ARB_shadow_ambient")) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 0.3);
+ printf("and GL_ARB_shadow_ambient\n");
+ }
+#elif defined(GL_SGIX_shadow_ambient)
+ if (glutExtensionSupported("GL_SGIX_shadow_ambient")) {
+ glTexParameterf(GL_TEXTURE_2D, GL_SHADOW_AMBIENT_SGIX, 0.3);
+ printf("and GL_SGIX_shadow_ambient\n");
+ }
+#endif
+
+ /* setup 1-D grayscale texture image for SHOW_DISTANCE mode */
+ {
+ GLuint i;
+ GLubyte image[256];
+ for (i = 0; i < 256; i++)
+ image[i] = i;
+ glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE,
+ 256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, image);
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+}
+
+
+static void
+PrintHelp(void)
+{
+ printf("Keys:\n");
+ printf(" a = toggle animation\n");
+ printf(" i = show depth texture image\n");
+ printf(" m = show depth texture mapping\n");
+ printf(" d = show fragment distance from light source\n");
+ printf(" n = show normal, shadowed image\n");
+ printf(" f = toggle nearest/bilinear texture filtering\n");
+ printf(" b/B = decrease/increase shadow map Z bias\n");
+ printf(" cursor keys = rotate scene\n");
+ printf(" <shift> + cursor keys = rotate light source\n");
+ if (HaveEXTshadowFuncs)
+ printf(" o = cycle through comparison modes\n");
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ glutInit(&argc, argv);
+ glutInitWindowPosition(0, 0);
+ glutInitWindowSize(WindowWidth, WindowHeight);
+ glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
+ glutCreateWindow(argv[0]);
+ glutReshapeFunc(Reshape);
+ glutKeyboardFunc(Key);
+ glutSpecialFunc(SpecialKey);
+ glutDisplayFunc(Display);
+ if (Anim)
+ glutIdleFunc(Idle);
+ Init();
+ PrintHelp();
+ glutMainLoop();
+ return 0;
+}