demos: remove OSMESA stuff from Makefile
[mesa.git] / progs / demos / shadowtex.c
index a68ad77a1f8bdaa1ba577a68854ec26f57553086..4787f0f29d2aa6b86e597ffe970e51cf2bfba71f 100644 (file)
@@ -1,13 +1,17 @@
-/* $Id: shadowtex.c,v 1.1 2001/02/20 16:43:50 brianp Exp $ */
-
 /*
- * Shadow demo using the GL_SGIX_depth_texture, GL_SGIX_shadow and
- * GL_SGIX_shadow_ambient extensions.
+ * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and
+ * GL_ARB_shadow_ambient extensions.
  *
  * Brian Paul
  * 19 Feb 2001
  *
- * Copyright (C) 1999-2001  Brian Paul   All Rights Reserved.
+ * Added GL_EXT_shadow_funcs support on 23 March 2002
+ * Added GL_EXT_packed_depth_stencil support on 15 March 2006.
+ * Added GL_EXT_framebuffer_object support on 27 March 2006.
+ * Removed old SGIX extension support on 5 April 2006.
+ * Added vertex / fragment program support on 7 June 2007 (Ian Romanick).
+ *
+ * Copyright (C) 1999-2006  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"),
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-
+#define GL_GLEXT_PROTOTYPES
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <math.h>
 #include <GL/glut.h>
-#include "../util/showbuffer.c"
-
+#include "showbuffer.h"
 
 #define DEG_TO_RAD (3.14159 / 180.0)
 
@@ -54,19 +59,162 @@ static GLfloat SpotAngle = 40.0 * DEG_TO_RAD;
 static GLfloat ShadowNear = 4.0, ShadowFar = 24.0;
 static GLint ShadowTexWidth = 256, ShadowTexHeight = 256;
 
-static GLboolean ShowDepth = GL_FALSE;
-
-static GLfloat ShadowImage[256*256];
+static GLboolean LinearFilter = GL_FALSE;
 
 static GLfloat Bias = -0.06;
 
 static GLboolean Anim = GL_TRUE;
 
+static GLboolean NeedNewShadowMap = GL_FALSE;
+static GLuint ShadowTexture, GrayTexture;
+static GLuint ShadowFBO;
+
+static GLfloat lightModelview[16];
+static GLfloat lightProjection[16];
+
+static GLuint vert_prog;
+static GLuint frag_progs[3];
+static GLuint curr_frag = 0;
+static GLuint max_frag = 1;
+
+#define NUM_FRAG_MODES 3
+static const char *FragProgNames[] = {
+   "fixed-function",
+   "program without \"OPTION ARB_fragment_program_shadow\"",
+   "program with \"OPTION ARB_fragment_program_shadow\"",
+};
+
+static GLboolean HaveShadow = GL_FALSE;
+static GLboolean HaveFBO = GL_FALSE;
+static GLboolean UseFBO = GL_FALSE;
+static GLboolean HaveVP = GL_FALSE;
+static GLboolean HaveFP = GL_FALSE;
+static GLboolean HaveFP_Shadow = GL_FALSE;
+static GLboolean UseVP = GL_FALSE;
+static GLboolean HavePackedDepthStencil = GL_FALSE;
+static GLboolean UsePackedDepthStencil = GL_FALSE;
+static GLboolean HaveEXTshadowFuncs = GL_FALSE;
+static GLboolean HaveShadowAmbient = 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_SHADOWS        0
+#define SHOW_DEPTH_IMAGE    1
+#define SHOW_DEPTH_MAPPING  2
+#define SHOW_DISTANCE       3
+
+
+
+#define MAT4_MUL(dest_vec, src_mat, src_vec) \
+    "DP4       " dest_vec ".x, " src_mat "[0], " src_vec ";\n" \
+    "DP4       " dest_vec ".y, " src_mat "[1], " src_vec ";\n" \
+    "DP4       " dest_vec ".z, " src_mat "[2], " src_vec ";\n" \
+    "DP4       " dest_vec ".w, " src_mat "[3], " src_vec ";\n"
+
+#define MAT3_MUL(dest_vec, src_mat, src_vec) \
+    "DP3       " dest_vec ".x, " src_mat "[0], " src_vec ";\n" \
+    "DP3       " dest_vec ".y, " src_mat "[1], " src_vec ";\n" \
+    "DP3       " dest_vec ".z, " src_mat "[2], " src_vec ";\n"
+
+#define NORMALIZE(dest, src) \
+    "DP3       " dest ".w, " src ", " src ";\n" \
+    "RSQ       " dest ".w, " dest ".w;\n" \
+    "MUL       " dest ", " src ", " dest ".w;\n"
+   
+/**
+ * Vertex program for shadow mapping.
+ */
+static const char vert_code[] =
+    "!!ARBvp1.0\n"
+    "ATTRIB iPos        = vertex.position;\n"
+    "ATTRIB iNorm       = vertex.normal;\n"
+
+    "PARAM  mvinv[4]    = { state.matrix.modelview.invtrans };\n"
+    "PARAM  mvp[4]      = { state.matrix.mvp };\n"
+    "PARAM  mv[4]       = { state.matrix.modelview };\n"
+    "PARAM  texmat[4]   = { state.matrix.texture[0] };\n"
+    "PARAM  lightPos    = state.light[0].position;\n"
+    "PARAM  ambientCol  = state.lightprod[0].ambient;\n"
+    "PARAM  diffuseCol  = state.lightprod[0].diffuse;\n"
+
+    "TEMP   n, lightVec;\n"
+    "ALIAS  V = lightVec;\n"
+    "ALIAS  NdotL = n;\n"
+
+    "OUTPUT oPos = result.position;\n"
+    "OUTPUT oColor = result.color;\n"
+    "OUTPUT oTex = result.texcoord[0];\n"
+
+    /* Transform the vertex to clip coordinates. */
+    MAT4_MUL("oPos", "mvp",    "iPos")
+
+    /* Transform the vertex to eye coordinates. */
+    MAT4_MUL("V",    "mv",     "iPos")
+
+    /* Transform the vertex to projected light coordinates. */
+    MAT4_MUL("oTex", "texmat", "iPos")
+
+    /* Transform the normal to eye coordinates. */
+    MAT3_MUL("n",    "mvinv",  "iNorm")
+
+    /* Calculate the vector from the vertex to the light in eye
+     * coordinates.
+     */
+    "SUB       lightVec, lightPos, V;\n"
+    NORMALIZE("lightVec", "lightVec")
+
+    /* Compute diffuse lighting coefficient.
+     */
+    "DP3       NdotL.x, n, lightVec;\n"
+    "MAX       NdotL.x, NdotL.x, {0.0};\n"
+    "MIN       NdotL.x, NdotL.x, {1.0};\n"
+
+    /* Accumulate color contributions.
+     */
+    "MOV       oColor, diffuseCol;\n"
+    "MAD       oColor.xyz, NdotL.x, diffuseCol, ambientCol;\n"
+    "END\n"
+    ;
+
+static const char frag_code[] =
+    "!!ARBfp1.0\n"
+
+    "TEMP   shadow, temp;\n"
+
+    "TXP       shadow, fragment.texcoord[0], texture[0], 2D;\n"
+    "RCP       temp.x, fragment.texcoord[0].w;\n"
+    "MUL       temp.x, temp.x, fragment.texcoord[0].z;\n"
+    "SGE       shadow, shadow.x, temp.x;\n"
+    "MUL       result.color.rgb, fragment.color, shadow.x;\n"
+    "MOV       result.color.a, fragment.color;\n"
+    "END\n"
+    ;
+
+static const char frag_shadow_code[] =
+    "!!ARBfp1.0\n"
+    "OPTION ARB_fragment_program_shadow;\n"
+
+    "TEMP   shadow;\n"
+
+    "TXP       shadow, fragment.texcoord[0], texture[0], SHADOW2D;\n"
+    "MUL       result.color.rgb, fragment.color, shadow.x;\n"
+    "MOV       result.color.a, fragment.color.a;\n"
+    "END\n"
+    ;
 
 static void
 DrawScene(void)
 {
    GLfloat k = 6;
+
    /* sphere */
    glPushMatrix();
    glTranslatef(1.6, 2.2, 2.7);
@@ -105,33 +253,62 @@ DrawScene(void)
 }
 
 
-/*
- * Load the GL_TEXTURE matrix with the projection from the light
- * source's point of view.
+/**
+ * Calculate modelview and project matrices for the light
+ * 
+ * Stores the results in \c lightProjection (projection matrix) and
+ * \c lightModelview (modelview matrix).
  */
 static void
-ShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3],
-             GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar)
+MakeShadowMatrix(const GLfloat lightPos[4], const GLfloat spotDir[3],
+                 GLfloat spotAngle, GLfloat shadowNear, GLfloat shadowFar)
 {
-   GLfloat d;
-   
-   glMatrixMode(GL_TEXTURE);
+   /* compute frustum to enclose spot light cone */
+   const GLfloat d = shadowNear * tan(spotAngle);
+
+   glMatrixMode(GL_PROJECTION);
+   glPushMatrix();
    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);
+   glGetFloatv(GL_PROJECTION_MATRIX, lightProjection);
+   glPopMatrix();
+
+   glMatrixMode(GL_MODELVIEW);
+   glPushMatrix();
+   glLoadIdentity();
    gluLookAt(lightPos[0], lightPos[1], lightPos[2],
              lightPos[0] + spotDir[0],
              lightPos[1] + spotDir[1],
              lightPos[2] + spotDir[2],
-             0, 1, 0);
+             0.0, 1.0, 0.0);
+   glGetFloatv(GL_MODELVIEW_MATRIX, lightModelview);
+   glPopMatrix();
+}
+
+
+/**
+ * Load \c GL_TEXTURE matrix with light's MVP matrix.
+ */
+static void SetShadowTextureMatrix(void)
+{
+   static const GLfloat biasMatrix[16] = {
+      0.5, 0.0, 0.0, 0.0,
+      0.0, 0.5, 0.0, 0.0,
+      0.0, 0.0, 0.5, 0.0,
+      0.5, 0.5, 0.5, 1.0,
+   };
+
+   glMatrixMode(GL_TEXTURE);
+   glLoadMatrixf(biasMatrix);
+   glTranslatef(0.0, 0.0, Bias);
+   glMultMatrixf(lightProjection);
+   glMultMatrixf(lightModelview);
    glMatrixMode(GL_MODELVIEW);
 }
 
 
 static void
-EnableTexgen(void)
+EnableIdentityTexgen(void)
 {
    /* texgen so that texcoord = vertex coord */
    static GLfloat sPlane[4] = { 1, 0, 0, 0 };
@@ -155,6 +332,46 @@ EnableTexgen(void)
 }
 
 
+/*
+ * 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)
 {
@@ -169,6 +386,7 @@ 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);
@@ -179,66 +397,277 @@ ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude,
 }
 
 
+/**
+ * Render the shadow map / depth texture.
+ * The result will be in the texture object named ShadowTexture.
+ */
 static void
-Display(void)
+RenderShadowMap(void)
 {
-   GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight;
-   GLfloat d;
+   GLenum depthFormat; /* GL_DEPTH_COMPONENT or GL_DEPTH_STENCIL_EXT */
+   GLenum depthType; /* GL_UNSIGNED_INT_24_8_EXT or GL_UNSIGNED_INT */
+
+   if (WindowWidth >= 1024 && WindowHeight >= 1024) {
+      ShadowTexWidth = ShadowTexHeight = 1024;
+   }
+   else if (WindowWidth >= 512 && WindowHeight >= 512) {
+      ShadowTexWidth = ShadowTexHeight = 512;
+   }
+   else if (WindowWidth >= 256 && WindowHeight >= 256) {
+      ShadowTexWidth = ShadowTexHeight = 256;
+   }
+   else {
+      ShadowTexWidth = ShadowTexHeight = 128;
+   }
+   printf("Rendering %d x %d depth texture\n", ShadowTexWidth, ShadowTexHeight);
+
+   if (UsePackedDepthStencil) {
+      depthFormat = GL_DEPTH_STENCIL_EXT;
+      depthType = GL_UNSIGNED_INT_24_8_EXT;
+   }
+   else {
+      depthFormat = GL_DEPTH_COMPONENT;
+      depthType = GL_UNSIGNED_INT;
+   }
 
-   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);
+   glLoadMatrixf(lightProjection);
+
    glMatrixMode(GL_MODELVIEW);
-   glLoadIdentity();
-   gluLookAt(LightPos[0], LightPos[1], LightPos[2], /* from */
-             0, 0, 0, /* target */
-             0, 1, 0); /* up */
+   glLoadMatrixf(lightModelview);
+
+   if (UseFBO) {
+      GLenum fbo_status;
+
+      glTexImage2D(GL_TEXTURE_2D, 0, depthFormat,
+                   ShadowTexWidth, ShadowTexHeight, 0,
+                   depthFormat, depthType, NULL);
+
+      /* Set the filter mode so that the texture is texture-complete.
+       * Otherwise it will cause the framebuffer to fail the framebuffer
+       * completeness test.
+       */
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ShadowFBO);
+      glDrawBuffer(GL_NONE);
+      glReadBuffer(GL_NONE);
+
+      fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+      if (fbo_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+         fprintf(stderr, "FBO not complete!  status = 0x%04x\n", fbo_status);
+         assert(fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT);
+      }
+   }
+
+   assert(!glIsEnabled(GL_TEXTURE_1D));
+   assert(!glIsEnabled(GL_TEXTURE_2D));
 
    glViewport(0, 0, ShadowTexWidth, ShadowTexHeight);
    glClear(GL_DEPTH_BUFFER_BIT);
+   glEnable(GL_DEPTH_TEST);
    DrawScene();
 
-   /*
-    * Step 2: copy depth buffer into texture map
-    */
-   glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
-                    0, 0, ShadowTexWidth, ShadowTexHeight, 0);
+   if (UseFBO) {
+      /* all done! */
+      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+   }
+   else {
+      /*
+       * copy depth buffer into the texture map
+       */
+      if (DisplayMode == SHOW_DEPTH_MAPPING) {
+         /* load depth image as gray-scale luminance texture */
+         GLuint *depth = (GLuint *)
+            malloc(ShadowTexWidth * ShadowTexHeight * sizeof(GLuint));
+         assert(depth);
+         glReadPixels(0, 0, ShadowTexWidth, ShadowTexHeight,
+                      depthFormat, depthType, depth);
+         glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
+                      ShadowTexWidth, ShadowTexHeight, 0,
+                      GL_LUMINANCE, GL_UNSIGNED_INT, depth);
+         free(depth);
+      }
+      else {
+         /* The normal shadow case - a real depth texture */
+         glCopyTexImage2D(GL_TEXTURE_2D, 0, depthFormat,
+                          0, 0, ShadowTexWidth, ShadowTexHeight, 0);
+         if (UsePackedDepthStencil) {
+            /* debug check */
+            GLint intFormat;
+            glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,
+                                     GL_TEXTURE_INTERNAL_FORMAT, &intFormat);
+            assert(intFormat == GL_DEPTH_STENCIL_EXT);
+         }
+      }
+   }
+}
+
+
+/**
+ * Show the shadow map as a grayscale image.
+ */
+static void
+ShowShadowMap(void)
+{
+   glClear(GL_COLOR_BUFFER_BIT);
+
+   glMatrixMode(GL_TEXTURE);
+   glLoadIdentity();
 
-   /*
-    * Step 3: render scene from point of view of the camera
-    */
-   glViewport(0, 0, WindowWidth, WindowHeight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
-   glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0);
+   glOrtho(0, WindowWidth, 0, WindowHeight, -1, 1);
+
    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);
-   if (ShowDepth) {
-      ShowDepthBuffer(WindowWidth, WindowHeight, 1, 0);
+
+   glDisable(GL_DEPTH_TEST);
+   glDisable(GL_LIGHTING);
+
+   glEnable(GL_TEXTURE_2D);
+
+   DisableTexgen();
+
+   /* interpret texture's depth values as luminance values */
+   if (HaveShadow) {
+      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
+   }
+
+   glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
+   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+   glBegin(GL_POLYGON);
+   glTexCoord2f(0, 0);  glVertex2f(0, 0);
+   glTexCoord2f(1, 0);  glVertex2f(ShadowTexWidth, 0);
+   glTexCoord2f(1, 1);  glVertex2f(ShadowTexWidth, ShadowTexHeight);
+   glTexCoord2f(0, 1);  glVertex2f(0, ShadowTexHeight);
+   glEnd();
+
+   glDisable(GL_TEXTURE_2D);
+   glEnable(GL_DEPTH_TEST);
+   glEnable(GL_LIGHTING);
+}
+
+
+/**
+ * Redraw window image
+ */
+static void
+Display(void)
+{
+   GLenum error;
+
+   ComputeLightPos(LightDist, LightLatitude, LightLongitude,
+                   LightPos, SpotDir);
+
+   if (NeedNewShadowMap) {
+      MakeShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
+      RenderShadowMap();
+      NeedNewShadowMap = GL_FALSE;
+   }
+
+   glViewport(0, 0, WindowWidth, WindowHeight);
+   if (DisplayMode == SHOW_DEPTH_IMAGE) {
+      ShowShadowMap();
    }
    else {
+      /* prepare to draw scene from camera's view */
+      const GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight;
+
+      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);
-      glEnable(GL_TEXTURE_2D);
-      ShadowMatrix(LightPos, SpotDir, SpotAngle, ShadowNear, ShadowFar);
-      EnableTexgen();
+
+      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 (HaveShadow) {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
+         }
+         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+         glEnable(GL_TEXTURE_2D);
+
+         SetShadowTextureMatrix();
+         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);
+         assert(!glIsEnabled(GL_TEXTURE_2D));
+      }
+      else {
+         assert(DisplayMode == SHOW_SHADOWS);
+         if (HaveShadow) {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB,
+                            GL_COMPARE_R_TO_TEXTURE_ARB);
+         }
+
+         if (curr_frag > 0) {
+            glEnable(GL_FRAGMENT_PROGRAM_ARB);
+         }
+         else {
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+         }
+         glEnable(GL_TEXTURE_2D);
+
+         SetShadowTextureMatrix();
+
+         if (UseVP) {
+            glEnable(GL_VERTEX_PROGRAM_ARB);
+         }
+         else {
+            glEnable(GL_LIGHTING);
+            EnableIdentityTexgen();
+         }
+      }
+
       DrawScene();
-      DisableTexgen();
+
+      if (UseVP) {
+         glDisable(GL_VERTEX_PROGRAM_ARB);
+      }
+      else {
+         DisableTexgen();
+         glDisable(GL_LIGHTING);
+      }
+
+      if (curr_frag > 0) {
+         glDisable(GL_FRAGMENT_PROGRAM_ARB);
+      }
+
+      glDisable(GL_TEXTURE_1D);
       glDisable(GL_TEXTURE_2D);
    }
 
    glutSwapBuffers();
+
+   error = glGetError();
+   if (error) {
+      printf("GL Error: %s\n", (char *) gluErrorString(error));
+   }
 }
 
 
@@ -247,13 +676,20 @@ Reshape(int width, int height)
 {
    WindowWidth = width;
    WindowHeight = height;
+   NeedNewShadowMap = GL_TRUE;
 }
 
 
 static void
 Idle(void)
 {
-   Yrot += 5.0;
+   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();
 }
@@ -275,12 +711,72 @@ Key(unsigned char key, int x, int y)
          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':
-         ShowDepth = !ShowDepth;
+         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 'M':
+         curr_frag = (1 + curr_frag) % max_frag;
+         if (!HaveShadow && (curr_frag == 0)) {
+            curr_frag = 1;
+         }
+
+         printf("Using fragment %s\n", FragProgNames[curr_frag]);
+
+         if (HaveFP) {
+            glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, frag_progs[curr_frag]);
+         }
+         break;
+      case 'n':
+      case 's':
+      case ' ':
+         DisplayMode = SHOW_SHADOWS;
+         break;
+      case 'o':
+         if (HaveEXTshadowFuncs) {
+            Operator++;
+            if (Operator >= 8)
+               Operator = 0;
+            printf("Operator: %s\n", OperatorName[Operator]);
+            if (HaveShadow) {
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB,
+                               OperatorFunc[Operator]);
+            }
+         }
+         break;
+      case 'p':
+         UsePackedDepthStencil = !UsePackedDepthStencil;
+         if (UsePackedDepthStencil && !HavePackedDepthStencil) {
+            printf("Sorry, GL_EXT_packed_depth_stencil not supported\n");
+            UsePackedDepthStencil = GL_FALSE;
+         }
+         else {
+            printf("Use GL_DEPTH_STENCIL_EXT: %d\n", UsePackedDepthStencil);
+            /* Don't really need to regenerate shadow map texture, but do so
+             * to exercise more code more often.
+             */
+            NeedNewShadowMap = GL_TRUE;
+         }
+         break;
+      case 'v':
+         UseVP = !UseVP && HaveVP;
+         printf("Using vertex %s mode.\n",
+                UseVP ? "program" : "fixed-function");
          break;
       case 'z':
          Zrot -= step;
@@ -293,7 +789,6 @@ Key(unsigned char key, int x, int y)
          break;
    }
    glutPostRedisplay();
-   printf("Bias %g\n", Bias);
 }
 
 
@@ -330,29 +825,170 @@ SpecialKey(int key, int x, int y)
             Yrot -= step;
          break;
    }
+   if (mod)
+      NeedNewShadowMap = GL_TRUE;
+
    glutPostRedisplay();
 }
 
 
+/* A helper for finding errors in program strings */
+static int FindLine( const char *program, int position )
+{
+   int i, line = 1;
+   for (i = 0; i < position; i++) {
+      if (program[i] == '\n')
+         line++;
+   }
+   return line;
+}
+
+
+static GLuint
+compile_program(GLenum target, const char *code)
+{
+   GLuint p;
+   GLint errorPos;
+
+
+   glGenProgramsARB(1, & p);
+
+   glBindProgramARB(target, p);
+   glProgramStringARB(target, GL_PROGRAM_FORMAT_ASCII_ARB,
+                      strlen(code), code);
+   glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
+   if (glGetError() != GL_NO_ERROR || errorPos != -1) {
+      int l = FindLine(code, errorPos);
+      printf("Fragment Program Error (pos=%d line=%d): %s\n", errorPos, l,
+             (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB));
+      exit(0);
+   }
+
+   glBindProgramARB(target, 0);
+   return p;
+}
+
 static void
 Init(void)
 {
-   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");
+   static const GLfloat borderColor[4] = {1.0, 0.0, 0.0, 0.0};
+
+   if (!glutExtensionSupported("GL_ARB_depth_texture")) {
+      printf("Sorry, this demo requires the GL_ARB_depth_texture extension\n");
+      exit(1);
+   }
+
+   HaveShadow = glutExtensionSupported("GL_ARB_shadow");
+   HaveVP = glutExtensionSupported("GL_ARB_vertex_program");
+   HaveFP = glutExtensionSupported("GL_ARB_fragment_program");
+   HaveFP_Shadow = glutExtensionSupported("GL_ARB_fragment_program_shadow");
+
+   if (!HaveShadow && !HaveFP) {
+      printf("Sorry, this demo requires either the GL_ARB_shadow extension "
+            "or the GL_ARB_fragment_program extension\n");
       exit(1);
    }
 
-   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   printf("Using GL_ARB_depth_texture\n");
+   if (HaveShadow) {
+      printf("and GL_ARB_shadow\n");
+   }
+
+   if (HaveFP) {
+      printf("and GL_ARB_fragment_program\n");
+   }
+
+   HaveShadowAmbient = glutExtensionSupported("GL_ARB_shadow_ambient");
+   if (HaveShadowAmbient) {
+      printf("and GL_ARB_shadow_ambient\n");
+   }
+
+   HaveEXTshadowFuncs = glutExtensionSupported("GL_EXT_shadow_funcs");
+
+   HavePackedDepthStencil = glutExtensionSupported("GL_EXT_packed_depth_stencil");
+   UsePackedDepthStencil = HavePackedDepthStencil;
+
+#if defined(GL_EXT_framebuffer_object)
+   HaveFBO = glutExtensionSupported("GL_EXT_framebuffer_object");
+   UseFBO = HaveFBO;
+   if (UseFBO) {
+      printf("Using GL_EXT_framebuffer_object\n");
+   }
+#endif
+
+   /*
+    * Set up the 2D shadow map texture
+    */
+   glGenTextures(1, &ShadowTexture);
+   glBindTexture(GL_TEXTURE_2D, ShadowTexture);
+   glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_SGIX, GL_TRUE);
-   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_OPERATOR_SGIX,
-                   GL_TEXTURE_LEQUAL_R_SGIX);
 
-   if (glutExtensionSupported("GL_SGIX_shadow_ambient"))
-      glTexParameterf(GL_TEXTURE_2D, GL_SHADOW_AMBIENT_SGIX, 0.3);
+   if (HaveShadow) {
+      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);
+   }
+
+   if (HaveShadowAmbient) {
+      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FAIL_VALUE_ARB, 0.3);
+   }
+
+#if defined(GL_EXT_framebuffer_object)
+   if (UseFBO) {
+      glGenFramebuffersEXT(1, &ShadowFBO);
+      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, ShadowFBO);
+      glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
+                                   GL_COLOR_ATTACHMENT0_EXT,
+                                   GL_RENDERBUFFER_EXT, 0);
+      glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                GL_TEXTURE_2D, ShadowTexture, 0);
+
+      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+   }
+#endif
+
+   /*
+    * Setup 1-D grayscale texture image for SHOW_DISTANCE mode
+    */
+   glGenTextures(1, &GrayTexture);
+   glBindTexture(GL_TEXTURE_1D, GrayTexture);
+   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);
+   {
+      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);
+   }
+
+   if (HaveVP) {
+      vert_prog = compile_program(GL_VERTEX_PROGRAM_ARB, vert_code);
+      glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vert_prog);
+   }
+
+   max_frag = 1;
+   frag_progs[0] = 0;
+
+   if (HaveFP) {
+      frag_progs[1] = compile_program(GL_FRAGMENT_PROGRAM_ARB, frag_code);
+      max_frag = 2;
+   }
+   
+   if (HaveFP && HaveFP_Shadow) {
+      frag_progs[2] = compile_program(GL_FRAGMENT_PROGRAM_ARB, 
+                                      frag_shadow_code);
+      max_frag = 3;
+   }
+
+   if (!HaveShadow) {
+      curr_frag = 1;
+      glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, frag_progs[curr_frag]);
+   }
 
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
@@ -365,10 +1001,19 @@ PrintHelp(void)
 {
    printf("Keys:\n");
    printf("  a = toggle animation\n");
-   printf("  d = toggle display of depth texture\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("  p = toggle use of packed depth/stencil\n");
+   printf("  M = cycle through fragment program modes\n");
+   printf("  v = toggle vertex program modes\n");
    printf("  cursor keys = rotate scene\n");
    printf("  <shift> + cursor keys = rotate light source\n");
+   if (HaveEXTshadowFuncs)
+      printf("  o = cycle through comparison modes\n");
 }
 
 
@@ -378,7 +1023,7 @@ main(int argc, char *argv[])
    glutInit(&argc, argv);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize(WindowWidth, WindowHeight);
-   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
+   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
    glutCreateWindow(argv[0]);
    glutReshapeFunc(Reshape);
    glutKeyboardFunc(Key);