/* ================ R_CalcInteractionFacing Determines which triangles of the surface are facing towards the light origin. The facing array should be allocated with one extra index than the number of surface triangles, which will be used to handle dangling edge silhouettes. ================ */ void R_CalcInteractionFacing( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) { SCOPED_PROFILE_EVENT( "R_CalcInteractionFacing" ); if ( cullInfo.facing != NULL ) { return; } idVec3 localLightOrigin; R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin ); const int numFaces = tri->numIndexes / 3; cullInfo.facing = (byte *) R_StaticAlloc( ( numFaces + 1 ) * sizeof( cullInfo.facing[0] ), TAG_RENDER_INTERACTION ); // exact geometric cull against face for ( int i = 0, face = 0; i < tri->numIndexes; i += 3, face++ ) { const idDrawVert & v0 = tri->verts[tri->indexes[i + 0]]; const idDrawVert & v1 = tri->verts[tri->indexes[i + 1]]; const idDrawVert & v2 = tri->verts[tri->indexes[i + 2]]; const idPlane plane( v0.xyz, v1.xyz, v2.xyz ); const float d = plane.Distance( localLightOrigin ); cullInfo.facing[face] = ( d >= 0.0f ); } cullInfo.facing[numFaces] = 1; // for dangling edges to reference }
/* ================ R_CalcInteractionFacing Determines which triangles of the surface are facing towards the light origin. The facing array should be allocated with one extra index than the number of surface triangles, which will be used to handle dangling edge silhouettes. ================ */ void R_CalcInteractionFacing( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) { idVec3 localLightOrigin; if ( cullInfo.facing != NULL ) { return; } R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin ); int numFaces = tri->numIndexes / 3; if ( !tri->facePlanes || !tri->facePlanesCalculated ) { R_DeriveFacePlanes( const_cast<srfTriangles_t *>(tri) ); } cullInfo.facing = (byte *) R_StaticAlloc( ( numFaces + 1 ) * sizeof( cullInfo.facing[0] ) ); // calculate back face culling float *planeSide = (float *) _alloca16( numFaces * sizeof( float ) ); // exact geometric cull against face SIMDProcessor->Dot( planeSide, localLightOrigin, tri->facePlanes, numFaces ); SIMDProcessor->CmpGE( cullInfo.facing, planeSide, 0.0f, numFaces ); cullInfo.facing[ numFaces ] = 1; // for dangling edges to reference }
/* ================== RB_SetProgramEnvironmentSpace Sets variables related to the current space that can be used by all vertex programs ================== */ void RB_SetProgramEnvironmentSpace( void ) { if ( !glConfig.ARBVertexProgramAvailable ) { return; } const struct viewEntity_s *space = backEnd.currentSpace; float parm[4]; // set eye position in local space R_GlobalPointToLocal( space->modelMatrix, backEnd.viewDef->renderView.vieworg, *(idVec3 *)parm ); parm[3] = 1.0; qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 5, parm ); // we need the model matrix without it being combined with the view matrix // so we can transform local vectors to global coordinates parm[0] = space->modelMatrix[0]; parm[1] = space->modelMatrix[4]; parm[2] = space->modelMatrix[8]; parm[3] = space->modelMatrix[12]; qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 6, parm ); parm[0] = space->modelMatrix[1]; parm[1] = space->modelMatrix[5]; parm[2] = space->modelMatrix[9]; parm[3] = space->modelMatrix[13]; qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 7, parm ); parm[0] = space->modelMatrix[2]; parm[1] = space->modelMatrix[6]; parm[2] = space->modelMatrix[10]; parm[3] = space->modelMatrix[14]; qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 8, parm ); }
/* =============== idRenderModelBeam::Bounds =============== */ idBounds idRenderModelBeam::Bounds( const struct renderEntity_s* renderEntity ) const { idBounds b; b.Zero(); if( !renderEntity ) { b.ExpandSelf( 8.0f ); } else { idVec3 target = *reinterpret_cast<const idVec3*>( &renderEntity->shaderParms[SHADERPARM_BEAM_END_X] ); idVec3 localTarget; float modelMatrix[16]; R_AxisToModelMatrix( renderEntity->axis, renderEntity->origin, modelMatrix ); R_GlobalPointToLocal( modelMatrix, target, localTarget ); b.AddPoint( localTarget ); if( renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] != 0.0f ) { b.ExpandSelf( renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] * 0.5f ); } } return b; }
/* =================== R_ProjectPointsToFarPlane make a projected copy of the even verts into the odd spots that is on the far light clip plane =================== */ static void R_ProjectPointsToFarPlane( const idRenderEntityLocal *ent, const idRenderLightLocal *light, const idPlane &lightPlaneLocal, int firstShadowVert, int numShadowVerts ) { idVec3 lv; idVec4 mat[4]; int i; idVec4 *in; R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, lv ); R_LightProjectionMatrix( lv, lightPlaneLocal, mat ); #if 1 // make a projected copy of the even verts into the odd spots in = &shadowVerts[firstShadowVert]; for ( i = firstShadowVert ; i < numShadowVerts ; i+= 2, in += 2 ) { float w, oow; in[0].w = 1; w = in->ToVec3() * mat[3].ToVec3() + mat[3][3]; if ( w == 0 ) { in[1] = in[0]; continue; } oow = 1.0 / w; in[1].x = ( in->ToVec3() * mat[0].ToVec3() + mat[0][3] ) * oow; in[1].y = ( in->ToVec3() * mat[1].ToVec3() + mat[1][3] ) * oow; in[1].z = ( in->ToVec3() * mat[2].ToVec3() + mat[2][3] ) * oow; in[1].w = 1; } #else // messing with W seems to cause some depth precision problems // make a projected copy of the even verts into the odd spots in = &shadowVerts[firstShadowVert]; for ( i = firstShadowVert ; i < numShadowVerts ; i+= 2, in += 2 ) { in[0].w = 1; in[1].x = *in * mat[0].ToVec3() + mat[0][3]; in[1].y = *in * mat[1].ToVec3() + mat[1][3]; in[1].z = *in * mat[2].ToVec3() + mat[2][3]; in[1].w = *in * mat[3].ToVec3() + mat[3][3]; } #endif }
/* ================= idRenderModelDecal::CreateProjectionInfo ================= */ void idRenderModelDecal::GlobalProjectionInfoToLocal( decalProjectionInfo_t &localInfo, const decalProjectionInfo_t &info, const idVec3 &origin, const idMat3 &axis ) { float modelMatrix[16]; R_AxisToModelMatrix( axis, origin, modelMatrix ); for( int j = 0; j < NUM_DECAL_BOUNDING_PLANES; j++ ) { R_GlobalPlaneToLocal( modelMatrix, info.boundingPlanes[j], localInfo.boundingPlanes[j] ); } R_GlobalPlaneToLocal( modelMatrix, info.fadePlanes[0], localInfo.fadePlanes[0] ); R_GlobalPlaneToLocal( modelMatrix, info.fadePlanes[1], localInfo.fadePlanes[1] ); R_GlobalPlaneToLocal( modelMatrix, info.textureAxis[0], localInfo.textureAxis[0] ); R_GlobalPlaneToLocal( modelMatrix, info.textureAxis[1], localInfo.textureAxis[1] ); R_GlobalPointToLocal( modelMatrix, info.projectionOrigin, localInfo.projectionOrigin ); localInfo.projectionBounds = info.projectionBounds; localInfo.projectionBounds.TranslateSelf( -origin ); localInfo.projectionBounds.RotateSelf( axis.Transpose() ); localInfo.material = info.material; localInfo.parallel = info.parallel; localInfo.fadeDepth = info.fadeDepth; localInfo.startTime = info.startTime; localInfo.force = info.force; }
/* ================== R_SkyboxTexGen ================== */ void R_SkyboxTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) { idVec3 localViewOrigin; R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin ); int numVerts = surf->geo->numVerts; int size = numVerts * sizeof( idVec3 ); idVec3 *texCoords = ( idVec3 * ) _alloca16( size ); const idDrawVert *verts = surf->geo->verts; for( int i = 0; i < numVerts; i++ ) { texCoords[i][0] = verts[i].xyz[0] - localViewOrigin[0]; texCoords[i][1] = verts[i].xyz[1] - localViewOrigin[1]; texCoords[i][2] = verts[i].xyz[2] - localViewOrigin[2]; } surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size ); }
/* =================== R_ProjectPointsToFarPlane make a projected copy of the even verts into the odd spots that is on the far light clip plane =================== */ static void R_ProjectPointsToFarPlane(stencilRef_t *st, const idRenderEntityLocal *ent, const idRenderLightLocal *light, const idPlane &lightPlaneLocal, int firstShadowVert, int numShadowVerts) { idVec3 lv; idVec4 mat[4]; int i; idVec4 *in; R_GlobalPointToLocal(ent->modelMatrix, light->globalLightOrigin, lv); R_LightProjectionMatrix(lv, lightPlaneLocal, mat); // make a projected copy of the even verts into the odd spots in = &st->shadowVerts[firstShadowVert]; for (i = firstShadowVert; i < numShadowVerts; i += 2, in += 2) { float w, oow; in[0].w = 1.0f; w = in->ToVec3() * mat[3].ToVec3() + mat[3][3]; if (w == 0) { in[1] = in[0]; continue; } oow = 1.0f / w; in[1].x = (in->ToVec3() * mat[0].ToVec3() + mat[0][3]) * oow; in[1].y = (in->ToVec3() * mat[1].ToVec3() + mat[1][3]) * oow; in[1].z = (in->ToVec3() * mat[2].ToVec3() + mat[2][3]) * oow; in[1].w = 1; } }
/* ================== idInteraction::AddActiveInteraction Create and add any necessary light and shadow triangles If the model doesn't have any surfaces that need interactions with this type of light, it can be skipped, but we might need to instantiate the dynamic model to find out ================== */ void idInteraction::AddActiveInteraction( void ) { viewLight_t * vLight; viewEntity_t * vEntity; idScreenRect shadowScissor; idScreenRect lightScissor; idVec3 localLightOrigin; idVec3 localViewOrigin; vLight = lightDef->viewLight; vEntity = entityDef->viewEntity; // do not waste time culling the interaction frustum if there will be no shadows if ( !HasShadows() ) { // use the entity scissor rectangle shadowScissor = vEntity->scissorRect; // culling does not seem to be worth it for static world models } else if ( entityDef->parms.hModel->IsStaticWorldModel() ) { // use the light scissor rectangle shadowScissor = vLight->scissorRect; } else { // try to cull the interaction // this will also cull the case where the light origin is inside the // view frustum and the entity bounds are outside the view frustum if ( CullInteractionByViewFrustum( tr.viewDef->viewFrustum ) ) { return; } // calculate the shadow scissor rectangle shadowScissor = CalcInteractionScissorRectangle( tr.viewDef->viewFrustum ); } // get out before making the dynamic model if the shadow scissor rectangle is empty if ( shadowScissor.IsEmpty() ) { return; } // We will need the dynamic surface created to make interactions, even if the // model itself wasn't visible. This just returns a cached value after it // has been generated once in the view. idRenderModel *model = R_EntityDefDynamicModel( entityDef ); if ( model == NULL || model->NumSurfaces() <= 0 ) { return; } // the dynamic model may have changed since we built the surface list if ( !IsDeferred() && entityDef->dynamicModelFrameCount != dynamicModelFrameCount ) { FreeSurfaces(); } dynamicModelFrameCount = entityDef->dynamicModelFrameCount; // actually create the interaction if needed, building light and shadow surfaces as needed if ( IsDeferred() ) { CreateInteraction( model ); } R_GlobalPointToLocal( vEntity->modelMatrix, lightDef->globalLightOrigin, localLightOrigin ); R_GlobalPointToLocal( vEntity->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin ); // calculate the scissor as the intersection of the light and model rects // this is used for light triangles, but not for shadow triangles lightScissor = vLight->scissorRect; lightScissor.Intersect( vEntity->scissorRect ); bool lightScissorsEmpty = lightScissor.IsEmpty(); // for each surface of this entity / light interaction for ( int i = 0; i < numSurfaces; i++ ) { surfaceInteraction_t *sint = &surfaces[i]; // see if the base surface is visible, we may still need to add shadows even if empty if ( !lightScissorsEmpty && sint->ambientTris && sint->ambientTris->ambientViewCount == tr.viewCount ) { // make sure we have created this interaction, which may have been deferred // on a previous use that only needed the shadow if ( sint->lightTris == LIGHT_TRIS_DEFERRED ) { sint->lightTris = R_CreateLightTris( vEntity->entityDef, sint->ambientTris, vLight->lightDef, sint->shader, sint->cullInfo ); R_FreeInteractionCullInfo( sint->cullInfo ); } srfTriangles_t *lightTris = sint->lightTris; if ( lightTris ) { // try to cull before adding // FIXME: this may not be worthwhile. We have already done culling on the ambient, // but individual surfaces may still be cropped somewhat more if ( !R_CullLocalBox( lightTris->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) { // make sure the original surface has its ambient cache created srfTriangles_t *tri = sint->ambientTris; if ( !tri->ambientCache ) { if ( !R_CreateAmbientCache( tri, sint->shader->ReceivesLighting() ) ) { // skip if we were out of vertex memory continue; } } // reference the original surface's ambient cache lightTris->ambientCache = tri->ambientCache; // touch the ambient surface so it won't get purged vertexCache.Touch(lightTris->ambientCache); if (!lightTris->indexCache) { vertexCache.Alloc(lightTris->indexes, lightTris->numIndexes * sizeof(lightTris->indexes[0]), &lightTris->indexCache, true); vertexCache.Touch(lightTris->indexCache); } // add the surface to the light list const idMaterial *shader = sint->shader; R_GlobalShaderOverride(&shader); // there will only be localSurfaces if the light casts shadows and // there are surfaces with NOSELFSHADOW if ( sint->shader->Coverage() == MC_TRANSLUCENT ) { R_LinkLightSurf( &vLight->translucentInteractions, lightTris, vEntity, lightDef, shader, lightScissor, false ); } else if ( !lightDef->parms.noShadows && sint->shader->TestMaterialFlag(MF_NOSELFSHADOW) ) { R_LinkLightSurf( &vLight->localInteractions, lightTris, vEntity, lightDef, shader, lightScissor, false ); } else { R_LinkLightSurf( &vLight->globalInteractions, lightTris, vEntity, lightDef, shader, lightScissor, false ); } } } } srfTriangles_t *shadowTris = sint->shadowTris; // the shadows will always have to be added, unless we can tell they // are from a surface in an unconnected area if ( shadowTris ) { // check for view specific shadow suppression (player shadows, etc) if ( !r_skipSuppress.GetBool() ) { if ( entityDef->parms.suppressShadowInViewID && entityDef->parms.suppressShadowInViewID == tr.viewDef->renderView.viewID ) { continue; } if ( entityDef->parms.suppressShadowInLightID && entityDef->parms.suppressShadowInLightID == lightDef->parms.lightId ) { continue; } } // cull static shadows that have a non-empty bounds // dynamic shadows that use the turboshadow code will not have valid // bounds, because the perspective projection extends them to infinity if ( r_useShadowCulling.GetBool() && !shadowTris->bounds.IsCleared() ) { if ( R_CullLocalBox( shadowTris->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) { continue; } } // copy the shadow vertexes to the vertex cache if they have been purged // if we are using shared shadowVertexes and letting a vertex program fix them up, // get the shadowCache from the parent ambient surface if ( !shadowTris->shadowVertexes ) { // the data may have been purged, so get the latest from the "home position" shadowTris->shadowCache = sint->ambientTris->shadowCache; } // if we have been purged, re-upload the shadowVertexes if ( !shadowTris->shadowCache ) { if ( shadowTris->shadowVertexes ) { // each interaction has unique vertexes R_CreatePrivateShadowCache( shadowTris ); } else { R_CreateVertexProgramShadowCache( sint->ambientTris ); shadowTris->shadowCache = sint->ambientTris->shadowCache; } // if we are out of vertex cache space, skip the interaction if ( !shadowTris->shadowCache ) { continue; } } // touch the shadow surface so it won't get purged vertexCache.Touch( shadowTris->shadowCache ); if ( !shadowTris->indexCache ) { vertexCache.Alloc( shadowTris->indexes, shadowTris->numIndexes * sizeof( shadowTris->indexes[0] ), &shadowTris->indexCache, true ); vertexCache.Touch( shadowTris->indexCache ); } // see if we can avoid using the shadow volume caps bool inside = R_PotentiallyInsideInfiniteShadow( sint->ambientTris, localViewOrigin, localLightOrigin ); if ( sint->shader->TestMaterialFlag( MF_NOSELFSHADOW ) ) { R_LinkLightSurf( &vLight->localShadows, shadowTris, vEntity, lightDef, NULL, shadowScissor, inside ); } else { R_LinkLightSurf( &vLight->globalShadows, shadowTris, vEntity, lightDef, NULL, shadowScissor, inside ); } } } }
/* ===================== R_CreateTurboShadowVolume ===================== */ srfTriangles_t *R_CreateTurboShadowVolume( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) { int i, j; idVec3 localLightOrigin; srfTriangles_t *newTri; silEdge_t *sil; const glIndex_t *indexes; const byte *facing; R_CalcInteractionFacing( ent, tri, light, cullInfo ); if ( r_useShadowProjectedCull.GetBool() ) { R_CalcInteractionCullBits( ent, tri, light, cullInfo ); } int numFaces = tri->numIndexes / 3; int numShadowingFaces = 0; facing = cullInfo.facing; // if all the triangles are inside the light frustum if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT || !r_useShadowProjectedCull.GetBool() ) { // count the number of shadowing faces for ( i = 0; i < numFaces; i++ ) { numShadowingFaces += facing[i]; } numShadowingFaces = numFaces - numShadowingFaces; } else { // make all triangles that are outside the light frustum "facing", so they won't cast shadows indexes = tri->indexes; byte *modifyFacing = cullInfo.facing; const byte *cullBits = cullInfo.cullBits; for ( j = i = 0; i < tri->numIndexes; i += 3, j++ ) { if ( !modifyFacing[j] ) { int i1 = indexes[i+0]; int i2 = indexes[i+1]; int i3 = indexes[i+2]; if ( cullBits[i1] & cullBits[i2] & cullBits[i3] ) { modifyFacing[j] = 1; } else { numShadowingFaces++; } } } } if ( !numShadowingFaces ) { // no faces are inside the light frustum and still facing the right way return NULL; } newTri = R_AllocStaticTriSurf(); #ifdef USE_TRI_DATA_ALLOCATOR R_AllocStaticTriSurfShadowVerts( newTri, tri->numVerts * 2 ); shadowCache_t *shadowVerts = newTri->shadowVertexes; #else shadowCache_t *shadowVerts = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowVerts[0] ) ); #endif R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin ); int *vertRemap = (int *)_alloca16( tri->numVerts * sizeof( vertRemap[0] ) ); SIMDProcessor->Memset( vertRemap, -1, tri->numVerts * sizeof( vertRemap[0] ) ); for ( i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) { if ( facing[j] ) { continue; } // this may pull in some vertexes that are outside // the frustum, because they connect to vertexes inside vertRemap[tri->silIndexes[i+0]] = 0; vertRemap[tri->silIndexes[i+1]] = 0; vertRemap[tri->silIndexes[i+2]] = 0; } newTri->numVerts = SIMDProcessor->CreateShadowCache( &shadowVerts->xyz, vertRemap, localLightOrigin, tri->verts, tri->numVerts ); c_turboUsedVerts += newTri->numVerts; c_turboUnusedVerts += tri->numVerts * 2 - newTri->numVerts; #ifdef USE_TRI_DATA_ALLOCATOR R_ResizeStaticTriSurfShadowVerts( newTri, newTri->numVerts ); #else R_AllocStaticTriSurfShadowVerts( newTri, newTri->numVerts ); SIMDProcessor->Memcpy( newTri->shadowVertexes, shadowVerts, newTri->numVerts * sizeof( shadowVerts[0] ) ); #endif // alloc the max possible size #ifdef USE_TRI_DATA_ALLOCATOR R_AllocStaticTriSurfIndexes( newTri, ( numShadowingFaces + tri->numSilEdges ) * 6 ); glIndex_t *tempIndexes = newTri->indexes; glIndex_t *shadowIndexes = newTri->indexes; #else glIndex_t *tempIndexes = (glIndex_t *)_alloca16( tri->numSilEdges * 6 * sizeof( tempIndexes[0] ) ); glIndex_t *shadowIndexes = tempIndexes; #endif // create new triangles along sil planes for ( sil = tri->silEdges, i = tri->numSilEdges; i > 0; i--, sil++ ) { int f1 = facing[sil->p1]; int f2 = facing[sil->p2]; if ( !( f1 ^ f2 ) ) { continue; } int v1 = vertRemap[sil->v1]; int v2 = vertRemap[sil->v2]; // set the two triangle winding orders based on facing // without using a poorly-predictable branch shadowIndexes[0] = v1; shadowIndexes[1] = v2 ^ f1; shadowIndexes[2] = v2 ^ f2; shadowIndexes[3] = v1 ^ f2; shadowIndexes[4] = v1 ^ f1; shadowIndexes[5] = v2 ^ 1; shadowIndexes += 6; } int numShadowIndexes = shadowIndexes - tempIndexes; // we aren't bothering to separate front and back caps on these newTri->numIndexes = newTri->numShadowIndexesNoFrontCaps = numShadowIndexes + numShadowingFaces * 6; newTri->numShadowIndexesNoCaps = numShadowIndexes; newTri->shadowCapPlaneBits = SHADOW_CAP_INFINITE; #ifdef USE_TRI_DATA_ALLOCATOR // decrease the size of the memory block to only store the used indexes R_ResizeStaticTriSurfIndexes( newTri, newTri->numIndexes ); #else // allocate memory for the indexes R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes ); // copy the indexes we created for the sil planes SIMDProcessor->Memcpy( newTri->indexes, tempIndexes, numShadowIndexes * sizeof( tempIndexes[0] ) ); #endif // these have no effect, because they extend to infinity newTri->bounds.Clear(); // put some faces on the model and some on the distant projection indexes = tri->silIndexes; shadowIndexes = newTri->indexes + numShadowIndexes; for ( i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) { if ( facing[j] ) { continue; } int i0 = vertRemap[indexes[i+0]]; shadowIndexes[2] = i0; shadowIndexes[3] = i0 ^ 1; int i1 = vertRemap[indexes[i+1]]; shadowIndexes[1] = i1; shadowIndexes[4] = i1 ^ 1; int i2 = vertRemap[indexes[i+2]]; shadowIndexes[0] = i2; shadowIndexes[5] = i2 ^ 1; shadowIndexes += 6; } return newTri; }
/* =============== idRenderModelBeam::InstantiateDynamicModel =============== */ idRenderModel *idRenderModelBeam::InstantiateDynamicModel(const struct renderEntity_s *renderEntity, const struct viewDef_s *viewDef, idRenderModel *cachedModel) { idRenderModelStatic *staticModel; srfTriangles_t *tri; modelSurface_t surf; if (cachedModel) { delete cachedModel; cachedModel = NULL; } if (renderEntity == NULL || viewDef == NULL) { delete cachedModel; return NULL; } if (cachedModel != NULL) { assert(dynamic_cast<idRenderModelStatic *>(cachedModel) != NULL); assert(idStr::Icmp(cachedModel->Name(), beam_SnapshotName) == 0); staticModel = static_cast<idRenderModelStatic *>(cachedModel); surf = *staticModel->Surface(0); tri = surf.geometry; } else { staticModel = new idRenderModelStatic; staticModel->InitEmpty(beam_SnapshotName); tri = R_AllocStaticTriSurf(); R_AllocStaticTriSurfVerts(tri, 4); R_AllocStaticTriSurfIndexes(tri, 6); tri->verts[0].Clear(); tri->verts[0].st[0] = 0; tri->verts[0].st[1] = 0; tri->verts[1].Clear(); tri->verts[1].st[0] = 0; tri->verts[1].st[1] = 1; tri->verts[2].Clear(); tri->verts[2].st[0] = 1; tri->verts[2].st[1] = 0; tri->verts[3].Clear(); tri->verts[3].st[0] = 1; tri->verts[3].st[1] = 1; tri->indexes[0] = 0; tri->indexes[1] = 2; tri->indexes[2] = 1; tri->indexes[3] = 2; tri->indexes[4] = 3; tri->indexes[5] = 1; tri->numVerts = 4; tri->numIndexes = 6; surf.geometry = tri; surf.id = 0; surf.shader = tr.defaultMaterial; staticModel->AddSurface(surf); } idVec3 target = *reinterpret_cast<const idVec3 *>(&renderEntity->shaderParms[SHADERPARM_BEAM_END_X]); // we need the view direction to project the minor axis of the tube // as the view changes idVec3 localView, localTarget; float modelMatrix[16]; R_AxisToModelMatrix(renderEntity->axis, renderEntity->origin, modelMatrix); R_GlobalPointToLocal(modelMatrix, viewDef->renderView.vieworg, localView); R_GlobalPointToLocal(modelMatrix, target, localTarget); idVec3 major = localTarget; idVec3 minor; idVec3 mid = 0.5f * localTarget; idVec3 dir = mid - localView; minor.Cross(major, dir); minor.Normalize(); if (renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] != 0.0f) { minor *= renderEntity->shaderParms[SHADERPARM_BEAM_WIDTH] * 0.5f; } int red = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_RED] * 255.0f); int green = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_GREEN] * 255.0f); int blue = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_BLUE] * 255.0f); int alpha = idMath::FtoiFast(renderEntity->shaderParms[SHADERPARM_ALPHA] * 255.0f); tri->verts[0].xyz = minor; tri->verts[0].color[0] = red; tri->verts[0].color[1] = green; tri->verts[0].color[2] = blue; tri->verts[0].color[3] = alpha; tri->verts[1].xyz = -minor; tri->verts[1].color[0] = red; tri->verts[1].color[1] = green; tri->verts[1].color[2] = blue; tri->verts[1].color[3] = alpha; tri->verts[2].xyz = localTarget + minor; tri->verts[2].color[0] = red; tri->verts[2].color[1] = green; tri->verts[2].color[2] = blue; tri->verts[2].color[3] = alpha; tri->verts[3].xyz = localTarget - minor; tri->verts[3].color[0] = red; tri->verts[3].color[1] = green; tri->verts[3].color[2] = blue; tri->verts[3].color[3] = alpha; R_BoundTriSurf(tri); staticModel->bounds = tri->bounds; return staticModel; }
/* ================== 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(); } }
/* =================== R_AddSingleModel May be run in parallel. Here is where dynamic models actually get instantiated, and necessary interaction surfaces get created. This is all done on a sort-by-model basis to keep source data in cache (most likely L2) as any interactions and shadows are generated, since dynamic models will typically be lit by two or more lights. =================== */ void R_AddSingleModel( viewEntity_t* vEntity ) { // we will add all interaction surfs here, to be chained to the lights in later serial code vEntity->drawSurfs = NULL; vEntity->staticShadowVolumes = NULL; vEntity->dynamicShadowVolumes = NULL; // globals we really should pass in... const viewDef_t* viewDef = tr.viewDef; idRenderEntityLocal* entityDef = vEntity->entityDef; const renderEntity_t* renderEntity = &entityDef->parms; const idRenderWorldLocal* world = entityDef->world; if( viewDef->isXraySubview && entityDef->parms.xrayIndex == 1 ) { return; } else if( !viewDef->isXraySubview && entityDef->parms.xrayIndex == 2 ) { return; } SCOPED_PROFILE_EVENT( renderEntity->hModel == NULL ? "Unknown Model" : renderEntity->hModel->Name() ); // calculate the znear for testing whether or not the view is inside a shadow projection const float znear = ( viewDef->renderView.cramZNear ) ? ( r_znear.GetFloat() * 0.25f ) : r_znear.GetFloat(); // if the entity wasn't seen through a portal chain, it was added just for light shadows const bool modelIsVisible = !vEntity->scissorRect.IsEmpty(); const bool addInteractions = modelIsVisible && ( !viewDef->isXraySubview || entityDef->parms.xrayIndex == 2 ); const int entityIndex = entityDef->index; //--------------------------- // Find which of the visible lights contact this entity // // If the entity doesn't accept light or cast shadows from any surface, // this can be skipped. // // OPTIMIZE: world areas can assume all referenced lights are used //--------------------------- int numContactedLights = 0; static const int MAX_CONTACTED_LIGHTS = 128; viewLight_t* contactedLights[MAX_CONTACTED_LIGHTS]; idInteraction* staticInteractions[MAX_CONTACTED_LIGHTS]; if( renderEntity->hModel == NULL || renderEntity->hModel->ModelHasInteractingSurfaces() || renderEntity->hModel->ModelHasShadowCastingSurfaces() ) { SCOPED_PROFILE_EVENT( "Find lights" ); for( viewLight_t* vLight = viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { if( vLight->scissorRect.IsEmpty() ) { continue; } if( vLight->entityInteractionState != NULL ) { // new code path, everything was done in AddLight if( vLight->entityInteractionState[entityIndex] == viewLight_t::INTERACTION_YES ) { contactedLights[numContactedLights] = vLight; staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex]; if( ++numContactedLights == MAX_CONTACTED_LIGHTS ) { break; } } continue; } const idRenderLightLocal* lightDef = vLight->lightDef; if( !lightDef->globalLightBounds.IntersectsBounds( entityDef->globalReferenceBounds ) ) { continue; } if( R_CullModelBoundsToLight( lightDef, entityDef->localReferenceBounds, entityDef->modelRenderMatrix ) ) { continue; } if( !modelIsVisible ) { // some lights have their center of projection outside the world if( lightDef->areaNum != -1 ) { // if no part of the model is in an area that is connected to // the light center (it is behind a solid, closed door), we can ignore it bool areasConnected = false; for( areaReference_t* ref = entityDef->entityRefs; ref != NULL; ref = ref->ownerNext ) { if( world->AreasAreConnected( lightDef->areaNum, ref->area->areaNum, PS_BLOCK_VIEW ) ) { areasConnected = true; break; } } if( areasConnected == false ) { // can't possibly be seen or shadowed continue; } } // check more precisely for shadow visibility idBounds shadowBounds; R_ShadowBounds( entityDef->globalReferenceBounds, lightDef->globalLightBounds, lightDef->globalLightOrigin, shadowBounds ); // this doesn't say that the shadow can't effect anything, only that it can't // effect anything in the view if( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) ) { continue; } } contactedLights[numContactedLights] = vLight; staticInteractions[numContactedLights] = world->interactionTable[vLight->lightDef->index * world->interactionTableWidth + entityIndex]; if( ++numContactedLights == MAX_CONTACTED_LIGHTS ) { break; } } } // if we aren't visible and none of the shadows stretch into the view, // we don't need to do anything else if( !modelIsVisible && numContactedLights == 0 ) { return; } //--------------------------- // create a dynamic model if the geometry isn't static //--------------------------- idRenderModel* model = R_EntityDefDynamicModel( entityDef ); if( model == NULL || model->NumSurfaces() <= 0 ) { return; } // add the lightweight blood decal surfaces if the model is directly visible if( modelIsVisible ) { assert( !vEntity->scissorRect.IsEmpty() ); if( entityDef->decals != NULL && !r_skipDecals.GetBool() ) { entityDef->decals->CreateDeferredDecals( model ); unsigned int numDrawSurfs = entityDef->decals->GetNumDecalDrawSurfs(); for( unsigned int i = 0; i < numDrawSurfs; i++ ) { drawSurf_t* decalDrawSurf = entityDef->decals->CreateDecalDrawSurf( vEntity, i ); if( decalDrawSurf != NULL ) { decalDrawSurf->linkChain = NULL; decalDrawSurf->nextOnLight = vEntity->drawSurfs; vEntity->drawSurfs = decalDrawSurf; } } } if( entityDef->overlays != NULL && !r_skipOverlays.GetBool() ) { entityDef->overlays->CreateDeferredOverlays( model ); unsigned int numDrawSurfs = entityDef->overlays->GetNumOverlayDrawSurfs(); for( unsigned int i = 0; i < numDrawSurfs; i++ ) { drawSurf_t* overlayDrawSurf = entityDef->overlays->CreateOverlayDrawSurf( vEntity, model, i ); if( overlayDrawSurf != NULL ) { overlayDrawSurf->linkChain = NULL; overlayDrawSurf->nextOnLight = vEntity->drawSurfs; vEntity->drawSurfs = overlayDrawSurf; } } } } //--------------------------- // copy matrix related stuff for back-end use // and setup a render matrix for faster culling //--------------------------- vEntity->modelDepthHack = renderEntity->modelDepthHack; vEntity->weaponDepthHack = renderEntity->weaponDepthHack; vEntity->skipMotionBlur = renderEntity->skipMotionBlur; memcpy( vEntity->modelMatrix, entityDef->modelMatrix, sizeof( vEntity->modelMatrix ) ); R_MatrixMultiply( entityDef->modelMatrix, viewDef->worldSpace.modelViewMatrix, vEntity->modelViewMatrix ); idRenderMatrix viewMat; idRenderMatrix::Transpose( *( idRenderMatrix* )vEntity->modelViewMatrix, viewMat ); idRenderMatrix::Multiply( viewDef->projectionRenderMatrix, viewMat, vEntity->mvp ); if( renderEntity->weaponDepthHack ) { idRenderMatrix::ApplyDepthHack( vEntity->mvp ); } if( renderEntity->modelDepthHack != 0.0f ) { idRenderMatrix::ApplyModelDepthHack( vEntity->mvp, renderEntity->modelDepthHack ); } // local light and view origins are used to determine if the view is definitely outside // an extruded shadow volume, which means we can skip drawing the end caps idVec3 localViewOrigin; R_GlobalPointToLocal( vEntity->modelMatrix, viewDef->renderView.vieworg, localViewOrigin ); //--------------------------- // add all the model surfaces //--------------------------- for( int surfaceNum = 0; surfaceNum < model->NumSurfaces(); surfaceNum++ ) { const modelSurface_t* surf = model->Surface( surfaceNum ); // for debugging, only show a single surface at a time if( r_singleSurface.GetInteger() >= 0 && surfaceNum != r_singleSurface.GetInteger() ) { continue; } srfTriangles_t* tri = surf->geometry; if( tri == NULL ) { continue; } if( tri->numIndexes == 0 ) { continue; // happens for particles } const idMaterial* shader = surf->shader; if( shader == NULL ) { continue; } if( !shader->IsDrawn() ) { continue; // collision hulls, etc } // RemapShaderBySkin if( entityDef->parms.customShader != NULL ) { // this is sort of a hack, but causes deformed surfaces to map to empty surfaces, // so the item highlight overlay doesn't highlight the autosprite surface if( shader->Deform() ) { continue; } shader = entityDef->parms.customShader; } else if( entityDef->parms.customSkin ) { shader = entityDef->parms.customSkin->RemapShaderBySkin( shader ); if( shader == NULL ) { continue; } if( !shader->IsDrawn() ) { continue; } } // optionally override with the renderView->globalMaterial if( tr.primaryRenderView.globalMaterial != NULL ) { shader = tr.primaryRenderView.globalMaterial; } SCOPED_PROFILE_EVENT( shader->GetName() ); // debugging tool to make sure we have the correct pre-calculated bounds if( r_checkBounds.GetBool() ) { for( int j = 0; j < tri->numVerts; j++ ) { int k; for( k = 0; k < 3; k++ ) { if( tri->verts[j].xyz[k] > tri->bounds[1][k] + CHECK_BOUNDS_EPSILON || tri->verts[j].xyz[k] < tri->bounds[0][k] - CHECK_BOUNDS_EPSILON ) { common->Printf( "bad tri->bounds on %s:%s\n", entityDef->parms.hModel->Name(), shader->GetName() ); break; } if( tri->verts[j].xyz[k] > entityDef->localReferenceBounds[1][k] + CHECK_BOUNDS_EPSILON || tri->verts[j].xyz[k] < entityDef->localReferenceBounds[0][k] - CHECK_BOUNDS_EPSILON ) { common->Printf( "bad referenceBounds on %s:%s\n", entityDef->parms.hModel->Name(), shader->GetName() ); break; } } if( k != 3 ) { break; } } } // view frustum culling for the precise surface bounds, which is tighter // than the entire entity reference bounds // If the entire model wasn't visible, there is no need to check the // individual surfaces. const bool surfaceDirectlyVisible = modelIsVisible && !idRenderMatrix::CullBoundsToMVP( vEntity->mvp, tri->bounds ); // RB: added check wether GPU skinning is available at all const bool gpuSkinned = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() && glConfig.gpuSkinningAvailable ); // RB end //-------------------------- // base drawing surface //-------------------------- drawSurf_t* baseDrawSurf = NULL; if( surfaceDirectlyVisible ) { // make sure we have an ambient cache and all necessary normals / tangents if( !vertexCache.CacheIsCurrent( tri->indexCache ) ) { tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); } if( !vertexCache.CacheIsCurrent( tri->ambientCache ) ) { // we are going to use it for drawing, so make sure we have the tangents and normals if( shader->ReceivesLighting() && !tri->tangentsCalculated ) { assert( tri->staticModelWithJoints == NULL ); R_DeriveTangents( tri ); // RB: this was hit by parametric particle models .. //assert( false ); // this should no longer be hit // RB end } tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( idDrawVert ), VERTEX_CACHE_ALIGN ) ); } // add the surface for drawing // we can re-use some of the values for light interaction surfaces baseDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *baseDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); baseDrawSurf->frontEndGeo = tri; baseDrawSurf->space = vEntity; baseDrawSurf->scissorRect = vEntity->scissorRect; baseDrawSurf->extraGLState = 0; baseDrawSurf->renderZFail = 0; R_SetupDrawSurfShader( baseDrawSurf, shader, renderEntity ); // Check for deformations (eyeballs, flares, etc) const deform_t shaderDeform = shader->Deform(); if( shaderDeform != DFRM_NONE ) { drawSurf_t* deformDrawSurf = R_DeformDrawSurf( baseDrawSurf ); if( deformDrawSurf != NULL ) { // any deforms may have created multiple draw surfaces for( drawSurf_t* surf = deformDrawSurf, * next = NULL; surf != NULL; surf = next ) { next = surf->nextOnLight; surf->linkChain = NULL; surf->nextOnLight = vEntity->drawSurfs; vEntity->drawSurfs = surf; } } } // Most deform source surfaces do not need to be rendered. // However, particles are rendered in conjunction with the source surface. if( shaderDeform == DFRM_NONE || shaderDeform == DFRM_PARTICLE || shaderDeform == DFRM_PARTICLE2 ) { // copy verts and indexes to this frame's hardware memory if they aren't already there if( !vertexCache.CacheIsCurrent( tri->ambientCache ) ) { tri->ambientCache = vertexCache.AllocVertex( tri->verts, ALIGN( tri->numVerts * sizeof( tri->verts[0] ), VERTEX_CACHE_ALIGN ) ); } if( !vertexCache.CacheIsCurrent( tri->indexCache ) ) { tri->indexCache = vertexCache.AllocIndex( tri->indexes, ALIGN( tri->numIndexes * sizeof( tri->indexes[0] ), INDEX_CACHE_ALIGN ) ); } R_SetupDrawSurfJoints( baseDrawSurf, tri, shader ); baseDrawSurf->numIndexes = tri->numIndexes; baseDrawSurf->ambientCache = tri->ambientCache; baseDrawSurf->indexCache = tri->indexCache; baseDrawSurf->shadowCache = 0; baseDrawSurf->linkChain = NULL; // link to the view baseDrawSurf->nextOnLight = vEntity->drawSurfs; vEntity->drawSurfs = baseDrawSurf; } } //---------------------------------------- // add all light interactions //---------------------------------------- for( int contactedLight = 0; contactedLight < numContactedLights; contactedLight++ ) { viewLight_t* vLight = contactedLights[contactedLight]; const idRenderLightLocal* lightDef = vLight->lightDef; const idInteraction* interaction = staticInteractions[contactedLight]; // check for a static interaction surfaceInteraction_t* surfInter = NULL; if( interaction > INTERACTION_EMPTY && interaction->staticInteraction ) { // we have a static interaction that was calculated accurately assert( model->NumSurfaces() == interaction->numSurfaces ); surfInter = &interaction->surfaces[surfaceNum]; } else { // try to do a more precise cull of this model surface to the light if( R_CullModelBoundsToLight( lightDef, tri->bounds, entityDef->modelRenderMatrix ) ) { continue; } } // "invisible ink" lights and shaders (imp spawn drawing on walls, etc) if( shader->Spectrum() != lightDef->lightShader->Spectrum() ) { continue; } // Calculate the local light origin to determine if the view is inside the shadow // projection and to calculate the triangle facing for dynamic shadow volumes. idVec3 localLightOrigin; R_GlobalPointToLocal( vEntity->modelMatrix, lightDef->globalLightOrigin, localLightOrigin ); //-------------------------- // surface light interactions //-------------------------- dynamicShadowVolumeParms_t* dynamicShadowParms = NULL; if( addInteractions && surfaceDirectlyVisible && shader->ReceivesLighting() ) { // static interactions can commonly find that no triangles from a surface // contact the light, even when the total model does if( surfInter == NULL || surfInter->lightTrisIndexCache > 0 ) { // create a drawSurf for this interaction drawSurf_t* lightDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *lightDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); if( surfInter != NULL ) { // optimized static interaction lightDrawSurf->numIndexes = surfInter->numLightTrisIndexes; lightDrawSurf->indexCache = surfInter->lightTrisIndexCache; } else { // throw the entire source surface at it without any per-triangle culling lightDrawSurf->numIndexes = tri->numIndexes; lightDrawSurf->indexCache = tri->indexCache; // optionally cull the triangles to the light volume if( r_cullDynamicLightTriangles.GetBool() ) { vertCacheHandle_t lightIndexCache = vertexCache.AllocIndex( NULL, ALIGN( lightDrawSurf->numIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); if( vertexCache.CacheIsCurrent( lightIndexCache ) ) { lightDrawSurf->indexCache = lightIndexCache; dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); dynamicShadowParms->verts = tri->verts; dynamicShadowParms->numVerts = tri->numVerts; dynamicShadowParms->indexes = tri->indexes; dynamicShadowParms->numIndexes = tri->numIndexes; dynamicShadowParms->silEdges = tri->silEdges; dynamicShadowParms->numSilEdges = tri->numSilEdges; dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL; dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0; dynamicShadowParms->triangleBounds = tri->bounds; dynamicShadowParms->triangleMVP = vEntity->mvp; dynamicShadowParms->localLightOrigin = localLightOrigin; dynamicShadowParms->localViewOrigin = localViewOrigin; idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject ); dynamicShadowParms->zNear = znear; dynamicShadowParms->lightZMin = vLight->scissorRect.zmin; dynamicShadowParms->lightZMax = vLight->scissorRect.zmax; dynamicShadowParms->cullShadowTrianglesToLight = false; dynamicShadowParms->forceShadowCaps = false; dynamicShadowParms->useShadowPreciseInsideTest = false; dynamicShadowParms->useShadowDepthBounds = false; dynamicShadowParms->tempFacing = NULL; dynamicShadowParms->tempCulled = NULL; dynamicShadowParms->tempVerts = NULL; dynamicShadowParms->indexBuffer = NULL; dynamicShadowParms->shadowIndices = NULL; dynamicShadowParms->maxShadowIndices = 0; dynamicShadowParms->numShadowIndices = NULL; dynamicShadowParms->lightIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( lightIndexCache ); dynamicShadowParms->maxLightIndices = lightDrawSurf->numIndexes; dynamicShadowParms->numLightIndices = &lightDrawSurf->numIndexes; dynamicShadowParms->renderZFail = NULL; dynamicShadowParms->shadowZMin = NULL; dynamicShadowParms->shadowZMax = NULL; dynamicShadowParms->shadowVolumeState = & lightDrawSurf->shadowVolumeState; lightDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; dynamicShadowParms->next = vEntity->dynamicShadowVolumes; vEntity->dynamicShadowVolumes = dynamicShadowParms; } } } lightDrawSurf->ambientCache = tri->ambientCache; lightDrawSurf->shadowCache = 0; lightDrawSurf->frontEndGeo = tri; lightDrawSurf->space = vEntity; lightDrawSurf->material = shader; lightDrawSurf->extraGLState = 0; lightDrawSurf->scissorRect = vLight->scissorRect; // interactionScissor; lightDrawSurf->sort = 0.0f; lightDrawSurf->renderZFail = 0; lightDrawSurf->shaderRegisters = baseDrawSurf->shaderRegisters; R_SetupDrawSurfJoints( lightDrawSurf, tri, shader ); // Determine which linked list to add the light surface to. // There will only be localSurfaces if the light casts shadows and // there are surfaces with NOSELFSHADOW. if( shader->Coverage() == MC_TRANSLUCENT ) { lightDrawSurf->linkChain = &vLight->translucentInteractions; } else if( !lightDef->parms.noShadows && shader->TestMaterialFlag( MF_NOSELFSHADOW ) ) { lightDrawSurf->linkChain = &vLight->localInteractions; } else { lightDrawSurf->linkChain = &vLight->globalInteractions; } lightDrawSurf->nextOnLight = vEntity->drawSurfs; vEntity->drawSurfs = lightDrawSurf; } } //-------------------------- // surface shadows //-------------------------- if( !shader->SurfaceCastsShadow() ) { continue; } if( !lightDef->LightCastsShadows() ) { continue; } if( tri->silEdges == NULL ) { continue; // can happen for beam models (shouldn't use a shadow casting material, though...) } // if the static shadow does not have any shadows if( surfInter != NULL && surfInter->numShadowIndexes == 0 ) { continue; } // some entities, like view weapons, don't cast any shadows if( entityDef->parms.noShadow ) { continue; } // No shadow if it's suppressed for this light. if( entityDef->parms.suppressShadowInLightID && entityDef->parms.suppressShadowInLightID == lightDef->parms.lightId ) { continue; } if( lightDef->parms.prelightModel && lightDef->lightHasMoved == false && entityDef->parms.hModel->IsStaticWorldModel() && !r_skipPrelightShadows.GetBool() ) { // static light / world model shadow interacitons // are always captured in the prelight shadow volume continue; } // If the shadow is drawn (or translucent), but the model isn't, we must include the shadow caps // because we may be able to see into the shadow volume even though the view is outside it. // This happens for the player world weapon and possibly some animations in multiplayer. const bool forceShadowCaps = !addInteractions || r_forceShadowCaps.GetBool(); drawSurf_t* shadowDrawSurf = ( drawSurf_t* )R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); if( surfInter != NULL ) { shadowDrawSurf->numIndexes = 0; shadowDrawSurf->indexCache = surfInter->shadowIndexCache; shadowDrawSurf->shadowCache = tri->shadowCache; shadowDrawSurf->scissorRect = vLight->scissorRect; // default to the light scissor and light depth bounds shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE; // assume the shadow volume is done in case r_skipStaticShadows is set if( !r_skipStaticShadows.GetBool() ) { staticShadowVolumeParms_t* staticShadowParms = ( staticShadowVolumeParms_t* )R_FrameAlloc( sizeof( staticShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); staticShadowParms->verts = tri->staticShadowVertexes; staticShadowParms->numVerts = tri->numVerts * 2; staticShadowParms->indexes = surfInter->shadowIndexes; staticShadowParms->numIndexes = surfInter->numShadowIndexes; staticShadowParms->numShadowIndicesWithCaps = surfInter->numShadowIndexes; staticShadowParms->numShadowIndicesNoCaps = surfInter->numShadowIndexesNoCaps; staticShadowParms->triangleBounds = tri->bounds; staticShadowParms->triangleMVP = vEntity->mvp; staticShadowParms->localLightOrigin = localLightOrigin; staticShadowParms->localViewOrigin = localViewOrigin; staticShadowParms->zNear = znear; staticShadowParms->lightZMin = vLight->scissorRect.zmin; staticShadowParms->lightZMax = vLight->scissorRect.zmax; staticShadowParms->forceShadowCaps = forceShadowCaps; staticShadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool(); staticShadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool(); staticShadowParms->numShadowIndices = & shadowDrawSurf->numIndexes; staticShadowParms->renderZFail = & shadowDrawSurf->renderZFail; staticShadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin; staticShadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax; staticShadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState; shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; staticShadowParms->next = vEntity->staticShadowVolumes; vEntity->staticShadowVolumes = staticShadowParms; } } else { // When CPU skinning the dynamic shadow verts of a dynamic model may not have been copied to buffer memory yet. if( !vertexCache.CacheIsCurrent( tri->shadowCache ) ) { assert( !gpuSkinned ); // the shadow cache should be static when using GPU skinning // Extracts just the xyz values from a set of full size drawverts, and // duplicates them with w set to 0 and 1 for the vertex program to project. // This is constant for any number of lights, the vertex program takes care // of projecting the verts to infinity for a particular light. tri->shadowCache = vertexCache.AllocVertex( NULL, ALIGN( tri->numVerts * 2 * sizeof( idShadowVert ), VERTEX_CACHE_ALIGN ) ); idShadowVert* shadowVerts = ( idShadowVert* )vertexCache.MappedVertexBuffer( tri->shadowCache ); idShadowVert::CreateShadowCache( shadowVerts, tri->verts, tri->numVerts ); } const int maxShadowVolumeIndexes = tri->numSilEdges * 6 + tri->numIndexes * 2; shadowDrawSurf->numIndexes = 0; shadowDrawSurf->indexCache = vertexCache.AllocIndex( NULL, ALIGN( maxShadowVolumeIndexes * sizeof( triIndex_t ), INDEX_CACHE_ALIGN ) ); shadowDrawSurf->shadowCache = tri->shadowCache; shadowDrawSurf->scissorRect = vLight->scissorRect; // default to the light scissor and light depth bounds shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE; // assume the shadow volume is done in case the index cache allocation failed // if the index cache was successfully allocated then setup the parms to create a shadow volume in parallel if( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) && !r_skipDynamicShadows.GetBool() ) { // if the parms were not already allocated for culling interaction triangles to the light frustum if( dynamicShadowParms == NULL ) { dynamicShadowParms = ( dynamicShadowVolumeParms_t* )R_FrameAlloc( sizeof( dynamicShadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); } else { // the shadow volume will be rendered first so when the interaction surface is drawn the triangles have been culled for sure *dynamicShadowParms->shadowVolumeState = SHADOWVOLUME_DONE; } dynamicShadowParms->verts = tri->verts; dynamicShadowParms->numVerts = tri->numVerts; dynamicShadowParms->indexes = tri->indexes; dynamicShadowParms->numIndexes = tri->numIndexes; dynamicShadowParms->silEdges = tri->silEdges; dynamicShadowParms->numSilEdges = tri->numSilEdges; dynamicShadowParms->joints = gpuSkinned ? tri->staticModelWithJoints->jointsInverted : NULL; dynamicShadowParms->numJoints = gpuSkinned ? tri->staticModelWithJoints->numInvertedJoints : 0; dynamicShadowParms->triangleBounds = tri->bounds; dynamicShadowParms->triangleMVP = vEntity->mvp; dynamicShadowParms->localLightOrigin = localLightOrigin; dynamicShadowParms->localViewOrigin = localViewOrigin; idRenderMatrix::Multiply( vLight->lightDef->baseLightProject, entityDef->modelRenderMatrix, dynamicShadowParms->localLightProject ); dynamicShadowParms->zNear = znear; dynamicShadowParms->lightZMin = vLight->scissorRect.zmin; dynamicShadowParms->lightZMax = vLight->scissorRect.zmax; dynamicShadowParms->cullShadowTrianglesToLight = r_cullDynamicShadowTriangles.GetBool(); dynamicShadowParms->forceShadowCaps = forceShadowCaps; dynamicShadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool(); dynamicShadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool(); dynamicShadowParms->tempFacing = NULL; dynamicShadowParms->tempCulled = NULL; dynamicShadowParms->tempVerts = NULL; dynamicShadowParms->indexBuffer = NULL; dynamicShadowParms->shadowIndices = ( triIndex_t* )vertexCache.MappedIndexBuffer( shadowDrawSurf->indexCache ); dynamicShadowParms->maxShadowIndices = maxShadowVolumeIndexes; dynamicShadowParms->numShadowIndices = & shadowDrawSurf->numIndexes; // dynamicShadowParms->lightIndices may have already been set for the interaction surface // dynamicShadowParms->maxLightIndices may have already been set for the interaction surface // dynamicShadowParms->numLightIndices may have already been set for the interaction surface dynamicShadowParms->renderZFail = & shadowDrawSurf->renderZFail; dynamicShadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin; dynamicShadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax; dynamicShadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState; shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; // if the parms we not already linked for culling interaction triangles to the light frustum if( dynamicShadowParms->lightIndices == NULL ) { dynamicShadowParms->next = vEntity->dynamicShadowVolumes; vEntity->dynamicShadowVolumes = dynamicShadowParms; } tr.pc.c_createShadowVolumes++; } } assert( vertexCache.CacheIsCurrent( shadowDrawSurf->shadowCache ) ); assert( vertexCache.CacheIsCurrent( shadowDrawSurf->indexCache ) ); shadowDrawSurf->ambientCache = 0; shadowDrawSurf->frontEndGeo = NULL; shadowDrawSurf->space = vEntity; shadowDrawSurf->material = NULL; shadowDrawSurf->extraGLState = 0; shadowDrawSurf->sort = 0.0f; shadowDrawSurf->shaderRegisters = NULL; R_SetupDrawSurfJoints( shadowDrawSurf, tri, NULL ); // determine which linked list to add the shadow surface to shadowDrawSurf->linkChain = shader->TestMaterialFlag( MF_NOSELFSHADOW ) ? &vLight->localShadows : &vLight->globalShadows; shadowDrawSurf->nextOnLight = vEntity->drawSurfs; vEntity->drawSurfs = shadowDrawSurf; } } }
/* ========================= R_PreciseCullSurface Check the surface for visibility on a per-triangle basis for cases when it is going to be VERY expensive to draw (subviews) If not culled, also returns the bounding box of the surface in Normalized Device Coordinates, so it can be used to crop the scissor rect. OPTIMIZE: we could also take exact portal passing into consideration ========================= */ bool R_PreciseCullSurface( const drawSurf_t *drawSurf, idBounds &ndcBounds ) { const srfTriangles_t *tri; int numTriangles; idPlane clip, eye; int i, j; unsigned int pointOr; unsigned int pointAnd; idVec3 localView; idFixedWinding w; tri = drawSurf->geo; pointOr = 0; pointAnd = (unsigned int)~0; // get an exact bounds of the triangles for scissor cropping ndcBounds.Clear(); for ( i = 0; i < tri->numVerts; i++ ) { int j; unsigned int pointFlags; R_TransformModelToClip( tri->verts[i].xyz, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); pointFlags = 0; for ( j = 0; j < 3; j++ ) { if ( clip[j] >= clip[3] ) { pointFlags |= (1 << (j*2)); } else if ( clip[j] <= -clip[3] ) { pointFlags |= ( 1 << (j*2+1)); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if ( pointAnd ) { return true; } // backface and frustum cull numTriangles = tri->numIndexes / 3; R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localView ); for ( i = 0; i < tri->numIndexes; i += 3 ) { idVec3 dir, normal; float dot; idVec3 d1, d2; const idVec3 &v1 = tri->verts[tri->indexes[i]].xyz; const idVec3 &v2 = tri->verts[tri->indexes[i+1]].xyz; const idVec3 &v3 = tri->verts[tri->indexes[i+2]].xyz; // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized // axis that we get from the gui view transform. It doesn't hurt anything, because // we know that all gui generated surfaces are front facing if ( tr.guiRecursionLevel == 0 ) { // we don't care that it isn't normalized, // all we want is the sign d1 = v2 - v1; d2 = v3 - v1; normal = d2.Cross( d1 ); dir = v1 - localView; dot = normal * dir; if ( dot >= 0.0f ) { return true; } } // now find the exact screen bounds of the clipped triangle w.SetNumPoints( 3 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() ); w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f; for ( j = 0; j < 4; j++ ) { if ( !w.ClipInPlace( -tr.viewDef->frustum[j], 0.1f ) ) { break; } } for ( j = 0; j < w.GetNumPoints(); j++ ) { idVec3 screen; R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen ); ndcBounds.AddPoint( screen ); } } // if we don't enclose any area, return if ( ndcBounds.IsCleared() ) { return true; } return false; }
/* ================== R_WobbleskyTexGen ================== */ void R_WobbleskyTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) { idVec3 localViewOrigin; const int *parms = surf->material->GetTexGenRegisters(); float wobbleDegrees = surf->shaderRegisters[parms[0]]; float wobbleSpeed = surf->shaderRegisters[parms[1]]; float rotateSpeed = surf->shaderRegisters[parms[2]]; wobbleDegrees = wobbleDegrees * idMath::PI / 180; wobbleSpeed = wobbleSpeed * 2 * idMath::PI / 60; rotateSpeed = rotateSpeed * 2 * idMath::PI / 60; // very ad-hoc "wobble" transform float transform[16]; float a = tr.viewDef->floatTime * wobbleSpeed; float s = sin( a ) * sin( wobbleDegrees ); float c = cos( a ) * sin( wobbleDegrees ); float z = cos( wobbleDegrees ); idVec3 axis[3]; axis[2][0] = c; axis[2][1] = s; axis[2][2] = z; axis[1][0] = -sin( a * 2 ) * sin( wobbleDegrees ); axis[1][2] = -s * sin( wobbleDegrees ); axis[1][1] = sqrt( 1.0f - ( axis[1][0] * axis[1][0] + axis[1][2] * axis[1][2] ) ); // make the second vector exactly perpendicular to the first axis[1] -= ( axis[2] * axis[1] ) * axis[2]; axis[1].Normalize(); // construct the third with a cross axis[0].Cross( axis[1], axis[2] ); // add the rotate s = sin( rotateSpeed * tr.viewDef->floatTime ); c = cos( rotateSpeed * tr.viewDef->floatTime ); transform[0] = axis[0][0] * c + axis[1][0] * s; transform[4] = axis[0][1] * c + axis[1][1] * s; transform[8] = axis[0][2] * c + axis[1][2] * s; transform[1] = axis[1][0] * c - axis[0][0] * s; transform[5] = axis[1][1] * c - axis[0][1] * s; transform[9] = axis[1][2] * c - axis[0][2] * s; transform[2] = axis[2][0]; transform[6] = axis[2][1]; transform[10] = axis[2][2]; transform[3] = transform[7] = transform[11] = 0.0f; transform[12] = transform[13] = transform[14] = 0.0f; R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin ); int numVerts = surf->geo->numVerts; int size = numVerts * sizeof( idVec3 ); idVec3 *texCoords = ( idVec3 * ) _alloca16( size ); const idDrawVert *verts = surf->geo->verts; for( int i = 0; i < numVerts; i++ ) { idVec3 v; v[0] = verts[i].xyz[0] - localViewOrigin[0]; v[1] = verts[i].xyz[1] - localViewOrigin[1]; v[2] = verts[i].xyz[2] - localViewOrigin[2]; R_LocalPointToGlobal( transform, v, texCoords[i] ); } surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size ); }
/* ============= 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(); } }
/* ============= RB_GLSL_CreateDrawInteractions ============= */ static void RB_GLSL_CreateDrawInteractions(const viewLight_t& vLight, const drawSurf_t *surf, InteractionList& interactionList ) { if (r_skipInteractions.GetBool()) { return; } for (; surf; surf = surf->nextOnLight) { const idMaterial *surfaceShader = surf->material; const float *surfaceRegs = surf->shaderRegisters; const idMaterial *lightShader = vLight.lightShader; const float *lightRegs = vLight.shaderRegisters; drawInteraction_t inter; inter.hasBumpMatrix = inter.hasDiffuseMatrix = inter.hasSpecularMatrix = false; if (!surf->geo || !surf->geo->ambientCache) { continue; } 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; // 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, 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) { float tmp[16]; RB_GetShaderTextureMatrix( lightRegs, &lightStage->texture, tmp ); RB_BakeTextureMatrixIntoTexgen( reinterpret_cast<class idPlane *>(inter.lightProjection), tmp ); } 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, interactionList, lightShader->IsAmbientLight() ); inter.diffuseImage = NULL; inter.specularImage = NULL; R_SetDrawInteraction( surfaceStage, surfaceRegs, &inter.bumpImage, inter.bumpMatrix, NULL ); inter.hasBumpMatrix = surfaceStage->texture.hasMatrix; break; } case SL_DIFFUSE: { // ignore stage that fails the condition if (!surfaceRegs[surfaceStage->conditionRegister]) { break; } if (inter.diffuseImage) { RB_SubmittInteraction( &inter, interactionList, lightShader->IsAmbientLight() ); } 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; inter.hasDiffuseMatrix = surfaceStage->texture.hasMatrix; break; } case SL_SPECULAR: { // ignore stage that fails the condition if (!surfaceRegs[surfaceStage->conditionRegister]) { break; } if (inter.specularImage) { RB_SubmittInteraction( &inter, interactionList, lightShader->IsAmbientLight() ); } 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.specularColor *= r_specularScale.GetFloat(); inter.vertexColor = surfaceStage->vertexColor; inter.hasSpecularMatrix = surfaceStage->texture.hasMatrix; break; } } } // draw the final interaction RB_SubmittInteraction( &inter, interactionList, lightShader->IsAmbientLight() ); } } }
/* ========================= R_PreciseCullSurface Check the surface for visibility on a per-triangle basis for cases when it is going to be VERY expensive to draw (subviews) If not culled, also returns the bounding box of the surface in Normalized Device Coordinates, so it can be used to crop the scissor rect. OPTIMIZE: we could also take exact portal passing into consideration ========================= */ bool R_PreciseCullSurface( const drawSurf_t* drawSurf, idBounds& ndcBounds ) { const srfTriangles_t* tri = drawSurf->frontEndGeo; unsigned int pointOr = 0; unsigned int pointAnd = ( unsigned int )~0; // get an exact bounds of the triangles for scissor cropping ndcBounds.Clear(); const idJointMat* joints = ( tri->staticModelWithJoints != NULL && r_useGPUSkinning.GetBool() ) ? tri->staticModelWithJoints->jointsInverted : NULL; for( int i = 0; i < tri->numVerts; i++ ) { const idVec3 vXYZ = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[i], joints ); idPlane eye, clip; R_TransformModelToClip( vXYZ, drawSurf->space->modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip ); unsigned int pointFlags = 0; for( int j = 0; j < 3; j++ ) { if( clip[j] >= clip[3] ) { pointFlags |= ( 1 << ( j * 2 + 0 ) ); } else if( clip[j] <= -clip[3] ) // FIXME: the D3D near clip plane is at zero instead of -1 { pointFlags |= ( 1 << ( j * 2 + 1 ) ); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if( pointAnd != 0 ) { return true; } // backface and frustum cull idVec3 localViewOrigin; R_GlobalPointToLocal( drawSurf->space->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin ); for( int i = 0; i < tri->numIndexes; i += 3 ) { const idVec3 v1 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 0 ] ], joints ); const idVec3 v2 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 1 ] ], joints ); const idVec3 v3 = idDrawVert::GetSkinnedDrawVertPosition( tri->verts[ tri->indexes[ i + 2 ] ], joints ); // this is a hack, because R_GlobalPointToLocal doesn't work with the non-normalized // axis that we get from the gui view transform. It doesn't hurt anything, because // we know that all gui generated surfaces are front facing if( tr.guiRecursionLevel == 0 ) { // we don't care that it isn't normalized, // all we want is the sign const idVec3 d1 = v2 - v1; const idVec3 d2 = v3 - v1; const idVec3 normal = d2.Cross( d1 ); const idVec3 dir = v1 - localViewOrigin; const float dot = normal * dir; if( dot >= 0.0f ) { return true; } } // now find the exact screen bounds of the clipped triangle idFixedWinding w; w.SetNumPoints( 3 ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v1, w[0].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v2, w[1].ToVec3() ); R_LocalPointToGlobal( drawSurf->space->modelMatrix, v3, w[2].ToVec3() ); w[0].s = w[0].t = w[1].s = w[1].t = w[2].s = w[2].t = 0.0f; for( int j = 0; j < 4; j++ ) { if( !w.ClipInPlace( -tr.viewDef->frustums[FRUSTUM_PRIMARY][j], 0.1f ) ) { break; } } for( int j = 0; j < w.GetNumPoints(); j++ ) { idVec3 screen; R_GlobalToNormalizedDeviceCoordinates( w[j].ToVec3(), screen ); ndcBounds.AddPoint( screen ); } } // if we don't enclose any area, return if( ndcBounds.IsCleared() ) { return true; } return false; }
/* ===================== RB_T_Shadow the shadow volumes face INSIDE ===================== */ static void RB_T_Shadow( const drawSurf_t *surf ) { const srfTriangles_t *tri; // set the light position if we are using a vertex program to project the rear surfaces if ( tr.backEndRendererHasVertexPrograms && r_useShadowVertexProgram.GetBool() && surf->space != backEnd.currentSpace ) { idVec4 localLight; R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.vLight->globalLightOrigin, localLight.ToVec3() ); localLight.w = 0.0f; qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, localLight.ToFloatPtr() ); } tri = surf->geo; if ( !tri->shadowCache ) { return; } qglVertexPointer( 4, GL_FLOAT, sizeof( shadowCache_t ), vertexCache.Position(tri->shadowCache) ); // we always draw the sil planes, but we may not need to draw the front or rear caps int numIndexes; bool external = false; if ( !r_useExternalShadows.GetInteger() ) { numIndexes = tri->numIndexes; } else if ( r_useExternalShadows.GetInteger() == 2 ) { // force to no caps for testing numIndexes = tri->numShadowIndexesNoCaps; } else if ( !(surf->dsFlags & DSF_VIEW_INSIDE_SHADOW) ) { // if we aren't inside the shadow projection, no caps are ever needed needed numIndexes = tri->numShadowIndexesNoCaps; external = true; } else if ( !backEnd.vLight->viewInsideLight && !(surf->geo->shadowCapPlaneBits & SHADOW_CAP_INFINITE) ) { // if we are inside the shadow projection, but outside the light, and drawing // a non-infinite shadow, we can skip some caps if ( backEnd.vLight->viewSeesShadowPlaneBits & surf->geo->shadowCapPlaneBits ) { // we can see through a rear cap, so we need to draw it, but we can skip the // caps on the actual surface numIndexes = tri->numShadowIndexesNoFrontCaps; } else { // we don't need to draw any caps numIndexes = tri->numShadowIndexesNoCaps; } external = true; } else { // must draw everything numIndexes = tri->numIndexes; } // set depth bounds if( glConfig.depthBoundsTestAvailable && r_useDepthBoundsTest.GetBool() ) { qglDepthBoundsEXT( surf->scissorRect.zmin, surf->scissorRect.zmax ); } // debug visualization if ( r_showShadows.GetInteger() ) { if ( r_showShadows.GetInteger() == 3 ) { if ( external ) { qglColor3f( 0.1/backEnd.overBright, 1/backEnd.overBright, 0.1/backEnd.overBright ); } else { // these are the surfaces that require the reverse qglColor3f( 1/backEnd.overBright, 0.1/backEnd.overBright, 0.1/backEnd.overBright ); } } else { // draw different color for turboshadows if ( surf->geo->shadowCapPlaneBits & SHADOW_CAP_INFINITE ) { if ( numIndexes == tri->numIndexes ) { qglColor3f( 1/backEnd.overBright, 0.1/backEnd.overBright, 0.1/backEnd.overBright ); } else { qglColor3f( 1/backEnd.overBright, 0.4/backEnd.overBright, 0.1/backEnd.overBright ); } } else { if ( numIndexes == tri->numIndexes ) { qglColor3f( 0.1/backEnd.overBright, 1/backEnd.overBright, 0.1/backEnd.overBright ); } else if ( numIndexes == tri->numShadowIndexesNoFrontCaps ) { qglColor3f( 0.1/backEnd.overBright, 1/backEnd.overBright, 0.6/backEnd.overBright ); } else { qglColor3f( 0.6/backEnd.overBright, 1/backEnd.overBright, 0.1/backEnd.overBright ); } } } qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); qglDisable( GL_STENCIL_TEST ); GL_Cull( CT_TWO_SIDED ); RB_DrawShadowElementsWithCounters( tri, numIndexes ); GL_Cull( CT_FRONT_SIDED ); qglEnable( GL_STENCIL_TEST ); return; } // patent-free work around if ( !external ) { // "preload" the stencil buffer with the number of volumes // that get clipped by the near or far clip plane qglStencilOp( GL_KEEP, tr.stencilDecr, tr.stencilDecr ); GL_Cull( CT_FRONT_SIDED ); RB_DrawShadowElementsWithCounters( tri, numIndexes ); qglStencilOp( GL_KEEP, tr.stencilIncr, tr.stencilIncr ); GL_Cull( CT_BACK_SIDED ); RB_DrawShadowElementsWithCounters( tri, numIndexes ); } // traditional depth-pass stencil shadows qglStencilOp( GL_KEEP, GL_KEEP, tr.stencilIncr ); GL_Cull( CT_FRONT_SIDED ); RB_DrawShadowElementsWithCounters( tri, numIndexes ); qglStencilOp( GL_KEEP, GL_KEEP, tr.stencilDecr ); GL_Cull( CT_BACK_SIDED ); RB_DrawShadowElementsWithCounters( tri, numIndexes ); }
/* ============= RB_ARB2_CreateDrawInteractions ============= */ void RB_GLSL_CreateDrawInteractions( const drawSurf_t *surf ) { if ( !surf ) { return; } // perform setup here that will be constant for all interactions GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc ); //GL_Cull(CT_TWO_SIDED); qglEnableClientState(GL_VERTEX_ARRAY); //qglDisableClientState(GL_VERTEX_ARRAY); qglDisableClientState(GL_TEXTURE_COORD_ARRAY); glUseProgram(program); float modelMatrix[16]; myGlMultMatrix( surf->space->modelViewMatrix, backEnd.viewDef->projectionMatrix, modelMatrix); glUniformMatrix4fv(wvp, 1, GL_FALSE, &modelMatrix[0] ); //glUniformMatrix4fv(wvp1, 1, GL_FALSE, &surf->space->modelViewMatrix[0] ); //const float* mat = backEnd.viewDef->projectionMatrix; //glUniformMatrix4fv(wvp, 1, GL_FALSE, &mat[0] ); // enable the vertex arrays glEnableVertexAttribArray( 1 ); glEnableVertexAttribArray( 2 ); glEnableVertexAttribArray( 3 ); glEnableVertexAttribArray( 4 ); glEnableVertexAttribArray( 5 ); glEnableVertexAttribArray( 6 ); // texture 0 is the normalization cube map for the vector towards the light glActiveTexture(GL_TEXTURE0); backEnd.glState.currenttmu = 0; if ( backEnd.vLight->lightShader->IsAmbientLight() ) { globalImages->ambientNormalMap->Bind(); } else { globalImages->normalCubeMapImage->Bind(); } // texture 6 is the specular lookup table glActiveTexture(GL_TEXTURE6); backEnd.glState.currenttmu = 6; if ( r_testARBProgram.GetBool() ) { globalImages->specular2DTableImage->Bind(); // variable specularity in alpha channel } else { globalImages->specularTableImage->Bind(); } for ( ; surf ; surf=surf->nextOnLight ) { // set the vertex pointers idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache ); qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(idDrawVert), ac->xyz.ToFloatPtr()); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(idDrawVert), ac->st.ToFloatPtr()); glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(idDrawVert), ac->color); glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(idDrawVert), ac->normal.ToFloatPtr()); glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(idDrawVert), ac->tangents[0].ToFloatPtr()); glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(idDrawVert), ac->tangents[1].ToFloatPtr()); idVec4 localLight; R_GlobalPointToLocal( surf->space->modelMatrix, backEnd.vLight->globalLightOrigin, localLight.ToVec3() ); localLight.w = 0.0f; glUniform3fv(lightOrgin, 1, localLight.ToFloatPtr()); //glDrawElements(GL_TRIANGLES, surf->geo->numIndexes, GL_UNSIGNED_SHORT, surf->geo->indexes); // this may cause RB_ARB2_DrawInteraction to be exacuted multiple // times with different colors and images if the surface or light have multiple layers RB_CreateSingleDrawInteractions( surf, RB_GLSL_DrawInteraction ); } glDisableVertexAttribArray( 1 ); glDisableVertexAttribArray( 2 ); glDisableVertexAttribArray( 3 ); glDisableVertexAttribArray( 4 ); glDisableVertexAttribArray( 5 ); glDisableVertexAttribArray( 6 ); // disable features glActiveTexture( GL_TEXTURE6 ); backEnd.glState.currenttmu = 6; globalImages->BindNull(); glActiveTexture( GL_TEXTURE5 ); backEnd.glState.currenttmu = 5; globalImages->BindNull(); glActiveTexture( GL_TEXTURE4 ); backEnd.glState.currenttmu = 4; globalImages->BindNull(); glActiveTexture( GL_TEXTURE3 ); backEnd.glState.currenttmu = 3; globalImages->BindNull(); glActiveTexture( GL_TEXTURE2 ); backEnd.glState.currenttmu = 2; globalImages->BindNull(); glActiveTexture( GL_TEXTURE1 ); backEnd.glState.currenttmu = 1; globalImages->BindNull(); glUseProgram(0); backEnd.glState.currenttmu = -1; GL_SelectTexture(0 ); }