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"
35 ===========================================================================
37 idInteraction implementation
39 ===========================================================================
42 // FIXME: use private allocator for srfCullInfo_t
46 R_CalcInteractionFacing
48 Determines which triangles of the surface are facing towards the light origin.
50 The facing array should be allocated with one extra index than
51 the number of surface triangles, which will be used to handle dangling
55 void R_CalcInteractionFacing( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) {
56 idVec3 localLightOrigin;
58 if ( cullInfo.facing != NULL ) {
62 R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
64 int numFaces = tri->numIndexes / 3;
66 if ( !tri->facePlanes || !tri->facePlanesCalculated ) {
67 R_DeriveFacePlanes( const_cast<srfTriangles_t *>(tri) );
70 cullInfo.facing = (byte *) R_StaticAlloc( ( numFaces + 1 ) * sizeof( cullInfo.facing[0] ) );
72 // calculate back face culling
73 float *planeSide = (float *) _alloca16( numFaces * sizeof( float ) );
75 // exact geometric cull against face
76 SIMDProcessor->Dot( planeSide, localLightOrigin, tri->facePlanes, numFaces );
77 SIMDProcessor->CmpGE( cullInfo.facing, planeSide, 0.0f, numFaces );
79 cullInfo.facing[ numFaces ] = 1; // for dangling edges to reference
84 R_CalcInteractionCullBits
86 We want to cull a little on the sloppy side, because the pre-clipping
87 of geometry to the lights in dmap will give many cases that are right
88 at the border we throw things out on the border, because if any one
89 vertex is clearly inside, the entire triangle will be accepted.
92 void R_CalcInteractionCullBits( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) {
95 if ( cullInfo.cullBits != NULL ) {
101 // cull the triangle surface bounding box
102 for ( i = 0; i < 6; i++ ) {
104 R_GlobalPlaneToLocal( ent->modelMatrix, -light->frustum[i], cullInfo.localClipPlanes[i] );
106 // get front bits for the whole surface
107 if ( tri->bounds.PlaneDistance( cullInfo.localClipPlanes[i] ) >= LIGHT_CLIP_EPSILON ) {
112 // if the surface is completely inside the light frustum
113 if ( frontBits == ( ( 1 << 6 ) - 1 ) ) {
114 cullInfo.cullBits = LIGHT_CULL_ALL_FRONT;
118 cullInfo.cullBits = (byte *) R_StaticAlloc( tri->numVerts * sizeof( cullInfo.cullBits[0] ) );
119 SIMDProcessor->Memset( cullInfo.cullBits, 0, tri->numVerts * sizeof( cullInfo.cullBits[0] ) );
121 float *planeSide = (float *) _alloca16( tri->numVerts * sizeof( float ) );
123 for ( i = 0; i < 6; i++ ) {
124 // if completely infront of this clipping plane
125 if ( frontBits & ( 1 << i ) ) {
128 SIMDProcessor->Dot( planeSide, cullInfo.localClipPlanes[i], tri->verts, tri->numVerts );
129 SIMDProcessor->CmpLT( cullInfo.cullBits, i, planeSide, LIGHT_CLIP_EPSILON, tri->numVerts );
135 R_FreeInteractionCullInfo
138 void R_FreeInteractionCullInfo( srfCullInfo_t &cullInfo ) {
139 if ( cullInfo.facing != NULL ) {
140 R_StaticFree( cullInfo.facing );
141 cullInfo.facing = NULL;
143 if ( cullInfo.cullBits != NULL ) {
144 if ( cullInfo.cullBits != LIGHT_CULL_ALL_FRONT ) {
145 R_StaticFree( cullInfo.cullBits );
147 cullInfo.cullBits = NULL;
151 #define MAX_CLIPPED_POINTS 20
154 idVec3 verts[MAX_CLIPPED_POINTS];
161 Clips a triangle from one buffer to another, setting edge flags
162 The returned buffer may be the same as inNum if no clipping is done
163 If entirely clipped away, clipTris[returned].numVerts == 0
165 I have some worries about edge flag cases when polygons are clipped
166 multiple times near the epsilon.
169 static int R_ChopWinding( clipTri_t clipTris[2], int inNum, const idPlane plane ) {
171 float dists[MAX_CLIPPED_POINTS];
172 int sides[MAX_CLIPPED_POINTS];
179 in = &clipTris[inNum];
180 out = &clipTris[inNum^1];
181 counts[0] = counts[1] = counts[2] = 0;
183 // determine sides for each point
185 for ( i = 0; i < in->numVerts; i++ ) {
186 dot = in->verts[i] * plane.Normal() + plane[3];
188 if ( dot < LIGHT_CLIP_EPSILON ) { // slop onto the back
189 sides[i] = SIDE_BACK;
191 sides[i] = SIDE_FRONT;
192 if ( dot > LIGHT_CLIP_EPSILON ) {
199 // if none in front, it is completely clipped away
204 if ( !counts[SIDE_BACK] ) {
205 return inNum; // inout stays the same
208 // avoid wrapping checks by duplicating first value to end
211 in->verts[in->numVerts] = in->verts[0];
214 for ( i = 0 ; i < in->numVerts ; i++ ) {
215 idVec3 &p1 = in->verts[i];
217 if ( sides[i] == SIDE_FRONT ) {
218 out->verts[out->numVerts] = p1;
222 if ( sides[i+1] == sides[i] ) {
226 // generate a split point
227 idVec3 &p2 = in->verts[i+1];
229 dot = dists[i] / ( dists[i] - dists[i+1] );
230 for ( j = 0; j < 3; j++ ) {
231 mid[j] = p1[j] + dot * ( p2[j] - p1[j] );
234 out->verts[out->numVerts] = mid;
244 R_ClipTriangleToLight
246 Returns false if nothing is left after clipping
249 static bool R_ClipTriangleToLight( const idVec3 &a, const idVec3 &b, const idVec3 &c, int planeBits, const idPlane frustum[6] ) {
251 clipTri_t pingPong[2];
254 pingPong[0].numVerts = 3;
255 pingPong[0].verts[0] = a;
256 pingPong[0].verts[1] = b;
257 pingPong[0].verts[2] = c;
260 for ( i = 0 ; i < 6 ; i++ ) {
261 if ( planeBits & ( 1 << i ) ) {
262 p = R_ChopWinding( pingPong, p, frustum[i] );
263 if ( pingPong[p].numVerts < 1 ) {
276 The resulting surface will be a subset of the original triangles,
277 it will never clip triangles, but it may cull on a per-triangle basis.
280 static srfTriangles_t *R_CreateLightTris( const idRenderEntityLocal *ent,
281 const srfTriangles_t *tri, const idRenderLightLocal *light,
282 const idMaterial *shader, srfCullInfo_t &cullInfo ) {
286 srfTriangles_t *newTri;
290 bool includeBackFaces;
293 tr.pc.c_createLightTris++;
300 // it is debatable if non-shadowing lights should light back faces. we aren't at the moment
301 if ( r_lightAllBackFaces.GetBool() || light->lightShader->LightEffectsBackSides()
302 || shader->ReceivesLightingOnBackSides()
303 || ent->parms.noSelfShadow || ent->parms.noShadow ) {
304 includeBackFaces = true;
306 includeBackFaces = false;
309 // allocate a new surface for the lit triangles
310 newTri = R_AllocStaticTriSurf();
312 // save a reference to the original surface
313 newTri->ambientSurface = const_cast<srfTriangles_t *>(tri);
315 // the light surface references the verts of the ambient surface
316 newTri->numVerts = tri->numVerts;
317 R_ReferenceStaticTriSurfVerts( newTri, tri );
319 // calculate cull information
320 if ( !includeBackFaces ) {
321 R_CalcInteractionFacing( ent, tri, light, cullInfo );
323 R_CalcInteractionCullBits( ent, tri, light, cullInfo );
325 // if the surface is completely inside the light frustum
326 if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT ) {
328 // if we aren't self shadowing, let back facing triangles get
329 // through so the smooth shaded bump maps light all the way around
330 if ( includeBackFaces ) {
332 // the whole surface is lit so the light surface just references the indexes of the ambient surface
333 R_ReferenceStaticTriSurfIndexes( newTri, tri );
334 numIndexes = tri->numIndexes;
335 bounds = tri->bounds;
339 // the light tris indexes are going to be a subset of the original indexes so we generally
340 // allocate too much memory here but we decrease the memory block when the number of indexes is known
341 R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes );
343 // back face cull the individual triangles
344 indexes = newTri->indexes;
345 const byte *facing = cullInfo.facing;
346 for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) {
347 if ( !facing[ faceNum ] ) {
351 indexes[numIndexes+0] = tri->indexes[i+0];
352 indexes[numIndexes+1] = tri->indexes[i+1];
353 indexes[numIndexes+2] = tri->indexes[i+2];
357 // get bounds for the surface
358 SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes );
360 // decrease the size of the memory block to the size of the number of used indexes
361 R_ResizeStaticTriSurfIndexes( newTri, numIndexes );
366 // the light tris indexes are going to be a subset of the original indexes so we generally
367 // allocate too much memory here but we decrease the memory block when the number of indexes is known
368 R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes );
370 // cull individual triangles
371 indexes = newTri->indexes;
372 const byte *facing = cullInfo.facing;
373 const byte *cullBits = cullInfo.cullBits;
374 for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) {
377 // if we aren't self shadowing, let back facing triangles get
378 // through so the smooth shaded bump maps light all the way around
379 if ( !includeBackFaces ) {
381 if ( !facing[ faceNum ] ) {
387 i1 = tri->indexes[i+0];
388 i2 = tri->indexes[i+1];
389 i3 = tri->indexes[i+2];
391 // fast cull outside the frustum
392 // if all three points are off one plane side, it definately isn't visible
393 if ( cullBits[i1] & cullBits[i2] & cullBits[i3] ) {
398 if ( r_usePreciseTriangleInteractions.GetBool() ) {
399 // do a precise clipped cull if none of the points is completely inside the frustum
400 // note that we do not actually use the clipped triangle, which would have Z fighting issues.
401 if ( cullBits[i1] && cullBits[i2] && cullBits[i3] ) {
402 int cull = cullBits[i1] | cullBits[i2] | cullBits[i3];
403 if ( !R_ClipTriangleToLight( tri->verts[i1].xyz, tri->verts[i2].xyz, tri->verts[i3].xyz, cull, cullInfo.localClipPlanes ) ) {
410 indexes[numIndexes+0] = i1;
411 indexes[numIndexes+1] = i2;
412 indexes[numIndexes+2] = i3;
416 // get bounds for the surface
417 SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes );
419 // decrease the size of the memory block to the size of the number of used indexes
420 R_ResizeStaticTriSurfIndexes( newTri, numIndexes );
424 R_ReallyFreeStaticTriSurf( newTri );
428 newTri->numIndexes = numIndexes;
430 newTri->bounds = bounds;
437 idInteraction::idInteraction
440 idInteraction::idInteraction( void ) {
449 dynamicModelFrameCount = 0;
450 frustumState = FRUSTUM_UNINITIALIZED;
456 idInteraction::AllocAndLink
459 idInteraction *idInteraction::AllocAndLink( idRenderEntityLocal *edef, idRenderLightLocal *ldef ) {
460 if ( !edef || !ldef ) {
461 common->Error( "idInteraction::AllocAndLink: NULL parm" );
464 idRenderWorldLocal *renderWorld = edef->world;
466 idInteraction *interaction = renderWorld->interactionAllocator.Alloc();
468 // link and initialize
469 interaction->dynamicModelFrameCount = 0;
471 interaction->lightDef = ldef;
472 interaction->entityDef = edef;
474 interaction->numSurfaces = -1; // not checked yet
475 interaction->surfaces = NULL;
477 interaction->frustumState = idInteraction::FRUSTUM_UNINITIALIZED;
478 interaction->frustumAreas = NULL;
480 // link at the start of the entity's list
481 interaction->lightNext = ldef->firstInteraction;
482 interaction->lightPrev = NULL;
483 ldef->firstInteraction = interaction;
484 if ( interaction->lightNext != NULL ) {
485 interaction->lightNext->lightPrev = interaction;
487 ldef->lastInteraction = interaction;
490 // link at the start of the light's list
491 interaction->entityNext = edef->firstInteraction;
492 interaction->entityPrev = NULL;
493 edef->firstInteraction = interaction;
494 if ( interaction->entityNext != NULL ) {
495 interaction->entityNext->entityPrev = interaction;
497 edef->lastInteraction = interaction;
500 // update the interaction table
501 if ( renderWorld->interactionTable ) {
502 int index = ldef->index * renderWorld->interactionTableWidth + edef->index;
503 if ( renderWorld->interactionTable[index] != NULL ) {
504 common->Error( "idInteraction::AllocAndLink: non NULL table entry" );
506 renderWorld->interactionTable[ index ] = interaction;
514 idInteraction::FreeSurfaces
516 Frees the surfaces, but leaves the interaction linked in, so it
517 will be regenerated automatically
520 void idInteraction::FreeSurfaces( void ) {
521 if ( this->surfaces ) {
522 for ( int i = 0 ; i < this->numSurfaces ; i++ ) {
523 surfaceInteraction_t *sint = &this->surfaces[i];
525 if ( sint->lightTris ) {
526 if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) {
527 R_FreeStaticTriSurf( sint->lightTris );
529 sint->lightTris = NULL;
531 if ( sint->shadowTris ) {
532 // if it doesn't have an entityDef, it is part of a prelight
533 // model, not a generated interaction
534 if ( this->entityDef ) {
535 R_FreeStaticTriSurf( sint->shadowTris );
536 sint->shadowTris = NULL;
539 R_FreeInteractionCullInfo( sint->cullInfo );
542 R_StaticFree( this->surfaces );
543 this->surfaces = NULL;
545 this->numSurfaces = -1;
550 idInteraction::Unlink
553 void idInteraction::Unlink( void ) {
555 // unlink from the entity's list
556 if ( this->entityPrev ) {
557 this->entityPrev->entityNext = this->entityNext;
559 this->entityDef->firstInteraction = this->entityNext;
561 if ( this->entityNext ) {
562 this->entityNext->entityPrev = this->entityPrev;
564 this->entityDef->lastInteraction = this->entityPrev;
566 this->entityNext = this->entityPrev = NULL;
568 // unlink from the light's list
569 if ( this->lightPrev ) {
570 this->lightPrev->lightNext = this->lightNext;
572 this->lightDef->firstInteraction = this->lightNext;
574 if ( this->lightNext ) {
575 this->lightNext->lightPrev = this->lightPrev;
577 this->lightDef->lastInteraction = this->lightPrev;
579 this->lightNext = this->lightPrev = NULL;
584 idInteraction::UnlinkAndFree
586 Removes links and puts it back on the free list.
589 void idInteraction::UnlinkAndFree( void ) {
591 // clear the table pointer
592 idRenderWorldLocal *renderWorld = this->lightDef->world;
593 if ( renderWorld->interactionTable ) {
594 int index = this->lightDef->index * renderWorld->interactionTableWidth + this->entityDef->index;
595 if ( renderWorld->interactionTable[index] != this ) {
596 common->Error( "idInteraction::UnlinkAndFree: interactionTable wasn't set" );
598 renderWorld->interactionTable[index] = NULL;
605 // free the interaction area references
606 areaNumRef_t *area, *nextArea;
607 for ( area = frustumAreas; area; area = nextArea ) {
608 nextArea = area->next;
609 renderWorld->areaNumRefAllocator.Free( area );
612 // put it back on the free list
613 renderWorld->interactionAllocator.Free( this );
618 idInteraction::MakeEmpty
620 Makes the interaction empty and links it at the end of the entity's and light's interaction lists.
623 void idInteraction::MakeEmpty( void ) {
625 // an empty interaction has no surfaces
630 // relink at the end of the entity's list
631 this->entityNext = NULL;
632 this->entityPrev = this->entityDef->lastInteraction;
633 this->entityDef->lastInteraction = this;
634 if ( this->entityPrev ) {
635 this->entityPrev->entityNext = this;
637 this->entityDef->firstInteraction = this;
640 // relink at the end of the light's list
641 this->lightNext = NULL;
642 this->lightPrev = this->lightDef->lastInteraction;
643 this->lightDef->lastInteraction = this;
644 if ( this->lightPrev ) {
645 this->lightPrev->lightNext = this;
647 this->lightDef->firstInteraction = this;
653 idInteraction::HasShadows
656 ID_INLINE bool idInteraction::HasShadows( void ) const {
657 return ( !lightDef->parms.noShadows && !entityDef->parms.noShadow && lightDef->lightShader->LightCastsShadows() );
662 idInteraction::MemoryUsed
664 Counts up the memory used by all the surfaceInteractions, which
665 will be used to determine when we need to start purging old interactions.
668 int idInteraction::MemoryUsed( void ) {
671 for ( int i = 0 ; i < numSurfaces ; i++ ) {
672 surfaceInteraction_t *inter = &surfaces[i];
674 total += R_TriSurfMemory( inter->lightTris );
675 total += R_TriSurfMemory( inter->shadowTris );
683 idInteraction::CalcInteractionScissorRectangle
686 idScreenRect idInteraction::CalcInteractionScissorRectangle( const idFrustum &viewFrustum ) {
687 idBounds projectionBounds;
688 idScreenRect portalRect;
689 idScreenRect scissorRect;
691 if ( r_useInteractionScissors.GetInteger() == 0 ) {
692 return lightDef->viewLight->scissorRect;
695 if ( r_useInteractionScissors.GetInteger() < 0 ) {
696 // this is the code from Cass at nvidia, it is more precise, but slower
697 return R_CalcIntersectionScissor( lightDef, entityDef, tr.viewDef );
700 // the following is Mr.E's code
702 // frustum must be initialized and valid
703 if ( frustumState == idInteraction::FRUSTUM_UNINITIALIZED || frustumState == idInteraction::FRUSTUM_INVALID ) {
704 return lightDef->viewLight->scissorRect;
707 // calculate scissors for the portals through which the interaction is visible
708 if ( r_useInteractionScissors.GetInteger() > 1 ) {
711 if ( frustumState == idInteraction::FRUSTUM_VALID ) {
712 // retrieve all the areas the interaction frustum touches
713 for ( areaReference_t *ref = entityDef->entityRefs; ref; ref = ref->ownerNext ) {
714 area = entityDef->world->areaNumRefAllocator.Alloc();
715 area->areaNum = ref->area->areaNum;
716 area->next = frustumAreas;
719 frustumAreas = tr.viewDef->renderWorld->FloodFrustumAreas( frustum, frustumAreas );
720 frustumState = idInteraction::FRUSTUM_VALIDAREAS;
724 for ( area = frustumAreas; area; area = area->next ) {
725 portalRect.Union( entityDef->world->GetAreaScreenRect( area->areaNum ) );
727 portalRect.Intersect( lightDef->viewLight->scissorRect );
729 portalRect = lightDef->viewLight->scissorRect;
732 // early out if the interaction is not visible through any portals
733 if ( portalRect.IsEmpty() ) {
737 // calculate bounds of the interaction frustum projected into the view frustum
738 if ( lightDef->parms.pointLight ) {
739 viewFrustum.ClippedProjectionBounds( frustum, idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), projectionBounds );
741 viewFrustum.ClippedProjectionBounds( frustum, idBox( lightDef->frustumTris->bounds ), projectionBounds );
744 if ( projectionBounds.IsCleared() ) {
748 // derive a scissor rectangle from the projection bounds
749 scissorRect = R_ScreenRectFromViewFrustumBounds( projectionBounds );
751 // intersect with the portal crossing scissor rectangle
752 scissorRect.Intersect( portalRect );
754 if ( r_showInteractionScissors.GetInteger() > 0 ) {
755 R_ShowColoredScreenRect( scissorRect, lightDef->index );
763 idInteraction::CullInteractionByViewFrustum
766 bool idInteraction::CullInteractionByViewFrustum( const idFrustum &viewFrustum ) {
768 if ( !r_useInteractionCulling.GetBool() ) {
772 if ( frustumState == idInteraction::FRUSTUM_INVALID ) {
776 if ( frustumState == idInteraction::FRUSTUM_UNINITIALIZED ) {
778 frustum.FromProjection( idBox( entityDef->referenceBounds, entityDef->parms.origin, entityDef->parms.axis ), lightDef->globalLightOrigin, MAX_WORLD_SIZE );
780 if ( !frustum.IsValid() ) {
781 frustumState = idInteraction::FRUSTUM_INVALID;
785 if ( lightDef->parms.pointLight ) {
786 frustum.ConstrainToBox( idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ) );
788 frustum.ConstrainToBox( idBox( lightDef->frustumTris->bounds ) );
791 frustumState = idInteraction::FRUSTUM_VALID;
794 if ( !viewFrustum.IntersectsFrustum( frustum ) ) {
798 if ( r_showInteractionFrustums.GetInteger() ) {
799 static idVec4 colors[] = { colorRed, colorGreen, colorBlue, colorYellow, colorMagenta, colorCyan, colorWhite, colorPurple };
800 tr.viewDef->renderWorld->DebugFrustum( colors[lightDef->index & 7], frustum, ( r_showInteractionFrustums.GetInteger() > 1 ) );
801 if ( r_showInteractionFrustums.GetInteger() > 2 ) {
802 tr.viewDef->renderWorld->DebugBox( colorWhite, idBox( entityDef->referenceBounds, entityDef->parms.origin, entityDef->parms.axis ) );
811 idInteraction::CreateInteraction
813 Called when a entityDef and a lightDef are both present in a
814 portalArea, and might be visible. Performs cull checking before doing the expensive
817 References tr.viewCount so lighting surfaces will only be created if the ambient surface is visible,
818 otherwise it will be marked as deferred.
820 The results of this are cached and valid until the light or entity change.
823 void idInteraction::CreateInteraction( const idRenderModel *model ) {
824 const idMaterial * lightShader = lightDef->lightShader;
825 const idMaterial* shader;
826 bool interactionGenerated;
829 tr.pc.c_createInteractions++;
831 bounds = model->Bounds( &entityDef->parms );
833 // if it doesn't contact the light frustum, none of the surfaces will
834 if ( R_CullLocalBox( bounds, entityDef->modelMatrix, 6, lightDef->frustum ) ) {
839 // use the turbo shadow path
840 shadowGen_t shadowGen = SG_DYNAMIC;
842 // really large models, like outside terrain meshes, should use
843 // the more exactly culled static shadow path instead of the turbo shadow path.
844 // FIXME: this is a HACK, we should probably have a material flag.
845 if ( bounds[1][0] - bounds[0][0] > 3000 ) {
846 shadowGen = SG_STATIC;
850 // create slots for each of the model's surfaces
852 numSurfaces = model->NumSurfaces();
853 surfaces = (surfaceInteraction_t *)R_ClearedStaticAlloc( sizeof( *surfaces ) * numSurfaces );
855 interactionGenerated = false;
857 // check each surface in the model
858 for ( int c = 0 ; c < model->NumSurfaces() ; c++ ) {
859 const modelSurface_t *surf;
862 surf = model->Surface( c );
864 tri = surf->geometry;
869 // determine the shader for this surface, possibly by skinning
870 shader = surf->shader;
871 shader = R_RemapShaderBySkin( shader, entityDef->parms.customSkin, entityDef->parms.customShader );
877 // try to cull each surface
878 if ( R_CullLocalBox( tri->bounds, entityDef->modelMatrix, 6, lightDef->frustum ) ) {
882 surfaceInteraction_t *sint = &surfaces[c];
884 sint->shader = shader;
886 // save the ambient tri pointer so we can reject lightTri interactions
887 // when the ambient surface isn't in view, and we can get shared vertex
888 // and shadow data from the source surface
889 sint->ambientTris = tri;
891 // "invisible ink" lights and shaders
892 if ( shader->Spectrum() != lightShader->Spectrum() ) {
896 // generate a lighted surface and add it
897 if ( shader->ReceivesLighting() ) {
898 if ( tri->ambientViewCount == tr.viewCount ) {
899 sint->lightTris = R_CreateLightTris( entityDef, tri, lightDef, shader, sint->cullInfo );
901 // this will be calculated when sint->ambientTris is actually in view
902 sint->lightTris = LIGHT_TRIS_DEFERRED;
904 interactionGenerated = true;
907 // if the interaction has shadows and this surface casts a shadow
908 if ( HasShadows() && shader->SurfaceCastsShadow() && tri->silEdges != NULL ) {
910 // if the light has an optimized shadow volume, don't create shadows for any models that are part of the base areas
911 if ( lightDef->parms.prelightModel == NULL || !model->IsStaticWorldModel() || !r_useOptimizedShadows.GetBool() ) {
913 // this is the only place during gameplay (outside the utilities) that R_CreateShadowVolume() is called
914 sint->shadowTris = R_CreateShadowVolume( entityDef, tri, lightDef, shadowGen, sint->cullInfo );
915 if ( sint->shadowTris ) {
916 if ( shader->Coverage() != MC_OPAQUE || ( !r_skipSuppress.GetBool() && entityDef->parms.suppressSurfaceInViewID ) ) {
917 // if any surface is a shadow-casting perforated or translucent surface, or the
918 // base surface is suppressed in the view (world weapon shadows) we can't use
919 // the external shadow optimizations because we can see through some of the faces
920 sint->shadowTris->numShadowIndexesNoCaps = sint->shadowTris->numIndexes;
921 sint->shadowTris->numShadowIndexesNoFrontCaps = sint->shadowTris->numIndexes;
924 interactionGenerated = true;
928 // free the cull information when it's no longer needed
929 if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) {
930 R_FreeInteractionCullInfo( sint->cullInfo );
934 // if none of the surfaces generated anything, don't even bother checking?
935 if ( !interactionGenerated ) {
941 ======================
942 R_PotentiallyInsideInfiniteShadow
944 If we know that we are "off to the side" of an infinite shadow volume,
945 we can draw it without caps in zpass mode
946 ======================
948 static bool R_PotentiallyInsideInfiniteShadow( const srfTriangles_t *occluder,
949 const idVec3 &localView, const idVec3 &localLight ) {
952 // expand the bounds to account for the near clip plane, because the
953 // view could be mathematically outside, but if the near clip plane
954 // chops a volume edge, the zpass rendering would fail.
955 float znear = r_znear.GetFloat();
956 if ( tr.viewDef->renderView.cramZNear ) {
959 float stretch = znear * 2; // in theory, should vary with FOV
960 exp[0][0] = occluder->bounds[0][0] - stretch;
961 exp[0][1] = occluder->bounds[0][1] - stretch;
962 exp[0][2] = occluder->bounds[0][2] - stretch;
963 exp[1][0] = occluder->bounds[1][0] + stretch;
964 exp[1][1] = occluder->bounds[1][1] + stretch;
965 exp[1][2] = occluder->bounds[1][2] + stretch;
967 if ( exp.ContainsPoint( localView ) ) {
970 if ( exp.ContainsPoint( localLight ) ) {
974 // if the ray from localLight to localView intersects a face of the
975 // expanded bounds, we will be inside the projection
977 idVec3 ray = localView - localLight;
979 // intersect the ray from the view to the light with the near side of the bounds
980 for ( int axis = 0; axis < 3; axis++ ) {
984 if ( localLight[axis] < exp[0][axis] ) {
985 if ( localView[axis] < exp[0][axis] ) {
988 d = exp[0][axis] - localLight[axis];
989 frac = d / ray[axis];
990 hit = localLight + frac * ray;
991 hit[axis] = exp[0][axis];
992 } else if ( localLight[axis] > exp[1][axis] ) {
993 if ( localView[axis] > exp[1][axis] ) {
996 d = exp[1][axis] - localLight[axis];
997 frac = d / ray[axis];
998 hit = localLight + frac * ray;
999 hit[axis] = exp[1][axis];
1004 if ( exp.ContainsPoint( hit ) ) {
1009 // the view is definitely not inside the projected shadow
1015 idInteraction::AddActiveInteraction
1017 Create and add any necessary light and shadow triangles
1019 If the model doesn't have any surfaces that need interactions
1020 with this type of light, it can be skipped, but we might need to
1021 instantiate the dynamic model to find out
1024 void idInteraction::AddActiveInteraction( void ) {
1025 viewLight_t * vLight;
1026 viewEntity_t * vEntity;
1027 idScreenRect shadowScissor;
1028 idScreenRect lightScissor;
1029 idVec3 localLightOrigin;
1030 idVec3 localViewOrigin;
1032 vLight = lightDef->viewLight;
1033 vEntity = entityDef->viewEntity;
1035 // do not waste time culling the interaction frustum if there will be no shadows
1036 if ( !HasShadows() ) {
1038 // use the entity scissor rectangle
1039 shadowScissor = vEntity->scissorRect;
1041 // culling does not seem to be worth it for static world models
1042 } else if ( entityDef->parms.hModel->IsStaticWorldModel() ) {
1044 // use the light scissor rectangle
1045 shadowScissor = vLight->scissorRect;
1049 // try to cull the interaction
1050 // this will also cull the case where the light origin is inside the
1051 // view frustum and the entity bounds are outside the view frustum
1052 if ( CullInteractionByViewFrustum( tr.viewDef->viewFrustum ) ) {
1056 // calculate the shadow scissor rectangle
1057 shadowScissor = CalcInteractionScissorRectangle( tr.viewDef->viewFrustum );
1060 // get out before making the dynamic model if the shadow scissor rectangle is empty
1061 if ( shadowScissor.IsEmpty() ) {
1065 // We will need the dynamic surface created to make interactions, even if the
1066 // model itself wasn't visible. This just returns a cached value after it
1067 // has been generated once in the view.
1068 idRenderModel *model = R_EntityDefDynamicModel( entityDef );
1069 if ( model == NULL || model->NumSurfaces() <= 0 ) {
1073 // the dynamic model may have changed since we built the surface list
1074 if ( !IsDeferred() && entityDef->dynamicModelFrameCount != dynamicModelFrameCount ) {
1077 dynamicModelFrameCount = entityDef->dynamicModelFrameCount;
1079 // actually create the interaction if needed, building light and shadow surfaces as needed
1080 if ( IsDeferred() ) {
1081 CreateInteraction( model );
1084 R_GlobalPointToLocal( vEntity->modelMatrix, lightDef->globalLightOrigin, localLightOrigin );
1085 R_GlobalPointToLocal( vEntity->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin );
1087 // calculate the scissor as the intersection of the light and model rects
1088 // this is used for light triangles, but not for shadow triangles
1089 lightScissor = vLight->scissorRect;
1090 lightScissor.Intersect( vEntity->scissorRect );
1092 bool lightScissorsEmpty = lightScissor.IsEmpty();
1094 // for each surface of this entity / light interaction
1095 for ( int i = 0; i < numSurfaces; i++ ) {
1096 surfaceInteraction_t *sint = &surfaces[i];
1098 // see if the base surface is visible, we may still need to add shadows even if empty
1099 if ( !lightScissorsEmpty && sint->ambientTris && sint->ambientTris->ambientViewCount == tr.viewCount ) {
1101 // make sure we have created this interaction, which may have been deferred
1102 // on a previous use that only needed the shadow
1103 if ( sint->lightTris == LIGHT_TRIS_DEFERRED ) {
1104 sint->lightTris = R_CreateLightTris( vEntity->entityDef, sint->ambientTris, vLight->lightDef, sint->shader, sint->cullInfo );
1105 R_FreeInteractionCullInfo( sint->cullInfo );
1108 srfTriangles_t *lightTris = sint->lightTris;
1112 // try to cull before adding
1113 // FIXME: this may not be worthwhile. We have already done culling on the ambient,
1114 // but individual surfaces may still be cropped somewhat more
1115 if ( !R_CullLocalBox( lightTris->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
1117 // make sure the original surface has its ambient cache created
1118 srfTriangles_t *tri = sint->ambientTris;
1119 if ( !tri->ambientCache ) {
1120 if ( !R_CreateAmbientCache( tri, sint->shader->ReceivesLighting() ) ) {
1121 // skip if we were out of vertex memory
1126 // reference the original surface's ambient cache
1127 lightTris->ambientCache = tri->ambientCache;
1129 // touch the ambient surface so it won't get purged
1130 vertexCache.Touch( lightTris->ambientCache );
1132 // regenerate the lighting cache (for non-vertex program cards) if it has been purged
1133 if ( !lightTris->lightingCache ) {
1134 if ( !R_CreateLightingCache( entityDef, lightDef, lightTris ) ) {
1135 // skip if we are out of vertex memory
1139 // touch the light surface so it won't get purged
1140 // (vertex program cards won't have a light cache at all)
1141 if ( lightTris->lightingCache ) {
1142 vertexCache.Touch( lightTris->lightingCache );
1145 if ( !lightTris->indexCache && r_useIndexBuffers.GetBool() ) {
1146 vertexCache.Alloc( lightTris->indexes, lightTris->numIndexes * sizeof( lightTris->indexes[0] ), &lightTris->indexCache, true );
1148 if ( lightTris->indexCache ) {
1149 vertexCache.Touch( lightTris->indexCache );
1152 // add the surface to the light list
1154 const idMaterial *shader = sint->shader;
1155 R_GlobalShaderOverride( &shader );
1157 // there will only be localSurfaces if the light casts shadows and
1158 // there are surfaces with NOSELFSHADOW
1159 if ( sint->shader->Coverage() == MC_TRANSLUCENT ) {
1160 R_LinkLightSurf( &vLight->translucentInteractions, lightTris,
1161 vEntity, lightDef, shader, lightScissor, false );
1162 } else if ( !lightDef->parms.noShadows && sint->shader->TestMaterialFlag(MF_NOSELFSHADOW) ) {
1163 R_LinkLightSurf( &vLight->localInteractions, lightTris,
1164 vEntity, lightDef, shader, lightScissor, false );
1166 R_LinkLightSurf( &vLight->globalInteractions, lightTris,
1167 vEntity, lightDef, shader, lightScissor, false );
1173 srfTriangles_t *shadowTris = sint->shadowTris;
1175 // the shadows will always have to be added, unless we can tell they
1176 // are from a surface in an unconnected area
1179 // check for view specific shadow suppression (player shadows, etc)
1180 if ( !r_skipSuppress.GetBool() ) {
1181 if ( entityDef->parms.suppressShadowInViewID &&
1182 entityDef->parms.suppressShadowInViewID == tr.viewDef->renderView.viewID ) {
1185 if ( entityDef->parms.suppressShadowInLightID &&
1186 entityDef->parms.suppressShadowInLightID == lightDef->parms.lightId ) {
1191 // cull static shadows that have a non-empty bounds
1192 // dynamic shadows that use the turboshadow code will not have valid
1193 // bounds, because the perspective projection extends them to infinity
1194 if ( r_useShadowCulling.GetBool() && !shadowTris->bounds.IsCleared() ) {
1195 if ( R_CullLocalBox( shadowTris->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
1200 // copy the shadow vertexes to the vertex cache if they have been purged
1202 // if we are using shared shadowVertexes and letting a vertex program fix them up,
1203 // get the shadowCache from the parent ambient surface
1204 if ( !shadowTris->shadowVertexes ) {
1205 // the data may have been purged, so get the latest from the "home position"
1206 shadowTris->shadowCache = sint->ambientTris->shadowCache;
1209 // if we have been purged, re-upload the shadowVertexes
1210 if ( !shadowTris->shadowCache ) {
1211 if ( shadowTris->shadowVertexes ) {
1212 // each interaction has unique vertexes
1213 R_CreatePrivateShadowCache( shadowTris );
1215 R_CreateVertexProgramShadowCache( sint->ambientTris );
1216 shadowTris->shadowCache = sint->ambientTris->shadowCache;
1218 // if we are out of vertex cache space, skip the interaction
1219 if ( !shadowTris->shadowCache ) {
1224 // touch the shadow surface so it won't get purged
1225 vertexCache.Touch( shadowTris->shadowCache );
1227 if ( !shadowTris->indexCache && r_useIndexBuffers.GetBool() ) {
1228 vertexCache.Alloc( shadowTris->indexes, shadowTris->numIndexes * sizeof( shadowTris->indexes[0] ), &shadowTris->indexCache, true );
1229 vertexCache.Touch( shadowTris->indexCache );
1232 // see if we can avoid using the shadow volume caps
1233 bool inside = R_PotentiallyInsideInfiniteShadow( sint->ambientTris, localViewOrigin, localLightOrigin );
1235 if ( sint->shader->TestMaterialFlag( MF_NOSELFSHADOW ) ) {
1236 R_LinkLightSurf( &vLight->localShadows,
1237 shadowTris, vEntity, lightDef, NULL, shadowScissor, inside );
1239 R_LinkLightSurf( &vLight->globalShadows,
1240 shadowTris, vEntity, lightDef, NULL, shadowScissor, inside );
1248 R_ShowInteractionMemory_f
1251 void R_ShowInteractionMemory_f( const idCmdArgs &args ) {
1254 int interactions = 0;
1255 int deferredInteractions = 0;
1256 int emptyInteractions = 0;
1258 int lightTriVerts = 0;
1259 int lightTriIndexes = 0;
1261 int shadowTriVerts = 0;
1262 int shadowTriIndexes = 0;
1264 for ( int i = 0; i < tr.primaryWorld->entityDefs.Num(); i++ ) {
1265 idRenderEntityLocal *def = tr.primaryWorld->entityDefs[i];
1269 if ( def->firstInteraction == NULL ) {
1274 for ( idInteraction *inter = def->firstInteraction; inter != NULL; inter = inter->entityNext ) {
1276 total += inter->MemoryUsed();
1278 if ( inter->IsDeferred() ) {
1279 deferredInteractions++;
1282 if ( inter->IsEmpty() ) {
1283 emptyInteractions++;
1287 for ( int j = 0; j < inter->numSurfaces; j++ ) {
1288 surfaceInteraction_t *srf = &inter->surfaces[j];
1290 if ( srf->lightTris && srf->lightTris != LIGHT_TRIS_DEFERRED ) {
1292 lightTriVerts += srf->lightTris->numVerts;
1293 lightTriIndexes += srf->lightTris->numIndexes;
1295 if ( srf->shadowTris ) {
1297 shadowTriVerts += srf->shadowTris->numVerts;
1298 shadowTriIndexes += srf->shadowTris->numIndexes;
1304 common->Printf( "%i entities with %i total interactions totalling %ik\n", entities, interactions, total / 1024 );
1305 common->Printf( "%i deferred interactions, %i empty interactions\n", deferredInteractions, emptyInteractions );
1306 common->Printf( "%5i indexes %5i verts in %5i light tris\n", lightTriIndexes, lightTriVerts, lightTris );
1307 common->Printf( "%5i indexes %5i verts in %5i shadow tris\n", shadowTriIndexes, shadowTriVerts, shadowTris );