/* ==================== 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 }
/* ====================== 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); }
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; } }
/* ============= 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 ); } }
/* ==================== 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; }
/* ============= 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); }
/* ================== 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(); } }
/* ==================== 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); }