tr_frontend_addlights.cpp 24 KB

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