2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
34 static const float CHECK_BOUNDS_EPSILON = 1.0f;
38 ===========================================================================================
40 VERTEX CACHE GENERATORS
42 ===========================================================================================
52 bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) {
53 if ( tri->ambientCache ) {
56 // we are going to use it for drawing, so make sure we have the tangents and normals
57 if ( needsLighting && !tri->tangentsCalculated ) {
58 R_DeriveTangents( tri );
61 vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache );
62 if ( !tri->ambientCache ) {
72 Returns false if the cache couldn't be allocated, in which case the surface should be skipped.
75 bool R_CreateLightingCache( const idRenderEntityLocal *ent, const idRenderLightLocal *light, srfTriangles_t *tri ) {
76 idVec3 localLightOrigin;
78 // fogs and blends don't need light vectors
79 if ( light->lightShader->IsFogLight() || light->lightShader->IsBlendLight() ) {
83 // not needed if we have vertex programs
84 if ( tr.backEndRendererHasVertexPrograms ) {
88 R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
90 int size = tri->ambientSurface->numVerts * sizeof( lightingCache_t );
91 lightingCache_t *cache = (lightingCache_t *)_alloca16( size );
95 SIMDProcessor->CreateTextureSpaceLightVectors( &cache[0].localLightVector, localLightOrigin,
96 tri->ambientSurface->verts, tri->ambientSurface->numVerts, tri->indexes, tri->numIndexes );
100 bool *used = (bool *)_alloca16( tri->ambientSurface->numVerts * sizeof( used[0] ) );
101 memset( used, 0, tri->ambientSurface->numVerts * sizeof( used[0] ) );
103 // because the interaction may be a very small subset of the full surface,
104 // it makes sense to only deal with the verts used
105 for ( int j = 0; j < tri->numIndexes; j++ ) {
106 int i = tri->indexes[j];
115 v = &tri->ambientSurface->verts[i];
117 lightDir = localLightOrigin - v->xyz;
119 cache[i].localLightVector[0] = lightDir * v->tangents[0];
120 cache[i].localLightVector[1] = lightDir * v->tangents[1];
121 cache[i].localLightVector[2] = lightDir * v->normal;
126 vertexCache.Alloc( cache, size, &tri->lightingCache );
127 if ( !tri->lightingCache ) {
135 R_CreatePrivateShadowCache
137 This is used only for a specific light
140 void R_CreatePrivateShadowCache( srfTriangles_t *tri ) {
141 if ( !tri->shadowVertexes ) {
145 vertexCache.Alloc( tri->shadowVertexes, tri->numVerts * sizeof( *tri->shadowVertexes ), &tri->shadowCache );
150 R_CreateVertexProgramShadowCache
152 This is constant for any number of lights, the vertex program
153 takes care of projecting the verts to infinity.
156 void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ) {
157 if ( tri->verts == NULL ) {
161 shadowCache_t *temp = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowCache_t ) );
165 SIMDProcessor->CreateVertexProgramShadowCache( &temp->xyz, tri->verts, tri->numVerts );
169 int numVerts = tri->numVerts;
170 const idDrawVert *verts = tri->verts;
171 for ( int i = 0; i < numVerts; i++ ) {
172 const float *v = verts[i].xyz.ToFloatPtr();
173 temp[i*2+0].xyz[0] = v[0];
174 temp[i*2+1].xyz[0] = v[0];
175 temp[i*2+0].xyz[1] = v[1];
176 temp[i*2+1].xyz[1] = v[1];
177 temp[i*2+0].xyz[2] = v[2];
178 temp[i*2+1].xyz[2] = v[2];
179 temp[i*2+0].xyz[3] = 1.0f; // on the model surface
180 temp[i*2+1].xyz[3] = 0.0f; // will be projected to infinity
185 vertexCache.Alloc( temp, tri->numVerts * 2 * sizeof( shadowCache_t ), &tri->shadowCache );
193 void R_SkyboxTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) {
195 idVec3 localViewOrigin;
197 R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
199 int numVerts = surf->geo->numVerts;
200 int size = numVerts * sizeof( idVec3 );
201 idVec3 *texCoords = (idVec3 *) _alloca16( size );
203 const idDrawVert *verts = surf->geo->verts;
204 for ( i = 0; i < numVerts; i++ ) {
205 texCoords[i][0] = verts[i].xyz[0] - localViewOrigin[0];
206 texCoords[i][1] = verts[i].xyz[1] - localViewOrigin[1];
207 texCoords[i][2] = verts[i].xyz[2] - localViewOrigin[2];
210 surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
218 void R_WobbleskyTexGen( drawSurf_t *surf, const idVec3 &viewOrg ) {
220 idVec3 localViewOrigin;
222 const int *parms = surf->material->GetTexGenRegisters();
224 float wobbleDegrees = surf->shaderRegisters[ parms[0] ];
225 float wobbleSpeed = surf->shaderRegisters[ parms[1] ];
226 float rotateSpeed = surf->shaderRegisters[ parms[2] ];
228 wobbleDegrees = wobbleDegrees * idMath::PI / 180;
229 wobbleSpeed = wobbleSpeed * 2 * idMath::PI / 60;
230 rotateSpeed = rotateSpeed * 2 * idMath::PI / 60;
232 // very ad-hoc "wobble" transform
234 float a = tr.viewDef->floatTime * wobbleSpeed;
235 float s = sin( a ) * sin( wobbleDegrees );
236 float c = cos( a ) * sin( wobbleDegrees );
237 float z = cos( wobbleDegrees );
245 axis[1][0] = -sin( a * 2 ) * sin( wobbleDegrees );
246 axis[1][2] = -s * sin( wobbleDegrees );
247 axis[1][1] = sqrt( 1.0f - ( axis[1][0] * axis[1][0] + axis[1][2] * axis[1][2] ) );
249 // make the second vector exactly perpendicular to the first
250 axis[1] -= ( axis[2] * axis[1] ) * axis[2];
253 // construct the third with a cross
254 axis[0].Cross( axis[1], axis[2] );
257 s = sin( rotateSpeed * tr.viewDef->floatTime );
258 c = cos( rotateSpeed * tr.viewDef->floatTime );
260 transform[0] = axis[0][0] * c + axis[1][0] * s;
261 transform[4] = axis[0][1] * c + axis[1][1] * s;
262 transform[8] = axis[0][2] * c + axis[1][2] * s;
264 transform[1] = axis[1][0] * c - axis[0][0] * s;
265 transform[5] = axis[1][1] * c - axis[0][1] * s;
266 transform[9] = axis[1][2] * c - axis[0][2] * s;
268 transform[2] = axis[2][0];
269 transform[6] = axis[2][1];
270 transform[10] = axis[2][2];
272 transform[3] = transform[7] = transform[11] = 0.0f;
273 transform[12] = transform[13] = transform[14] = 0.0f;
275 R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
277 int numVerts = surf->geo->numVerts;
278 int size = numVerts * sizeof( idVec3 );
279 idVec3 *texCoords = (idVec3 *) _alloca16( size );
281 const idDrawVert *verts = surf->geo->verts;
282 for ( i = 0; i < numVerts; i++ ) {
285 v[0] = verts[i].xyz[0] - localViewOrigin[0];
286 v[1] = verts[i].xyz[1] - localViewOrigin[1];
287 v[2] = verts[i].xyz[2] - localViewOrigin[2];
289 R_LocalPointToGlobal( transform, v, texCoords[i] );
292 surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
299 Calculates the specular coordinates for cards without vertex programs.
302 static void R_SpecularTexGen( drawSurf_t *surf, const idVec3 &globalLightOrigin, const idVec3 &viewOrg ) {
303 const srfTriangles_t *tri;
304 idVec3 localLightOrigin;
305 idVec3 localViewOrigin;
307 R_GlobalPointToLocal( surf->space->modelMatrix, globalLightOrigin, localLightOrigin );
308 R_GlobalPointToLocal( surf->space->modelMatrix, viewOrg, localViewOrigin );
312 // FIXME: change to 3 component?
313 int size = tri->numVerts * sizeof( idVec4 );
314 idVec4 *texCoords = (idVec4 *) _alloca16( size );
318 SIMDProcessor->CreateSpecularTextureCoords( texCoords, localLightOrigin, localViewOrigin,
319 tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
323 bool *used = (bool *)_alloca16( tri->numVerts * sizeof( used[0] ) );
324 memset( used, 0, tri->numVerts * sizeof( used[0] ) );
326 // because the interaction may be a very small subset of the full surface,
327 // it makes sense to only deal with the verts used
328 for ( int j = 0; j < tri->numIndexes; j++ ) {
329 int i = tri->indexes[j];
337 const idDrawVert *v = &tri->verts[i];
339 idVec3 lightDir = localLightOrigin - v->xyz;
340 idVec3 viewDir = localViewOrigin - v->xyz;
342 ilength = idMath::RSqrt( lightDir * lightDir );
343 lightDir[0] *= ilength;
344 lightDir[1] *= ilength;
345 lightDir[2] *= ilength;
347 ilength = idMath::RSqrt( viewDir * viewDir );
348 viewDir[0] *= ilength;
349 viewDir[1] *= ilength;
350 viewDir[2] *= ilength;
354 texCoords[i][0] = lightDir * v->tangents[0];
355 texCoords[i][1] = lightDir * v->tangents[1];
356 texCoords[i][2] = lightDir * v->normal;
362 surf->dynamicTexCoords = vertexCache.AllocFrameTemp( texCoords, size );
366 //=======================================================================================================
370 R_SetEntityDefViewEntity
372 If the entityDef isn't already on the viewEntity list, create
373 a viewEntity and add it to the list with an empty scissor rect.
375 This does not instantiate dynamic models for the entity yet.
378 viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def ) {
379 viewEntity_t *vModel;
381 if ( def->viewCount == tr.viewCount ) {
382 return def->viewEntity;
384 def->viewCount = tr.viewCount;
386 // set the model and modelview matricies
387 vModel = (viewEntity_t *)R_ClearedFrameAlloc( sizeof( *vModel ) );
388 vModel->entityDef = def;
390 // the scissorRect will be expanded as the model bounds is accepted into visible portal chains
391 vModel->scissorRect.Clear();
393 // copy the model and weapon depth hack for back-end use
394 vModel->modelDepthHack = def->parms.modelDepthHack;
395 vModel->weaponDepthHack = def->parms.weaponDepthHack;
397 R_AxisToModelMatrix( def->parms.axis, def->parms.origin, vModel->modelMatrix );
399 // we may not have a viewDef if we are just creating shadows at entity creation time
401 myGlMultMatrix( vModel->modelMatrix, tr.viewDef->worldSpace.modelViewMatrix, vModel->modelViewMatrix );
403 vModel->next = tr.viewDef->viewEntitys;
404 tr.viewDef->viewEntitys = vModel;
407 def->viewEntity = vModel;
414 R_TestPointInViewLight
417 static const float INSIDE_LIGHT_FRUSTUM_SLOP = 32;
418 // this needs to be greater than the dist from origin to corner of near clip plane
419 static bool R_TestPointInViewLight( const idVec3 &org, const idRenderLightLocal *light ) {
423 for ( i = 0 ; i < 6 ; i++ ) {
424 float d = light->frustum[i].Distance( org );
425 if ( d > INSIDE_LIGHT_FRUSTUM_SLOP ) {
437 Assumes positive sides face outward
440 static bool R_PointInFrustum( idVec3 &p, idPlane *planes, int numPlanes ) {
441 for ( int i = 0 ; i < numPlanes ; i++ ) {
442 float d = planes[i].Distance( p );
452 R_SetLightDefViewLight
454 If the lightDef isn't already on the viewLight list, create
455 a viewLight and add it to the list with an empty scissor rect.
458 viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *light ) {
461 if ( light->viewCount == tr.viewCount ) {
462 return light->viewLight;
464 light->viewCount = tr.viewCount;
466 // add to the view light chain
467 vLight = (viewLight_t *)R_ClearedFrameAlloc( sizeof( *vLight ) );
468 vLight->lightDef = light;
470 // the scissorRect will be expanded as the light bounds is accepted into visible portal chains
471 vLight->scissorRect.Clear();
473 // calculate the shadow cap optimization states
474 vLight->viewInsideLight = R_TestPointInViewLight( tr.viewDef->renderView.vieworg, light );
475 if ( !vLight->viewInsideLight ) {
476 vLight->viewSeesShadowPlaneBits = 0;
477 for ( int i = 0 ; i < light->numShadowFrustums ; i++ ) {
478 float d = light->shadowFrustums[i].planes[5].Distance( tr.viewDef->renderView.vieworg );
479 if ( d < INSIDE_LIGHT_FRUSTUM_SLOP ) {
480 vLight->viewSeesShadowPlaneBits|= 1 << i;
484 // this should not be referenced in this case
485 vLight->viewSeesShadowPlaneBits = 63;
488 // see if the light center is in view, which will allow us to cull invisible shadows
489 vLight->viewSeesGlobalLightOrigin = R_PointInFrustum( light->globalLightOrigin, tr.viewDef->frustum, 4 );
491 // copy data used by backend
492 vLight->globalLightOrigin = light->globalLightOrigin;
493 vLight->lightProject[0] = light->lightProject[0];
494 vLight->lightProject[1] = light->lightProject[1];
495 vLight->lightProject[2] = light->lightProject[2];
496 vLight->lightProject[3] = light->lightProject[3];
497 vLight->fogPlane = light->frustum[5];
498 vLight->frustumTris = light->frustumTris;
499 vLight->falloffImage = light->falloffImage;
500 vLight->lightShader = light->lightShader;
501 vLight->shaderRegisters = NULL; // allocated and evaluated in R_AddLightSurfaces
503 // link the view light
504 vLight->next = tr.viewDef->viewLights;
505 tr.viewDef->viewLights = vLight;
507 light->viewLight = vLight;
514 idRenderWorldLocal::CreateLightDefInteractions
516 When a lightDef is determined to effect the view (contact the frustum and non-0 light), it will check to
517 make sure that it has interactions for all the entityDefs that it might possibly contact.
519 This does not guarantee that all possible interactions for this light are generated, only that
520 the ones that may effect the current view are generated. so it does need to be called every view.
522 This does not cause entityDefs to create dynamic models, all work is done on the referenceBounds.
524 All entities that have non-empty interactions with viewLights will
525 have viewEntities made for them and be put on the viewEntity list,
526 even if their surfaces aren't visible, because they may need to cast shadows.
528 Interactions are usually removed when a entityDef or lightDef is modified, unless the change
529 is known to not effect them, so there is no danger of getting a stale interaction, we just need to
530 check that needed ones are created.
532 An interaction can be at several levels:
534 Don't interact (but share an area) (numSurfaces = 0)
535 Entity reference bounds touches light frustum, but surfaces haven't been generated (numSurfaces = -1)
536 Shadow surfaces have been generated, but light surfaces have not. The shadow surface may still be empty due to bounds being conservative.
537 Both shadow and light surfaces have been generated. Either or both surfaces may still be empty due to conservative bounds.
541 void idRenderWorldLocal::CreateLightDefInteractions( idRenderLightLocal *ldef ) {
542 areaReference_t *eref;
543 areaReference_t *lref;
544 idRenderEntityLocal *edef;
546 idInteraction *inter;
548 for ( lref = ldef->references ; lref ; lref = lref->ownerNext ) {
551 // check all the models in this area
552 for ( eref = area->entityRefs.areaNext ; eref != &area->entityRefs ; eref = eref->areaNext ) {
555 // if the entity doesn't have any light-interacting surfaces, we could skip this,
556 // but we don't want to instantiate dynamic models yet, so we can't check that on
559 // if the entity isn't viewed
560 if ( tr.viewDef && edef->viewCount != tr.viewCount ) {
561 // if the light doesn't cast shadows, skip
562 if ( !ldef->lightShader->LightCastsShadows() ) {
565 // if we are suppressing its shadow in this view, skip
566 if ( !r_skipSuppress.GetBool() ) {
567 if ( edef->parms.suppressShadowInViewID && edef->parms.suppressShadowInViewID == tr.viewDef->renderView.viewID ) {
570 if ( edef->parms.suppressShadowInLightID && edef->parms.suppressShadowInLightID == ldef->parms.lightId ) {
576 // some big outdoor meshes are flagged to not create any dynamic interactions
577 // when the level designer knows that nearby moving lights shouldn't actually hit them
578 if ( edef->parms.noDynamicInteractions && edef->world->generateAllInteractionsCalled ) {
582 // if any of the edef's interaction match this light, we don't
583 // need to consider it.
584 if ( r_useInteractionTable.GetBool() && this->interactionTable ) {
585 // allocating these tables may take several megs on big maps, but it saves 3% to 5% of
586 // the CPU time. The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
587 int index = ldef->index * this->interactionTableWidth + edef->index;
588 inter = this->interactionTable[ index ];
590 // if this entity wasn't in view already, the scissor rect will be empty,
591 // so it will only be used for shadow casting
592 if ( !inter->IsEmpty() ) {
593 R_SetEntityDefViewEntity( edef );
598 // scan the doubly linked lists, which may have several dozen entries
600 // we could check either model refs or light refs for matches, but it is
601 // assumed that there will be less lights in an area than models
602 // so the entity chains should be somewhat shorter (they tend to be fairly close).
603 for ( inter = edef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
604 if ( inter->lightDef == ldef ) {
609 // if we already have an interaction, we don't need to do anything
610 if ( inter != NULL ) {
611 // if this entity wasn't in view already, the scissor rect will be empty,
612 // so it will only be used for shadow casting
613 if ( !inter->IsEmpty() ) {
614 R_SetEntityDefViewEntity( edef );
621 // create a new interaction, but don't do any work other than bbox to frustum culling
623 idInteraction *inter = idInteraction::AllocAndLink( edef, ldef );
625 // do a check of the entity reference bounds against the light frustum,
626 // trying to avoid creating a viewEntity if it hasn't been already
627 float modelMatrix[16];
630 if ( edef->viewCount == tr.viewCount ) {
631 m = edef->viewEntity->modelMatrix;
633 R_AxisToModelMatrix( edef->parms.axis, edef->parms.origin, modelMatrix );
637 if ( R_CullLocalBox( edef->referenceBounds, m, 6, ldef->frustum ) ) {
642 // we will do a more precise per-surface check when we are checking the entity
644 // if this entity wasn't in view already, the scissor rect will be empty,
645 // so it will only be used for shadow casting
646 R_SetEntityDefViewEntity( edef );
651 //===============================================================================================================
658 void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
659 const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow ) {
660 drawSurf_t *drawSurf;
663 space = &tr.viewDef->worldSpace;
666 drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ) );
669 drawSurf->space = space;
670 drawSurf->material = shader;
671 drawSurf->scissorRect = scissor;
672 drawSurf->dsFlags = 0;
673 if ( viewInsideShadow ) {
674 drawSurf->dsFlags |= DSF_VIEW_INSIDE_SHADOW;
678 // shadows won't have a shader
679 drawSurf->shaderRegisters = NULL;
681 // process the shader expressions for conditionals / color / texcoords
682 const float *constRegs = shader->ConstantRegisters();
684 // this shader has only constants for parameters
685 drawSurf->shaderRegisters = constRegs;
687 // FIXME: share with the ambient surface?
688 float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ) );
689 drawSurf->shaderRegisters = regs;
690 shader->EvaluateRegisters( regs, space->entityDef->parms.shaderParms, tr.viewDef, space->entityDef->parms.referenceSound );
693 // calculate the specular coordinates if we aren't using vertex programs
694 if ( !tr.backEndRendererHasVertexPrograms && !r_skipSpecular.GetBool() && tr.backEndRenderer != BE_ARB ) {
695 R_SpecularTexGen( drawSurf, light->globalLightOrigin, tr.viewDef->renderView.vieworg );
696 // if we failed to allocate space for the specular calculations, drop the surface
697 if ( !drawSurf->dynamicTexCoords ) {
703 // actually link it in
704 drawSurf->nextOnLight = *link;
709 ======================
710 R_ClippedLightScissorRectangle
711 ======================
713 idScreenRect R_ClippedLightScissorRectangle( viewLight_t *vLight ) {
715 const idRenderLightLocal *light = vLight->lightDef;
721 for ( i = 0 ; i < 6 ; i++ ) {
722 const idWinding *ow = light->frustumWindings[i];
724 // projected lights may have one of the frustums degenerated
729 // the light frustum planes face out from the light,
730 // so the planes that have the view origin on the negative
731 // side will be the "back" faces of the light, which must have
732 // some fragment inside the portalStack to be visible
733 if ( light->frustum[i].Distance( tr.viewDef->renderView.vieworg ) >= 0 ) {
739 // now check the winding against each of the frustum planes
740 for ( j = 0; j < 5; j++ ) {
741 if ( !w.ClipInPlace( -tr.viewDef->frustum[j] ) ) {
746 // project these points to the screen and add to bounds
747 for ( j = 0; j < w.GetNumPoints(); j++ ) {
751 R_TransformModelToClip( w[j].ToVec3(), tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
753 if ( clip[3] <= 0.01f ) {
757 R_TransformClipToDevice( clip, tr.viewDef, ndc );
759 float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 );
760 float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 );
762 if ( windowX > tr.viewDef->scissor.x2 ) {
763 windowX = tr.viewDef->scissor.x2;
764 } else if ( windowX < tr.viewDef->scissor.x1 ) {
765 windowX = tr.viewDef->scissor.x1;
767 if ( windowY > tr.viewDef->scissor.y2 ) {
768 windowY = tr.viewDef->scissor.y2;
769 } else if ( windowY < tr.viewDef->scissor.y1 ) {
770 windowY = tr.viewDef->scissor.y1;
773 r.AddPoint( windowX, windowY );
777 // add the fudge boundary
785 R_CalcLightScissorRectangle
787 The light screen bounds will be used to crop the scissor rect during
788 stencil clears and interaction drawing
791 int c_clippedLight, c_unclippedLight;
793 idScreenRect R_CalcLightScissorRectangle( viewLight_t *vLight ) {
799 if ( vLight->lightDef->parms.pointLight ) {
801 idRenderLightLocal *lightDef = vLight->lightDef;
802 tr.viewDef->viewFrustum.ProjectionBounds( idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), bounds );
803 return R_ScreenRectFromViewFrustumBounds( bounds );
806 if ( r_useClippedLightScissors.GetInteger() == 2 ) {
807 return R_ClippedLightScissorRectangle( vLight );
812 tri = vLight->lightDef->frustumTris;
813 for ( int i = 0 ; i < tri->numVerts ; i++ ) {
814 R_TransformModelToClip( tri->verts[i].xyz, tr.viewDef->worldSpace.modelViewMatrix,
815 tr.viewDef->projectionMatrix, eye, clip );
817 // if it is near clipped, clip the winding polygons to the view frustum
818 if ( clip[3] <= 1 ) {
820 if ( r_useClippedLightScissors.GetInteger() ) {
821 return R_ClippedLightScissorRectangle( vLight );
824 r.x2 = ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 ) - 1;
825 r.y2 = ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 ) - 1;
830 R_TransformClipToDevice( clip, tr.viewDef, ndc );
832 float windowX = 0.5f * ( 1.0f + ndc[0] ) * ( tr.viewDef->viewport.x2 - tr.viewDef->viewport.x1 );
833 float windowY = 0.5f * ( 1.0f + ndc[1] ) * ( tr.viewDef->viewport.y2 - tr.viewDef->viewport.y1 );
835 if ( windowX > tr.viewDef->scissor.x2 ) {
836 windowX = tr.viewDef->scissor.x2;
837 } else if ( windowX < tr.viewDef->scissor.x1 ) {
838 windowX = tr.viewDef->scissor.x1;
840 if ( windowY > tr.viewDef->scissor.y2 ) {
841 windowY = tr.viewDef->scissor.y2;
842 } else if ( windowY < tr.viewDef->scissor.y1 ) {
843 windowY = tr.viewDef->scissor.y1;
846 r.AddPoint( windowX, windowY );
849 // add the fudge boundary
861 Calc the light shader values, removing any light from the viewLight list
862 if it is determined to not have any visible effect due to being flashed off or turned off.
864 Adds entities to the viewEntity list if they are needed for shadow casting.
866 Add any precomputed shadow volumes.
868 Removes lights from the viewLights list if they are completely
869 turned off, or completely off screen.
871 Create any new interactions needed between the viewLights
872 and the viewEntitys due to game movement
875 void R_AddLightSurfaces( void ) {
877 idRenderLightLocal *light;
880 // go through each visible light, possibly removing some from the list
881 ptr = &tr.viewDef->viewLights;
884 light = vLight->lightDef;
886 const idMaterial *lightShader = light->lightShader;
887 if ( !lightShader ) {
888 common->Error( "R_AddLightSurfaces: NULL lightShader" );
891 // see if we are suppressing the light in this view
892 if ( !r_skipSuppress.GetBool() ) {
893 if ( light->parms.suppressLightInViewID
894 && light->parms.suppressLightInViewID == tr.viewDef->renderView.viewID ) {
896 light->viewCount = -1;
899 if ( light->parms.allowLightInViewID
900 && light->parms.allowLightInViewID != tr.viewDef->renderView.viewID ) {
902 light->viewCount = -1;
907 // evaluate the light shader registers
908 float *lightRegs =(float *)R_FrameAlloc( lightShader->GetNumRegisters() * sizeof( float ) );
909 vLight->shaderRegisters = lightRegs;
910 lightShader->EvaluateRegisters( lightRegs, light->parms.shaderParms, tr.viewDef, light->parms.referenceSound );
912 // if this is a purely additive light and no stage in the light shader evaluates
913 // to a positive light value, we can completely skip the light
914 if ( !lightShader->IsFogLight() && !lightShader->IsBlendLight() ) {
916 for ( lightStageNum = 0 ; lightStageNum < lightShader->GetNumStages() ; lightStageNum++ ) {
917 const shaderStage_t *lightStage = lightShader->GetStage( lightStageNum );
919 // ignore stages that fail the condition
920 if ( !lightRegs[ lightStage->conditionRegister ] ) {
924 const int *registers = lightStage->color.registers;
926 // snap tiny values to zero to avoid lights showing up with the wrong color
927 if ( lightRegs[ registers[0] ] < 0.001f ) {
928 lightRegs[ registers[0] ] = 0.0f;
930 if ( lightRegs[ registers[1] ] < 0.001f ) {
931 lightRegs[ registers[1] ] = 0.0f;
933 if ( lightRegs[ registers[2] ] < 0.001f ) {
934 lightRegs[ registers[2] ] = 0.0f;
937 // FIXME: when using the following values the light shows up bright red when using nvidia drivers/hardware
938 // this seems to have been fixed ?
939 //lightRegs[ registers[0] ] = 1.5143074e-005f;
940 //lightRegs[ registers[1] ] = 1.5483369e-005f;
941 //lightRegs[ registers[2] ] = 1.7014690e-005f;
943 if ( lightRegs[ registers[0] ] > 0.0f ||
944 lightRegs[ registers[1] ] > 0.0f ||
945 lightRegs[ registers[2] ] > 0.0f ) {
949 if ( lightStageNum == lightShader->GetNumStages() ) {
950 // we went through all the stages and didn't find one that adds anything
951 // remove the light from the viewLights list, and change its frame marker
952 // so interaction generation doesn't think the light is visible and
953 // create a shadow for it
955 light->viewCount = -1;
960 if ( r_useLightScissors.GetBool() ) {
961 // calculate the screen area covered by the light frustum
962 // which will be used to crop the stencil cull
963 idScreenRect scissorRect = R_CalcLightScissorRectangle( vLight );
964 // intersect with the portal crossing scissor rectangle
965 vLight->scissorRect.Intersect( scissorRect );
967 if ( r_showLightScissors.GetBool() ) {
968 R_ShowColoredScreenRect( vLight->scissorRect, light->index );
973 // this never happens, because CullLightByPortals() does a more precise job
974 if ( vLight->scissorRect.IsEmpty() ) {
975 // this light doesn't touch anything on screen, so remove it from the list
981 // this one stays on the list
984 // if we are doing a soft-shadow novelty test, regenerate the light with
985 // a random offset every time
986 if ( r_lightSourceRadius.GetFloat() != 0.0f ) {
987 for ( int i = 0 ; i < 3 ; i++ ) {
988 light->globalLightOrigin[i] += r_lightSourceRadius.GetFloat() * ( -1 + 2 * (rand()&0xfff)/(float)0xfff );
992 // create interactions with all entities the light may touch, and add viewEntities
993 // that may cast shadows, even if they aren't directly visible. Any real work
994 // will be deferred until we walk through the viewEntities
995 tr.viewDef->renderWorld->CreateLightDefInteractions( light );
996 tr.pc.c_viewLights++;
998 // fog lights will need to draw the light frustum triangles, so make sure they
999 // are in the vertex cache
1000 if ( lightShader->IsFogLight() ) {
1001 if ( !light->frustumTris->ambientCache ) {
1002 if ( !R_CreateAmbientCache( light->frustumTris, false ) ) {
1003 // skip if we are out of vertex memory
1007 // touch the surface so it won't get purged
1008 vertexCache.Touch( light->frustumTris->ambientCache );
1011 // add the prelight shadows for the static world geometry
1012 if ( light->parms.prelightModel && r_useOptimizedShadows.GetBool() ) {
1014 if ( !light->parms.prelightModel->NumSurfaces() ) {
1015 common->Error( "no surfs in prelight model '%s'", light->parms.prelightModel->Name() );
1018 srfTriangles_t *tri = light->parms.prelightModel->Surface( 0 )->geometry;
1019 if ( !tri->shadowVertexes ) {
1020 common->Error( "R_AddLightSurfaces: prelight model '%s' without shadowVertexes", light->parms.prelightModel->Name() );
1023 // these shadows will all have valid bounds, and can be culled normally
1024 if ( r_useShadowCulling.GetBool() ) {
1025 if ( R_CullLocalBox( tri->bounds, tr.viewDef->worldSpace.modelMatrix, 5, tr.viewDef->frustum ) ) {
1030 // if we have been purged, re-upload the shadowVertexes
1031 if ( !tri->shadowCache ) {
1032 R_CreatePrivateShadowCache( tri );
1033 if ( !tri->shadowCache ) {
1038 // touch the shadow surface so it won't get purged
1039 vertexCache.Touch( tri->shadowCache );
1041 if ( !tri->indexCache && r_useIndexBuffers.GetBool() ) {
1042 vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
1044 if ( tri->indexCache ) {
1045 vertexCache.Touch( tri->indexCache );
1048 R_LinkLightSurf( &vLight->globalShadows, tri, NULL, light, NULL, vLight->scissorRect, true /* FIXME? */ );
1053 //===============================================================================================================
1057 R_IssueEntityDefCallback
1060 bool R_IssueEntityDefCallback( idRenderEntityLocal *def ) {
1064 if ( r_checkBounds.GetBool() ) {
1065 oldBounds = def->referenceBounds;
1068 def->archived = false; // will need to be written to the demo file
1069 tr.pc.c_entityDefCallbacks++;
1071 update = def->parms.callback( &def->parms, &tr.viewDef->renderView );
1073 update = def->parms.callback( &def->parms, NULL );
1076 if ( !def->parms.hModel ) {
1077 common->Error( "R_IssueEntityDefCallback: dynamic entity callback didn't set model" );
1080 if ( r_checkBounds.GetBool() ) {
1081 if ( oldBounds[0][0] > def->referenceBounds[0][0] + CHECK_BOUNDS_EPSILON ||
1082 oldBounds[0][1] > def->referenceBounds[0][1] + CHECK_BOUNDS_EPSILON ||
1083 oldBounds[0][2] > def->referenceBounds[0][2] + CHECK_BOUNDS_EPSILON ||
1084 oldBounds[1][0] < def->referenceBounds[1][0] - CHECK_BOUNDS_EPSILON ||
1085 oldBounds[1][1] < def->referenceBounds[1][1] - CHECK_BOUNDS_EPSILON ||
1086 oldBounds[1][2] < def->referenceBounds[1][2] - CHECK_BOUNDS_EPSILON ) {
1087 common->Printf( "entity %i callback extended reference bounds\n", def->index );
1096 R_EntityDefDynamicModel
1098 Issues a deferred entity callback if necessary.
1099 If the model isn't dynamic, it returns the original.
1100 Returns the cached dynamic model if present, otherwise creates
1101 it and any necessary overlays
1104 idRenderModel *R_EntityDefDynamicModel( idRenderEntityLocal *def ) {
1105 bool callbackUpdate;
1107 // allow deferred entities to construct themselves
1108 if ( def->parms.callback ) {
1109 callbackUpdate = R_IssueEntityDefCallback( def );
1111 callbackUpdate = false;
1114 idRenderModel *model = def->parms.hModel;
1117 common->Error( "R_EntityDefDynamicModel: NULL model" );
1120 if ( model->IsDynamicModel() == DM_STATIC ) {
1121 def->dynamicModel = NULL;
1122 def->dynamicModelFrameCount = 0;
1126 // continously animating models (particle systems, etc) will have their snapshot updated every single view
1127 if ( callbackUpdate || ( model->IsDynamicModel() == DM_CONTINUOUS && def->dynamicModelFrameCount != tr.frameCount ) ) {
1128 R_ClearEntityDefDynamicModel( def );
1131 // if we don't have a snapshot of the dynamic model, generate it now
1132 if ( !def->dynamicModel ) {
1134 // instantiate the snapshot of the dynamic model, possibly reusing memory from the cached snapshot
1135 def->cachedDynamicModel = model->InstantiateDynamicModel( &def->parms, tr.viewDef, def->cachedDynamicModel );
1137 if ( def->cachedDynamicModel ) {
1139 // add any overlays to the snapshot of the dynamic model
1140 if ( def->overlay && !r_skipOverlays.GetBool() ) {
1141 def->overlay->AddOverlaySurfacesToModel( def->cachedDynamicModel );
1143 idRenderModelOverlay::RemoveOverlaySurfacesFromModel( def->cachedDynamicModel );
1146 if ( r_checkBounds.GetBool() ) {
1147 idBounds b = def->cachedDynamicModel->Bounds();
1148 if ( b[0][0] < def->referenceBounds[0][0] - CHECK_BOUNDS_EPSILON ||
1149 b[0][1] < def->referenceBounds[0][1] - CHECK_BOUNDS_EPSILON ||
1150 b[0][2] < def->referenceBounds[0][2] - CHECK_BOUNDS_EPSILON ||
1151 b[1][0] > def->referenceBounds[1][0] + CHECK_BOUNDS_EPSILON ||
1152 b[1][1] > def->referenceBounds[1][1] + CHECK_BOUNDS_EPSILON ||
1153 b[1][2] > def->referenceBounds[1][2] + CHECK_BOUNDS_EPSILON ) {
1154 common->Printf( "entity %i dynamic model exceeded reference bounds\n", def->index );
1159 def->dynamicModel = def->cachedDynamicModel;
1160 def->dynamicModelFrameCount = tr.frameCount;
1163 // set model depth hack value
1164 if ( def->dynamicModel && model->DepthHack() != 0.0f && tr.viewDef ) {
1167 R_TransformModelToClip( def->parms.origin, tr.viewDef->worldSpace.modelViewMatrix, tr.viewDef->projectionMatrix, eye, clip );
1168 R_TransformClipToDevice( clip, tr.viewDef, ndc );
1169 def->parms.modelDepthHack = model->DepthHack() * ( 1.0f - ndc.z );
1172 // FIXME: if any of the surfaces have deforms, create a frame-temporary model with references to the
1173 // undeformed surfaces. This would allow deforms to be light interacting.
1175 return def->dynamicModel;
1183 void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
1184 const idMaterial *shader, const idScreenRect &scissor ) {
1185 drawSurf_t *drawSurf;
1186 const float *shaderParms;
1187 static float refRegs[MAX_EXPRESSION_REGISTERS]; // don't put on stack, or VC++ will do a page touch
1188 float generatedShaderParms[MAX_ENTITY_SHADER_PARMS];
1190 drawSurf = (drawSurf_t *)R_FrameAlloc( sizeof( *drawSurf ) );
1191 drawSurf->geo = tri;
1192 drawSurf->space = space;
1193 drawSurf->material = shader;
1194 drawSurf->scissorRect = scissor;
1195 drawSurf->sort = shader->GetSort() + tr.sortOffset;
1196 drawSurf->dsFlags = 0;
1198 // bumping this offset each time causes surfaces with equal sort orders to still
1199 // deterministically draw in the order they are added
1200 tr.sortOffset += 0.000001f;
1202 // if it doesn't fit, resize the list
1203 if ( tr.viewDef->numDrawSurfs == tr.viewDef->maxDrawSurfs ) {
1204 drawSurf_t **old = tr.viewDef->drawSurfs;
1207 if ( tr.viewDef->maxDrawSurfs == 0 ) {
1208 tr.viewDef->maxDrawSurfs = INITIAL_DRAWSURFS;
1211 count = tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] );
1212 tr.viewDef->maxDrawSurfs *= 2;
1214 tr.viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) );
1215 memcpy( tr.viewDef->drawSurfs, old, count );
1217 tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs] = drawSurf;
1218 tr.viewDef->numDrawSurfs++;
1220 // process the shader expressions for conditionals / color / texcoords
1221 const float *constRegs = shader->ConstantRegisters();
1223 // shader only uses constant values
1224 drawSurf->shaderRegisters = constRegs;
1226 float *regs = (float *)R_FrameAlloc( shader->GetNumRegisters() * sizeof( float ) );
1227 drawSurf->shaderRegisters = regs;
1229 // a reference shader will take the calculated stage color value from another shader
1230 // and use that for the parm0-parm3 of the current shader, which allows a stage of
1231 // a light model and light flares to pick up different flashing tables from
1232 // different light shaders
1233 if ( renderEntity->referenceShader ) {
1234 // evaluate the reference shader to find our shader parms
1235 const shaderStage_t *pStage;
1237 renderEntity->referenceShader->EvaluateRegisters( refRegs, renderEntity->shaderParms, tr.viewDef, renderEntity->referenceSound );
1238 pStage = renderEntity->referenceShader->GetStage(0);
1240 memcpy( generatedShaderParms, renderEntity->shaderParms, sizeof( generatedShaderParms ) );
1241 generatedShaderParms[0] = refRegs[ pStage->color.registers[0] ];
1242 generatedShaderParms[1] = refRegs[ pStage->color.registers[1] ];
1243 generatedShaderParms[2] = refRegs[ pStage->color.registers[2] ];
1245 shaderParms = generatedShaderParms;
1247 // evaluate with the entityDef's shader parms
1248 shaderParms = renderEntity->shaderParms;
1254 if ( space->entityDef && space->entityDef->parms.timeGroup ) {
1255 oldFloatTime = tr.viewDef->floatTime;
1256 oldTime = tr.viewDef->renderView.time;
1258 tr.viewDef->floatTime = game->GetTimeGroupTime( space->entityDef->parms.timeGroup ) * 0.001;
1259 tr.viewDef->renderView.time = game->GetTimeGroupTime( space->entityDef->parms.timeGroup );
1262 shader->EvaluateRegisters( regs, shaderParms, tr.viewDef, renderEntity->referenceSound );
1264 if ( space->entityDef && space->entityDef->parms.timeGroup ) {
1265 tr.viewDef->floatTime = oldFloatTime;
1266 tr.viewDef->renderView.time = oldTime;
1270 // check for deformations
1271 R_DeformDrawSurf( drawSurf );
1273 // skybox surfaces need a dynamic texgen
1274 switch( shader->Texgen() ) {
1275 case TG_SKYBOX_CUBE:
1276 R_SkyboxTexGen( drawSurf, tr.viewDef->renderView.vieworg );
1278 case TG_WOBBLESKY_CUBE:
1279 R_WobbleskyTexGen( drawSurf, tr.viewDef->renderView.vieworg );
1283 // check for gui surfaces
1284 idUserInterface *gui = NULL;
1286 if ( !space->entityDef ) {
1287 gui = shader->GlobalGui();
1289 int guiNum = shader->GetEntityGui() - 1;
1290 if ( guiNum >= 0 && guiNum < MAX_RENDERENTITY_GUI ) {
1291 gui = renderEntity->gui[ guiNum ];
1293 if ( gui == NULL ) {
1294 gui = shader->GlobalGui();
1299 // force guis on the fast time
1303 oldFloatTime = tr.viewDef->floatTime;
1304 oldTime = tr.viewDef->renderView.time;
1306 tr.viewDef->floatTime = game->GetTimeGroupTime( 1 ) * 0.001;
1307 tr.viewDef->renderView.time = game->GetTimeGroupTime( 1 );
1311 if ( !R_PreciseCullSurface( drawSurf, ndcBounds ) ) {
1312 // did we ever use this to forward an entity color to a gui that didn't set color?
1313 // memcpy( tr.guiShaderParms, shaderParms, sizeof( tr.guiShaderParms ) );
1314 R_RenderGuiSurf( gui, drawSurf );
1317 tr.viewDef->floatTime = oldFloatTime;
1318 tr.viewDef->renderView.time = oldTime;
1321 // we can't add subviews at this point, because that would
1322 // increment tr.viewCount, messing up the rest of the surface
1323 // adds for this view
1328 R_AddAmbientDrawsurfs
1330 Adds surfaces for the given viewEntity
1331 Walks through the viewEntitys list and creates drawSurf_t for each surface of
1332 each viewEntity that has a non-empty scissorRect
1335 static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
1337 idRenderEntityLocal *def;
1338 srfTriangles_t *tri;
1339 idRenderModel *model;
1340 const idMaterial *shader;
1342 def = vEntity->entityDef;
1344 if ( def->dynamicModel ) {
1345 model = def->dynamicModel;
1347 model = def->parms.hModel;
1350 // add all the surfaces
1351 total = model->NumSurfaces();
1352 for ( i = 0 ; i < total ; i++ ) {
1353 const modelSurface_t *surf = model->Surface( i );
1355 // for debugging, only show a single surface at a time
1356 if ( r_singleSurface.GetInteger() >= 0 && i != r_singleSurface.GetInteger() ) {
1360 tri = surf->geometry;
1364 if ( !tri->numIndexes ) {
1367 shader = surf->shader;
1368 shader = R_RemapShaderBySkin( shader, def->parms.customSkin, def->parms.customShader );
1370 R_GlobalShaderOverride( &shader );
1375 if ( !shader->IsDrawn() ) {
1379 // debugging tool to make sure we are have the correct pre-calculated bounds
1380 if ( r_checkBounds.GetBool() ) {
1382 for ( j = 0 ; j < tri->numVerts ; j++ ) {
1383 for ( k = 0 ; k < 3 ; k++ ) {
1384 if ( tri->verts[j].xyz[k] > tri->bounds[1][k] + CHECK_BOUNDS_EPSILON
1385 || tri->verts[j].xyz[k] < tri->bounds[0][k] - CHECK_BOUNDS_EPSILON ) {
1386 common->Printf( "bad tri->bounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() );
1389 if ( tri->verts[j].xyz[k] > def->referenceBounds[1][k] + CHECK_BOUNDS_EPSILON
1390 || tri->verts[j].xyz[k] < def->referenceBounds[0][k] - CHECK_BOUNDS_EPSILON ) {
1391 common->Printf( "bad referenceBounds on %s:%s\n", def->parms.hModel->Name(), shader->GetName() );
1401 if ( !R_CullLocalBox( tri->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
1403 def->visibleCount = tr.viewCount;
1405 // make sure we have an ambient cache
1406 if ( !R_CreateAmbientCache( tri, shader->ReceivesLighting() ) ) {
1407 // don't add anything if the vertex cache was too full to give us an ambient cache
1410 // touch it so it won't get purged
1411 vertexCache.Touch( tri->ambientCache );
1413 if ( r_useIndexBuffers.GetBool() && !tri->indexCache ) {
1414 vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
1416 if ( tri->indexCache ) {
1417 vertexCache.Touch( tri->indexCache );
1420 // add the surface for drawing
1421 R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect );
1423 // ambientViewCount is used to allow light interactions to be rejected
1424 // if the ambient surface isn't visible at all
1425 tri->ambientViewCount = tr.viewCount;
1429 // add the lightweight decal surfaces
1430 for ( idRenderModelDecal *decal = def->decals; decal; decal = decal->Next() ) {
1431 decal->AddDecalDrawSurf( vEntity );
1437 R_CalcEntityScissorRectangle
1440 idScreenRect R_CalcEntityScissorRectangle( viewEntity_t *vEntity ) {
1442 idRenderEntityLocal *def = vEntity->entityDef;
1444 tr.viewDef->viewFrustum.ProjectionBounds( idBox( def->referenceBounds, def->parms.origin, def->parms.axis ), bounds );
1446 return R_ScreenRectFromViewFrustumBounds( bounds );
1453 Here is where dynamic models actually get instantiated, and necessary
1454 interactions get created. This is all done on a sort-by-model basis
1455 to keep source data in cache (most likely L2) as any interactions and
1456 shadows are generated, since dynamic models will typically be lit by
1460 void R_AddModelSurfaces( void ) {
1461 viewEntity_t *vEntity;
1462 idInteraction *inter, *next;
1463 idRenderModel *model;
1465 // clear the ambient surface list
1466 tr.viewDef->numDrawSurfs = 0;
1467 tr.viewDef->maxDrawSurfs = 0; // will be set to INITIAL_DRAWSURFS on R_AddDrawSurf
1469 // go through each entity that is either visible to the view, or to
1470 // any light that intersects the view (for shadows)
1471 for ( vEntity = tr.viewDef->viewEntitys; vEntity; vEntity = vEntity->next ) {
1473 if ( r_useEntityScissors.GetBool() ) {
1474 // calculate the screen area covered by the entity
1475 idScreenRect scissorRect = R_CalcEntityScissorRectangle( vEntity );
1476 // intersect with the portal crossing scissor rectangle
1477 vEntity->scissorRect.Intersect( scissorRect );
1479 if ( r_showEntityScissors.GetBool() ) {
1480 R_ShowColoredScreenRect( vEntity->scissorRect, vEntity->entityDef->index );
1487 game->SelectTimeGroup( vEntity->entityDef->parms.timeGroup );
1489 if ( vEntity->entityDef->parms.timeGroup ) {
1490 oldFloatTime = tr.viewDef->floatTime;
1491 oldTime = tr.viewDef->renderView.time;
1493 tr.viewDef->floatTime = game->GetTimeGroupTime( vEntity->entityDef->parms.timeGroup ) * 0.001;
1494 tr.viewDef->renderView.time = game->GetTimeGroupTime( vEntity->entityDef->parms.timeGroup );
1497 if ( tr.viewDef->isXraySubview && vEntity->entityDef->parms.xrayIndex == 1 ) {
1498 if ( vEntity->entityDef->parms.timeGroup ) {
1499 tr.viewDef->floatTime = oldFloatTime;
1500 tr.viewDef->renderView.time = oldTime;
1503 } else if ( !tr.viewDef->isXraySubview && vEntity->entityDef->parms.xrayIndex == 2 ) {
1504 if ( vEntity->entityDef->parms.timeGroup ) {
1505 tr.viewDef->floatTime = oldFloatTime;
1506 tr.viewDef->renderView.time = oldTime;
1511 // add the ambient surface if it has a visible rectangle
1512 if ( !vEntity->scissorRect.IsEmpty() ) {
1513 model = R_EntityDefDynamicModel( vEntity->entityDef );
1514 if ( model == NULL || model->NumSurfaces() <= 0 ) {
1515 if ( vEntity->entityDef->parms.timeGroup ) {
1516 tr.viewDef->floatTime = oldFloatTime;
1517 tr.viewDef->renderView.time = oldTime;
1522 R_AddAmbientDrawsurfs( vEntity );
1523 tr.pc.c_visibleViewEntities++;
1525 tr.pc.c_shadowViewEntities++;
1529 // for all the entity / light interactions on this entity, add them to the view
1531 if ( tr.viewDef->isXraySubview ) {
1532 if ( vEntity->entityDef->parms.xrayIndex == 2 ) {
1533 for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
1534 next = inter->entityNext;
1535 if ( inter->lightDef->viewCount != tr.viewCount ) {
1538 inter->AddActiveInteraction();
1542 // all empty interactions are at the end of the list so once the
1543 // first is encountered all the remaining interactions are empty
1544 for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
1545 next = inter->entityNext;
1547 // skip any lights that aren't currently visible
1548 // this is run after any lights that are turned off have already
1549 // been removed from the viewLights list, and had their viewCount cleared
1550 if ( inter->lightDef->viewCount != tr.viewCount ) {
1553 inter->AddActiveInteraction();
1557 if ( vEntity->entityDef->parms.timeGroup ) {
1558 tr.viewDef->floatTime = oldFloatTime;
1559 tr.viewDef->renderView.time = oldTime;
1566 =====================
1567 R_RemoveUnecessaryViewLights
1568 =====================
1570 void R_RemoveUnecessaryViewLights( void ) {
1571 viewLight_t *vLight;
1573 // go through each visible light
1574 for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
1575 // if the light didn't have any lit surfaces visible, there is no need to
1576 // draw any of the shadows. We still keep the vLight for debugging
1578 if ( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) {
1579 vLight->localShadows = NULL;
1580 vLight->globalShadows = NULL;
1584 if ( r_useShadowSurfaceScissor.GetBool() ) {
1585 // shrink the light scissor rect to only intersect the surfaces that will actually be drawn.
1586 // This doesn't seem to actually help, perhaps because the surface scissor
1587 // rects aren't actually the surface, but only the portal clippings.
1588 for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
1589 const drawSurf_t *surf;
1590 idScreenRect surfRect;
1592 if ( !vLight->lightShader->LightCastsShadows() ) {
1598 for ( surf = vLight->globalInteractions ; surf ; surf = surf->nextOnLight ) {
1599 surfRect.Union( surf->scissorRect );
1601 for ( surf = vLight->localShadows ; surf ; surf = surf->nextOnLight ) {
1602 const_cast<drawSurf_t *>(surf)->scissorRect.Intersect( surfRect );
1605 for ( surf = vLight->localInteractions ; surf ; surf = surf->nextOnLight ) {
1606 surfRect.Union( surf->scissorRect );
1608 for ( surf = vLight->globalShadows ; surf ; surf = surf->nextOnLight ) {
1609 const_cast<drawSurf_t *>(surf)->scissorRect.Intersect( surfRect );
1612 for ( surf = vLight->translucentInteractions ; surf ; surf = surf->nextOnLight ) {
1613 surfRect.Union( surf->scissorRect );
1616 vLight->scissorRect.Intersect( surfRect );