DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }