/*
====================
GL_SelectTextureNoClient
====================
*/
static void GL_SelectTextureNoClient( int unit ) {
#if !defined(GL_ES_VERSION_2_0)
	backEnd.glState.currenttmu = unit;
	glActiveTextureARB( GL_TEXTURE0_ARB + unit );
	RB_LogComment( "glActiveTextureARB( %i )\n", unit );
#endif
}
Exemple #2
0
/*
======================
RB_SetDefaultGLState

This should initialize all GL state that any part of the entire program
may touch, including the editor.
======================
*/
void RB_SetDefaultGLState( void ) {
	int		i;

	RB_LogComment( "--- R_SetDefaultGLState ---\n" );

	qglClearDepth( 1.0f );
	qglColor4f (1,1,1,1);

	// the vertex array is always enabled
	qglEnableClientState( GL_VERTEX_ARRAY );
	qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
	qglDisableClientState( GL_COLOR_ARRAY );

	//
	// make sure our GL state vector is set correctly
	//
	memset( &backEnd.glState, 0, sizeof( backEnd.glState ) );
	backEnd.glState.forceGlState = true;

	qglColorMask( 1, 1, 1, 1 );

	qglEnable( GL_DEPTH_TEST );
	qglEnable( GL_BLEND );
	qglEnable( GL_SCISSOR_TEST );
	qglEnable( GL_CULL_FACE );
	qglDisable( GL_LIGHTING );
	qglDisable( GL_LINE_STIPPLE );
	qglDisable( GL_STENCIL_TEST );

	qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	qglDepthMask( GL_TRUE );
	qglDepthFunc( GL_ALWAYS );
 
	qglCullFace( GL_FRONT_AND_BACK );
	qglShadeModel( GL_SMOOTH );

	if ( r_useScissor.GetBool() ) {
		qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight );
	}

	for ( i = glConfig.maxTextureUnits - 1 ; i >= 0 ; i-- ) {
		GL_SelectTexture( i );

		// object linear texgen is our default
		qglTexGenf( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
		qglTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
		qglTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
		qglTexGenf( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );

		GL_TexEnv( GL_MODULATE );
		qglDisable( GL_TEXTURE_2D );
		if ( glConfig.texture3DAvailable ) {
			qglDisable( GL_TEXTURE_3D );
		}
		if ( glConfig.cubeMapAvailable ) {
			qglDisable( GL_TEXTURE_CUBE_MAP_EXT );
		}
	}
}
/*
=====================
RB_StencilShadowPass

Stencil test should already be enabled, and the stencil buffer should have
been set to 128 on any surfaces that might receive shadows
=====================
*/
void RB_StencilShadowPass(const drawSurf_t *drawSurfs)
{
	if (!r_shadows.GetBool()) {
		return;
	}

	if (!drawSurfs) {
		return;
	}

	RB_LogComment("---------- RB_StencilShadowPass ----------\n");

	globalImages->BindNull();
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);

	// for visualizing the shadows
	if (r_showShadows.GetInteger()) {
		if (r_showShadows.GetInteger() == 2) {
			// draw filled in
			GL_State(GLS_DEPTHMASK | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_LESS);
		} else {
			// draw as lines, filling the depth buffer
			GL_State(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_POLYMODE_LINE | GLS_DEPTHFUNC_ALWAYS);
		}
	} else {
		// don't write to the color buffer, just the stencil buffer
		GL_State(GLS_DEPTHMASK | GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHFUNC_LESS);
	}

	if (r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat()) {
		qglPolygonOffset(r_shadowPolygonFactor.GetFloat(), -r_shadowPolygonOffset.GetFloat());
		qglEnable(GL_POLYGON_OFFSET_FILL);
	}

	qglStencilFunc(GL_ALWAYS, 1, 255);

	if (glConfig.depthBoundsTestAvailable && r_useDepthBoundsTest.GetBool()) {
		qglEnable(GL_DEPTH_BOUNDS_TEST_EXT);
	}

	RB_RenderDrawSurfChainWithFunction(drawSurfs, RB_T_Shadow);

	GL_Cull(CT_FRONT_SIDED);

	if (r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat()) {
		qglDisable(GL_POLYGON_OFFSET_FILL);
	}

	if (glConfig.depthBoundsTestAvailable && r_useDepthBoundsTest.GetBool()) {
		qglDisable(GL_DEPTH_BOUNDS_TEST_EXT);
	}

	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	qglStencilFunc(GL_GEQUAL, 128, 255);
	qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
/*
==================
RB_NV20_DI_SpecularColorPass

==================
*/
static void RB_NV20_DI_SpecularColorPass(const drawInteraction_t *din)
{
	RB_LogComment("---------- RB_NV20_SpecularColorPass ----------\n");

	GL_State(GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_ALPHAMASK
	         | backEnd.depthFunc);

	// texture 0 is the normalization cube map for the half angle
#ifdef MACOS_X
	GL_SelectTexture(0);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(0);
#endif
	globalImages->normalCubeMapImage->Bind();

	// texture 1 will be the per-surface bump map
#ifdef MACOS_X
	GL_SelectTexture(1);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(1);
#endif
	din->bumpImage->Bind();

	// texture 2 will be the per-surface specular map
#ifdef MACOS_X
	GL_SelectTexture(2);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(2);
#endif
	din->specularImage->Bind();

	// texture 3 will be the light projected texture
#ifdef MACOS_X
	GL_SelectTexture(3);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(3);
#endif
	din->lightImage->Bind();

	// bind our "fragment program"
	RB_NV20_SpecularColorFragment();

	// override one parameter for inverted vertex color
	if (din->vertexColor == SVC_INVERSE_MODULATE) {
		qglCombinerInputNV(GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_B_NV,
		                   GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB);
	}

	// draw it
	qglBindProgramARB(GL_VERTEX_PROGRAM_ARB, VPROG_NV20_SPECULAR_COLOR);
	RB_DrawElementsWithCounters(din->surf->geo);
}
/*
==================
RB_RenderViewLight

==================
*/
static void RB_RenderViewLight(viewLight_t *vLight)
{
	backEnd.vLight = vLight;

	// do fogging later
	if (vLight->lightShader->IsFogLight()) {
		return;
	}

	if (vLight->lightShader->IsBlendLight()) {
		return;
	}

	RB_LogComment("---------- RB_RenderViewLight 0x%p ----------\n", vLight);

	// clear the stencil buffer if needed
	if (vLight->globalShadows || vLight->localShadows) {
		backEnd.currentScissor = vLight->scissorRect;

		if (r_useScissor.GetBool()) {
			qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
			           backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
			           backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
			           backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
		}

		qglClear(GL_STENCIL_BUFFER_BIT);
	} else {
		// no shadows, so no need to read or write the stencil buffer
		// we might in theory want to use GL_ALWAYS instead of disabling
		// completely, to satisfy the invarience rules
		qglStencilFunc(GL_ALWAYS, 128, 255);
	}

	backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
	RB_StencilShadowPass(vLight->globalShadows);
	RB_CreateDrawInteractions(vLight->localInteractions);
	RB_StencilShadowPass(vLight->localShadows);
	RB_CreateDrawInteractions(vLight->globalInteractions);

	if (r_skipTranslucent.GetBool()) {
		return;
	}

	// disable stencil testing for translucent interactions, because
	// the shadow isn't calculated at their point, and the shadow
	// behind them may be depth fighting with a back side, so there
	// isn't any reasonable thing to do
	qglStencilFunc(GL_ALWAYS, 128, 255);
	backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
	RB_CreateDrawInteractions(vLight->translucentInteractions);
}
Exemple #6
0
void Framebuffer::Bind()
{
#if 0
	if( r_logFile.GetBool() )
	{
		RB_LogComment( "--- Framebuffer::Bind( name = '%s' ) ---\n", fboName.c_str() );
	}
#endif
	
	if( backEnd.glState.currentFramebuffer != this )
	{
		glBindFramebuffer( GL_FRAMEBUFFER, frameBuffer );
		backEnd.glState.currentFramebuffer = this;
	}
}
Exemple #7
0
/*
=============
RB_CopyRender

Copy part of the current framebuffer to an image
=============
*/
const void	RB_CopyRender( const void *data ) {
	const copyRenderCommand_t	*cmd;

	cmd = (const copyRenderCommand_t *)data;

	if ( r_skipCopyTexture.GetBool() ) {
		return;
	}

    RB_LogComment( "***************** RB_CopyRender *****************\n" );

	if (cmd->image) {
		cmd->image->CopyFramebuffer( cmd->x, cmd->y, cmd->imageWidth, cmd->imageHeight, false );
	}
}
Exemple #8
0
/*
====================
GL_SelectTexture
====================
*/
void GL_SelectTexture( int unit ) {
	if ( backEnd.glState.currenttmu == unit ) {
		return;
	}

	if ( unit < 0 || (unit >= glConfig.maxTextureUnits && unit >= glConfig.maxTextureImageUnits) ) {
		common->Warning( "GL_SelectTexture: unit = %i", unit );
		return;
	}

	qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
	qglClientActiveTextureARB( GL_TEXTURE0_ARB + unit );
	RB_LogComment( "glActiveTextureARB( %i );\nglClientActiveTextureARB( %i );\n", unit, unit );

	backEnd.glState.currenttmu = unit;
}
Exemple #9
0
/*
=============
RB_SwapBuffers

=============
*/
const void	RB_SwapBuffers( const void *data ) {
	// texture swapping test
	if ( r_showImages.GetInteger() != 0 ) {
		RB_ShowImages();
	}

	// force a gl sync if requested
	if ( r_finish.GetBool() ) {
		qglFinish();
	}

    RB_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" );

	// don't flip if drawing to front buffer
	if ( !r_frontBuffer.GetBool() ) {
	    GLimp_SwapBuffers();
	}
}
/*
=====================
RB_STD_FillDepthBuffer

If we are rendering a subview with a near clip plane, use a second texture
to force the alpha test to fail when behind that clip plane
=====================
*/
void RB_STD_FillDepthBuffer(drawSurf_t **drawSurfs, int numDrawSurfs)
{
	// if we are just doing 2D rendering, no need to fill the depth buffer
	if (!backEnd.viewDef->viewEntitys) {
		return;
	}

	RB_LogComment("---------- RB_STD_FillDepthBuffer ----------\n");

	// enable the second texture for mirror plane clipping if needed
	if (backEnd.viewDef->numClipPlanes) {
		GL_SelectTexture(1);
		globalImages->alphaNotchImage->Bind();
		qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
		qglEnable(GL_TEXTURE_GEN_S);
		qglTexCoord2f(1, 0.5);
	}

	// the first texture will be used for alpha tested surfaces
	GL_SelectTexture(0);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	// decal surfaces may enable polygon offset
	qglPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat());

	GL_State(GLS_DEPTHFUNC_LESS);

	// Enable stencil test if we are going to be using it for shadows.
	// If we didn't do this, it would be legal behavior to get z fighting
	// from the ambient pass and the light passes.
	qglEnable(GL_STENCIL_TEST);
	qglStencilFunc(GL_ALWAYS, 1, 255);

	RB_RenderDrawSurfListWithFunction(drawSurfs, numDrawSurfs, RB_T_FillDepthBuffer);

	if (backEnd.viewDef->numClipPlanes) {
		GL_SelectTexture(1);
		globalImages->BindNull();
		qglDisable(GL_TEXTURE_GEN_S);
		GL_SelectTexture(0);
	}

}
/*
==================
RB_NV20_DI_DiffuseAndSpecularColorPass

==================
*/
static void RB_NV20_DI_DiffuseAndSpecularColorPass(const drawInteraction_t *din)
{
	RB_LogComment("---------- RB_NV20_DI_DiffuseAndSpecularColorPass ----------\n");

	GL_State(GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc);

	// texture 0 is the normalization cube map for the half angle
// still bound from RB_NV_BumpAndLightPass
//	GL_SelectTextureNoClient( 0 );
//	GL_Bind( tr.normalCubeMapImage );

	// texture 1 is the per-surface bump map
// still bound from RB_NV_BumpAndLightPass
//	GL_SelectTextureNoClient( 1 );
//	GL_Bind( din->bumpImage );

	// texture 2 is the per-surface diffuse map
#ifdef MACOS_X
	GL_SelectTexture(2);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(2);
#endif
	din->diffuseImage->Bind();

	// texture 3 is the per-surface specular map
#ifdef MACOS_X
	GL_SelectTexture(3);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(3);
#endif
	din->specularImage->Bind();

	// bind our "fragment program"
	RB_NV20_DiffuseAndSpecularColorFragment();

	// draw it
	qglBindProgramARB(GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_AND_SPECULAR_COLOR);
	RB_DrawElementsWithCounters(din->surf->geo);
}
Exemple #12
0
/*
==================
RB_RenderInteraction

backEnd.vLight
backEnd.lightScale


backEnd.depthFunc must be equal for alpha tested surfaces to work right,
it is set to lessThan for blended transparent surfaces

This expects a bumpmap stage before a diffuse stage before a specular stage
The material code is responsible for guaranteeing that, but conditional stages
can still make it invalid.

you can't blend two bumpmaps, but you can change bump maps between
blended diffuse / specular maps to get the same effect


==================
*/
static void RB_RenderInteraction( const drawSurf_t *surf ) {
	const idMaterial	*surfaceShader = surf->material;
	const float			*surfaceRegs = surf->shaderRegisters;
	const viewLight_t	*vLight = backEnd.vLight;
	const idMaterial	*lightShader = vLight->lightShader;
	const float			*lightRegs = vLight->shaderRegisters;
	static idPlane		lightProject[4];	// reused across function calls
	const srfTriangles_t	*tri = surf->geo;
	const shaderStage_t	*lastBumpStage = NULL;

	RB_LogComment( "---------- RB_RenderInteraction %s on %s ----------\n", 
		lightShader->GetName(), surfaceShader->GetName() );

	// change the matrix and light projection vectors if needed
	if ( surf->space != backEnd.currentSpace ) {
		backEnd.currentSpace = surf->space;
		qglLoadMatrixf( surf->space->modelViewMatrix );

		for ( int i = 0 ; i < 4 ; i++ ) {
			R_GlobalPlaneToLocal( surf->space->modelMatrix, backEnd.vLight->lightProject[i], lightProject[i] );
		}
	}

	// change the scissor if needed
	if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( surf->scissorRect ) ) {
		backEnd.currentScissor = surf->scissorRect;
		qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, 
			backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
			backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
			backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
	}

	// hack depth range if needed
	if ( surf->space->weaponDepthHack ) {
		RB_EnterWeaponDepthHack();
	}

	if ( surf->space->modelDepthHack != 0.0f ) {
		RB_EnterModelDepthHack( surf->space->modelDepthHack );
	}


	// set the vertex arrays, which may not all be enabled on a given pass
	idDrawVert	*ac = (idDrawVert *)vertexCache.Position(tri->ambientCache);
	qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
	GL_SelectTexture( 0 );
	qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
	qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );


	// go through the individual stages
	for ( int i = 0 ; i < surfaceShader->GetNumStages() ; i++ ) {
		const shaderStage_t	*surfaceStage = surfaceShader->GetStage( i );

		// ignore ambient stages while drawing interactions
		if ( surfaceStage->lighting == SL_AMBIENT ) {
			continue;
		}

		// ignore stages that fail the condition
		if ( !surfaceRegs[ surfaceStage->conditionRegister ] ) {
			continue;
		}

		//-----------------------------------------------------
		//
		// bump / falloff
		//
		//-----------------------------------------------------
		if ( surfaceStage->lighting == SL_BUMP ) {
			// render light falloff * bumpmap lighting

			if ( surfaceStage->vertexColor != SVC_IGNORE ) {
				common->Printf( "shader %s: vertexColor on a bump stage\n",
					surfaceShader->GetName() );
			}

			// check for RGBA modulations in the stage, which are also illegal?

			// save the bump map stage for the specular calculation and diffuse
			// error checking
			lastBumpStage = surfaceStage;

			//
			// ambient lights combine non-directional bump and falloff
			// and write to the alpha channel
			//
			if ( lightShader->IsAmbientLight() ) {
				GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );

				// texture 0 will be the per-surface bump map
				GL_SelectTexture( 0 );
				qglEnableClientState( GL_TEXTURE_COORD_ARRAY );

				RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );
				// development aid
				if ( r_skipBump.GetBool() ) {
					globalImages->flatNormalMap->Bind();
				}

				// texture 1 will be the light falloff
				GL_SelectTexture( 1 );

				qglEnable( GL_TEXTURE_GEN_S );
				qglTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[3].ToFloatPtr() );
				qglTexCoord2f( 0, 0.5 );
				vLight->falloffImage->Bind();

				qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 2 );

				// set the constant color to a bit of an angle
				qglCombinerParameterfvNV( GL_CONSTANT_COLOR0_NV, tr.ambientLightVector.ToFloatPtr() );

				// stage 0 sets primary_color = bump dot constant color
				qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, 
					GL_CONSTANT_COLOR0_NV, GL_EXPAND_NORMAL_NV, GL_RGB );
				qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, 
					GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
				qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB, 
					GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
					GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );

				// stage 1 alpha sets primary_color = primary_color * falloff
				qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_A_NV, 
					GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
				qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_B_NV, 
					GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
				qglCombinerOutputNV( GL_COMBINER1_NV, GL_ALPHA, 
					GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
					GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );

				// final combiner takes the result for the alpha channel
				qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
					GL_UNSIGNED_IDENTITY_NV, GL_RGB );
				qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
					GL_UNSIGNED_IDENTITY_NV, GL_RGB );
				qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
					GL_UNSIGNED_IDENTITY_NV, GL_RGB );
				qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
					GL_UNSIGNED_IDENTITY_NV, GL_RGB );
				qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_PRIMARY_COLOR_NV,
					GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );

				// draw it
				RB_DrawElementsWithCounters( tri );

				globalImages->BindNull();
				qglDisable( GL_TEXTURE_GEN_S );

				GL_SelectTexture( 0 );
				RB_FinishStageTexture( &surfaceStage->texture, surf );
				continue;
			}

			//
			// draw light falloff to the alpha channel
			//
			GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );
			qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
			qglDisableClientState( GL_COLOR_ARRAY );
			qglEnable( GL_TEXTURE_GEN_S );
			qglTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[3].ToFloatPtr() );
			qglTexCoord2f( 0, 0.5 );
			vLight->falloffImage->Bind();

			// make sure a combiner output doesn't step on the texture
			qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );
			qglCombinerOutputNV( GL_COMBINER0_NV, GL_ALPHA, 
				GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV,
				GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );

			// final combiner
			qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_TEXTURE0_ARB,
				GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );

			// draw it
			RB_DrawElementsWithCounters( tri );

			qglDisable( GL_TEXTURE_GEN_S );

			//
			// draw the bump map result onto the alpha channel
			//
			GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ZERO | GLS_COLORMASK | GLS_DEPTHMASK 
				| backEnd.depthFunc );

			// texture 0 will be the per-surface bump map
			GL_SelectTexture( 0 );
			qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
			RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );

			// texture 1 is the normalization cube map
			// the texccords are the non-normalized vector towards the light origin
			GL_SelectTexture( 1 );
			globalImages->normalCubeMapImage->Bind();
			qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
			qglTexCoordPointer( 3, GL_FLOAT, sizeof( lightingCache_t ), ((lightingCache_t *)vertexCache.Position(tri->lightingCache))->localLightVector.ToFloatPtr() );

			qglDisableClientState( GL_COLOR_ARRAY );

			// program the nvidia register combiners
			// I just want alpha = Dot( texture0, texture1 )
			qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );

			// stage 0 rgb performs the dot product
			// SPARE0 = TEXTURE0 dot TEXTURE1
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, 
				GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, 
				GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
			qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB, 
				GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
				GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );

			// final combiner just takes the dot result and puts it in alpha
			qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_SPARE0_NV,
				GL_UNSIGNED_IDENTITY_NV, GL_BLUE );

			// draw it
			RB_DrawElementsWithCounters( tri );

			globalImages->BindNull();
			qglDisableClientState( GL_TEXTURE_COORD_ARRAY );

			GL_SelectTexture( 0 );
			RB_FinishStageTexture( &surfaceStage->texture, surf );
			continue;
		}

		if ( surfaceStage->lighting == SL_DIFFUSE ) {
			if ( !lastBumpStage ) {
				common->Printf( "shader %s: diffuse stage without a preceeding bumpmap stage\n",
					surfaceShader->GetName() );
				continue;
			}
		}

		//-----------------------------------------------------
		//
		// specular exponent modification of the bump / falloff
		//
		//-----------------------------------------------------
		if ( surfaceStage->lighting == SL_SPECULAR ) {
			// put specular bump map into alpha channel, then treat as a diffuse

			// allow the specular to be skipped as a user speed optimization
			if ( r_skipSpecular.GetBool() ) {
				continue;
			}

			// ambient lights don't have specular
			if ( lightShader->IsAmbientLight() ) {
				continue;
			}

			if ( !lastBumpStage ) {
				common->Printf( "shader %s: specular stage without a preceeding bumpmap stage\n",
					surfaceShader->GetName() );
				continue;
			}

			GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_SRC_ALPHA | GLS_COLORMASK | GLS_DEPTHMASK 
				| backEnd.depthFunc );

			// texture 0 will be the per-surface bump map
			GL_SelectTexture( 0 );
			qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
			RB_BindStageTexture( surfaceRegs, &lastBumpStage->texture, surf );

			// development aid
			if ( r_skipBump.GetBool() ) {
				globalImages->flatNormalMap->Bind();
			}

			// texture 1 is the normalization cube map
			// indexed by the dynamic halfangle texcoords
			GL_SelectTexture( 1 );
			globalImages->normalCubeMapImage->Bind();
			qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
			qglTexCoordPointer( 4, GL_FLOAT, 0, vertexCache.Position( surf->dynamicTexCoords ) );

			// program the nvidia register combiners
			qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 2 );

			// stage 0 rgb performs the dot product
			// GL_PRIMARY_COLOR_NV = ( TEXTURE0 dot TEXTURE1 - 0.5 ) * 2
			// the scale and bias steepen the specular curve
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, 
				GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, 
				GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
			qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB, 
				GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
				GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_TRUE, GL_FALSE, GL_FALSE );

			// stage 0 alpha does nothing
			qglCombinerOutputNV( GL_COMBINER0_NV, GL_ALPHA, 
				GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV,
				GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );

			// stage 1 rgb does nothing
			qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB, 
				GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
				GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );

			// stage 1 alpha takes bump * bump
			// PRIMARY_COLOR = ( GL_PRIMARY_COLOR_NV * GL_PRIMARY_COLOR_NV - 0.5 ) * 2
			// the scale and bias steepen the specular curve
			qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_A_NV, 
				GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
			qglCombinerInputNV( GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_B_NV, 
				GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
			qglCombinerOutputNV( GL_COMBINER1_NV, GL_ALPHA, 
				GL_PRIMARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
				GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE );

			// final combiner
			qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_PRIMARY_COLOR_NV,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_PRIMARY_COLOR_NV,
				GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );

			// draw it
			RB_DrawElementsWithCounters( tri );

			globalImages->BindNull();
			qglDisableClientState( GL_TEXTURE_COORD_ARRAY );

			GL_SelectTexture( 0 );

			RB_FinishStageTexture( &lastBumpStage->texture, surf );

			// the bump map in the alpha channel is now corrupted, so a normal diffuse
			// map can't be drawn unless a new bumpmap is put down
			lastBumpStage = NULL;

			// fall through to the common handling of diffuse and specular projected lighting
		}

		//-----------------------------------------------------
		//
		// projected light / surface color for diffuse and specular maps
		//
		//-----------------------------------------------------
		if ( surfaceStage->lighting == SL_DIFFUSE || surfaceStage->lighting == SL_SPECULAR ) {
			// don't trash alpha
			GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_ALPHAMASK | GLS_DEPTHMASK 
			| backEnd.depthFunc );

			// texture 0 will get the surface color texture
			GL_SelectTexture( 0 );
			qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
			RB_BindStageTexture( surfaceRegs, &surfaceStage->texture, surf );

			// development aid
			if ( ( surfaceStage->lighting == SL_DIFFUSE && r_skipDiffuse.GetBool() )
				|| ( surfaceStage->lighting == SL_SPECULAR && r_skipSpecular.GetBool() ) ) {
				globalImages->blackImage->Bind();
			}

			// texture 1 will get the light projected texture
			GL_SelectTexture( 1 );
			qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
			qglEnable( GL_TEXTURE_GEN_S );
			qglEnable( GL_TEXTURE_GEN_T );
			qglEnable( GL_TEXTURE_GEN_Q );
			qglTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[0].ToFloatPtr() );
			qglTexGenfv( GL_T, GL_OBJECT_PLANE, lightProject[1].ToFloatPtr() );
			qglTexGenfv( GL_Q, GL_OBJECT_PLANE, lightProject[2].ToFloatPtr() );

			// texture0 * texture1 * primaryColor * constantColor
			qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );

			// SPARE0 = TEXTURE0 * PRIMARY_COLOR
			// SPARE1 = TEXTURE1 * CONSTANT_COLOR
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, 
				GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			// variable B will be overriden based on the stage vertexColor option
			if ( surfaceStage->vertexColor == SVC_MODULATE ) {
				qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, 
					GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
				qglEnableClientState( GL_COLOR_ARRAY );
			} else if ( surfaceStage->vertexColor == SVC_INVERSE_MODULATE ) {
				qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, 
					GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB );
				qglEnableClientState( GL_COLOR_ARRAY );
			} else {	// SVC_IGNORE
				qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, 
					GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB );
				qglDisableClientState( GL_COLOR_ARRAY );
			}
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, 
				GL_TEXTURE1_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, 
				GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB, 
				GL_SPARE0_NV, GL_SPARE1_NV, GL_DISCARD_NV,
				GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );

			// final combiner
			qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_SPARE1_NV,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_SPARE0_NV,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_RGB );
			qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
				GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );

			// for all light stages, multiply the projected color by the surface
			// color, and blend with the framebuffer
			for ( int j = 0 ; j < lightShader->GetNumStages() ; j++ ) {
				const shaderStage_t	*lightStage = lightShader->GetStage( j );
				float	color[4];

				// ignore stages that fail the condition
				if ( !lightRegs[ lightStage->conditionRegister ] ) {
					continue;
				}

				// set the color to the light color times the surface color
				color[0] = backEnd.lightScale
					* lightRegs[ lightStage->color.registers[0] ]
					* surfaceRegs[ surfaceStage->color.registers[0] ];
				color[1] = backEnd.lightScale
					* lightRegs[ lightStage->color.registers[1] ]
					* surfaceRegs[ surfaceStage->color.registers[1] ];
				color[2] = backEnd.lightScale
					* lightRegs[ lightStage->color.registers[2] ]
					* surfaceRegs[ surfaceStage->color.registers[2] ];
				color[3] = 1;

				// don't draw if it would be all black
				if ( color[0] == 0 && color[1] == 0 && color[2] == 0 ) {
					continue;
				}

				qglCombinerParameterfvNV( GL_CONSTANT_COLOR1_NV, color );

				RB_BindStageTexture( lightRegs, &lightStage->texture, surf );

				RB_DrawElementsWithCounters( tri );

				RB_FinishStageTexture( &lightStage->texture, surf );
			}

			if ( surfaceStage->vertexColor != SVC_IGNORE ) {
				qglDisableClientState( GL_COLOR_ARRAY );
			}

			qglDisable( GL_TEXTURE_GEN_S );
			qglDisable( GL_TEXTURE_GEN_T );
			qglDisable( GL_TEXTURE_GEN_Q );

			globalImages->BindNull();
			GL_SelectTexture( 0 );
			RB_FinishStageTexture( &surfaceStage->texture, surf );

			continue;
		}

	}
	// unhack depth range if needed
	if ( surf->space->weaponDepthHack || surf->space->modelDepthHack != 0.0f ) {
		RB_LeaveDepthHack();
	}
}
/*
=============
RB_STD_DrawView

=============
*/
void	RB_STD_DrawView(void)
{
	drawSurf_t	 **drawSurfs;
	int			numDrawSurfs;

	RB_LogComment("---------- RB_STD_DrawView ----------\n");

	backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;

	drawSurfs = (drawSurf_t **)&backEnd.viewDef->drawSurfs[0];
	numDrawSurfs = backEnd.viewDef->numDrawSurfs;

	// clear the z buffer, set the projection matrix, etc
	RB_BeginDrawingView();

	// decide how much overbrighting we are going to do
	RB_DetermineLightScale();

	// fill the depth buffer and clear color buffer to black except on
	// subviews
	RB_STD_FillDepthBuffer(drawSurfs, numDrawSurfs);

	// main light renderer
	switch (tr.backEndRenderer) {
		case BE_ARB:
			RB_ARB_DrawInteractions();
			break;
		case BE_ARB2:
			RB_ARB2_DrawInteractions();
			break;
		case BE_NV20:
			RB_NV20_DrawInteractions();
			break;
		case BE_NV10:
			RB_NV10_DrawInteractions();
			break;
		case BE_R200:
			RB_R200_DrawInteractions();
			break;
	}

	// disable stencil shadow test
	qglStencilFunc(GL_ALWAYS, 128, 255);

	// uplight the entire screen to crutch up not having better blending range
	RB_STD_LightScale();

	// now draw any non-light dependent shading passes
	int	processed = RB_STD_DrawShaderPasses(drawSurfs, numDrawSurfs);

	// fob and blend lights
	RB_STD_FogAllLights();

	// now draw any post-processing effects using _currentRender
	if (processed < numDrawSurfs) {
		RB_STD_DrawShaderPasses(drawSurfs+processed, numDrawSurfs-processed);
	}

	RB_RenderDebugTools(drawSurfs, numDrawSurfs);

}
/*
==================
RB_STD_LightScale

Perform extra blending passes to multiply the entire buffer by
a floating point value
==================
*/
void RB_STD_LightScale(void)
{
	float	v, f;

	if (backEnd.overBright == 1.0f) {
		return;
	}

	if (r_skipLightScale.GetBool()) {
		return;
	}

	RB_LogComment("---------- RB_STD_LightScale ----------\n");

	// the scissor may be smaller than the viewport for subviews
	if (r_useScissor.GetBool()) {
		qglScissor(backEnd.viewDef->viewport.x1 + backEnd.viewDef->scissor.x1,
		           backEnd.viewDef->viewport.y1 + backEnd.viewDef->scissor.y1,
		           backEnd.viewDef->scissor.x2 - backEnd.viewDef->scissor.x1 + 1,
		           backEnd.viewDef->scissor.y2 - backEnd.viewDef->scissor.y1 + 1);
		backEnd.currentScissor = backEnd.viewDef->scissor;
	}

	// full screen blends
	qglLoadIdentity();
	qglMatrixMode(GL_PROJECTION);
	qglPushMatrix();
	qglLoadIdentity();
	qglOrtho(0, 1, 0, 1, -1, 1);

	GL_State(GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_SRC_COLOR);
	GL_Cull(CT_TWO_SIDED);	// so mirror views also get it
	globalImages->BindNull();
	qglDisable(GL_DEPTH_TEST);
	qglDisable(GL_STENCIL_TEST);

	v = 1;

	while (idMath::Fabs(v - backEnd.overBright) > 0.01) {	// a little extra slop
		f = backEnd.overBright / v;
		f /= 2;

		if (f > 1) {
			f = 1;
		}

		qglColor3f(f, f, f);
		v = v * f * 2;

		qglBegin(GL_QUADS);
		qglVertex2f(0,0);
		qglVertex2f(0,1);
		qglVertex2f(1,1);
		qglVertex2f(1,0);
		qglEnd();
	}


	qglPopMatrix();
	qglEnable(GL_DEPTH_TEST);
	qglMatrixMode(GL_MODELVIEW);
	GL_Cull(CT_FRONT_SIDED);
}
/*
==================
RB_STD_FogAllLights
==================
*/
void RB_STD_FogAllLights(void)
{
	viewLight_t	*vLight;

	if (r_skipFogLights.GetBool() || r_showOverDraw.GetInteger() != 0
	    || backEnd.viewDef->isXraySubview /* dont fog in xray mode*/
	   ) {
		return;
	}

	RB_LogComment("---------- RB_STD_FogAllLights ----------\n");

	qglDisable(GL_STENCIL_TEST);

	for (vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next) {
		backEnd.vLight = vLight;

		if (!vLight->lightShader->IsFogLight() && !vLight->lightShader->IsBlendLight()) {
			continue;
		}

#if 0 // _D3XP disabled that

		if (r_ignore.GetInteger()) {
			// we use the stencil buffer to guarantee that no pixels will be
			// double fogged, which happens in some areas that are thousands of
			// units from the origin
			backEnd.currentScissor = vLight->scissorRect;

			if (r_useScissor.GetBool()) {
				qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
				           backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
				           backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
				           backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
			}

			qglClear(GL_STENCIL_BUFFER_BIT);

			qglEnable(GL_STENCIL_TEST);

			// only pass on the cleared stencil values
			qglStencilFunc(GL_EQUAL, 128, 255);

			// when we pass the stencil test and depth test and are going to draw,
			// increment the stencil buffer so we don't ever draw on that pixel again
			qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
		}

#endif

		if (vLight->lightShader->IsFogLight()) {
			RB_FogPass(vLight->globalInteractions, vLight->localInteractions);
		} else if (vLight->lightShader->IsBlendLight()) {
			RB_BlendLight(vLight->globalInteractions, vLight->localInteractions);
		}

		qglDisable(GL_STENCIL_TEST);
	}

	qglEnable(GL_STENCIL_TEST);
}
/*
==================
RB_FogPass
==================
*/
static void RB_FogPass(const drawSurf_t *drawSurfs,  const drawSurf_t *drawSurfs2)
{
	const srfTriangles_t *frustumTris;
	drawSurf_t			ds;
	const idMaterial	*lightShader;
	const shaderStage_t	*stage;
	const float			*regs;

	RB_LogComment("---------- RB_FogPass ----------\n");

	// create a surface for the light frustom triangles, which are oriented drawn side out
	frustumTris = backEnd.vLight->frustumTris;

	// if we ran out of vertex cache memory, skip it
	if (!frustumTris->ambientCache) {
		return;
	}

	memset(&ds, 0, sizeof(ds));
	ds.space = &backEnd.viewDef->worldSpace;
	ds.geo = frustumTris;
	ds.scissorRect = backEnd.viewDef->scissor;

	// find the current color and density of the fog
	lightShader = backEnd.vLight->lightShader;
	regs = backEnd.vLight->shaderRegisters;
	// assume fog shaders have only a single stage
	stage = lightShader->GetStage(0);

	backEnd.lightColor[0] = regs[ stage->color.registers[0] ];
	backEnd.lightColor[1] = regs[ stage->color.registers[1] ];
	backEnd.lightColor[2] = regs[ stage->color.registers[2] ];
	backEnd.lightColor[3] = regs[ stage->color.registers[3] ];

	qglColor3fv(backEnd.lightColor);

	// calculate the falloff planes
	float	a;

	// if they left the default value on, set a fog distance of 500
	if (backEnd.lightColor[3] <= 1.0) {
		a = -0.5f / DEFAULT_FOG_DISTANCE;
	} else {
		// otherwise, distance = alpha color
		a = -0.5f / backEnd.lightColor[3];
	}

	GL_State(GLS_DEPTHMASK | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL);

	// texture 0 is the falloff image
	GL_SelectTexture(0);
	globalImages->fogImage->Bind();
	//GL_Bind( tr.whiteImage );
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglEnable(GL_TEXTURE_GEN_S);
	qglEnable(GL_TEXTURE_GEN_T);
	qglTexCoord2f(0.5f, 0.5f);		// make sure Q is set

	fogPlanes[0][0] = a * backEnd.viewDef->worldSpace.modelViewMatrix[2];
	fogPlanes[0][1] = a * backEnd.viewDef->worldSpace.modelViewMatrix[6];
	fogPlanes[0][2] = a * backEnd.viewDef->worldSpace.modelViewMatrix[10];
	fogPlanes[0][3] = a * backEnd.viewDef->worldSpace.modelViewMatrix[14];

	fogPlanes[1][0] = a * backEnd.viewDef->worldSpace.modelViewMatrix[0];
	fogPlanes[1][1] = a * backEnd.viewDef->worldSpace.modelViewMatrix[4];
	fogPlanes[1][2] = a * backEnd.viewDef->worldSpace.modelViewMatrix[8];
	fogPlanes[1][3] = a * backEnd.viewDef->worldSpace.modelViewMatrix[12];


	// texture 1 is the entering plane fade correction
	GL_SelectTexture(1);
	globalImages->fogEnterImage->Bind();
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglEnable(GL_TEXTURE_GEN_S);
	qglEnable(GL_TEXTURE_GEN_T);

	// T will get a texgen for the fade plane, which is always the "top" plane on unrotated lights
	fogPlanes[2][0] = 0.001f * backEnd.vLight->fogPlane[0];
	fogPlanes[2][1] = 0.001f * backEnd.vLight->fogPlane[1];
	fogPlanes[2][2] = 0.001f * backEnd.vLight->fogPlane[2];
	fogPlanes[2][3] = 0.001f * backEnd.vLight->fogPlane[3];

	// S is based on the view origin
	float s = backEnd.viewDef->renderView.vieworg * fogPlanes[2].Normal() + fogPlanes[2][3];

	fogPlanes[3][0] = 0;
	fogPlanes[3][1] = 0;
	fogPlanes[3][2] = 0;
	fogPlanes[3][3] = FOG_ENTER + s;

	qglTexCoord2f(FOG_ENTER + s, FOG_ENTER);


	// draw it
	RB_RenderDrawSurfChainWithFunction(drawSurfs, RB_T_BasicFog);
	RB_RenderDrawSurfChainWithFunction(drawSurfs2, RB_T_BasicFog);

	// the light frustum bounding planes aren't in the depth buffer, so use depthfunc_less instead
	// of depthfunc_equal
	GL_State(GLS_DEPTHMASK | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_LESS);
	GL_Cull(CT_BACK_SIDED);
	RB_RenderDrawSurfChainWithFunction(&ds, RB_T_BasicFog);
	GL_Cull(CT_FRONT_SIDED);

	GL_SelectTexture(1);
	qglDisable(GL_TEXTURE_GEN_S);
	qglDisable(GL_TEXTURE_GEN_T);
	globalImages->BindNull();

	GL_SelectTexture(0);
	qglDisable(GL_TEXTURE_GEN_S);
	qglDisable(GL_TEXTURE_GEN_T);
}
/*
=====================
RB_BlendLight

Dual texture together the falloff and projection texture with a blend
mode to the framebuffer, instead of interacting with the surface texture
=====================
*/
static void RB_BlendLight(const drawSurf_t *drawSurfs,  const drawSurf_t *drawSurfs2)
{
	const idMaterial	*lightShader;
	const shaderStage_t	*stage;
	int					i;
	const float	*regs;

	if (!drawSurfs) {
		return;
	}

	if (r_skipBlendLights.GetBool()) {
		return;
	}

	RB_LogComment("---------- RB_BlendLight ----------\n");

	lightShader = backEnd.vLight->lightShader;
	regs = backEnd.vLight->shaderRegisters;

	// texture 1 will get the falloff texture
	GL_SelectTexture(1);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglEnable(GL_TEXTURE_GEN_S);
	qglTexCoord2f(0, 0.5);
	backEnd.vLight->falloffImage->Bind();

	// texture 0 will get the projected texture
	GL_SelectTexture(0);
	qglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	qglEnable(GL_TEXTURE_GEN_S);
	qglEnable(GL_TEXTURE_GEN_T);
	qglEnable(GL_TEXTURE_GEN_Q);

	for (i = 0 ; i < lightShader->GetNumStages() ; i++) {
		stage = lightShader->GetStage(i);

		if (!regs[ stage->conditionRegister ]) {
			continue;
		}

		GL_State(GLS_DEPTHMASK | stage->drawStateBits | GLS_DEPTHFUNC_EQUAL);

		GL_SelectTexture(0);
		stage->texture.image->Bind();

		if (stage->texture.hasMatrix) {
			RB_LoadShaderTextureMatrix(regs, &stage->texture);
		}

		// get the modulate values from the light, including alpha, unlike normal lights
		backEnd.lightColor[0] = regs[ stage->color.registers[0] ];
		backEnd.lightColor[1] = regs[ stage->color.registers[1] ];
		backEnd.lightColor[2] = regs[ stage->color.registers[2] ];
		backEnd.lightColor[3] = regs[ stage->color.registers[3] ];
		qglColor4fv(backEnd.lightColor);

		RB_RenderDrawSurfChainWithFunction(drawSurfs, RB_T_BlendLight);
		RB_RenderDrawSurfChainWithFunction(drawSurfs2, RB_T_BlendLight);

		if (stage->texture.hasMatrix) {
			GL_SelectTexture(0);
			qglMatrixMode(GL_TEXTURE);
			qglLoadIdentity();
			qglMatrixMode(GL_MODELVIEW);
		}
	}

	GL_SelectTexture(1);
	qglDisable(GL_TEXTURE_GEN_S);
	globalImages->BindNull();

	GL_SelectTexture(0);
	qglDisable(GL_TEXTURE_GEN_S);
	qglDisable(GL_TEXTURE_GEN_T);
	qglDisable(GL_TEXTURE_GEN_Q);
}
/*
=====================
RB_STD_DrawShaderPasses

Draw non-light dependent passes
=====================
*/
int RB_STD_DrawShaderPasses(drawSurf_t **drawSurfs, int numDrawSurfs)
{
	int				i;

	// only obey skipAmbient if we are rendering a view
	if (backEnd.viewDef->viewEntitys && r_skipAmbient.GetBool()) {
		return numDrawSurfs;
	}

	RB_LogComment("---------- RB_STD_DrawShaderPasses ----------\n");

	// if we are about to draw the first surface that needs
	// the rendering in a texture, copy it over
	if (drawSurfs[0]->material->GetSort() >= SS_POST_PROCESS) {
		if (r_skipPostProcess.GetBool()) {
			return 0;
		}

		// only dump if in a 3d view
		if (backEnd.viewDef->viewEntitys && tr.backEndRenderer == BE_ARB2) {
			globalImages->currentRenderImage->CopyFramebuffer(backEnd.viewDef->viewport.x1,
			                backEnd.viewDef->viewport.y1,  backEnd.viewDef->viewport.x2 -  backEnd.viewDef->viewport.x1 + 1,
			                backEnd.viewDef->viewport.y2 -  backEnd.viewDef->viewport.y1 + 1, true);
		}

		backEnd.currentRenderCopied = true;
	}

	GL_SelectTexture(1);
	globalImages->BindNull();

	GL_SelectTexture(0);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);

	RB_SetProgramEnvironment();

	// we don't use RB_RenderDrawSurfListWithFunction()
	// because we want to defer the matrix load because many
	// surfaces won't draw any ambient passes
	backEnd.currentSpace = NULL;

	for (i = 0  ; i < numDrawSurfs ; i++) {
		if (drawSurfs[i]->material->SuppressInSubview()) {
			continue;
		}

		if (backEnd.viewDef->isXraySubview && drawSurfs[i]->space->entityDef) {
			if (drawSurfs[i]->space->entityDef->parms.xrayIndex != 2) {
				continue;
			}
		}

		// we need to draw the post process shaders after we have drawn the fog lights
		if (drawSurfs[i]->material->GetSort() >= SS_POST_PROCESS
		    && !backEnd.currentRenderCopied) {
			break;
		}

		RB_STD_T_RenderShaderPasses(drawSurfs[i]);
	}

	GL_Cull(CT_FRONT_SIDED);
	qglColor3f(1, 1, 1);

	return i;
}
/*
==================
RB_R200_DrawInteractions

==================
*/
void RB_R200_DrawInteractions( void ) {
	qglEnable( GL_STENCIL_TEST );

	for ( viewLight_t *vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
		// do fogging later
		if ( vLight->lightShader->IsFogLight() ) {
			continue;
		}
		if ( vLight->lightShader->IsBlendLight() ) {
			continue;
		}

		backEnd.vLight = vLight;

		RB_LogComment( "---------- RB_RenderViewLight 0x%p ----------\n", vLight );

		// clear the stencil buffer if needed
		if ( vLight->globalShadows || vLight->localShadows ) {
			backEnd.currentScissor = vLight->scissorRect;
			if ( r_useScissor.GetBool() ) {
				qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, 
					backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
					backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
					backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
			}
			qglClear( GL_STENCIL_BUFFER_BIT );
		} else {
			// no shadows, so no need to read or write the stencil buffer
			// we might in theory want to use GL_ALWAYS instead of disabling
			// completely, to satisfy the invarience rules
			qglStencilFunc( GL_ALWAYS, 128, 255 );
		}

		if ( r_useShadowVertexProgram.GetBool() ) {
			qglEnable( GL_VERTEX_PROGRAM_ARB );
			qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
			RB_StencilShadowPass( vLight->globalShadows );

			RB_R200_ARB_CreateDrawInteractions( vLight->localInteractions );

			qglEnable( GL_VERTEX_PROGRAM_ARB );
			qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
			RB_StencilShadowPass( vLight->localShadows );

			RB_R200_ARB_CreateDrawInteractions( vLight->globalInteractions );

			qglDisable( GL_VERTEX_PROGRAM_ARB );	// if there weren't any globalInteractions, it would have stayed on
		} else {
			RB_StencilShadowPass( vLight->globalShadows );
			RB_R200_ARB_CreateDrawInteractions( vLight->localInteractions );

			RB_StencilShadowPass( vLight->localShadows );
			RB_R200_ARB_CreateDrawInteractions( vLight->globalInteractions );
		}

		if ( r_skipTranslucent.GetBool() ) {
			continue;
		}

		// disable stencil testing for translucent interactions, because
		// the shadow isn't calculated at their point, and the shadow
		// behind them may be depth fighting with a back side, so there
		// isn't any reasonable thing to do
		qglStencilFunc( GL_ALWAYS, 128, 255 );
		RB_R200_ARB_CreateDrawInteractions( vLight->translucentInteractions );
	}
}
/*
==================
RB_NV20_DrawInteractions
==================
*/
void RB_NV20_DrawInteractions(void)
{
	viewLight_t		*vLight;

	//
	// for each light, perform adding and shadowing
	//
	for (vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next) {
		// do fogging later
		if (vLight->lightShader->IsFogLight()) {
			continue;
		}

		if (vLight->lightShader->IsBlendLight()) {
			continue;
		}

		if (!vLight->localInteractions && !vLight->globalInteractions
		    && !vLight->translucentInteractions) {
			continue;
		}

		backEnd.vLight = vLight;

		RB_LogComment("---------- RB_RenderViewLight 0x%p ----------\n", vLight);

		// clear the stencil buffer if needed
		if (vLight->globalShadows || vLight->localShadows) {
			backEnd.currentScissor = vLight->scissorRect;

			if (r_useScissor.GetBool()) {
				qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
				           backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
				           backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
				           backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
			}

			qglClear(GL_STENCIL_BUFFER_BIT);
		} else {
			// no shadows, so no need to read or write the stencil buffer
			// we might in theory want to use GL_ALWAYS instead of disabling
			// completely, to satisfy the invarience rules
			qglStencilFunc(GL_ALWAYS, 128, 255);
		}

		backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;

		if (r_useShadowVertexProgram.GetBool()) {
			qglEnable(GL_VERTEX_PROGRAM_ARB);
			qglBindProgramARB(GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW);
			RB_StencilShadowPass(vLight->globalShadows);
			RB_NV20_CreateDrawInteractions(vLight->localInteractions);
			qglEnable(GL_VERTEX_PROGRAM_ARB);
			qglBindProgramARB(GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW);
			RB_StencilShadowPass(vLight->localShadows);
			RB_NV20_CreateDrawInteractions(vLight->globalInteractions);
			qglDisable(GL_VERTEX_PROGRAM_ARB);	// if there weren't any globalInteractions, it would have stayed on
		} else {
			RB_StencilShadowPass(vLight->globalShadows);
			RB_NV20_CreateDrawInteractions(vLight->localInteractions);
			RB_StencilShadowPass(vLight->localShadows);
			RB_NV20_CreateDrawInteractions(vLight->globalInteractions);
		}

		// translucent surfaces never get stencil shadowed
		if (r_skipTranslucent.GetBool()) {
			continue;
		}

		qglStencilFunc(GL_ALWAYS, 128, 255);

		backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
		RB_NV20_CreateDrawInteractions(vLight->translucentInteractions);

		backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
	}
}
/*
=============
RB_DrawSSAOView
=============
*/
void RB_DrawSSAOView( const void *data ) {
	const drawSurfsCommand_t	*cmd;

	cmd = (const drawSurfsCommand_t *)data;

	backEnd.viewDef = cmd->viewDef;

	// we will need to do a new copyTexSubImage of the screen
	// when a SS_POST_PROCESS material is used
	backEnd.currentRenderCopied = false;

	// if there aren't any drawsurfs, do nothing
	if ( !backEnd.viewDef->numDrawSurfs ) {
		return;
	}

	// skip render bypasses everything that has models, assuming
	// them to be 3D views, but leaves 2D rendering visible
	if ( r_skipRender.GetBool() && backEnd.viewDef->viewEntitys ) {
		return;
	}

	// skip render context sets the wgl context to NULL,
	// which should factor out the API cost, under the assumption
	// that all gl calls just return if the context isn't valid
	if ( r_skipRenderContext.GetBool() && backEnd.viewDef->viewEntitys ) {
		GLimp_DeactivateContext();
	}

	backEnd.pc.c_surfaces += backEnd.viewDef->numDrawSurfs;

//	RB_ShowOverdraw();

	// render the scene, jumping to the hardware specific interaction renderers
//	RB_STD_DrawView();
	drawSurf_t	 **drawSurfs;
	int			numDrawSurfs;

	RB_LogComment( "---------- RB_STD_DrawView ----------\n" );

	backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;

	drawSurfs = (drawSurf_t **)&backEnd.viewDef->drawSurfs[0];
	numDrawSurfs = backEnd.viewDef->numDrawSurfs;

	// clear the z buffer, set the projection matrix, etc
	RB_BeginDrawingView();

	// decide how much overbrighting we are going to do
	//RB_DetermineLightScale();

	// fill the depth buffer and clear color buffer to black except on
	// subviews
	RB_STD_FillDepthBuffer( drawSurfs, numDrawSurfs );

// ---> sikk - copy of current depth buffer
	globalImages->currentDepthImage->CopyDepthbuffer( backEnd.viewDef->viewport.x1, backEnd.viewDef->viewport.y1, 
		backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1,
		backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1 );
// <--- sikk - copy of current depth buffer
		
// restore the context for 2D drawing if we were stubbing it out
	if ( r_skipRenderContext.GetBool() && backEnd.viewDef->viewEntitys ) {
		GLimp_ActivateContext();
		RB_SetDefaultGLState();
	}
}
/*
=============
RB_CreateSingleDrawInteractions

This can be used by different draw_* backends to decompose a complex light / surface
interaction into primitive interactions
=============
*/
void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInteraction)(const drawInteraction_t *) ) {
	const idMaterial	*surfaceShader = surf->material;
	const float			*surfaceRegs = surf->shaderRegisters;
	const viewLight_t	*vLight = backEnd.vLight;
	const idMaterial	*lightShader = vLight->lightShader;
	const float			*lightRegs = vLight->shaderRegisters;
	drawInteraction_t	inter;

	if ( r_skipInteractions.GetBool() || !surf->geo || !surf->geo->ambientCache ) {
		return;
	}

	if ( tr.logFile ) {
		RB_LogComment( "---------- RB_CreateSingleDrawInteractions %s on %s ----------\n", lightShader->GetName(), surfaceShader->GetName() );
	}

	// change the matrix and light projection vectors if needed
	if ( surf->space != backEnd.currentSpace ) {
		backEnd.currentSpace = surf->space;
		qglLoadMatrixf( surf->space->modelViewMatrix );
	}

	// change the scissor if needed
	if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( surf->scissorRect ) ) {
		backEnd.currentScissor = surf->scissorRect;
		qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, 
			backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
			backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
			backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
	}

	// hack depth range if needed
	if ( surf->space->weaponDepthHack ) {
		RB_EnterWeaponDepthHack();
	}

	if ( surf->space->modelDepthHack ) {
		RB_EnterModelDepthHack( surf->space->modelDepthHack );
	}

	inter.surf = surf;
	inter.lightFalloffImage = vLight->falloffImage;

	R_GlobalPointToLocal( surf->space->modelMatrix, vLight->globalLightOrigin, inter.localLightOrigin.ToVec3() );
	R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.viewDef->renderView.vieworg, inter.localViewOrigin.ToVec3() );
	inter.localLightOrigin[3] = 0;
	inter.localViewOrigin[3] = 1;
	inter.ambientLight = lightShader->IsAmbientLight();

	// the base projections may be modified by texture matrix on light stages
	idPlane lightProject[4];
	for ( int i = 0 ; i < 4 ; i++ ) {
		R_GlobalPlaneToLocal( surf->space->modelMatrix, backEnd.vLight->lightProject[i], lightProject[i] );
	}

	for ( int lightStageNum = 0 ; lightStageNum < lightShader->GetNumStages() ; lightStageNum++ ) {
		const shaderStage_t	*lightStage = lightShader->GetStage( lightStageNum );

		// ignore stages that fail the condition
		if ( !lightRegs[ lightStage->conditionRegister ] ) {
			continue;
		}

		inter.lightImage = lightStage->texture.image;

		memcpy( inter.lightProjection, lightProject, sizeof( inter.lightProjection ) );
		// now multiply the texgen by the light texture matrix
		if ( lightStage->texture.hasMatrix ) {
			RB_GetShaderTextureMatrix( lightRegs, &lightStage->texture, backEnd.lightTextureMatrix );
			RB_BakeTextureMatrixIntoTexgen( reinterpret_cast<class idPlane *>(inter.lightProjection), backEnd.lightTextureMatrix );
		}

		inter.bumpImage = NULL;
		inter.specularImage = NULL;
		inter.diffuseImage = NULL;
		inter.diffuseColor[0] = inter.diffuseColor[1] = inter.diffuseColor[2] = inter.diffuseColor[3] = 0;
		inter.specularColor[0] = inter.specularColor[1] = inter.specularColor[2] = inter.specularColor[3] = 0;

		float lightColor[4];

		// backEnd.lightScale is calculated so that lightColor[] will never exceed
		// tr.backEndRendererMaxLight
		lightColor[0] = backEnd.lightScale * lightRegs[ lightStage->color.registers[0] ];
		lightColor[1] = backEnd.lightScale * lightRegs[ lightStage->color.registers[1] ];
		lightColor[2] = backEnd.lightScale * lightRegs[ lightStage->color.registers[2] ];
		lightColor[3] = lightRegs[ lightStage->color.registers[3] ];

		// go through the individual stages
		for ( int surfaceStageNum = 0 ; surfaceStageNum < surfaceShader->GetNumStages() ; surfaceStageNum++ ) {
			const shaderStage_t	*surfaceStage = surfaceShader->GetStage( surfaceStageNum );

			switch( surfaceStage->lighting ) {
				case SL_AMBIENT: {
					// ignore ambient stages while drawing interactions
					break;
				}
				case SL_BUMP: {
					// ignore stage that fails the condition
					if ( !surfaceRegs[ surfaceStage->conditionRegister ] ) {
						break;
					}
					// draw any previous interaction
					RB_SubmittInteraction( &inter, DrawInteraction );
					inter.diffuseImage = NULL;
					inter.specularImage = NULL;
					R_SetDrawInteraction( surfaceStage, surfaceRegs, &inter.bumpImage, inter.bumpMatrix, NULL );
					break;
				}
				case SL_DIFFUSE: {
					// ignore stage that fails the condition
					if ( !surfaceRegs[ surfaceStage->conditionRegister ] ) {
						break;
					}
					if ( inter.diffuseImage ) {
						RB_SubmittInteraction( &inter, DrawInteraction );
					}
					R_SetDrawInteraction( surfaceStage, surfaceRegs, &inter.diffuseImage,
											inter.diffuseMatrix, inter.diffuseColor.ToFloatPtr() );
					inter.diffuseColor[0] *= lightColor[0];
					inter.diffuseColor[1] *= lightColor[1];
					inter.diffuseColor[2] *= lightColor[2];
					inter.diffuseColor[3] *= lightColor[3];
					inter.vertexColor = surfaceStage->vertexColor;
					break;
				}
				case SL_SPECULAR: {
					// ignore stage that fails the condition
					if ( !surfaceRegs[ surfaceStage->conditionRegister ] ) {
						break;
					}
					if ( inter.specularImage ) {
						RB_SubmittInteraction( &inter, DrawInteraction );
					}
					R_SetDrawInteraction( surfaceStage, surfaceRegs, &inter.specularImage,
											inter.specularMatrix, inter.specularColor.ToFloatPtr() );
					inter.specularColor[0] *= lightColor[0];
					inter.specularColor[1] *= lightColor[1];
					inter.specularColor[2] *= lightColor[2];
					inter.specularColor[3] *= lightColor[3];
					inter.vertexColor = surfaceStage->vertexColor;
					break;
				}
			}
		}

		// draw the final interaction
		RB_SubmittInteraction( &inter, DrawInteraction );
	}

	// unhack depth range if needed
	if ( surf->space->weaponDepthHack || surf->space->modelDepthHack != 0.0f ) {
		RB_LeaveDepthHack();
	}
}
Exemple #23
0
/*
====================
GL_SelectTextureNoClient
====================
*/
static void GL_SelectTextureNoClient( int unit ) {
	backEnd.glState.currenttmu = unit;
	qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
	RB_LogComment( "glActiveTextureARB( %i )\n", unit );
}
/*
==================
RB_NV20_DI_BumpAndLightPass

We are going to write alpha as light falloff * ( bump dot light ) * lightProjection
If the light isn't a monoLightShader, the lightProjection will be skipped, because
it will have to be done on an itterated basis
==================
*/
static void RB_NV20_DI_BumpAndLightPass(const drawInteraction_t *din, bool monoLightShader)
{
	RB_LogComment("---------- RB_NV_BumpAndLightPass ----------\n");

	GL_State(GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc);

	// texture 0 is the normalization cube map
	// GL_TEXTURE0_ARB will be the normalized vector
	// towards the light source
#ifdef MACOS_X
	GL_SelectTexture(0);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(0);
#endif

	if (din->ambientLight) {
		globalImages->ambientNormalMap->Bind();
	} else {
		globalImages->normalCubeMapImage->Bind();
	}

	// texture 1 will be the per-surface bump map
#ifdef MACOS_X
	GL_SelectTexture(1);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(1);
#endif
	din->bumpImage->Bind();

	// texture 2 will be the light falloff texture
#ifdef MACOS_X
	GL_SelectTexture(2);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(2);
#endif
	din->lightFalloffImage->Bind();

	// texture 3 will be the light projection texture
#ifdef MACOS_X
	GL_SelectTexture(3);
	qglEnableClientState(GL_TEXTURE_COORD_ARRAY);
#else
	GL_SelectTextureNoClient(3);
#endif

	if (monoLightShader) {
		din->lightImage->Bind();
	} else {
		// if the projected texture is multi-colored, we
		// will need to do it in subsequent passes
		globalImages->whiteImage->Bind();
	}

	// bind our "fragment program"
	RB_NV20_BumpAndLightFragment();

	// draw it
	qglBindProgramARB(GL_VERTEX_PROGRAM_ARB, VPROG_NV20_BUMP_AND_LIGHT);
	RB_DrawElementsWithCounters(din->surf->geo);
}