Qore Programming Language

  • Increase font size
  • Default font size
  • Decrease font size

shadowtex demo

E-mail Print PDF

This is an OpenGL shadowtex demo by Brian Paul, ported from C to Qore.

Screenshot:

opengl shadowtex demo

/Users/david/src/qore/module-glut/test/shadowtex.q

#!/usr/bin/env qore

%require-our

# the glut module automatically loads the opengl module
%requires glut

#/*
# * Shadow demo using the GL_ARB_depth_texture, GL_ARB_shadow and
# * GL_ARB_shadow_ambient extensions.
# *
# * Brian Paul
# * 19 Feb 2001
# *
# * 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"),
# * 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.
# */

const DEG_TO_RAD = (3.1415927 / 180.0);

our $WindowWidth = 450;
our $WindowHeight = 300;
our $Xrot = 15;
our $Yrot = 0.0;
our $Zrot = 0.0;

const Red = (1, 0, 0, 1);
const Green = (0, 1, 0, 1);
const Blue = (0, 0, 1, 1);
const Yellow = (1, 1, 0, 1);

const LightDist = 10;
our $LightLatitude = 45.0;
our $LightLongitude = 45.0;
our $LightPos;
our $SpotDir;
const SpotAngle = 40.0 * DEG_TO_RAD;
const ShadowNear = 4.0;
const ShadowFar = 24.0;
our $ShadowTexWidth = 256;
our $ShadowTexHeight = 256;

our $LinearFilter = GL_FALSE;

our $Bias = -0.06;

our $Anim = GL_TRUE;

our $NeedNewShadowMap = GL_TRUE;
our $ShadowTexture;
our $GrayTexture;
our $ShadowFBO;

our $lightModelview;
our $lightProjection;

our $vert_prog;
our $frag_progs;
our $curr_frag = 0;
our $max_frag = 1;

const NUM_FRAG_MODES = 3;

const FragProgNames = (
    "fixed-function",
    "program without \"OPTION ARB_fragment_program_shadow\"",
    "program with \"OPTION ARB_fragment_program_shadow\"",
    );

our $HaveShadow = GL_FALSE;
our $HaveFBO = GL_FALSE;
our $UseFBO = GL_FALSE;
our $HaveVP = GL_FALSE;
our $HaveFP = GL_FALSE;
our $HaveFP_Shadow = GL_FALSE;
our $UseVP = GL_FALSE;
our $HavePackedDepthStencil = GL_FALSE;
our $UsePackedDepthStencil = GL_FALSE;
our $HaveEXTshadowFuncs = GL_FALSE;
our $HaveShadowAmbient = GL_FALSE;

our $Operator = 0;
const OperatorFunc = ( GL_LEQUAL, GL_LESS, GL_GEQUAL, GL_GREATER,
		       GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER );
const OperatorName = ( "GL_LEQUAL", "GL_LESS", "GL_GEQUAL", "GL_GREATER",
		       "GL_EQUAL", "GL_NOTEQUAL", "GL_ALWAYS", "GL_NEVER" );


our $DisplayMode;
const SHOW_SHADOWS        = 0;
const SHOW_DEPTH_IMAGE    = 1;
const SHOW_DEPTH_MAPPING  = 2;
const SHOW_DISTANCE       = 3;

const biasMatrix = (
    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,
    );

our $t0 = -1.0;

const step = 3.0;

const borderColor = (1.0, 0.0, 0.0, 0.0);

sub MAT4_MUL($dest_vec, $src_mat, $src_vec) {
    return "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";
}

sub MAT3_MUL($dest_vec, $src_mat, $src_vec) {
    return "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";
}

sub NORMALIZE($dest, $src) {
    return "DP3	" + $dest + ".w, " + $src + ", " + $src + ";\n"
	"RSQ	" + $dest + ".w, " + $dest + ".w;\n"
	"MUL	" + $dest + ", "  + $src + ", " + $dest + ".w;\n";
}

# Vertex program for shadow mapping.
our $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"
    ;

const 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"
    ;

const 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"
    ;


const sPlane = ( 1, 0, 0, 0 );
const tPlane = ( 0, 1, 0, 0 );
const rPlane = ( 0, 0, 1, 0 );
const qPlane = ( 0, 0, 0, 1 );

sub assert($str) {
    stderr.vprintf("ASSERT: " + $str, $argv);
    stderr.printf("\n");
    exit(1);
}

sub DrawScene() {
    my $k = 6.0;

    # 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();
}

# Calculate modelview and project matrices for the light
# Stores the results in \c $lightProjection (projection matrix) and
# \c $lightModelview (modelview matrix).

sub MakeShadowMatrix($lightPos, $spotDir, $spotAngle, $shadowNear, $shadowFar) {
    # compute frustum to enclose spot light cone
    my $d = $shadowNear * tan($spotAngle);

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glFrustum(-$d, $d, -$d, $d, $shadowNear, $shadowFar);
    $lightProjection = glGetFloatv(GL_PROJECTION_MATRIX);
    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.0, 1.0, 0.0);
    $lightModelview = glGetFloatv(GL_MODELVIEW_MATRIX);
    glPopMatrix();
}

# Load \c GL_TEXTURE matrix with light's MVP matrix.
sub SetShadowTextureMatrix() {
    glMatrixMode(GL_TEXTURE);
    glLoadMatrixf(biasMatrix);
    glTranslatef(0.0, 0.0, $Bias);
    glMultMatrixf($lightProjection);
    glMultMatrixf($lightModelview);
    glMatrixMode(GL_MODELVIEW);
}

sub EnableIdentityTexgen() {
    # texgen so that texcoord = vertex coord
    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.
sub EnableDistanceTexgen($lightPos, $lightDir, $lightNear, $lightFar) {
    my ($m, $d);
    my $sPlane;
    my $nearPoint;

    $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);
}

sub DisableTexgen() {
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
    glDisable(GL_TEXTURE_GEN_R);
    glDisable(GL_TEXTURE_GEN_Q);
}

sub ComputeLightPos($dist, $latitude, $longitude, $pos, $dir) {
    $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];
}

# * Render the shadow map / depth texture.
# * The result will be in the texture object named $ShadowTexture.
sub RenderShadowMap() {
    my $depthFormat; # GL_DEPTH_COMPONENT or GL_DEPTH_STENCIL_EXT
    my $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;
    }

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf($lightProjection);

    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf($lightModelview);

    if ($UseFBO) {
	my $fbo_status;

	glTexImage2D(GL_TEXTURE_2D, 0, $depthFormat,
		     $ShadowTexWidth, $ShadowTexHeight, 0,
		     $depthFormat, $depthType);

	# 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) {
	    stderr.printf("FBO not complete! status = 0x%04x\n", $fbo_status);
	    if (GL_FRAMEBUFFER_UNSUPPORTED_EXT)
		assert("unsupported framebuffer extension");
	    assert("framebuffer not complete");
	}
    }

    if (glIsEnabled(GL_TEXTURE_1D)) assert("GL_TEXTURE_1D");

    if (glIsEnabled(GL_TEXTURE_2D)) assert("GL_TEXTURE_2D");

    glViewport(0, 0, $ShadowTexWidth, $ShadowTexHeight);
    glClear(GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    DrawScene();

    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
	    my $depth = glReadPixels(0, 0, $ShadowTexWidth, $ShadowTexHeight, $depthFormat, $depthType);
	    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, $ShadowTexWidth, $ShadowTexHeight, 0,
			 GL_LUMINANCE, GL_UNSIGNED_INT, $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
		my $intFormat = glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT);
		if ($intFormat != GL_DEPTH_STENCIL_EXT) stderr.printf("texture format not set to GL_DEPTH_STENCIL_EXT (%d) internal format=%d\n", $depthFormat, $intFormat);
	    }
	}
    }
}

# Show the shadow map as a grayscale image.
sub ShowShadowMap() {
    glClear(GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, $WindowWidth, 0, $WindowHeight, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    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
sub Display() {
    my $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 */
	my $ar = float($WindowWidth) / $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);

	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);
	    if (glIsEnabled(GL_TEXTURE_2D)) throw "ASSERT";
	}
	else {
	    if ($DisplayMode != SHOW_SHADOWS) throw "ASSERT";
	    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();

	if ($UseVP) {
	    glDisable(GL_VERTEX_PROGRAM_ARB);
	}
	else {
	    DisableTexgen();
	    glDisable(GL_LIGHTING);
	}

	if ($curr_frag > 0) {
	    glDisable(GL_FRAGMENT_PROGRAM_ARB);
	}

	glDisable(GL_TEXTURE_2D);
    }

    glutSwapBuffers();

    $error = glGetError();
    if ($error) {
	printf("GL Error: %s\n", gluErrorString($error));
    }
}

sub Reshape($width, $height) {
    $WindowWidth = $width;
    $WindowHeight = $height;
    $NeedNewShadowMap = GL_TRUE;
}

sub Idle() {
    our $dt;
    our $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();
}

sub Key($key, $x, $y) {
    if ($key > 27)
	$key = chr($key);
    switch ($key) {
	case 'a':
	    $Anim = !$Anim;
	if ($Anim)
            glutIdleFunc(\Idle());
	else
            glutIdleFunc();
	break;
	case 'b':
	    $Bias -= 0.01;
	printf("Bias %n\n", $Bias);
	break;
	case 'B':
	    $Bias += 0.01;
	printf("Bias %n\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 '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;
	break;
	case 'Z':
	    $Zrot += step;
	break;
	case 27:
	exit(0);
	break;
    }
    glutPostRedisplay();
}

sub SpecialKey($key, $x, $y) {
    my $mod = glutGetModifiers();
    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;
    }
    if ($mod)
	$NeedNewShadowMap = GL_TRUE;

    glutPostRedisplay();
}

# A helper for finding errors in program strings
sub FindLine($program, $position) {
    my $l = split("\n", substr($program, 0, $position));
    return elements $l;
}

sub compile_program($target, $code) {
    my $errorPos;

    my $p = glGenProgramsARB(1);

    glBindProgramARB($target, $p);
    glProgramStringARB($target, GL_PROGRAM_FORMAT_ASCII_ARB, $code);
    my $errorPos = glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB);
    if (glGetError() != GL_NO_ERROR || $errorPos != -1) {
	my $l = FindLine($code, $errorPos);
	printf("Fragment Program Error (pos=%d line=%d): %s\n", $errorPos, $l, glGetString(GL_PROGRAM_ERROR_STRING_ARB));
	exit(0);
    }

    glBindProgramARB($target, 0);
    return $p;
}

sub Init() {
    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);
    }

    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 (GL_EXT_framebuffer_object) {
	$HaveFBO = glutExtensionSupported("GL_EXT_framebuffer_object");

	# disable framebuffer on Darwin initially - why doesn't it work?  I have no clue :-(
	$UseFBO = PlatformOS == "Darwin" ? False : $HaveFBO;
	if ($UseFBO) {
	    printf("Using GL_EXT_framebuffer_object\n");
	}
    }

    # 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);

    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 (GL_EXT_framebuffer_object) {
	if ($UseFBO) {
	    $ShadowFBO = glGenFramebuffersEXT(1);
	    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);
	}
    }

    #* Setup 1-D grayscale texture image for SHOW_DISTANCE mode
    $GrayTexture = glGenTextures(1);
    glBindTexture(GL_TEXTURE_1D, $GrayTexture);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    {
	my $i;
	my $image = "";
	for ($i = 0; $i < 256; $i++)
	    $image += chr($i);
	glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE,
		     256, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, binary($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);
    glEnable(GL_LIGHT0);
}

sub PrintHelp() {
    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("  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");
}

sub main() {
    glutInit(\$ARGV);
    glutInitWindowPosition(0, 0);
    glutInitWindowSize($WindowWidth, $WindowHeight);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
    glutCreateWindow("shadowtex.q");
    glutReshapeFunc(\Reshape());
    glutKeyboardFunc(\Key());
    glutSpecialFunc(\SpecialKey());
    glutDisplayFunc(\Display());
    if ($Anim)
	glutIdleFunc(\Idle());
    Init();
    PrintHelp();
    glutMainLoop();
    return 0;
}

main();

Last Updated on Saturday, 25 December 2010 19:49