tr_frontend_addlights.cpp (24885B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #pragma hdrstop 30 #include "../idlib/precompiled.h" 31 32 #include "tr_local.h" 33 34 extern idCVar r_useAreasConnectedForShadowCulling; 35 extern idCVar r_useParallelAddShadows; 36 extern idCVar r_forceShadowCaps; 37 extern idCVar r_useShadowPreciseInsideTest; 38 39 idCVar r_useAreasConnectedForShadowCulling( "r_useAreasConnectedForShadowCulling", "2", CVAR_RENDERER | CVAR_INTEGER, "cull entities cut off by doors" ); 40 idCVar r_useParallelAddLights( "r_useParallelAddLights", "1", CVAR_RENDERER | CVAR_BOOL, "aadd all lights in parallel with jobs" ); 41 42 /* 43 ============================ 44 R_ShadowBounds 45 46 Even though the extruded shadows are drawn projected to infinity, their effects are limited 47 to a fraction of the light's volume. An extruded box would require 12 faces to specify and 48 be a lot of trouble, but an axial bounding box is quick and easy to determine. 49 50 If the light is completely contained in the view, there is no value in trying to cull the 51 shadows, as they will all pass. 52 53 Pure function. 54 ============================ 55 */ 56 void R_ShadowBounds( const idBounds & modelBounds, const idBounds & lightBounds, const idVec3 & lightOrigin, idBounds & shadowBounds ) { 57 for ( int i = 0; i < 3; i++ ) { 58 shadowBounds[0][i] = __fsels( modelBounds[0][i] - lightOrigin[i], modelBounds[0][i], lightBounds[0][i] ); 59 shadowBounds[1][i] = __fsels( lightOrigin[i] - modelBounds[1][i], modelBounds[1][i], lightBounds[1][i] ); 60 } 61 } 62 63 /* 64 ============================ 65 idRenderEntityLocal::IsDirectlyVisible() 66 ============================ 67 */ 68 bool idRenderEntityLocal::IsDirectlyVisible() const { 69 if ( viewCount != tr.viewCount ) { 70 return false; 71 } 72 if ( viewEntity->scissorRect.IsEmpty() ) { 73 // a viewEntity was created for shadow generation, but the 74 // model global reference bounds isn't directly visible 75 return false; 76 } 77 return true; 78 } 79 80 /* 81 =================== 82 R_AddSingleLight 83 84 May be run in parallel. 85 86 Sets vLight->removeFromList to true if the light should be removed from the list. 87 Builds a chain of entities that need to be added for shadows only off vLight->shadowOnlyViewEntities. 88 Allocates and fills in vLight->entityInteractionState. 89 90 Calc the light shader values, removing any light from the viewLight list 91 if it is determined to not have any visible effect due to being flashed off or turned off. 92 93 Add any precomputed shadow volumes. 94 =================== 95 */ 96 static void R_AddSingleLight( viewLight_t * vLight ) { 97 // until proven otherwise 98 vLight->removeFromList = true; 99 vLight->shadowOnlyViewEntities = NULL; 100 vLight->preLightShadowVolumes = NULL; 101 102 // globals we really should pass in... 103 const viewDef_t * viewDef = tr.viewDef; 104 105 const idRenderLightLocal *light = vLight->lightDef; 106 const idMaterial * lightShader = light->lightShader; 107 if ( lightShader == NULL ) { 108 common->Error( "R_AddSingleLight: NULL lightShader" ); 109 return; 110 } 111 112 SCOPED_PROFILE_EVENT( lightShader->GetName() ); 113 114 // see if we are suppressing the light in this view 115 if ( !r_skipSuppress.GetBool() ) { 116 if ( light->parms.suppressLightInViewID && light->parms.suppressLightInViewID == viewDef->renderView.viewID ) { 117 return; 118 } 119 if ( light->parms.allowLightInViewID && light->parms.allowLightInViewID != viewDef->renderView.viewID ) { 120 return; 121 } 122 } 123 124 // evaluate the light shader registers 125 float * lightRegs = (float *)R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ), FRAME_ALLOC_SHADER_REGISTER ); 126 lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, viewDef->renderView.shaderParms, 127 tr.viewDef->renderView.time[0] * 0.001f, light->parms.referenceSound ); 128 129 // if this is a purely additive light and no stage in the light shader evaluates 130 // to a positive light value, we can completely skip the light 131 if ( !lightShader->IsFogLight() && !lightShader->IsBlendLight() ) { 132 int lightStageNum; 133 for ( lightStageNum = 0; lightStageNum < lightShader->GetNumStages(); lightStageNum++ ) { 134 const shaderStage_t *lightStage = lightShader->GetStage( lightStageNum ); 135 136 // ignore stages that fail the condition 137 if ( !lightRegs[ lightStage->conditionRegister ] ) { 138 continue; 139 } 140 141 const int * registers = lightStage->color.registers; 142 143 // snap tiny values to zero 144 if ( lightRegs[ registers[0] ] < 0.001f ) { 145 lightRegs[ registers[0] ] = 0.0f; 146 } 147 if ( lightRegs[ registers[1] ] < 0.001f ) { 148 lightRegs[ registers[1] ] = 0.0f; 149 } 150 if ( lightRegs[ registers[2] ] < 0.001f ) { 151 lightRegs[ registers[2] ] = 0.0f; 152 } 153 154 if ( lightRegs[ registers[0] ] > 0.0f || 155 lightRegs[ registers[1] ] > 0.0f || 156 lightRegs[ registers[2] ] > 0.0f ) { 157 break; 158 } 159 } 160 if ( lightStageNum == lightShader->GetNumStages() ) { 161 // we went through all the stages and didn't find one that adds anything 162 // remove the light from the viewLights list, and change its frame marker 163 // so interaction generation doesn't think the light is visible and 164 // create a shadow for it 165 return; 166 } 167 } 168 169 //-------------------------------------------- 170 // copy data used by backend 171 //-------------------------------------------- 172 vLight->globalLightOrigin = light->globalLightOrigin; 173 vLight->lightProject[0] = light->lightProject[0]; 174 vLight->lightProject[1] = light->lightProject[1]; 175 vLight->lightProject[2] = light->lightProject[2]; 176 vLight->lightProject[3] = light->lightProject[3]; 177 178 // the fog plane is the light far clip plane 179 idPlane fogPlane( light->baseLightProject[2][0] - light->baseLightProject[3][0], 180 light->baseLightProject[2][1] - light->baseLightProject[3][1], 181 light->baseLightProject[2][2] - light->baseLightProject[3][2], 182 light->baseLightProject[2][3] - light->baseLightProject[3][3] ); 183 const float planeScale = idMath::InvSqrt( fogPlane.Normal().LengthSqr() ); 184 vLight->fogPlane[0] = fogPlane[0] * planeScale; 185 vLight->fogPlane[1] = fogPlane[1] * planeScale; 186 vLight->fogPlane[2] = fogPlane[2] * planeScale; 187 vLight->fogPlane[3] = fogPlane[3] * planeScale; 188 189 // copy the matrix for deforming the 'zeroOneCubeModel' to exactly cover the light volume in world space 190 vLight->inverseBaseLightProject = light->inverseBaseLightProject; 191 192 vLight->falloffImage = light->falloffImage; 193 vLight->lightShader = light->lightShader; 194 vLight->shaderRegisters = lightRegs; 195 196 if ( r_useLightScissors.GetInteger() != 0 ) { 197 // Calculate the matrix that projects the zero-to-one cube to exactly cover the 198 // light frustum in clip space. 199 idRenderMatrix invProjectMVPMatrix; 200 idRenderMatrix::Multiply( viewDef->worldSpace.mvp, light->inverseBaseLightProject, invProjectMVPMatrix ); 201 202 // Calculate the projected bounds, either not clipped at all, near clipped, or fully clipped. 203 idBounds projected; 204 if ( r_useLightScissors.GetInteger() == 1 ) { 205 idRenderMatrix::ProjectedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube ); 206 } else if ( r_useLightScissors.GetInteger() == 2 ) { 207 idRenderMatrix::ProjectedNearClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube ); 208 } else { 209 idRenderMatrix::ProjectedFullyClippedBounds( projected, invProjectMVPMatrix, bounds_zeroOneCube ); 210 } 211 212 if ( projected[0][2] >= projected[1][2] ) { 213 // the light was culled to the view frustum 214 return; 215 } 216 217 float screenWidth = (float)viewDef->viewport.x2 - (float)viewDef->viewport.x1; 218 float screenHeight = (float)viewDef->viewport.y2 - (float)viewDef->viewport.y1; 219 220 idScreenRect lightScissorRect; 221 lightScissorRect.x1 = idMath::Ftoi( projected[0][0] * screenWidth ); 222 lightScissorRect.x2 = idMath::Ftoi( projected[1][0] * screenWidth ); 223 lightScissorRect.y1 = idMath::Ftoi( projected[0][1] * screenHeight ); 224 lightScissorRect.y2 = idMath::Ftoi( projected[1][1] * screenHeight ); 225 lightScissorRect.Expand(); 226 227 vLight->scissorRect.Intersect( lightScissorRect ); 228 vLight->scissorRect.zmin = projected[0][2]; 229 vLight->scissorRect.zmax = projected[1][2]; 230 } 231 232 // this one stays on the list 233 vLight->removeFromList = false; 234 235 //-------------------------------------------- 236 // create interactions with all entities the light may touch, and add viewEntities 237 // that may cast shadows, even if they aren't directly visible. Any real work 238 // will be deferred until we walk through the viewEntities 239 //-------------------------------------------- 240 const int renderViewID = viewDef->renderView.viewID; 241 242 // this bool array will be set true whenever the entity will visibly interact with the light 243 vLight->entityInteractionState = (byte *)R_ClearedFrameAlloc( light->world->entityDefs.Num() * sizeof( vLight->entityInteractionState[0] ), FRAME_ALLOC_INTERACTION_STATE ); 244 245 const bool lightCastsShadows = light->LightCastsShadows(); 246 idInteraction * * const interactionTableRow = light->world->interactionTable + light->index * light->world->interactionTableWidth; 247 248 for ( areaReference_t * lref = light->references; lref != NULL; lref = lref->ownerNext ) { 249 portalArea_t *area = lref->area; 250 251 // some lights have their center of projection outside the world, but otherwise 252 // we want to ignore areas that are not connected to the light center due to a closed door 253 if ( light->areaNum != -1 && r_useAreasConnectedForShadowCulling.GetInteger() == 2 ) { 254 if ( !light->world->AreasAreConnected( light->areaNum, area->areaNum, PS_BLOCK_VIEW ) ) { 255 // can't possibly be seen or shadowed 256 continue; 257 } 258 } 259 260 // check all the models in this area 261 for ( areaReference_t * eref = area->entityRefs.areaNext; eref != &area->entityRefs; eref = eref->areaNext ) { 262 idRenderEntityLocal * edef = eref->entity; 263 264 if ( vLight->entityInteractionState[ edef->index ] != viewLight_t::INTERACTION_UNCHECKED ) { 265 continue; 266 } 267 // until proven otherwise 268 vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_NO; 269 270 // The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree() 271 const idInteraction * inter = interactionTableRow[ edef->index ]; 272 273 const renderEntity_t & eParms = edef->parms; 274 const idRenderModel * eModel = eParms.hModel; 275 276 // a large fraction of static entity / light pairs will still have no interactions even though 277 // they are both present in the same area(s) 278 if ( eModel != NULL && !eModel->IsDynamicModel() && inter == INTERACTION_EMPTY ) { 279 // the interaction was statically checked, and it didn't generate any surfaces, 280 // so there is no need to force the entity onto the view list if it isn't 281 // already there 282 continue; 283 } 284 285 // We don't want the lights on weapons to illuminate anything else. 286 // There are two assumptions here -- that allowLightInViewID is only 287 // used for weapon lights, and that all weapons will have weaponDepthHack. 288 // A more general solution would be to have an allowLightOnEntityID field. 289 // HACK: the armor-mounted flashlight is a private spot light, which is probably 290 // wrong -- you would expect to see them in multiplayer. 291 if ( light->parms.allowLightInViewID && light->parms.pointLight && !eParms.weaponDepthHack ) { 292 continue; 293 } 294 295 // non-shadow casting entities don't need to be added if they aren't 296 // directly visible 297 if ( ( eParms.noShadow || ( eModel && !eModel->ModelHasShadowCastingSurfaces() ) ) && !edef->IsDirectlyVisible() ) { 298 continue; 299 } 300 301 // if the model doesn't accept lighting or cast shadows, it doesn't need to be added 302 if ( eModel && !eModel->ModelHasInteractingSurfaces() && !eModel->ModelHasShadowCastingSurfaces() ) { 303 continue; 304 } 305 306 // no interaction present, so either the light or entity has moved 307 // assert( lightHasMoved || edef->entityHasMoved ); 308 if ( inter == NULL ) { 309 // some big outdoor meshes are flagged to not create any dynamic interactions 310 // when the level designer knows that nearby moving lights shouldn't actually hit them 311 if ( eParms.noDynamicInteractions ) { 312 continue; 313 } 314 315 // do a check of the entity reference bounds against the light frustum to see if they can't 316 // possibly interact, despite sharing one or more world areas 317 if ( R_CullModelBoundsToLight( light, edef->localReferenceBounds, edef->modelRenderMatrix ) ) { 318 continue; 319 } 320 } 321 322 // we now know that the entity and light do overlap 323 324 if ( edef->IsDirectlyVisible() ) { 325 // entity is directly visible, so the interaction is definitely needed 326 vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES; 327 continue; 328 } 329 330 // the entity is not directly visible, but if we can tell that it may cast 331 // shadows onto visible surfaces, we must make a viewEntity for it 332 if ( !lightCastsShadows ) { 333 // surfaces are never shadowed in this light 334 continue; 335 } 336 // if we are suppressing its shadow in this view (player shadows, etc), skip 337 if ( !r_skipSuppress.GetBool() ) { 338 if ( eParms.suppressShadowInViewID && eParms.suppressShadowInViewID == renderViewID ) { 339 continue; 340 } 341 if ( eParms.suppressShadowInLightID && eParms.suppressShadowInLightID == light->parms.lightId ) { 342 continue; 343 } 344 } 345 346 // should we use the shadow bounds from pre-calculated interactions? 347 idBounds shadowBounds; 348 R_ShadowBounds( edef->globalReferenceBounds, light->globalLightBounds, light->globalLightOrigin, shadowBounds ); 349 350 // this test is pointless if we knew the light was completely contained 351 // in the view frustum, but the entity would also be directly visible in most 352 // of those cases. 353 354 // this doesn't say that the shadow can't effect anything, only that it can't 355 // effect anything in the view, so we shouldn't set up a view entity 356 if ( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, shadowBounds ) ) { 357 continue; 358 } 359 360 // debug tool to allow viewing of only one entity at a time 361 if ( r_singleEntity.GetInteger() >= 0 && r_singleEntity.GetInteger() != edef->index ) { 362 continue; 363 } 364 365 // we do need it for shadows 366 vLight->entityInteractionState[ edef->index ] = viewLight_t::INTERACTION_YES; 367 368 // we will need to create a viewEntity_t for it in the serial code section 369 shadowOnlyEntity_t * shadEnt = (shadowOnlyEntity_t *)R_FrameAlloc( sizeof( shadowOnlyEntity_t ), FRAME_ALLOC_SHADOW_ONLY_ENTITY ); 370 shadEnt->next = vLight->shadowOnlyViewEntities; 371 shadEnt->edef = edef; 372 vLight->shadowOnlyViewEntities = shadEnt; 373 } 374 } 375 376 //-------------------------------------------- 377 // add the prelight shadows for the static world geometry 378 //-------------------------------------------- 379 if ( light->parms.prelightModel != NULL ) { 380 srfTriangles_t * tri = light->parms.prelightModel->Surface( 0 )->geometry; 381 382 // these shadows will have valid bounds, and can be culled normally, 383 // but they will typically cover most of the light's bounds 384 if ( idRenderMatrix::CullBoundsToMVP( viewDef->worldSpace.mvp, tri->bounds ) ) { 385 return; 386 } 387 388 // prelight models should always have static data that never gets purged 389 assert( vertexCache.CacheIsCurrent( tri->shadowCache ) ); 390 assert( vertexCache.CacheIsCurrent( tri->indexCache ) ); 391 392 drawSurf_t * shadowDrawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *shadowDrawSurf ), FRAME_ALLOC_DRAW_SURFACE ); 393 394 shadowDrawSurf->frontEndGeo = tri; 395 shadowDrawSurf->ambientCache = 0; 396 shadowDrawSurf->indexCache = tri->indexCache; 397 shadowDrawSurf->shadowCache = tri->shadowCache; 398 shadowDrawSurf->jointCache = 0; 399 shadowDrawSurf->numIndexes = 0; 400 shadowDrawSurf->space = &viewDef->worldSpace; 401 shadowDrawSurf->material = NULL; 402 shadowDrawSurf->extraGLState = 0; 403 shadowDrawSurf->shaderRegisters = NULL; 404 shadowDrawSurf->scissorRect = vLight->scissorRect; // default to the light scissor and light depth bounds 405 shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_DONE; // assume the shadow volume is done in case r_skipPrelightShadows is set 406 407 if ( !r_skipPrelightShadows.GetBool() ) { 408 preLightShadowVolumeParms_t * shadowParms = (preLightShadowVolumeParms_t *)R_FrameAlloc( sizeof( shadowParms[0] ), FRAME_ALLOC_SHADOW_VOLUME_PARMS ); 409 410 shadowParms->verts = tri->preLightShadowVertexes; 411 shadowParms->numVerts = tri->numVerts * 2; 412 shadowParms->indexes = tri->indexes; 413 shadowParms->numIndexes = tri->numIndexes; 414 shadowParms->triangleBounds = tri->bounds; 415 shadowParms->triangleMVP = viewDef->worldSpace.mvp; 416 shadowParms->localLightOrigin = vLight->globalLightOrigin; 417 shadowParms->localViewOrigin = viewDef->renderView.vieworg; 418 shadowParms->zNear = r_znear.GetFloat(); 419 shadowParms->lightZMin = vLight->scissorRect.zmin; 420 shadowParms->lightZMax = vLight->scissorRect.zmax; 421 shadowParms->forceShadowCaps = r_forceShadowCaps.GetBool(); 422 shadowParms->useShadowPreciseInsideTest = r_useShadowPreciseInsideTest.GetBool(); 423 shadowParms->useShadowDepthBounds = r_useShadowDepthBounds.GetBool(); 424 shadowParms->numShadowIndices = & shadowDrawSurf->numIndexes; 425 shadowParms->renderZFail = & shadowDrawSurf->renderZFail; 426 shadowParms->shadowZMin = & shadowDrawSurf->scissorRect.zmin; 427 shadowParms->shadowZMax = & shadowDrawSurf->scissorRect.zmax; 428 shadowParms->shadowVolumeState = & shadowDrawSurf->shadowVolumeState; 429 430 // the pre-light shadow volume "_prelight_light_3297" in "d3xpdm2" is malformed in that it contains the light origin so the precise inside test always fails 431 if ( tr.primaryWorld->mapName.IcmpPath( "maps/game/mp/d3xpdm2.map" ) == 0 && idStr::Icmp( light->parms.prelightModel->Name(), "_prelight_light_3297" ) == 0 ) { 432 shadowParms->useShadowPreciseInsideTest = false; 433 } 434 435 shadowDrawSurf->shadowVolumeState = SHADOWVOLUME_UNFINISHED; 436 437 shadowParms->next = vLight->preLightShadowVolumes; 438 vLight->preLightShadowVolumes = shadowParms; 439 } 440 441 // actually link it in 442 shadowDrawSurf->nextOnLight = vLight->globalShadows; 443 vLight->globalShadows = shadowDrawSurf; 444 } 445 } 446 447 REGISTER_PARALLEL_JOB( R_AddSingleLight, "R_AddSingleLight" ); 448 449 /* 450 ================= 451 R_AddLights 452 ================= 453 */ 454 void R_AddLights() { 455 SCOPED_PROFILE_EVENT( "R_AddLights" ); 456 457 //------------------------------------------------- 458 // check each light individually, possibly in parallel 459 //------------------------------------------------- 460 461 if ( r_useParallelAddLights.GetBool() ) { 462 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { 463 tr.frontEndJobList->AddJob( (jobRun_t)R_AddSingleLight, vLight ); 464 } 465 tr.frontEndJobList->Submit(); 466 tr.frontEndJobList->Wait(); 467 } else { 468 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { 469 R_AddSingleLight( vLight ); 470 } 471 } 472 473 //------------------------------------------------- 474 // cull lights from the list if they turned out to not be needed 475 //------------------------------------------------- 476 477 tr.pc.c_viewLights = 0; 478 viewLight_t **ptr = &tr.viewDef->viewLights; 479 while ( *ptr != NULL ) { 480 viewLight_t *vLight = *ptr; 481 482 if ( vLight->removeFromList ) { 483 vLight->lightDef->viewCount = -1; // this probably doesn't matter with current code 484 *ptr = vLight->next; 485 continue; 486 } 487 488 ptr = &vLight->next; 489 490 // serial work 491 tr.pc.c_viewLights++; 492 493 for ( shadowOnlyEntity_t * shadEnt = vLight->shadowOnlyViewEntities; shadEnt != NULL; shadEnt = shadEnt->next ) { 494 // this will add it to the viewEntities list, but with an empty scissor rect 495 R_SetEntityDefViewEntity( shadEnt->edef ); 496 } 497 498 if ( r_showLightScissors.GetBool() ) { 499 R_ShowColoredScreenRect( vLight->scissorRect, vLight->lightDef->index ); 500 } 501 } 502 503 //------------------------------------------------- 504 // Add jobs to setup pre-light shadow volumes. 505 //------------------------------------------------- 506 507 if ( r_useParallelAddShadows.GetInteger() == 1 ) { 508 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { 509 for ( preLightShadowVolumeParms_t * shadowParms = vLight->preLightShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) { 510 tr.frontEndJobList->AddJob( (jobRun_t)PreLightShadowVolumeJob, shadowParms ); 511 } 512 vLight->preLightShadowVolumes = NULL; 513 } 514 } else { 515 int start = Sys_Microseconds(); 516 517 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { 518 for ( preLightShadowVolumeParms_t * shadowParms = vLight->preLightShadowVolumes; shadowParms != NULL; shadowParms = shadowParms->next ) { 519 PreLightShadowVolumeJob( shadowParms ); 520 } 521 vLight->preLightShadowVolumes = NULL; 522 } 523 524 int end = Sys_Microseconds(); 525 backEnd.pc.shadowMicroSec += end - start; 526 } 527 } 528 529 /* 530 ===================== 531 R_OptimizeViewLightsList 532 ===================== 533 */ 534 void R_OptimizeViewLightsList() { 535 // go through each visible light 536 int numViewLights = 0; 537 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { 538 numViewLights++; 539 // If the light didn't have any lit surfaces visible, there is no need to 540 // draw any of the shadows. We still keep the vLight for debugging draws. 541 if ( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) { 542 vLight->localShadows = NULL; 543 vLight->globalShadows = NULL; 544 } 545 } 546 547 if ( r_useShadowSurfaceScissor.GetBool() ) { 548 // shrink the light scissor rect to only intersect the surfaces that will actually be drawn. 549 // This doesn't seem to actually help, perhaps because the surface scissor 550 // rects aren't actually the surface, but only the portal clippings. 551 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight; vLight = vLight->next ) { 552 drawSurf_t * surf; 553 idScreenRect surfRect; 554 555 if ( !vLight->lightShader->LightCastsShadows() ) { 556 continue; 557 } 558 559 surfRect.Clear(); 560 561 for ( surf = vLight->globalInteractions; surf != NULL; surf = surf->nextOnLight ) { 562 surfRect.Union( surf->scissorRect ); 563 } 564 for ( surf = vLight->localShadows; surf != NULL; surf = surf->nextOnLight ) { 565 surf->scissorRect.Intersect( surfRect ); 566 } 567 568 for ( surf = vLight->localInteractions; surf != NULL; surf = surf->nextOnLight ) { 569 surfRect.Union( surf->scissorRect ); 570 } 571 for ( surf = vLight->globalShadows; surf != NULL; surf = surf->nextOnLight ) { 572 surf->scissorRect.Intersect( surfRect ); 573 } 574 575 for ( surf = vLight->translucentInteractions; surf != NULL; surf = surf->nextOnLight ) { 576 surfRect.Union( surf->scissorRect ); 577 } 578 579 vLight->scissorRect.Intersect( surfRect ); 580 } 581 } 582 583 // sort the viewLights list so the largest lights come first, which will reduce 584 // the chance of GPU pipeline bubbles 585 struct sortLight_t { 586 viewLight_t * vLight; 587 int screenArea; 588 static int sort( const void * a, const void * b ) { 589 return ((sortLight_t *)a)->screenArea - ((sortLight_t *)b)->screenArea; 590 } 591 }; 592 sortLight_t * sortLights = (sortLight_t *)_alloca( sizeof( sortLight_t ) * numViewLights ); 593 int numSortLightsFilled = 0; 594 for ( viewLight_t * vLight = tr.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { 595 sortLights[ numSortLightsFilled ].vLight = vLight; 596 sortLights[ numSortLightsFilled ].screenArea = vLight->scissorRect.GetArea(); 597 numSortLightsFilled++; 598 } 599 600 qsort( sortLights, numSortLightsFilled, sizeof( sortLights[0] ), sortLight_t::sort ); 601 602 // rebuild the linked list in order 603 tr.viewDef->viewLights = NULL; 604 for ( int i = 0; i < numSortLightsFilled; i++ ) { 605 sortLights[i].vLight->next = tr.viewDef->viewLights; 606 tr.viewDef->viewLights = sortLights[i].vLight; 607 } 608 }