/* * R_RenderMeshGLSL_DynamicLights */ static void R_RenderMeshGLSL_DynamicLights( r_glslfeat_t programFeatures ) { int state; int program, object; shaderpass_t *pass = r_back.accumPasses[0]; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; programFeatures |= R_DlightbitsToProgramFeatures(); GL_SelectTexture( 0 ); GL_SetTexCoordArrayMode( 0 ); GL_TexEnv( GL_MODULATE ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); // update uniforms program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); R_UpdateProgramLightsParams( program, ri.currententity->origin, ri.currententity->axis, r_back.currentDlightBits ); r_back.doDynamicLightsPass = qfalse; R_FlushArrays(); qglUseProgramObjectARB( 0 ); } }
/* * R_RenderMeshGLSL_PlanarShadow */ static void R_RenderMeshGLSL_PlanarShadow( r_glslfeat_t programFeatures ) { int state; int program, object; shaderpass_t *pass = r_back.accumPasses[0]; float planeDist; vec3_t planeNormal, lightDir; if( !R_DeformVPlanarShadowParams( planeNormal, &planeDist, lightDir ) ) return; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; GL_SelectTexture( 0 ); GL_SetTexCoordArrayMode( 0 ); qglDisable( GL_TEXTURE_2D ); if( glState.stencilEnabled ) qglEnable( GL_STENCIL_TEST ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ); GL_SetState( state ); // calculate the fragment color R_ModifyColor( pass, qtrue, qtrue ); // update uniforms program = R_RegisterGLSLProgram( GLSL_PROGRAM_TYPE_PLANAR_SHADOW, DEFAULT_GLSL_PLANAR_SHADOW_PROGRAM, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); R_UpdateProgramPlanarShadowParams( program, r_back.currentShaderTime, planeNormal, planeDist, lightDir ); // submit animation data if( programFeatures & GLSL_COMMON_APPLY_BONETRANSFORMS ) { R_UpdateProgramBonesParams( program, r_back.currentAnimData->numBones, r_back.currentAnimData->dualQuats ); } R_FlushArrays(); qglUseProgramObjectARB( 0 ); } if( glState.stencilEnabled ) qglDisable( GL_STENCIL_TEST ); qglEnable( GL_TEXTURE_2D ); }
/* * R_RenderMeshGLSL_Outline */ static void R_RenderMeshGLSL_Outline( r_glslfeat_t programFeatures ) { int faceCull; int state; int program, object; shaderpass_t *pass = r_back.accumPasses[0]; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; if( ri.currentmodel && ri.currentmodel->type == mod_brush ) programFeatures |= GLSL_OUTLINE_APPLY_OUTLINES_CUTOFF; // update uniforms program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( !object ) return; faceCull = glState.faceCull; GL_Cull( GL_BACK ); GL_SelectTexture( 0 ); GL_SetTexCoordArrayMode( 0 ); // calculate the fragment color R_ModifyColor( pass, qfalse, qfalse ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); qglUseProgramObjectARB( object ); R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, vec3_origin, NULL, NULL, NULL, qtrue, 0, 0, ri.currententity->outlineHeight * r_outlines_scale->value, 0, 0, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); // submit animation data if( programFeatures & GLSL_COMMON_APPLY_BONETRANSFORMS ) { R_UpdateProgramBonesParams( program, r_back.currentAnimData->numBones, r_back.currentAnimData->dualQuats ); } R_FlushArrays(); qglUseProgramObjectARB( 0 ); GL_Cull( faceCull ); }
/* ============== GL_CleanUpTextureUnits ============== */ void GL_CleanUpTextureUnits( int last ) { int i; for( i = glState.activeTMU; i > (last - 1); i-- ) { // disable upper units if( glState.currentTextureTargets[i] != GL_NONE ) { pglDisable( glState.currentTextureTargets[i] ); glState.currentTextureTargets[i] = GL_NONE; glState.currentTextures[i] = -1; // unbind texture } GL_SetTexCoordArrayMode( GL_NONE ); GL_LoadIdentityTexMatrix(); GL_DisableAllTexGens(); GL_SelectTexture( i - 1 ); } }
/* * R_RenderMeshGLSL_Distortion */ static void R_RenderMeshGLSL_Distortion( r_glslfeat_t programFeatures ) { int i, last_slot; unsigned last_framenum; int state, tcgen; int width = 1, height = 1; int program, object; mat4x4_t unused; cplane_t plane; const char *key; shaderpass_t *pass = r_back.accumPasses[0]; image_t *portaltexture[2]; qboolean frontPlane; PlaneFromPoints( r_back.r_triangle0Copy, &plane ); plane.dist += DotProduct( ri.currententity->origin, plane.normal ); key = R_PortalKeyForPlane( &plane ); last_framenum = last_slot = 0; for( i = 0; i < 2; i++ ) { int slot; portaltexture[i] = NULL; slot = R_FindPortalTextureSlot( key, i+1 ); if( slot ) portaltexture[i] = r_portaltextures[slot-1]; if( portaltexture[i] == NULL ) { portaltexture[i] = r_blacktexture; } else { width = portaltexture[i]->upload_width; height = portaltexture[i]->upload_height; } // find the most recently updated texture if( portaltexture[i]->framenum > last_framenum ) { last_slot = i; last_framenum = i; } } // if textures were not updated sequentially, use the most recent one // and reset the remaining to black if( portaltexture[0]->framenum+1 != portaltexture[1]->framenum ) portaltexture[(last_slot+1)&1] = r_blacktexture; if( pass->anim_frames[0] != r_blankbumptexture ) programFeatures |= GLSL_DISTORTION_APPLY_DUDV; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; if( pass->flags & SHADERPASS_GRAYSCALE ) programFeatures |= GLSL_COMMON_APPLY_GRAYSCALE; if( portaltexture[0] != r_blacktexture ) programFeatures |= GLSL_DISTORTION_APPLY_REFLECTION; if( portaltexture[1] != r_blacktexture ) programFeatures |= GLSL_DISTORTION_APPLY_REFRACTION; frontPlane = (PlaneDiff( ri.viewOrigin, &ri.portalPlane ) > 0 ? qtrue : qfalse); if( frontPlane ) { if( pass->alphagen.type != ALPHA_GEN_IDENTITY ) programFeatures |= GLSL_DISTORTION_APPLY_DISTORTION_ALPHA; } tcgen = pass->tcgen; // store the original tcgen R_BindShaderpass( pass, pass->anim_frames[0], 0, NULL ); // dudvmap // calculate the fragment color R_ModifyColor( pass, programFeatures & GLSL_DISTORTION_APPLY_DISTORTION_ALPHA ? qtrue : qfalse, qfalse ); GL_TexEnv( GL_MODULATE ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); if( pass->anim_frames[1] ) { // eyeDot programFeatures |= GLSL_DISTORTION_APPLY_EYEDOT; pass->tcgen = TC_GEN_SVECTORS; GL_Bind( 1, pass->anim_frames[1] ); // normalmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, 1, unused, NULL ); } GL_Bind( 2, portaltexture[0] ); // reflection GL_Bind( 3, portaltexture[1] ); // refraction pass->tcgen = tcgen; // restore original tcgen // update uniforms program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, NULL, NULL, 0, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, vec3_origin, NULL, NULL, NULL, frontPlane, width, height, 0, 0, 0, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); R_FlushArrays(); qglUseProgramObjectARB( 0 ); } }
/* * R_RenderMeshGLSL_Material */ static void R_RenderMeshGLSL_Material( r_glslfeat_t programFeatures ) { int i; int tcgen, rgbgen; int state; int program, object; image_t *base, *normalmap, *glossmap, *decalmap, *entdecalmap; mat4x4_t unused; vec3_t lightDir = { 0.0f, 0.0f, 0.0f }; vec4_t ambient = { 0.0f, 0.0f, 0.0f, 0.0f }, diffuse = { 0.0f, 0.0f, 0.0f, 0.0f }; float offsetmappingScale, glossExponent; const superLightStyle_t *lightStyle = NULL; const mfog_t *fog = r_back.colorFog; shaderpass_t *pass = r_back.accumPasses[0]; qboolean applyDecal; // handy pointers base = pass->anim_frames[0]; normalmap = pass->anim_frames[1]; glossmap = pass->anim_frames[2]; decalmap = pass->anim_frames[3]; entdecalmap = pass->anim_frames[4]; tcgen = pass->tcgen; // store the original tcgen rgbgen = pass->rgbgen.type; // store the original rgbgen assert( normalmap ); if( normalmap->samples == 4 ) offsetmappingScale = r_offsetmapping_scale->value * r_back.currentShader->offsetmapping_scale; else // no alpha in normalmap, don't bother with offset mapping offsetmappingScale = 0; if( r_back.currentShader->gloss_exponent ) glossExponent = r_back.currentShader->gloss_exponent; else glossExponent = r_lighting_glossexponent->value; applyDecal = decalmap != NULL; if( ri.params & RP_CLIPPLANE ) programFeatures |= GLSL_COMMON_APPLY_CLIPPING; if( pass->flags & SHADERPASS_GRAYSCALE ) programFeatures |= GLSL_COMMON_APPLY_GRAYSCALE; if( fog ) { programFeatures |= GLSL_COMMON_APPLY_FOG; if( fog != ri.fog_eye ) programFeatures |= GLSL_COMMON_APPLY_FOG2; if( GL_IsAlphaBlending( pass->flags & GLSTATE_SRCBLEND_MASK, pass->flags & GLSTATE_DSTBLEND_MASK ) ) programFeatures |= GLSL_COMMON_APPLY_COLOR_FOG_ALPHA; } if( r_back.currentMeshBuffer->infokey > 0 && ( rgbgen != RGB_GEN_LIGHTING_DIFFUSE ) ) { if( !( r_offsetmapping->integer & 1 ) ) { offsetmappingScale = 0; } if( ri.params & RP_LIGHTMAP ) { programFeatures |= GLSL_MATERIAL_APPLY_BASETEX_ALPHA_ONLY; } if( ( ri.params & RP_DRAWFLAT ) && !( r_back.currentShader->flags & SHADER_NODRAWFLAT ) ) { programFeatures |= GLSL_COMMON_APPLY_DRAWFLAT|GLSL_MATERIAL_APPLY_BASETEX_ALPHA_ONLY; } } else if( ( r_back.currentMeshBuffer->sortkey & 3 ) == MB_POLY ) { // polys if( !( r_offsetmapping->integer & 2 ) ) offsetmappingScale = 0; R_BuildTangentVectors( r_backacc.numVerts, vertsArray, normalsArray, coordsArray, r_backacc.numElems/3, elemsArray, inSVectorsArray ); } else { // models and world lightingDiffuse materials if( r_back.currentMeshBuffer->infokey > 0 ) { if( !( r_offsetmapping->integer & 1 ) ) offsetmappingScale = 0; pass->rgbgen.type = RGB_GEN_VERTEX; } else { // models if( !( r_offsetmapping->integer & 4 ) ) offsetmappingScale = 0; #ifdef CELLSHADEDMATERIAL programFeatures |= GLSL_MATERIAL_APPLY_CELLSHADING; #endif #ifdef HALFLAMBERTLIGHTING programFeatures |= GLSL_MATERIAL_APPLY_HALFLAMBERT; #endif } } // add dynamic lights if( r_back.currentDlightBits ) { programFeatures |= R_DlightbitsToProgramFeatures(); } pass->tcgen = TC_GEN_BASE; R_BindShaderpass( pass, base, 0, &programFeatures ); // calculate vertex color R_ModifyColor( pass, applyDecal, r_back.currentMeshVBO != NULL ); // since R_ModifyColor has forcefully generated RGBA for the first vertex, // set the proper number of color elements here, so GL_COLOR_ARRAY will be enabled // before the DrawElements call if( !(pass->flags & SHADERPASS_NOCOLORARRAY) ) r_backacc.numColors = r_backacc.numVerts; // convert rgbgen and alphagen to GLSL feature defines programFeatures |= R_RGBAlphaGenToProgramFeatures( pass->rgbgen.type, pass->alphagen.type ); GL_TexEnv( GL_MODULATE ); // set shaderpass state (blending, depthwrite, etc) state = r_back.currentShaderState | ( pass->flags & r_back.currentShaderPassMask ) | GLSTATE_BLEND_MTEX; GL_SetState( state ); #if 1 // don't waste time on processing GLSL programs with zero colormask if( ( ri.params & RP_SHADOWMAPVIEW ) & !(programFeatures & GLSL_COMMON_APPLY_BONETRANSFORMS) ) { pass->tcgen = tcgen; // restore original tcgen R_FlushArrays(); return; } #endif // we only send S-vectors to GPU and recalc T-vectors as cross product // in vertex shader pass->tcgen = TC_GEN_SVECTORS; GL_Bind( 1, normalmap ); // normalmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, 1, unused, NULL ); if( glossmap && r_lighting_glossintensity->value ) { programFeatures |= GLSL_MATERIAL_APPLY_SPECULAR; GL_Bind( 2, glossmap ); // gloss GL_SetTexCoordArrayMode( 0 ); } if( applyDecal ) { programFeatures |= GLSL_MATERIAL_APPLY_DECAL; if( ri.params & RP_LIGHTMAP ) { decalmap = r_blacktexture; programFeatures |= GLSL_MATERIAL_APPLY_DECAL_ADD; } else { // if no alpha, use additive blending if( decalmap->samples == 3 ) programFeatures |= GLSL_MATERIAL_APPLY_DECAL_ADD; } GL_Bind( 3, decalmap ); // decal GL_SetTexCoordArrayMode( 0 ); } if( entdecalmap ) { programFeatures |= GLSL_MATERIAL_APPLY_ENTITY_DECAL; // if no alpha, use additive blending if( entdecalmap->samples == 3 ) programFeatures |= GLSL_MATERIAL_APPLY_ENTITY_DECAL_ADD; GL_Bind( 4, entdecalmap ); // decal GL_SetTexCoordArrayMode( 0 ); } if( offsetmappingScale > 0 ) programFeatures |= r_offsetmapping_reliefmapping->integer ? GLSL_MATERIAL_APPLY_RELIEFMAPPING : GLSL_MATERIAL_APPLY_OFFSETMAPPING; if( r_back.currentMeshBuffer->infokey > 0 && ( rgbgen != RGB_GEN_LIGHTING_DIFFUSE ) ) { // world surface if( r_back.superLightStyle && r_back.superLightStyle->lightmapNum[0] >= 0 ) { lightStyle = r_back.superLightStyle; // bind lightmap textures and set program's features for lightstyles pass->tcgen = TC_GEN_LIGHTMAP; for( i = 0; i < MAX_LIGHTMAPS && lightStyle->lightmapStyles[i] != 255; i++ ) { r_back.lightmapStyleNum[i+4] = i; GL_Bind( i+4, r_worldbrushmodel->lightmapImages[lightStyle->lightmapNum[i]] ); // lightmap GL_SetTexCoordArrayMode( GL_TEXTURE_COORD_ARRAY ); R_VertexTCBase( pass, i+4, unused, NULL ); } programFeatures |= ( i * GLSL_MATERIAL_APPLY_LIGHTSTYLE0 ); if( i == 1 && !mapConfig.lightingIntensity ) { vec_t *rgb = r_lightStyles[lightStyle->lightmapStyles[0]].rgb; // GLSL_MATERIAL_APPLY_FB_LIGHTMAP indicates that there's no need to renormalize // the lighting vector for specular (saves 3 adds, 3 muls and 1 normalize per pixel) if( rgb[0] == 1 && rgb[1] == 1 && rgb[2] == 1 ) programFeatures |= GLSL_MATERIAL_APPLY_FB_LIGHTMAP; } if( !VectorCompare( mapConfig.ambient, vec3_origin ) ) { VectorCopy( mapConfig.ambient, ambient ); programFeatures |= GLSL_MATERIAL_APPLY_AMBIENT_COMPENSATION; } } } else { vec3_t temp; programFeatures |= GLSL_MATERIAL_APPLY_DIRECTIONAL_LIGHT; if( ( r_back.currentMeshBuffer->sortkey & 3 ) == MB_POLY ) { VectorCopy( r_polys[-r_back.currentMeshBuffer->infokey-1].normal, lightDir ); Vector4Set( ambient, 0, 0, 0, 0 ); Vector4Set( diffuse, 1, 1, 1, 1 ); } else if( ri.currententity ) { if( ri.currententity->flags & RF_FULLBRIGHT ) { Vector4Set( ambient, 1, 1, 1, 1 ); Vector4Set( diffuse, 1, 1, 1, 1 ); } else { if( r_back.currentMeshBuffer->infokey > 0 ) { programFeatures |= GLSL_MATERIAL_APPLY_DIRECTIONAL_LIGHT_MIX; if( r_back.overBrightBits ) programFeatures |= GLSL_COMMON_APPLY_OVERBRIGHT_SCALING; } if( ri.currententity->model && ri.currententity != r_worldent ) { // get weighted incoming direction of world and dynamic lights R_LightForOrigin( ri.currententity->lightingOrigin, temp, ambient, diffuse, ri.currententity->model->radius * ri.currententity->scale); } else { VectorSet( temp, 0.1f, 0.2f, 0.7f ); } if( ri.currententity->flags & RF_MINLIGHT ) { if( ambient[0] <= 0.1f || ambient[1] <= 0.1f || ambient[2] <= 0.1f ) VectorSet( ambient, 0.1f, 0.1f, 0.1f ); } // rotate direction Matrix_TransformVector( ri.currententity->axis, temp, lightDir ); } } } pass->tcgen = tcgen; // restore original tcgen pass->rgbgen.type = rgbgen; // restore original rgbgen program = R_RegisterGLSLProgram( pass->program_type, pass->program, NULL, r_back.currentShader->name, r_back.currentShader->deforms, r_back.currentShader->numdeforms, programFeatures ); object = R_GetProgramObject( program ); if( object ) { qglUseProgramObjectARB( object ); // update uniforms R_UpdateProgramUniforms( program, ri.viewOrigin, vec3_origin, lightDir, ambient, diffuse, lightStyle, qtrue, 0, 0, 0, offsetmappingScale, glossExponent, colorArrayCopy[0], r_back.overBrightBits, r_back.currentShaderTime, r_back.entityColor ); if( programFeatures & GLSL_COMMON_APPLY_FOG ) { cplane_t fogPlane, vpnPlane; R_TransformFogPlanes( fog, fogPlane.normal, &fogPlane.dist, vpnPlane.normal, &vpnPlane.dist ); R_UpdateProgramFogParams( program, fog->shader->fog_color, fog->shader->fog_clearDist, fog->shader->fog_dist, &fogPlane, &vpnPlane, ri.fog_dist_to_eye[fog-r_worldbrushmodel->fogs] ); } // submit animation data if( programFeatures & GLSL_COMMON_APPLY_BONETRANSFORMS ) { R_UpdateProgramBonesParams( program, r_back.currentAnimData->numBones, r_back.currentAnimData->dualQuats ); } // dynamic lights if( r_back.currentDlightBits ) { R_UpdateProgramLightsParams( program, ri.currententity->origin, ri.currententity->axis, r_back.currentDlightBits ); r_back.doDynamicLightsPass = qfalse; } // r_drawflat if( programFeatures & GLSL_COMMON_APPLY_DRAWFLAT ) { R_UpdateDrawFlatParams( program, r_front.wallColor, r_front.floorColor ); } R_FlushArrays(); qglUseProgramObjectARB( 0 ); } }