/* ==================== RB_RenderDrawSurfListWithFunction The triangle functions can check backEnd.currentSpace != surf->space to see if they need to perform any new matrix setup. The modelview matrix will already have been loaded, and backEnd.currentSpace will be updated after the triangle function completes. ==================== */ void RB_RenderDrawSurfListWithFunction( drawSurf_t **drawSurfs, int numDrawSurfs, void (*triFunc_)( const drawSurf_t *) ) { int i; const drawSurf_t *drawSurf; backEnd.currentSpace = NULL; for (i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; // change the matrix if needed if ( drawSurf->space != backEnd.currentSpace ) { qglLoadMatrixf( drawSurf->space->modelViewMatrix ); } if ( drawSurf->space->weaponDepthHack ) { RB_EnterWeaponDepthHack(); } if ( drawSurf->space->modelDepthHack != 0.0f ) { RB_EnterModelDepthHack( drawSurf->space->modelDepthHack ); } // change the scissor if needed if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( drawSurf->scissorRect ) ) { backEnd.currentScissor = drawSurf->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 ); } // render it triFunc_( drawSurf ); if ( drawSurf->space->weaponDepthHack || drawSurf->space->modelDepthHack != 0.0f ) { RB_LeaveDepthHack(); } backEnd.currentSpace = drawSurf->space; } }
/* ================== RB_STD_T_RenderShaderPasses This is also called for the generated 2D rendering ================== */ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) { int stage; const idMaterial *shader; const shaderStage_t *pStage; const float *regs; float color[4]; const srfTriangles_t *tri; tri = surf->geo; shader = surf->material; if ( !shader->HasAmbient() ) { return; } if ( shader->IsPortalSky() ) { return; } // change the matrix if needed if ( surf->space != backEnd.currentSpace ) { qglLoadMatrixf( surf->space->modelViewMatrix ); backEnd.currentSpace = surf->space; RB_SetProgramEnvironmentSpace(); } // 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 ); } // some deforms may disable themselves by setting numIndexes = 0 if ( !tri->numIndexes ) { return; } if ( !tri->ambientCache ) { common->Printf( "RB_T_RenderShaderPasses: !tri->ambientCache\n" ); return; } // get the expressions for conditionals / color / texcoords regs = surf->shaderRegisters; // set face culling appropriately GL_Cull( shader->GetCullType() ); // set polygon offset if necessary if ( shader->TestMaterialFlag(MF_POLYGONOFFSET) ) { qglEnable( GL_POLYGON_OFFSET_FILL ); qglPolygonOffset( r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat() * shader->GetPolygonOffset() ); } if ( surf->space->weaponDepthHack ) { RB_EnterWeaponDepthHack(); } if ( surf->space->modelDepthHack != 0.0f ) { RB_EnterModelDepthHack( surf->space->modelDepthHack ); } idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache ); qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), reinterpret_cast<void *>(&ac->st) ); for ( stage = 0; stage < shader->GetNumStages() ; stage++ ) { pStage = shader->GetStage(stage); // check the enable condition if ( regs[ pStage->conditionRegister ] == 0 ) { continue; } // skip the stages involved in lighting if ( pStage->lighting != SL_AMBIENT ) { continue; } // skip if the stage is ( GL_ZERO, GL_ONE ), which is used for some alpha masks if ( ( pStage->drawStateBits & (GLS_SRCBLEND_BITS|GLS_DSTBLEND_BITS) ) == ( GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE ) ) { continue; } // see if we are a new-style stage newShaderStage_t *newStage = pStage->newStage; if ( newStage ) { //-------------------------- // // new style stages // //-------------------------- // completely skip the stage if we don't have the capability if ( tr.backEndRenderer != BE_ARB2 ) { continue; } if ( r_skipNewAmbient.GetBool() ) { continue; } qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color ); qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() ); qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() ); qglNormalPointer( GL_FLOAT, sizeof( idDrawVert ), ac->normal.ToFloatPtr() ); qglEnableClientState( GL_COLOR_ARRAY ); qglEnableVertexAttribArrayARB( 9 ); qglEnableVertexAttribArrayARB( 10 ); qglEnableClientState( GL_NORMAL_ARRAY ); GL_State( pStage->drawStateBits ); qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, newStage->vertexProgram ); qglEnable( GL_VERTEX_PROGRAM_ARB ); // megaTextures bind a lot of images and set a lot of parameters if ( newStage->megaTexture ) { newStage->megaTexture->SetMappingForSurface( tri ); idVec3 localViewer; R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.viewDef->renderView.vieworg, localViewer ); newStage->megaTexture->BindForViewOrigin( localViewer ); } for ( int i = 0 ; i < newStage->numVertexParms ; i++ ) { float parm[4]; parm[0] = regs[ newStage->vertexParms[i][0] ]; parm[1] = regs[ newStage->vertexParms[i][1] ]; parm[2] = regs[ newStage->vertexParms[i][2] ]; parm[3] = regs[ newStage->vertexParms[i][3] ]; qglProgramLocalParameter4fvARB( GL_VERTEX_PROGRAM_ARB, i, parm ); } for ( int i = 0 ; i < newStage->numFragmentProgramImages ; i++ ) { if ( newStage->fragmentProgramImages[i] ) { GL_SelectTexture( i ); newStage->fragmentProgramImages[i]->Bind(); } } qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, newStage->fragmentProgram ); qglEnable( GL_FRAGMENT_PROGRAM_ARB ); // draw it RB_DrawElementsWithCounters( tri ); for ( int i = 1 ; i < newStage->numFragmentProgramImages ; i++ ) { if ( newStage->fragmentProgramImages[i] ) { GL_SelectTexture( i ); globalImages->BindNull(); } } if ( newStage->megaTexture ) { newStage->megaTexture->Unbind(); } GL_SelectTexture( 0 ); qglDisable( GL_VERTEX_PROGRAM_ARB ); qglDisable( GL_FRAGMENT_PROGRAM_ARB ); // Fixme: Hack to get around an apparent bug in ATI drivers. Should remove as soon as it gets fixed. qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); qglDisableClientState( GL_COLOR_ARRAY ); qglDisableVertexAttribArrayARB( 9 ); qglDisableVertexAttribArrayARB( 10 ); qglDisableClientState( GL_NORMAL_ARRAY ); continue; } //-------------------------- // // old style stages // //-------------------------- // set the color color[0] = regs[ pStage->color.registers[0] ]; color[1] = regs[ pStage->color.registers[1] ]; color[2] = regs[ pStage->color.registers[2] ]; color[3] = regs[ pStage->color.registers[3] ]; // skip the entire stage if an add would be black if ( ( pStage->drawStateBits & (GLS_SRCBLEND_BITS|GLS_DSTBLEND_BITS) ) == ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ) && color[0] <= 0 && color[1] <= 0 && color[2] <= 0 ) { continue; } // skip the entire stage if a blend would be completely transparent if ( ( pStage->drawStateBits & (GLS_SRCBLEND_BITS|GLS_DSTBLEND_BITS) ) == ( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) && color[3] <= 0 ) { continue; } // select the vertex color source if ( pStage->vertexColor == SVC_IGNORE ) { qglColor4fv( color ); } else { qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color ); qglEnableClientState( GL_COLOR_ARRAY ); if ( pStage->vertexColor == SVC_INVERSE_MODULATE ) { GL_TexEnv( GL_COMBINE_ARB ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB ); qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR ); qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_COLOR ); qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1 ); } // for vertex color and modulated color, we need to enable a second // texture stage if ( color[0] != 1 || color[1] != 1 || color[2] != 1 || color[3] != 1 ) { GL_SelectTexture( 1 ); globalImages->whiteImage->Bind(); GL_TexEnv( GL_COMBINE_ARB ); qglTexEnvfv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT_ARB ); qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR ); qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR ); qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1 ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_CONSTANT_ARB ); qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA ); qglTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA ); qglTexEnvi( GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1 ); GL_SelectTexture( 0 ); } } // bind the texture RB_BindVariableStageImage( &pStage->texture, regs ); // set the state GL_State( pStage->drawStateBits ); RB_PrepareStageTexturing( pStage, surf, ac ); // draw it RB_DrawElementsWithCounters( tri ); RB_FinishStageTexturing( pStage, surf, ac ); if ( pStage->vertexColor != SVC_IGNORE ) { qglDisableClientState( GL_COLOR_ARRAY ); GL_SelectTexture( 1 ); GL_TexEnv( GL_MODULATE ); globalImages->BindNull(); GL_SelectTexture( 0 ); GL_TexEnv( GL_MODULATE ); } } // reset polygon offset if ( shader->TestMaterialFlag(MF_POLYGONOFFSET) ) { qglDisable( GL_POLYGON_OFFSET_FILL ); } if ( surf->space->weaponDepthHack || surf->space->modelDepthHack != 0.0f ) { RB_LeaveDepthHack(); } }
/* ================== 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_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(); } }
static void RB_GLSL_SubmitDrawInteractions(const viewLight_t& vLight, const InteractionList& interactionList) { if (interactionList.IsEmpty()) return; // perform setup here that will be constant for all interactions GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc ); GL_UseProgram( interactionProgram ); fhRenderProgram::SetShading( r_shading.GetInteger() ); fhRenderProgram::SetSpecularExp( r_specularExp.GetFloat() ); fhRenderProgram::SetAmbientLight( vLight.lightDef->lightShader->IsAmbientLight() ? 1 : 0 ); if (vLight.lightDef->ShadowMode() == shadowMode_t::ShadowMap) { const idVec4 globalLightOrigin = idVec4( vLight.globalLightOrigin, 1 ); fhRenderProgram::SetGlobalLightOrigin( globalLightOrigin ); const float shadowBrightness = vLight.lightDef->ShadowBrightness(); const float shadowSoftness = vLight.lightDef->ShadowSoftness(); fhRenderProgram::SetShadowParams( idVec4( shadowSoftness, shadowBrightness, vLight.nearClip[0], vLight.farClip[0] ) ); if(vLight.lightDef->parms.parallel) { //parallel light fhRenderProgram::SetShadowMappingMode( 3 ); fhRenderProgram::SetPointLightProjectionMatrices( vLight.viewProjectionMatrices[0].ToFloatPtr() ); fhRenderProgram::SetShadowCoords( vLight.shadowCoords, 6 ); fhRenderProgram::SetCascadeDistances( r_smCascadeDistance0.GetFloat(), r_smCascadeDistance1.GetFloat(), r_smCascadeDistance2.GetFloat(), r_smCascadeDistance3.GetFloat(), r_smCascadeDistance4.GetFloat()); idVec4 shadowmapSizes[6] = { idVec4(vLight.nearClip[0], vLight.farClip[0], vLight.width[0], vLight.height[0]), idVec4(vLight.nearClip[1], vLight.farClip[1], vLight.width[1], vLight.height[1]), idVec4(vLight.nearClip[2], vLight.farClip[2], vLight.width[2], vLight.height[2]), idVec4(vLight.nearClip[3], vLight.farClip[3], vLight.width[3], vLight.height[3]), idVec4(vLight.nearClip[4], vLight.farClip[4], vLight.width[4], vLight.height[4]), idVec4(vLight.nearClip[5], vLight.farClip[5], vLight.width[5], vLight.height[5]) }; fhRenderProgram::SetShadowMapSize(shadowmapSizes, 6); } else if (vLight.lightDef->parms.pointLight) { //point light fhRenderProgram::SetShadowMappingMode( 1 ); fhRenderProgram::SetPointLightProjectionMatrices( vLight.viewProjectionMatrices[0].ToFloatPtr() ); fhRenderProgram::SetShadowCoords(vLight.shadowCoords, 6); { const idMat3 axis = vLight.lightDef->parms.axis; float viewerMatrix[16]; viewerMatrix[0] = axis[0][0]; viewerMatrix[4] = axis[0][1]; viewerMatrix[8] = axis[0][2]; viewerMatrix[12] = 0; viewerMatrix[1] = axis[1][0]; viewerMatrix[5] = axis[1][1]; viewerMatrix[9] = axis[1][2]; viewerMatrix[13] = 0; viewerMatrix[2] = axis[2][0]; viewerMatrix[6] = axis[2][1]; viewerMatrix[10] = axis[2][2]; viewerMatrix[14] = 0; viewerMatrix[3] = 0; viewerMatrix[7] = 0; viewerMatrix[11] = 0; viewerMatrix[15] = 1; fhRenderProgram::SetInverseLightRotation( viewerMatrix ); } } else { //projected light fhRenderProgram::SetShadowMappingMode( 2 ); fhRenderProgram::SetSpotLightProjectionMatrix( vLight.viewProjectionMatrices[0].ToFloatPtr() ); fhRenderProgram::SetShadowCoords(vLight.shadowCoords, 1); } } else { //no shadows fhRenderProgram::SetShadowMappingMode( 0 ); } //make sure depth hacks are disabled //FIXME(johl): why is (sometimes) a depth hack enabled at this point? RB_LeaveDepthHack(); fhRenderProgram::SetProjectionMatrix( backEnd.viewDef->projectionMatrix ); fhRenderProgram::SetPomMaxHeight( -1 ); const viewEntity_t* currentSpace = nullptr; stageVertexColor_t currentVertexColor = (stageVertexColor_t)-1; bool currentPomEnabled = false; idScreenRect currentScissor; bool depthHackActive = false; bool currentHasBumpMatrix = false; bool currentHasDiffuseMatrix = false; bool currentHasSpecularMatrix = false; idVec4 currentDiffuseColor = idVec4( 1, 1, 1, 1 ); idVec4 currentSpecularColor = idVec4( 1, 1, 1, 1 ); fhRenderProgram::SetDiffuseColor( currentDiffuseColor ); fhRenderProgram::SetSpecularColor( currentSpecularColor ); fhRenderProgram::SetBumpMatrix( idVec4::identityS, idVec4::identityT ); fhRenderProgram::SetSpecularMatrix( idVec4::identityS, idVec4::identityT ); fhRenderProgram::SetDiffuseMatrix( idVec4::identityS, idVec4::identityT ); glDepthRange(0, 1); if (r_useScissor.GetBool()) { auto fb = fhFramebuffer::GetCurrentDrawBuffer(); glScissor( 0, 0, fb->GetWidth(), fb->GetHeight() ); currentScissor.x1 = 0; currentScissor.y1 = 0; currentScissor.x2 = fb->GetWidth(); currentScissor.y2 = fb->GetHeight(); } const int num = interactionList.Num(); for (int i = 0; i < num; ++i) { const auto& din = interactionList[i]; const auto offset = vertexCache.Bind( din.surf->geo->ambientCache ); GL_SetupVertexAttributes( fhVertexLayout::Draw, offset ); if (currentSpace != din.surf->space) { fhRenderProgram::SetModelMatrix( din.surf->space->modelMatrix ); fhRenderProgram::SetModelViewMatrix( din.surf->space->modelViewMatrix ); if (din.surf->space->modelDepthHack) { RB_EnterModelDepthHack( din.surf->space->modelDepthHack ); fhRenderProgram::SetProjectionMatrix( GL_ProjectionMatrix.Top() ); depthHackActive = true; } else if (din.surf->space->weaponDepthHack) { RB_EnterWeaponDepthHack(); fhRenderProgram::SetProjectionMatrix( GL_ProjectionMatrix.Top() ); depthHackActive = true; } else if (depthHackActive) { RB_LeaveDepthHack(); fhRenderProgram::SetProjectionMatrix( GL_ProjectionMatrix.Top() ); depthHackActive = false; } // change the scissor if needed if (r_useScissor.GetBool() && !currentScissor.Equals( din.surf->scissorRect )) { currentScissor = din.surf->scissorRect; glScissor( backEnd.viewDef->viewport.x1 + currentScissor.x1, backEnd.viewDef->viewport.y1 + currentScissor.y1, currentScissor.x2 + 1 - currentScissor.x1, currentScissor.y2 + 1 - currentScissor.y1 ); } currentSpace = din.surf->space; } fhRenderProgram::SetLocalLightOrigin( din.localLightOrigin ); fhRenderProgram::SetLocalViewOrigin( din.localViewOrigin ); fhRenderProgram::SetLightProjectionMatrix( din.lightProjection[0], din.lightProjection[1], din.lightProjection[2] ); fhRenderProgram::SetLightFallOff( din.lightProjection[3] ); if (din.hasBumpMatrix) { fhRenderProgram::SetBumpMatrix( din.bumpMatrix[0], din.bumpMatrix[1] ); currentHasBumpMatrix = true; } else if (currentHasBumpMatrix) { fhRenderProgram::SetBumpMatrix( idVec4::identityS, idVec4::identityT ); currentHasBumpMatrix = false; } if (din.hasDiffuseMatrix) { fhRenderProgram::SetDiffuseMatrix( din.diffuseMatrix[0], din.diffuseMatrix[1] ); currentHasDiffuseMatrix = true; } else if (currentHasDiffuseMatrix) { fhRenderProgram::SetDiffuseMatrix( idVec4::identityS, idVec4::identityT ); currentHasDiffuseMatrix = false; } if (din.hasSpecularMatrix) { fhRenderProgram::SetSpecularMatrix( din.specularMatrix[0], din.specularMatrix[1] ); currentHasSpecularMatrix = true; } else if (currentHasSpecularMatrix) { fhRenderProgram::SetSpecularMatrix( idVec4::identityS, idVec4::identityT ); currentHasSpecularMatrix = false; } if (currentVertexColor != din.vertexColor) { switch (din.vertexColor) { case SVC_IGNORE: fhRenderProgram::SetColorModulate( idVec4::zero ); fhRenderProgram::SetColorAdd( idVec4::one ); break; case SVC_MODULATE: fhRenderProgram::SetColorModulate( idVec4::one ); fhRenderProgram::SetColorAdd( idVec4::zero ); break; case SVC_INVERSE_MODULATE: fhRenderProgram::SetColorModulate( idVec4::negOne ); fhRenderProgram::SetColorAdd( idVec4::one ); break; } currentVertexColor = din.vertexColor; } if (din.diffuseColor != currentDiffuseColor) { fhRenderProgram::SetDiffuseColor( din.diffuseColor ); currentDiffuseColor = din.diffuseColor; } if (din.specularColor != currentSpecularColor) { fhRenderProgram::SetSpecularColor( din.specularColor ); currentSpecularColor = din.specularColor; } const bool pomEnabled = r_pomEnabled.GetBool() && din.specularImage->hasAlpha; if (pomEnabled != currentPomEnabled) { if (pomEnabled) { fhRenderProgram::SetPomMaxHeight( r_pomMaxHeight.GetFloat() ); } else { fhRenderProgram::SetPomMaxHeight( -1 ); } } fhRenderProgram::SetNormalMapEncoding( RB_GetNormalEncoding( din.bumpImage ) ); din.bumpImage->Bind( 1 ); din.lightFalloffImage->Bind( 2 ); din.lightImage->Bind( 3 ); din.diffuseImage->Bind( 4 ); din.specularImage->Bind( 5 ); // draw it backEnd.stats.groups[backEndGroup::Interaction].drawcalls += 1; backEnd.stats.groups[backEndGroup::Interaction].tris += din.surf->geo->numIndexes / 3; RB_DrawElementsWithCounters( din.surf->geo ); } if (depthHackActive) { RB_LeaveDepthHack(); fhRenderProgram::SetProjectionMatrix( GL_ProjectionMatrix.Top() ); } if (r_useScissor.GetBool()) { auto fb = fhFramebuffer::GetCurrentDrawBuffer(); glScissor( 0, 0, fb->GetWidth(), fb->GetHeight() ); backEnd.currentScissor.x1 = 0; backEnd.currentScissor.y1 = 0; backEnd.currentScissor.x2 = fb->GetWidth(); backEnd.currentScissor.y2 = fb->GetHeight(); } }