aboutsummaryrefslogtreecommitdiff
path: root/code/renderer/tr_shade.c
diff options
context:
space:
mode:
Diffstat (limited to 'code/renderer/tr_shade.c')
-rw-r--r--code/renderer/tr_shade.c1548
1 files changed, 1548 insertions, 0 deletions
diff --git a/code/renderer/tr_shade.c b/code/renderer/tr_shade.c
new file mode 100644
index 0000000..49c3690
--- /dev/null
+++ b/code/renderer/tr_shade.c
@@ -0,0 +1,1548 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// tr_shade.c
+
+#include "tr_local.h"
+#if idppc_altivec && !defined(MACOS_X)
+#include <altivec.h>
+#endif
+
+/*
+
+ THIS ENTIRE FILE IS BACK END
+
+ This file deals with applying shaders to surface data in the tess struct.
+*/
+#ifndef PANDORA
+/*
+================
+R_ArrayElementDiscrete
+
+This is just for OpenGL conformance testing, it should never be the fastest
+================
+*/
+static void APIENTRY R_ArrayElementDiscrete( GLint index ) {
+ qglColor4ubv( tess.svars.colors[ index ] );
+ if ( glState.currenttmu ) {
+ qglMultiTexCoord2fARB( 0, tess.svars.texcoords[ 0 ][ index ][0], tess.svars.texcoords[ 0 ][ index ][1] );
+ qglMultiTexCoord2fARB( 1, tess.svars.texcoords[ 1 ][ index ][0], tess.svars.texcoords[ 1 ][ index ][1] );
+ } else {
+ qglTexCoord2fv( tess.svars.texcoords[ 0 ][ index ] );
+ }
+ qglVertex3fv( tess.xyz[ index ] );
+}
+
+/*
+===================
+R_DrawStripElements
+
+===================
+*/
+static int c_vertexes; // for seeing how long our average strips are
+static int c_begins;
+static void R_DrawStripElements( int numIndexes, const glIndex_t *indexes, void ( APIENTRY *element )(GLint) ) {
+ int i;
+ int last[3] = { -1, -1, -1 };
+ qboolean even;
+
+ c_begins++;
+
+ if ( numIndexes <= 0 ) {
+ return;
+ }
+
+ qglBegin( GL_TRIANGLE_STRIP );
+
+ // prime the strip
+ element( indexes[0] );
+ element( indexes[1] );
+ element( indexes[2] );
+ c_vertexes += 3;
+
+ last[0] = indexes[0];
+ last[1] = indexes[1];
+ last[2] = indexes[2];
+
+ even = qfalse;
+
+ for ( i = 3; i < numIndexes; i += 3 )
+ {
+ // odd numbered triangle in potential strip
+ if ( !even )
+ {
+ // check previous triangle to see if we're continuing a strip
+ if ( ( indexes[i+0] == last[2] ) && ( indexes[i+1] == last[1] ) )
+ {
+ element( indexes[i+2] );
+ c_vertexes++;
+ assert( indexes[i+2] < tess.numVertexes );
+ even = qtrue;
+ }
+ // otherwise we're done with this strip so finish it and start
+ // a new one
+ else
+ {
+ qglEnd();
+
+ qglBegin( GL_TRIANGLE_STRIP );
+ c_begins++;
+
+ element( indexes[i+0] );
+ element( indexes[i+1] );
+ element( indexes[i+2] );
+
+ c_vertexes += 3;
+
+ even = qfalse;
+ }
+ }
+ else
+ {
+ // check previous triangle to see if we're continuing a strip
+ if ( ( last[2] == indexes[i+1] ) && ( last[0] == indexes[i+0] ) )
+ {
+ element( indexes[i+2] );
+ c_vertexes++;
+
+ even = qfalse;
+ }
+ // otherwise we're done with this strip so finish it and start
+ // a new one
+ else
+ {
+ qglEnd();
+
+ qglBegin( GL_TRIANGLE_STRIP );
+ c_begins++;
+
+ element( indexes[i+0] );
+ element( indexes[i+1] );
+ element( indexes[i+2] );
+ c_vertexes += 3;
+
+ even = qfalse;
+ }
+ }
+
+ // cache the last three vertices
+ last[0] = indexes[i+0];
+ last[1] = indexes[i+1];
+ last[2] = indexes[i+2];
+ }
+
+ qglEnd();
+}
+
+
+
+/*
+==================
+R_DrawElements
+
+Optionally performs our own glDrawElements that looks for strip conditions
+instead of using the single glDrawElements call that may be inefficient
+without compiled vertex arrays.
+==================
+*/
+static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) {
+ int primitives;
+
+ primitives = r_primitives->integer;
+
+ // default is to use triangles if compiled vertex arrays are present
+ if ( primitives == 0 ) {
+ if ( qglLockArraysEXT ) {
+ primitives = 2;
+ } else {
+ primitives = 1;
+ }
+ }
+
+
+ if ( primitives == 2 ) {
+ qglDrawElements( GL_TRIANGLES,
+ numIndexes,
+ GL_INDEX_TYPE,
+ indexes );
+ return;
+ }
+
+ if ( primitives == 1 ) {
+ R_DrawStripElements( numIndexes, indexes, qglArrayElement );
+ return;
+ }
+
+ if ( primitives == 3 ) {
+ R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete );
+ return;
+ }
+
+ // anything else will cause no drawing
+}
+#else
+static void R_DrawElements( int numIndexes, const glIndex_t* indexes )
+{
+ qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexes);
+}
+#endif
+
+/*
+=============================================================
+
+SURFACE SHADERS
+
+=============================================================
+*/
+
+shaderCommands_t tess;
+static qboolean setArraysOnce;
+
+/*
+=================
+R_BindAnimatedImage
+
+=================
+*/
+static void R_BindAnimatedImage( textureBundle_t *bundle ) {
+ int index;
+
+ if ( bundle->isVideoMap ) {
+ ri.CIN_RunCinematic(bundle->videoMapHandle);
+ ri.CIN_UploadCinematic(bundle->videoMapHandle);
+ return;
+ }
+
+ if ( bundle->numImageAnimations <= 1 ) {
+ GL_Bind( bundle->image[0] );
+ return;
+ }
+
+ // it is necessary to do this messy calc to make sure animations line up
+ // exactly with waveforms of the same frequency
+ index = myftol( tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE );
+ index >>= FUNCTABLE_SIZE2;
+
+ if ( index < 0 ) {
+ index = 0; // may happen with shader time offsets
+ }
+ index %= bundle->numImageAnimations;
+
+ GL_Bind( bundle->image[ index ] );
+}
+
+/*
+================
+DrawTris
+
+Draws triangle outlines for debugging
+================
+*/
+static void DrawTris (shaderCommands_t *input) {
+ GL_Bind( tr.whiteImage );
+#ifdef PANDORA
+ glColor4f (1,1,1,1);
+#else
+ qglColor3f (1,1,1);
+#endif
+ GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
+#ifdef PANDORA
+ qglDepthRangef( 0, 0 );
+#else
+ qglDepthRange( 0, 0 );
+#endif
+
+ qglDisableClientState (GL_COLOR_ARRAY);
+ qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD
+
+ if (qglLockArraysEXT) {
+ qglLockArraysEXT(0, input->numVertexes);
+ GLimp_LogComment( "glLockArraysEXT\n" );
+ }
+
+ R_DrawElements( input->numIndexes, input->indexes );
+
+ if (qglUnlockArraysEXT) {
+ qglUnlockArraysEXT();
+ GLimp_LogComment( "glUnlockArraysEXT\n" );
+ }
+#ifdef PANDORA
+ qglDepthRangef( 0, 1 );
+#else
+ qglDepthRange( 0, 1 );
+#endif
+}
+
+
+/*
+================
+DrawNormals
+
+Draws vertex normals for debugging
+================
+*/
+static void DrawNormals (shaderCommands_t *input) {
+ int i;
+ vec3_t temp;
+#ifdef PANDORA
+ vec3_t verts[2 * SHADER_MAX_VERTEXES];
+ glIndex_t indicies[2 * SHADER_MAX_VERTEXES];
+
+ for (i = 0; i < input->numVertexes; i++) {
+ VectorCopy(input->xyz[i], verts[i * 2]);
+ VectorMA(input->xyz[i], 2, input->normal[i], temp);
+ VectorCopy(temp, verts[(i * 2) + 1]);
+ indicies[(i * 2)] = i * 2;
+ indicies[(i * 2) + 1] = (i * 2) + 1;
+ }
+#endif
+ GL_Bind( tr.whiteImage );
+#ifdef PANDORA
+ glColor4f (1,1,1,1);
+ qglDepthRangef( 0, 0 ); // never occluded
+#else
+ qglColor3f (1,1,1);
+ qglDepthRange( 0, 0 ); // never occluded
+#endif
+ GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
+
+#ifdef PANDORA
+ qglVertexPointer(3, GL_FLOAT, 0, verts);
+ qglDrawElements(GL_LINES, i, GL_INDEX_TYPE, indicies);
+
+ qglDepthRangef( 0, 1 );
+#else
+ qglBegin (GL_LINES);
+ for (i = 0 ; i < input->numVertexes ; i++) {
+ qglVertex3fv (input->xyz[i]);
+ VectorMA (input->xyz[i], 2, input->normal[i], temp);
+ qglVertex3fv (temp);
+ }
+ qglEnd ();
+ qglDepthRange( 0, 1 );
+#endif
+}
+
+/*
+==============
+RB_BeginSurface
+
+We must set some things up before beginning any tesselation,
+because a surface may be forced to perform a RB_End due
+to overflow.
+==============
+*/
+void RB_BeginSurface( shader_t *shader, int fogNum ) {
+
+ shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader;
+
+ tess.numIndexes = 0;
+ tess.numVertexes = 0;
+ tess.shader = state;
+ tess.fogNum = fogNum;
+ tess.dlightBits = 0; // will be OR'd in by surface functions
+ tess.xstages = state->stages;
+ tess.numPasses = state->numUnfoggedPasses;
+ tess.currentStageIteratorFunc = state->optimalStageIteratorFunc;
+
+ tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
+ if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) {
+ tess.shaderTime = tess.shader->clampTime;
+ }
+
+
+}
+
+/*
+===================
+DrawMultitextured
+
+output = t0 * t1 or t0 + t1
+
+t0 = most upstream according to spec
+t1 = most downstream according to spec
+===================
+*/
+static void DrawMultitextured( shaderCommands_t *input, int stage ) {
+ shaderStage_t *pStage;
+
+ pStage = tess.xstages[stage];
+
+ GL_State( pStage->stateBits );
+#ifndef PANDORA
+ // this is an ugly hack to work around a GeForce driver
+ // bug with multitexture and clip planes
+ if ( backEnd.viewParms.isPortal ) {
+ qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+ }
+#endif
+ //
+ // base
+ //
+ GL_SelectTexture( 0 );
+ qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] );
+ R_BindAnimatedImage( &pStage->bundle[0] );
+
+ //
+ // lightmap/secondary pass
+ //
+ GL_SelectTexture( 1 );
+ qglEnable( GL_TEXTURE_2D );
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+
+ if ( r_lightmap->integer ) {
+ GL_TexEnv( GL_REPLACE );
+ } else {
+ GL_TexEnv( tess.shader->multitextureEnv );
+ }
+
+ qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] );
+
+ R_BindAnimatedImage( &pStage->bundle[1] );
+
+ R_DrawElements( input->numIndexes, input->indexes );
+
+ //
+ // disable texturing on TEXTURE1, then select TEXTURE0
+ //
+ //qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
+ qglDisable( GL_TEXTURE_2D );
+
+ GL_SelectTexture( 0 );
+}
+
+
+
+/*
+===================
+ProjectDlightTexture
+
+Perform dynamic lighting with another rendering pass
+===================
+*/
+#if idppc_altivec
+static void ProjectDlightTexture_altivec( void ) {
+ int i, l;
+ vec_t origin0, origin1, origin2;
+ float texCoords0, texCoords1;
+ vector float floatColorVec0, floatColorVec1;
+ vector float modulateVec, colorVec, zero;
+ vector short colorShort;
+ vector signed int colorInt;
+ vector unsigned char floatColorVecPerm, modulatePerm, colorChar;
+ vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff);
+ float *texCoords;
+ byte *colors;
+ byte clipBits[SHADER_MAX_VERTEXES];
+ float texCoordsArray[SHADER_MAX_VERTEXES][2];
+ byte colorArray[SHADER_MAX_VERTEXES][4];
+#ifdef PANDORA
+ glIndex_t hitIndexes[SHADER_MAX_INDEXES];
+#else
+ unsigned hitIndexes[SHADER_MAX_INDEXES];
+#endif
+ int numIndexes;
+ float scale;
+ float radius;
+ vec3_t floatColor;
+ float modulate = 0.0f;
+
+ if ( !backEnd.refdef.num_dlights ) {
+ return;
+ }
+
+ // There has to be a better way to do this so that floatColor
+ // and/or modulate are already 16-byte aligned.
+ floatColorVecPerm = vec_lvsl(0,(float *)floatColor);
+ modulatePerm = vec_lvsl(0,(float *)&modulate);
+ modulatePerm = (vector unsigned char)vec_splat((vector unsigned int)modulatePerm,0);
+ zero = (vector float)vec_splat_s8(0);
+
+ for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) {
+ dlight_t *dl;
+
+ if ( !( tess.dlightBits & ( 1 << l ) ) ) {
+ continue; // this surface definately doesn't have any of this light
+ }
+ texCoords = texCoordsArray[0];
+ colors = colorArray[0];
+
+ dl = &backEnd.refdef.dlights[l];
+ origin0 = dl->transformed[0];
+ origin1 = dl->transformed[1];
+ origin2 = dl->transformed[2];
+ radius = dl->radius;
+ scale = 1.0f / radius;
+
+ if(r_greyscale->integer)
+ {
+ float luminance;
+
+ luminance = (dl->color[0] * 255.0f + dl->color[1] * 255.0f + dl->color[2] * 255.0f) / 3;
+ floatColor[0] = floatColor[1] = floatColor[2] = luminance;
+ }
+ else
+ {
+ floatColor[0] = dl->color[0] * 255.0f;
+ floatColor[1] = dl->color[1] * 255.0f;
+ floatColor[2] = dl->color[2] * 255.0f;
+ }
+ floatColorVec0 = vec_ld(0, floatColor);
+ floatColorVec1 = vec_ld(11, floatColor);
+ floatColorVec0 = vec_perm(floatColorVec0,floatColorVec0,floatColorVecPerm);
+ for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) {
+ int clip = 0;
+ vec_t dist0, dist1, dist2;
+
+ dist0 = origin0 - tess.xyz[i][0];
+ dist1 = origin1 - tess.xyz[i][1];
+ dist2 = origin2 - tess.xyz[i][2];
+
+ backEnd.pc.c_dlightVertexes++;
+
+ texCoords0 = 0.5f + dist0 * scale;
+ texCoords1 = 0.5f + dist1 * scale;
+
+ if( !r_dlightBacks->integer &&
+ // dist . tess.normal[i]
+ ( dist0 * tess.normal[i][0] +
+ dist1 * tess.normal[i][1] +
+ dist2 * tess.normal[i][2] ) < 0.0f ) {
+ clip = 63;
+ } else {
+ if ( texCoords0 < 0.0f ) {
+ clip |= 1;
+ } else if ( texCoords0 > 1.0f ) {
+ clip |= 2;
+ }
+ if ( texCoords1 < 0.0f ) {
+ clip |= 4;
+ } else if ( texCoords1 > 1.0f ) {
+ clip |= 8;
+ }
+ texCoords[0] = texCoords0;
+ texCoords[1] = texCoords1;
+
+ // modulate the strength based on the height and color
+ if ( dist2 > radius ) {
+ clip |= 16;
+ modulate = 0.0f;
+ } else if ( dist2 < -radius ) {
+ clip |= 32;
+ modulate = 0.0f;
+ } else {
+ dist2 = Q_fabs(dist2);
+ if ( dist2 < radius * 0.5f ) {
+ modulate = 1.0f;
+ } else {
+ modulate = 2.0f * (radius - dist2) * scale;
+ }
+ }
+ }
+ clipBits[i] = clip;
+
+ modulateVec = vec_ld(0,(float *)&modulate);
+ modulateVec = vec_perm(modulateVec,modulateVec,modulatePerm);
+ colorVec = vec_madd(floatColorVec0,modulateVec,zero);
+ colorInt = vec_cts(colorVec,0); // RGBx
+ colorShort = vec_pack(colorInt,colorInt); // RGBxRGBx
+ colorChar = vec_packsu(colorShort,colorShort); // RGBxRGBxRGBxRGBx
+ colorChar = vec_sel(colorChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255
+ vec_ste((vector unsigned int)colorChar,0,(unsigned int *)colors); // store color
+ }
+
+ // build a list of triangles that need light
+ numIndexes = 0;
+ for ( i = 0 ; i < tess.numIndexes ; i += 3 ) {
+#ifdef PANDORA
+ glIndex_t a, b, c;
+#else
+ int a, b, c;
+#endif
+ a = tess.indexes[i];
+ b = tess.indexes[i+1];
+ c = tess.indexes[i+2];
+ if ( clipBits[a] & clipBits[b] & clipBits[c] ) {
+ continue; // not lighted
+ }
+ hitIndexes[numIndexes] = a;
+ hitIndexes[numIndexes+1] = b;
+ hitIndexes[numIndexes+2] = c;
+ numIndexes += 3;
+ }
+
+ if ( !numIndexes ) {
+ continue;
+ }
+
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] );
+
+ qglEnableClientState( GL_COLOR_ARRAY );
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray );
+
+ GL_Bind( tr.dlightImage );
+ // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light
+ // where they aren't rendered
+ if ( dl->additive ) {
+ GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL );
+ }
+ else {
+ GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL );
+ }
+ R_DrawElements( numIndexes, hitIndexes );
+ backEnd.pc.c_totalIndexes += numIndexes;
+ backEnd.pc.c_dlightIndexes += numIndexes;
+ }
+}
+#endif
+
+
+static void ProjectDlightTexture_scalar( void ) {
+ int i, l;
+ vec3_t origin;
+ float *texCoords;
+ byte *colors;
+ byte clipBits[SHADER_MAX_VERTEXES];
+ float texCoordsArray[SHADER_MAX_VERTEXES][2];
+ byte colorArray[SHADER_MAX_VERTEXES][4];
+#ifdef PANDORA
+ glIndex_t hitIndexes[SHADER_MAX_INDEXES];
+#else
+ unsigned hitIndexes[SHADER_MAX_INDEXES];
+#endif
+ int numIndexes;
+ float scale;
+ float radius;
+ vec3_t floatColor;
+ float modulate = 0.0f;
+
+ if ( !backEnd.refdef.num_dlights ) {
+ return;
+ }
+
+ for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) {
+ dlight_t *dl;
+
+ if ( !( tess.dlightBits & ( 1 << l ) ) ) {
+ continue; // this surface definately doesn't have any of this light
+ }
+ texCoords = texCoordsArray[0];
+ colors = colorArray[0];
+
+ dl = &backEnd.refdef.dlights[l];
+ VectorCopy( dl->transformed, origin );
+ radius = dl->radius;
+ scale = 1.0f / radius;
+
+ if(r_greyscale->integer)
+ {
+ float luminance;
+
+ luminance = (dl->color[0] * 255.0f + dl->color[1] * 255.0f + dl->color[2] * 255.0f) / 3;
+ floatColor[0] = floatColor[1] = floatColor[2] = luminance;
+ }
+ else
+ {
+ floatColor[0] = dl->color[0] * 255.0f;
+ floatColor[1] = dl->color[1] * 255.0f;
+ floatColor[2] = dl->color[2] * 255.0f;
+ }
+
+ for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) {
+ int clip = 0;
+ vec3_t dist;
+
+ VectorSubtract( origin, tess.xyz[i], dist );
+
+ backEnd.pc.c_dlightVertexes++;
+
+ texCoords[0] = 0.5f + dist[0] * scale;
+ texCoords[1] = 0.5f + dist[1] * scale;
+
+ if( !r_dlightBacks->integer &&
+ // dist . tess.normal[i]
+ ( dist[0] * tess.normal[i][0] +
+ dist[1] * tess.normal[i][1] +
+ dist[2] * tess.normal[i][2] ) < 0.0f ) {
+ clip = 63;
+ } else {
+ if ( texCoords[0] < 0.0f ) {
+ clip |= 1;
+ } else if ( texCoords[0] > 1.0f ) {
+ clip |= 2;
+ }
+ if ( texCoords[1] < 0.0f ) {
+ clip |= 4;
+ } else if ( texCoords[1] > 1.0f ) {
+ clip |= 8;
+ }
+ texCoords[0] = texCoords[0];
+ texCoords[1] = texCoords[1];
+
+ // modulate the strength based on the height and color
+ if ( dist[2] > radius ) {
+ clip |= 16;
+ modulate = 0.0f;
+ } else if ( dist[2] < -radius ) {
+ clip |= 32;
+ modulate = 0.0f;
+ } else {
+ dist[2] = Q_fabs(dist[2]);
+ if ( dist[2] < radius * 0.5f ) {
+ modulate = 1.0f;
+ } else {
+ modulate = 2.0f * (radius - dist[2]) * scale;
+ }
+ }
+ }
+ clipBits[i] = clip;
+ colors[0] = myftol(floatColor[0] * modulate);
+ colors[1] = myftol(floatColor[1] * modulate);
+ colors[2] = myftol(floatColor[2] * modulate);
+ colors[3] = 255;
+ }
+
+ // build a list of triangles that need light
+ numIndexes = 0;
+ for ( i = 0 ; i < tess.numIndexes ; i += 3 ) {
+#ifdef PANDORA
+ glIndex_t a, b, c;
+#else
+ int a, b, c;
+#endif
+
+ a = tess.indexes[i];
+ b = tess.indexes[i+1];
+ c = tess.indexes[i+2];
+ if ( clipBits[a] & clipBits[b] & clipBits[c] ) {
+ continue; // not lighted
+ }
+ hitIndexes[numIndexes] = a;
+ hitIndexes[numIndexes+1] = b;
+ hitIndexes[numIndexes+2] = c;
+ numIndexes += 3;
+ }
+
+ if ( !numIndexes ) {
+ continue;
+ }
+
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] );
+
+ qglEnableClientState( GL_COLOR_ARRAY );
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray );
+
+ GL_Bind( tr.dlightImage );
+ // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light
+ // where they aren't rendered
+ if ( dl->additive ) {
+ GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL );
+ }
+ else {
+ GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL );
+ }
+ R_DrawElements( numIndexes, hitIndexes );
+ backEnd.pc.c_totalIndexes += numIndexes;
+ backEnd.pc.c_dlightIndexes += numIndexes;
+ }
+}
+
+static void ProjectDlightTexture( void ) {
+#if idppc_altivec
+ if (com_altivec->integer) {
+ // must be in a seperate function or G3 systems will crash.
+ ProjectDlightTexture_altivec();
+ return;
+ }
+#endif
+ ProjectDlightTexture_scalar();
+}
+
+
+/*
+===================
+RB_FogPass
+
+Blends a fog texture on top of everything else
+===================
+*/
+static void RB_FogPass( void ) {
+ fog_t *fog;
+ int i;
+
+ qglEnableClientState( GL_COLOR_ARRAY );
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors );
+
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY);
+ qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] );
+
+ fog = tr.world->fogs + tess.fogNum;
+
+ for ( i = 0; i < tess.numVertexes; i++ ) {
+ * ( int * )&tess.svars.colors[i] = fog->colorInt;
+ }
+
+ RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[0] );
+
+ GL_Bind( tr.fogImage );
+
+ if ( tess.shader->fogPass == FP_EQUAL ) {
+ GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL );
+ } else {
+ GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
+ }
+
+ R_DrawElements( tess.numIndexes, tess.indexes );
+}
+
+/*
+===============
+ComputeColors
+===============
+*/
+static void ComputeColors( shaderStage_t *pStage )
+{
+ int i;
+
+ //
+ // rgbGen
+ //
+ switch ( pStage->rgbGen )
+ {
+ case CGEN_IDENTITY:
+ Com_Memset( tess.svars.colors, 0xff, tess.numVertexes * 4 );
+ break;
+ default:
+ case CGEN_IDENTITY_LIGHTING:
+ Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 );
+ break;
+ case CGEN_LIGHTING_DIFFUSE:
+ RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors );
+ break;
+ case CGEN_EXACT_VERTEX:
+ Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) );
+ break;
+ case CGEN_CONST:
+ for ( i = 0; i < tess.numVertexes; i++ ) {
+ *(int *)tess.svars.colors[i] = *(int *)pStage->constantColor;
+ }
+ break;
+ case CGEN_VERTEX:
+ if ( tr.identityLight == 1 )
+ {
+ Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) );
+ }
+ else
+ {
+ for ( i = 0; i < tess.numVertexes; i++ )
+ {
+ tess.svars.colors[i][0] = tess.vertexColors[i][0] * tr.identityLight;
+ tess.svars.colors[i][1] = tess.vertexColors[i][1] * tr.identityLight;
+ tess.svars.colors[i][2] = tess.vertexColors[i][2] * tr.identityLight;
+ tess.svars.colors[i][3] = tess.vertexColors[i][3];
+ }
+ }
+ break;
+ case CGEN_ONE_MINUS_VERTEX:
+ if ( tr.identityLight == 1 )
+ {
+ for ( i = 0; i < tess.numVertexes; i++ )
+ {
+ tess.svars.colors[i][0] = 255 - tess.vertexColors[i][0];
+ tess.svars.colors[i][1] = 255 - tess.vertexColors[i][1];
+ tess.svars.colors[i][2] = 255 - tess.vertexColors[i][2];
+ }
+ }
+ else
+ {
+ for ( i = 0; i < tess.numVertexes; i++ )
+ {
+ tess.svars.colors[i][0] = ( 255 - tess.vertexColors[i][0] ) * tr.identityLight;
+ tess.svars.colors[i][1] = ( 255 - tess.vertexColors[i][1] ) * tr.identityLight;
+ tess.svars.colors[i][2] = ( 255 - tess.vertexColors[i][2] ) * tr.identityLight;
+ }
+ }
+ break;
+ case CGEN_FOG:
+ {
+ fog_t *fog;
+
+ fog = tr.world->fogs + tess.fogNum;
+
+ for ( i = 0; i < tess.numVertexes; i++ ) {
+ * ( int * )&tess.svars.colors[i] = fog->colorInt;
+ }
+ }
+ break;
+ case CGEN_WAVEFORM:
+ RB_CalcWaveColor( &pStage->rgbWave, ( unsigned char * ) tess.svars.colors );
+ break;
+ case CGEN_ENTITY:
+ RB_CalcColorFromEntity( ( unsigned char * ) tess.svars.colors );
+ break;
+ case CGEN_ONE_MINUS_ENTITY:
+ RB_CalcColorFromOneMinusEntity( ( unsigned char * ) tess.svars.colors );
+ break;
+ }
+
+ //
+ // alphaGen
+ //
+ switch ( pStage->alphaGen )
+ {
+ case AGEN_SKIP:
+ break;
+ case AGEN_IDENTITY:
+ if ( pStage->rgbGen != CGEN_IDENTITY ) {
+ if ( ( pStage->rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) ||
+ pStage->rgbGen != CGEN_VERTEX ) {
+ for ( i = 0; i < tess.numVertexes; i++ ) {
+ tess.svars.colors[i][3] = 0xff;
+ }
+ }
+ }
+ break;
+ case AGEN_CONST:
+ if ( pStage->rgbGen != CGEN_CONST ) {
+ for ( i = 0; i < tess.numVertexes; i++ ) {
+ tess.svars.colors[i][3] = pStage->constantColor[3];
+ }
+ }
+ break;
+ case AGEN_WAVEFORM:
+ RB_CalcWaveAlpha( &pStage->alphaWave, ( unsigned char * ) tess.svars.colors );
+ break;
+ case AGEN_LIGHTING_SPECULAR:
+ RB_CalcSpecularAlpha( ( unsigned char * ) tess.svars.colors );
+ break;
+ case AGEN_ENTITY:
+ RB_CalcAlphaFromEntity( ( unsigned char * ) tess.svars.colors );
+ break;
+ case AGEN_ONE_MINUS_ENTITY:
+ RB_CalcAlphaFromOneMinusEntity( ( unsigned char * ) tess.svars.colors );
+ break;
+ case AGEN_VERTEX:
+ if ( pStage->rgbGen != CGEN_VERTEX ) {
+ for ( i = 0; i < tess.numVertexes; i++ ) {
+ tess.svars.colors[i][3] = tess.vertexColors[i][3];
+ }
+ }
+ break;
+ case AGEN_ONE_MINUS_VERTEX:
+ for ( i = 0; i < tess.numVertexes; i++ )
+ {
+ tess.svars.colors[i][3] = 255 - tess.vertexColors[i][3];
+ }
+ break;
+ case AGEN_PORTAL:
+ {
+ unsigned char alpha;
+
+ for ( i = 0; i < tess.numVertexes; i++ )
+ {
+ float len;
+ vec3_t v;
+
+ VectorSubtract( tess.xyz[i], backEnd.viewParms.or.origin, v );
+ len = VectorLength( v );
+
+ len /= tess.shader->portalRange;
+
+ if ( len < 0 )
+ {
+ alpha = 0;
+ }
+ else if ( len > 1 )
+ {
+ alpha = 0xff;
+ }
+ else
+ {
+ alpha = len * 0xff;
+ }
+
+ tess.svars.colors[i][3] = alpha;
+ }
+ }
+ break;
+ }
+
+ //
+ // fog adjustment for colors to fade out as fog increases
+ //
+ if ( tess.fogNum )
+ {
+ switch ( pStage->adjustColorsForFog )
+ {
+ case ACFF_MODULATE_RGB:
+ RB_CalcModulateColorsByFog( ( unsigned char * ) tess.svars.colors );
+ break;
+ case ACFF_MODULATE_ALPHA:
+ RB_CalcModulateAlphasByFog( ( unsigned char * ) tess.svars.colors );
+ break;
+ case ACFF_MODULATE_RGBA:
+ RB_CalcModulateRGBAsByFog( ( unsigned char * ) tess.svars.colors );
+ break;
+ case ACFF_NONE:
+ break;
+ }
+ }
+
+ // if in greyscale rendering mode turn all color values into greyscale.
+ if(r_greyscale->integer)
+ {
+ int scale;
+
+ for(i = 0; i < tess.numVertexes; i++)
+ {
+ scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3;
+ tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale;
+ }
+ }
+}
+
+/*
+===============
+ComputeTexCoords
+===============
+*/
+static void ComputeTexCoords( shaderStage_t *pStage ) {
+ int i;
+ int b;
+
+ for ( b = 0; b < NUM_TEXTURE_BUNDLES; b++ ) {
+ int tm;
+
+ //
+ // generate the texture coordinates
+ //
+ switch ( pStage->bundle[b].tcGen )
+ {
+ case TCGEN_IDENTITY:
+ Com_Memset( tess.svars.texcoords[b], 0, sizeof( float ) * 2 * tess.numVertexes );
+ break;
+ case TCGEN_TEXTURE:
+ for ( i = 0 ; i < tess.numVertexes ; i++ ) {
+ tess.svars.texcoords[b][i][0] = tess.texCoords[i][0][0];
+ tess.svars.texcoords[b][i][1] = tess.texCoords[i][0][1];
+ }
+ break;
+ case TCGEN_LIGHTMAP:
+ for ( i = 0 ; i < tess.numVertexes ; i++ ) {
+ tess.svars.texcoords[b][i][0] = tess.texCoords[i][1][0];
+ tess.svars.texcoords[b][i][1] = tess.texCoords[i][1][1];
+ }
+ break;
+ case TCGEN_VECTOR:
+ for ( i = 0 ; i < tess.numVertexes ; i++ ) {
+ tess.svars.texcoords[b][i][0] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[0] );
+ tess.svars.texcoords[b][i][1] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[1] );
+ }
+ break;
+ case TCGEN_FOG:
+ RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[b] );
+ break;
+ case TCGEN_ENVIRONMENT_MAPPED:
+ RB_CalcEnvironmentTexCoords( ( float * ) tess.svars.texcoords[b] );
+ break;
+ case TCGEN_BAD:
+ return;
+ }
+
+ //
+ // alter texture coordinates
+ //
+ for ( tm = 0; tm < pStage->bundle[b].numTexMods ; tm++ ) {
+ switch ( pStage->bundle[b].texMods[tm].type )
+ {
+ case TMOD_NONE:
+ tm = TR_MAX_TEXMODS; // break out of for loop
+ break;
+
+ case TMOD_TURBULENT:
+ RB_CalcTurbulentTexCoords( &pStage->bundle[b].texMods[tm].wave,
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ case TMOD_ENTITY_TRANSLATE:
+ RB_CalcScrollTexCoords( backEnd.currentEntity->e.shaderTexCoord,
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ case TMOD_SCROLL:
+ RB_CalcScrollTexCoords( pStage->bundle[b].texMods[tm].scroll,
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ case TMOD_SCALE:
+ RB_CalcScaleTexCoords( pStage->bundle[b].texMods[tm].scale,
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ case TMOD_STRETCH:
+ RB_CalcStretchTexCoords( &pStage->bundle[b].texMods[tm].wave,
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ case TMOD_TRANSFORM:
+ RB_CalcTransformTexCoords( &pStage->bundle[b].texMods[tm],
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ case TMOD_ROTATE:
+ RB_CalcRotateTexCoords( pStage->bundle[b].texMods[tm].rotateSpeed,
+ ( float * ) tess.svars.texcoords[b] );
+ break;
+
+ default:
+ ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'\n", pStage->bundle[b].texMods[tm].type, tess.shader->name );
+ break;
+ }
+ }
+ }
+}
+
+/*
+** RB_IterateStagesGeneric
+*/
+static void RB_IterateStagesGeneric( shaderCommands_t *input )
+{
+ int stage;
+
+ for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ )
+ {
+ shaderStage_t *pStage = tess.xstages[stage];
+
+ if ( !pStage )
+ {
+ break;
+ }
+
+ ComputeColors( pStage );
+ ComputeTexCoords( pStage );
+
+ if ( !setArraysOnce )
+ {
+ qglEnableClientState( GL_COLOR_ARRAY );
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors );
+ }
+
+ //
+ // do multitexture
+ //
+ if ( pStage->bundle[1].image[0] != 0 )
+ {
+ DrawMultitextured( input, stage );
+ }
+ else
+ {
+ if ( !setArraysOnce )
+ {
+ qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] );
+ }
+
+ //
+ // set state
+ //
+ if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer )
+ {
+ GL_Bind( tr.whiteImage );
+ }
+ else
+ R_BindAnimatedImage( &pStage->bundle[0] );
+
+ GL_State( pStage->stateBits );
+
+ //
+ // draw
+ //
+ R_DrawElements( input->numIndexes, input->indexes );
+ }
+ // allow skipping out to show just lightmaps during development
+ if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) )
+ {
+ break;
+ }
+ }
+}
+
+
+/*
+** RB_StageIteratorGeneric
+*/
+void RB_StageIteratorGeneric( void )
+{
+ shaderCommands_t *input;
+
+ input = &tess;
+
+ RB_DeformTessGeometry();
+
+ //
+ // log this call
+ //
+ if ( r_logFile->integer )
+ {
+ // don't just call LogComment, or we will get
+ // a call to va() every frame!
+ GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) );
+ }
+
+ //
+ // set face culling appropriately
+ //
+ GL_Cull( input->shader->cullType );
+
+ // set polygon offset if necessary
+ if ( input->shader->polygonOffset )
+ {
+ qglEnable( GL_POLYGON_OFFSET_FILL );
+ qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value );
+ }
+
+ //
+ // if there is only a single pass then we can enable color
+ // and texture arrays before we compile, otherwise we need
+ // to avoid compiling those arrays since they will change
+ // during multipass rendering
+ //
+ if ( tess.numPasses > 1 || input->shader->multitextureEnv )
+ {
+ setArraysOnce = qfalse;
+ qglDisableClientState (GL_COLOR_ARRAY);
+ qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ else
+ {
+ setArraysOnce = qtrue;
+
+ qglEnableClientState( GL_COLOR_ARRAY);
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors );
+
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY);
+ qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] );
+ }
+
+ //
+ // lock XYZ
+ //
+ qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD
+ if (qglLockArraysEXT)
+ {
+ qglLockArraysEXT(0, input->numVertexes);
+ GLimp_LogComment( "glLockArraysEXT\n" );
+ }
+
+ //
+ // enable color and texcoord arrays after the lock if necessary
+ //
+ if ( !setArraysOnce )
+ {
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ qglEnableClientState( GL_COLOR_ARRAY );
+ }
+
+ //
+ // call shader function
+ //
+ RB_IterateStagesGeneric( input );
+
+ //
+ // now do any dynamic lighting needed
+ //
+ if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE
+ && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) {
+ ProjectDlightTexture();
+ }
+
+ //
+ // now do fog
+ //
+ if ( tess.fogNum && tess.shader->fogPass ) {
+ RB_FogPass();
+ }
+
+ //
+ // unlock arrays
+ //
+ if (qglUnlockArraysEXT)
+ {
+ qglUnlockArraysEXT();
+ GLimp_LogComment( "glUnlockArraysEXT\n" );
+ }
+
+ //
+ // reset polygon offset
+ //
+ if ( input->shader->polygonOffset )
+ {
+ qglDisable( GL_POLYGON_OFFSET_FILL );
+ }
+}
+
+
+/*
+** RB_StageIteratorVertexLitTexture
+*/
+void RB_StageIteratorVertexLitTexture( void )
+{
+ shaderCommands_t *input;
+ shader_t *shader;
+
+ input = &tess;
+
+ shader = input->shader;
+
+ //
+ // compute colors
+ //
+ RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors );
+
+ //
+ // log this call
+ //
+ if ( r_logFile->integer )
+ {
+ // don't just call LogComment, or we will get
+ // a call to va() every frame!
+ GLimp_LogComment( va("--- RB_StageIteratorVertexLitTexturedUnfogged( %s ) ---\n", tess.shader->name) );
+ }
+
+ //
+ // set face culling appropriately
+ //
+ GL_Cull( input->shader->cullType );
+
+ //
+ // set arrays and lock
+ //
+ qglEnableClientState( GL_COLOR_ARRAY);
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY);
+
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors );
+ qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] );
+ qglVertexPointer (3, GL_FLOAT, 16, input->xyz);
+
+ if ( qglLockArraysEXT )
+ {
+ qglLockArraysEXT(0, input->numVertexes);
+ GLimp_LogComment( "glLockArraysEXT\n" );
+ }
+
+ //
+ // call special shade routine
+ //
+ R_BindAnimatedImage( &tess.xstages[0]->bundle[0] );
+ GL_State( tess.xstages[0]->stateBits );
+ R_DrawElements( input->numIndexes, input->indexes );
+
+ //
+ // now do any dynamic lighting needed
+ //
+ if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) {
+ ProjectDlightTexture();
+ }
+
+ //
+ // now do fog
+ //
+ if ( tess.fogNum && tess.shader->fogPass ) {
+ RB_FogPass();
+ }
+
+ //
+ // unlock arrays
+ //
+ if (qglUnlockArraysEXT)
+ {
+ qglUnlockArraysEXT();
+ GLimp_LogComment( "glUnlockArraysEXT\n" );
+ }
+}
+
+//define REPLACE_MODE
+
+void RB_StageIteratorLightmappedMultitexture( void ) {
+ shaderCommands_t *input;
+
+ input = &tess;
+
+ //
+ // log this call
+ //
+ if ( r_logFile->integer ) {
+ // don't just call LogComment, or we will get
+ // a call to va() every frame!
+ GLimp_LogComment( va("--- RB_StageIteratorLightmappedMultitexture( %s ) ---\n", tess.shader->name) );
+ }
+
+ //
+ // set face culling appropriately
+ //
+ GL_Cull( input->shader->cullType );
+
+ //
+ // set color, pointers, and lock
+ //
+ GL_State( GLS_DEFAULT );
+ qglVertexPointer( 3, GL_FLOAT, 16, input->xyz );
+
+#ifdef REPLACE_MODE
+ qglDisableClientState( GL_COLOR_ARRAY );
+ qglColor3f( 1, 1, 1 );
+ qglShadeModel( GL_FLAT );
+#else
+ qglEnableClientState( GL_COLOR_ARRAY );
+ qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.constantColor255 );
+#endif
+
+ //
+ // select base stage
+ //
+ GL_SelectTexture( 0 );
+
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ R_BindAnimatedImage( &tess.xstages[0]->bundle[0] );
+ qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] );
+
+ //
+ // configure second stage
+ //
+ GL_SelectTexture( 1 );
+ qglEnable( GL_TEXTURE_2D );
+ if ( r_lightmap->integer ) {
+ GL_TexEnv( GL_REPLACE );
+ } else {
+ GL_TexEnv( GL_MODULATE );
+ }
+ R_BindAnimatedImage( &tess.xstages[0]->bundle[1] );
+ qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
+ qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][1] );
+
+ //
+ // lock arrays
+ //
+ if ( qglLockArraysEXT ) {
+ qglLockArraysEXT(0, input->numVertexes);
+ GLimp_LogComment( "glLockArraysEXT\n" );
+ }
+
+ R_DrawElements( input->numIndexes, input->indexes );
+
+ //
+ // disable texturing on TEXTURE1, then select TEXTURE0
+ //
+ qglDisable( GL_TEXTURE_2D );
+ qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
+
+ GL_SelectTexture( 0 );
+#ifdef REPLACE_MODE
+ GL_TexEnv( GL_MODULATE );
+ qglShadeModel( GL_SMOOTH );
+#endif
+
+ //
+ // now do any dynamic lighting needed
+ //
+ if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) {
+ ProjectDlightTexture();
+ }
+
+ //
+ // now do fog
+ //
+ if ( tess.fogNum && tess.shader->fogPass ) {
+ RB_FogPass();
+ }
+
+ //
+ // unlock arrays
+ //
+ if ( qglUnlockArraysEXT ) {
+ qglUnlockArraysEXT();
+ GLimp_LogComment( "glUnlockArraysEXT\n" );
+ }
+}
+
+/*
+** RB_EndSurface
+*/
+void RB_EndSurface( void ) {
+ shaderCommands_t *input;
+
+ input = &tess;
+
+ if (input->numIndexes == 0) {
+ return;
+ }
+
+ if (input->indexes[SHADER_MAX_INDEXES-1] != 0) {
+ ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit");
+ }
+ if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) {
+ ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit");
+ }
+
+ if ( tess.shader == tr.shadowShader ) {
+ RB_ShadowTessEnd();
+ return;
+ }
+
+ // for debugging of sort order issues, stop rendering after a given sort value
+ if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) {
+ return;
+ }
+
+ //
+ // update performance counters
+ //
+ backEnd.pc.c_shaders++;
+ backEnd.pc.c_vertexes += tess.numVertexes;
+ backEnd.pc.c_indexes += tess.numIndexes;
+ backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses;
+
+ //
+ // call off to shader specific tess end function
+ //
+ tess.currentStageIteratorFunc();
+
+ //
+ // draw debugging stuff
+ //
+ if ( r_showtris->integer ) {
+ DrawTris (input);
+ }
+ if ( r_shownormals->integer ) {
+ DrawNormals (input);
+ }
+ // clear shader so we can tell we don't have any unclosed surfaces
+ tess.numIndexes = 0;
+
+ GLimp_LogComment( "----------\n" );
+}
+