1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- 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.
- 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.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "tr_local.h"
- /*
- ===========================================================================
- idInteraction implementation
- ===========================================================================
- */
- // FIXME: use private allocator for srfCullInfo_t
- /*
- ================
- R_CalcInteractionFacing
- Determines which triangles of the surface are facing towards the light origin.
- The facing array should be allocated with one extra index than
- the number of surface triangles, which will be used to handle dangling
- edge silhouettes.
- ================
- */
- void R_CalcInteractionFacing( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) {
- idVec3 localLightOrigin;
- if ( cullInfo.facing != NULL ) {
- return;
- }
- R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
- int numFaces = tri->numIndexes / 3;
- if ( !tri->facePlanes || !tri->facePlanesCalculated ) {
- R_DeriveFacePlanes( const_cast<srfTriangles_t *>(tri) );
- }
- cullInfo.facing = (byte *) R_StaticAlloc( ( numFaces + 1 ) * sizeof( cullInfo.facing[0] ) );
- // calculate back face culling
- float *planeSide = (float *) _alloca16( numFaces * sizeof( float ) );
- // exact geometric cull against face
- SIMDProcessor->Dot( planeSide, localLightOrigin, tri->facePlanes, numFaces );
- SIMDProcessor->CmpGE( cullInfo.facing, planeSide, 0.0f, numFaces );
- cullInfo.facing[ numFaces ] = 1; // for dangling edges to reference
- }
- /*
- =====================
- R_CalcInteractionCullBits
- We want to cull a little on the sloppy side, because the pre-clipping
- of geometry to the lights in dmap will give many cases that are right
- at the border we throw things out on the border, because if any one
- vertex is clearly inside, the entire triangle will be accepted.
- =====================
- */
- void R_CalcInteractionCullBits( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo ) {
- int i, frontBits;
- if ( cullInfo.cullBits != NULL ) {
- return;
- }
- frontBits = 0;
- // cull the triangle surface bounding box
- for ( i = 0; i < 6; i++ ) {
- R_GlobalPlaneToLocal( ent->modelMatrix, -light->frustum[i], cullInfo.localClipPlanes[i] );
- // get front bits for the whole surface
- if ( tri->bounds.PlaneDistance( cullInfo.localClipPlanes[i] ) >= LIGHT_CLIP_EPSILON ) {
- frontBits |= 1<<i;
- }
- }
- // if the surface is completely inside the light frustum
- if ( frontBits == ( ( 1 << 6 ) - 1 ) ) {
- cullInfo.cullBits = LIGHT_CULL_ALL_FRONT;
- return;
- }
- cullInfo.cullBits = (byte *) R_StaticAlloc( tri->numVerts * sizeof( cullInfo.cullBits[0] ) );
- SIMDProcessor->Memset( cullInfo.cullBits, 0, tri->numVerts * sizeof( cullInfo.cullBits[0] ) );
- float *planeSide = (float *) _alloca16( tri->numVerts * sizeof( float ) );
- for ( i = 0; i < 6; i++ ) {
- // if completely infront of this clipping plane
- if ( frontBits & ( 1 << i ) ) {
- continue;
- }
- SIMDProcessor->Dot( planeSide, cullInfo.localClipPlanes[i], tri->verts, tri->numVerts );
- SIMDProcessor->CmpLT( cullInfo.cullBits, i, planeSide, LIGHT_CLIP_EPSILON, tri->numVerts );
- }
- }
- /*
- ================
- R_FreeInteractionCullInfo
- ================
- */
- void R_FreeInteractionCullInfo( srfCullInfo_t &cullInfo ) {
- if ( cullInfo.facing != NULL ) {
- R_StaticFree( cullInfo.facing );
- cullInfo.facing = NULL;
- }
- if ( cullInfo.cullBits != NULL ) {
- if ( cullInfo.cullBits != LIGHT_CULL_ALL_FRONT ) {
- R_StaticFree( cullInfo.cullBits );
- }
- cullInfo.cullBits = NULL;
- }
- }
- #define MAX_CLIPPED_POINTS 20
- typedef struct {
- int numVerts;
- idVec3 verts[MAX_CLIPPED_POINTS];
- } clipTri_t;
- /*
- =============
- R_ChopWinding
- Clips a triangle from one buffer to another, setting edge flags
- The returned buffer may be the same as inNum if no clipping is done
- If entirely clipped away, clipTris[returned].numVerts == 0
- I have some worries about edge flag cases when polygons are clipped
- multiple times near the epsilon.
- =============
- */
- static int R_ChopWinding( clipTri_t clipTris[2], int inNum, const idPlane plane ) {
- clipTri_t *in, *out;
- float dists[MAX_CLIPPED_POINTS];
- int sides[MAX_CLIPPED_POINTS];
- int counts[3];
- float dot;
- int i, j;
- idVec3 mid;
- bool front;
- in = &clipTris[inNum];
- out = &clipTris[inNum^1];
- counts[0] = counts[1] = counts[2] = 0;
- // determine sides for each point
- front = false;
- for ( i = 0; i < in->numVerts; i++ ) {
- dot = in->verts[i] * plane.Normal() + plane[3];
- dists[i] = dot;
- if ( dot < LIGHT_CLIP_EPSILON ) { // slop onto the back
- sides[i] = SIDE_BACK;
- } else {
- sides[i] = SIDE_FRONT;
- if ( dot > LIGHT_CLIP_EPSILON ) {
- front = true;
- }
- }
- counts[sides[i]]++;
- }
- // if none in front, it is completely clipped away
- if ( !front ) {
- in->numVerts = 0;
- return inNum;
- }
- if ( !counts[SIDE_BACK] ) {
- return inNum; // inout stays the same
- }
- // avoid wrapping checks by duplicating first value to end
- sides[i] = sides[0];
- dists[i] = dists[0];
- in->verts[in->numVerts] = in->verts[0];
- out->numVerts = 0;
- for ( i = 0 ; i < in->numVerts ; i++ ) {
- idVec3 &p1 = in->verts[i];
-
- if ( sides[i] == SIDE_FRONT ) {
- out->verts[out->numVerts] = p1;
- out->numVerts++;
- }
- if ( sides[i+1] == sides[i] ) {
- continue;
- }
-
- // generate a split point
- idVec3 &p2 = in->verts[i+1];
-
- dot = dists[i] / ( dists[i] - dists[i+1] );
- for ( j = 0; j < 3; j++ ) {
- mid[j] = p1[j] + dot * ( p2[j] - p1[j] );
- }
-
- out->verts[out->numVerts] = mid;
- out->numVerts++;
- }
- return inNum ^ 1;
- }
- /*
- ===================
- R_ClipTriangleToLight
- Returns false if nothing is left after clipping
- ===================
- */
- static bool R_ClipTriangleToLight( const idVec3 &a, const idVec3 &b, const idVec3 &c, int planeBits, const idPlane frustum[6] ) {
- int i;
- clipTri_t pingPong[2];
- int p;
- pingPong[0].numVerts = 3;
- pingPong[0].verts[0] = a;
- pingPong[0].verts[1] = b;
- pingPong[0].verts[2] = c;
- p = 0;
- for ( i = 0 ; i < 6 ; i++ ) {
- if ( planeBits & ( 1 << i ) ) {
- p = R_ChopWinding( pingPong, p, frustum[i] );
- if ( pingPong[p].numVerts < 1 ) {
- return false;
- }
- }
- }
- return true;
- }
- /*
- ====================
- R_CreateLightTris
- The resulting surface will be a subset of the original triangles,
- it will never clip triangles, but it may cull on a per-triangle basis.
- ====================
- */
- static srfTriangles_t *R_CreateLightTris( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- const idMaterial *shader, srfCullInfo_t &cullInfo ) {
- int i;
- int numIndexes;
- glIndex_t *indexes;
- srfTriangles_t *newTri;
- int c_backfaced;
- int c_distance;
- idBounds bounds;
- bool includeBackFaces;
- int faceNum;
- tr.pc.c_createLightTris++;
- c_backfaced = 0;
- c_distance = 0;
- numIndexes = 0;
- indexes = NULL;
- // it is debatable if non-shadowing lights should light back faces. we aren't at the moment
- if ( r_lightAllBackFaces.GetBool() || light->lightShader->LightEffectsBackSides()
- || shader->ReceivesLightingOnBackSides()
- || ent->parms.noSelfShadow || ent->parms.noShadow ) {
- includeBackFaces = true;
- } else {
- includeBackFaces = false;
- }
- // allocate a new surface for the lit triangles
- newTri = R_AllocStaticTriSurf();
- // save a reference to the original surface
- newTri->ambientSurface = const_cast<srfTriangles_t *>(tri);
- // the light surface references the verts of the ambient surface
- newTri->numVerts = tri->numVerts;
- R_ReferenceStaticTriSurfVerts( newTri, tri );
- // calculate cull information
- if ( !includeBackFaces ) {
- R_CalcInteractionFacing( ent, tri, light, cullInfo );
- }
- R_CalcInteractionCullBits( ent, tri, light, cullInfo );
- // if the surface is completely inside the light frustum
- if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT ) {
- // if we aren't self shadowing, let back facing triangles get
- // through so the smooth shaded bump maps light all the way around
- if ( includeBackFaces ) {
- // the whole surface is lit so the light surface just references the indexes of the ambient surface
- R_ReferenceStaticTriSurfIndexes( newTri, tri );
- numIndexes = tri->numIndexes;
- bounds = tri->bounds;
- } else {
- // the light tris indexes are going to be a subset of the original indexes so we generally
- // allocate too much memory here but we decrease the memory block when the number of indexes is known
- R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes );
- // back face cull the individual triangles
- indexes = newTri->indexes;
- const byte *facing = cullInfo.facing;
- for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) {
- if ( !facing[ faceNum ] ) {
- c_backfaced++;
- continue;
- }
- indexes[numIndexes+0] = tri->indexes[i+0];
- indexes[numIndexes+1] = tri->indexes[i+1];
- indexes[numIndexes+2] = tri->indexes[i+2];
- numIndexes += 3;
- }
- // get bounds for the surface
- SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes );
- // decrease the size of the memory block to the size of the number of used indexes
- R_ResizeStaticTriSurfIndexes( newTri, numIndexes );
- }
- } else {
- // the light tris indexes are going to be a subset of the original indexes so we generally
- // allocate too much memory here but we decrease the memory block when the number of indexes is known
- R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes );
- // cull individual triangles
- indexes = newTri->indexes;
- const byte *facing = cullInfo.facing;
- const byte *cullBits = cullInfo.cullBits;
- for ( faceNum = i = 0; i < tri->numIndexes; i += 3, faceNum++ ) {
- int i1, i2, i3;
- // if we aren't self shadowing, let back facing triangles get
- // through so the smooth shaded bump maps light all the way around
- if ( !includeBackFaces ) {
- // back face cull
- if ( !facing[ faceNum ] ) {
- c_backfaced++;
- continue;
- }
- }
- i1 = tri->indexes[i+0];
- i2 = tri->indexes[i+1];
- i3 = tri->indexes[i+2];
- // fast cull outside the frustum
- // if all three points are off one plane side, it definately isn't visible
- if ( cullBits[i1] & cullBits[i2] & cullBits[i3] ) {
- c_distance++;
- continue;
- }
- if ( r_usePreciseTriangleInteractions.GetBool() ) {
- // do a precise clipped cull if none of the points is completely inside the frustum
- // note that we do not actually use the clipped triangle, which would have Z fighting issues.
- if ( cullBits[i1] && cullBits[i2] && cullBits[i3] ) {
- int cull = cullBits[i1] | cullBits[i2] | cullBits[i3];
- if ( !R_ClipTriangleToLight( tri->verts[i1].xyz, tri->verts[i2].xyz, tri->verts[i3].xyz, cull, cullInfo.localClipPlanes ) ) {
- continue;
- }
- }
- }
- // add to the list
- indexes[numIndexes+0] = i1;
- indexes[numIndexes+1] = i2;
- indexes[numIndexes+2] = i3;
- numIndexes += 3;
- }
- // get bounds for the surface
- SIMDProcessor->MinMax( bounds[0], bounds[1], tri->verts, indexes, numIndexes );
- // decrease the size of the memory block to the size of the number of used indexes
- R_ResizeStaticTriSurfIndexes( newTri, numIndexes );
- }
- if ( !numIndexes ) {
- R_ReallyFreeStaticTriSurf( newTri );
- return NULL;
- }
- newTri->numIndexes = numIndexes;
- newTri->bounds = bounds;
- return newTri;
- }
- /*
- ===============
- idInteraction::idInteraction
- ===============
- */
- idInteraction::idInteraction( void ) {
- numSurfaces = 0;
- surfaces = NULL;
- entityDef = NULL;
- lightDef = NULL;
- lightNext = NULL;
- lightPrev = NULL;
- entityNext = NULL;
- entityPrev = NULL;
- dynamicModelFrameCount = 0;
- frustumState = FRUSTUM_UNINITIALIZED;
- frustumAreas = NULL;
- }
- /*
- ===============
- idInteraction::AllocAndLink
- ===============
- */
- idInteraction *idInteraction::AllocAndLink( idRenderEntityLocal *edef, idRenderLightLocal *ldef ) {
- if ( !edef || !ldef ) {
- common->Error( "idInteraction::AllocAndLink: NULL parm" );
- }
- idRenderWorldLocal *renderWorld = edef->world;
- idInteraction *interaction = renderWorld->interactionAllocator.Alloc();
- // link and initialize
- interaction->dynamicModelFrameCount = 0;
- interaction->lightDef = ldef;
- interaction->entityDef = edef;
- interaction->numSurfaces = -1; // not checked yet
- interaction->surfaces = NULL;
- interaction->frustumState = idInteraction::FRUSTUM_UNINITIALIZED;
- interaction->frustumAreas = NULL;
- // link at the start of the entity's list
- interaction->lightNext = ldef->firstInteraction;
- interaction->lightPrev = NULL;
- ldef->firstInteraction = interaction;
- if ( interaction->lightNext != NULL ) {
- interaction->lightNext->lightPrev = interaction;
- } else {
- ldef->lastInteraction = interaction;
- }
- // link at the start of the light's list
- interaction->entityNext = edef->firstInteraction;
- interaction->entityPrev = NULL;
- edef->firstInteraction = interaction;
- if ( interaction->entityNext != NULL ) {
- interaction->entityNext->entityPrev = interaction;
- } else {
- edef->lastInteraction = interaction;
- }
- // update the interaction table
- if ( renderWorld->interactionTable ) {
- int index = ldef->index * renderWorld->interactionTableWidth + edef->index;
- if ( renderWorld->interactionTable[index] != NULL ) {
- common->Error( "idInteraction::AllocAndLink: non NULL table entry" );
- }
- renderWorld->interactionTable[ index ] = interaction;
- }
- return interaction;
- }
- /*
- ===============
- idInteraction::FreeSurfaces
- Frees the surfaces, but leaves the interaction linked in, so it
- will be regenerated automatically
- ===============
- */
- void idInteraction::FreeSurfaces( void ) {
- if ( this->surfaces ) {
- for ( int i = 0 ; i < this->numSurfaces ; i++ ) {
- surfaceInteraction_t *sint = &this->surfaces[i];
- if ( sint->lightTris ) {
- if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) {
- R_FreeStaticTriSurf( sint->lightTris );
- }
- sint->lightTris = NULL;
- }
- if ( sint->shadowTris ) {
- // if it doesn't have an entityDef, it is part of a prelight
- // model, not a generated interaction
- if ( this->entityDef ) {
- R_FreeStaticTriSurf( sint->shadowTris );
- sint->shadowTris = NULL;
- }
- }
- R_FreeInteractionCullInfo( sint->cullInfo );
- }
- R_StaticFree( this->surfaces );
- this->surfaces = NULL;
- }
- this->numSurfaces = -1;
- }
- /*
- ===============
- idInteraction::Unlink
- ===============
- */
- void idInteraction::Unlink( void ) {
- // unlink from the entity's list
- if ( this->entityPrev ) {
- this->entityPrev->entityNext = this->entityNext;
- } else {
- this->entityDef->firstInteraction = this->entityNext;
- }
- if ( this->entityNext ) {
- this->entityNext->entityPrev = this->entityPrev;
- } else {
- this->entityDef->lastInteraction = this->entityPrev;
- }
- this->entityNext = this->entityPrev = NULL;
- // unlink from the light's list
- if ( this->lightPrev ) {
- this->lightPrev->lightNext = this->lightNext;
- } else {
- this->lightDef->firstInteraction = this->lightNext;
- }
- if ( this->lightNext ) {
- this->lightNext->lightPrev = this->lightPrev;
- } else {
- this->lightDef->lastInteraction = this->lightPrev;
- }
- this->lightNext = this->lightPrev = NULL;
- }
- /*
- ===============
- idInteraction::UnlinkAndFree
- Removes links and puts it back on the free list.
- ===============
- */
- void idInteraction::UnlinkAndFree( void ) {
- // clear the table pointer
- idRenderWorldLocal *renderWorld = this->lightDef->world;
- if ( renderWorld->interactionTable ) {
- int index = this->lightDef->index * renderWorld->interactionTableWidth + this->entityDef->index;
- if ( renderWorld->interactionTable[index] != this ) {
- common->Error( "idInteraction::UnlinkAndFree: interactionTable wasn't set" );
- }
- renderWorld->interactionTable[index] = NULL;
- }
- Unlink();
- FreeSurfaces();
- // free the interaction area references
- areaNumRef_t *area, *nextArea;
- for ( area = frustumAreas; area; area = nextArea ) {
- nextArea = area->next;
- renderWorld->areaNumRefAllocator.Free( area );
- }
- // put it back on the free list
- renderWorld->interactionAllocator.Free( this );
- }
- /*
- ===============
- idInteraction::MakeEmpty
- Makes the interaction empty and links it at the end of the entity's and light's interaction lists.
- ===============
- */
- void idInteraction::MakeEmpty( void ) {
- // an empty interaction has no surfaces
- numSurfaces = 0;
- Unlink();
- // relink at the end of the entity's list
- this->entityNext = NULL;
- this->entityPrev = this->entityDef->lastInteraction;
- this->entityDef->lastInteraction = this;
- if ( this->entityPrev ) {
- this->entityPrev->entityNext = this;
- } else {
- this->entityDef->firstInteraction = this;
- }
- // relink at the end of the light's list
- this->lightNext = NULL;
- this->lightPrev = this->lightDef->lastInteraction;
- this->lightDef->lastInteraction = this;
- if ( this->lightPrev ) {
- this->lightPrev->lightNext = this;
- } else {
- this->lightDef->firstInteraction = this;
- }
- }
- /*
- ===============
- idInteraction::HasShadows
- ===============
- */
- ID_INLINE bool idInteraction::HasShadows( void ) const {
- return ( !lightDef->parms.noShadows && !entityDef->parms.noShadow && lightDef->lightShader->LightCastsShadows() );
- }
- /*
- ===============
- idInteraction::MemoryUsed
- Counts up the memory used by all the surfaceInteractions, which
- will be used to determine when we need to start purging old interactions.
- ===============
- */
- int idInteraction::MemoryUsed( void ) {
- int total = 0;
- for ( int i = 0 ; i < numSurfaces ; i++ ) {
- surfaceInteraction_t *inter = &surfaces[i];
- total += R_TriSurfMemory( inter->lightTris );
- total += R_TriSurfMemory( inter->shadowTris );
- }
- return total;
- }
- /*
- ==================
- idInteraction::CalcInteractionScissorRectangle
- ==================
- */
- idScreenRect idInteraction::CalcInteractionScissorRectangle( const idFrustum &viewFrustum ) {
- idBounds projectionBounds;
- idScreenRect portalRect;
- idScreenRect scissorRect;
- if ( r_useInteractionScissors.GetInteger() == 0 ) {
- return lightDef->viewLight->scissorRect;
- }
- if ( r_useInteractionScissors.GetInteger() < 0 ) {
- // this is the code from Cass at nvidia, it is more precise, but slower
- return R_CalcIntersectionScissor( lightDef, entityDef, tr.viewDef );
- }
- // the following is Mr.E's code
- // frustum must be initialized and valid
- if ( frustumState == idInteraction::FRUSTUM_UNINITIALIZED || frustumState == idInteraction::FRUSTUM_INVALID ) {
- return lightDef->viewLight->scissorRect;
- }
- // calculate scissors for the portals through which the interaction is visible
- if ( r_useInteractionScissors.GetInteger() > 1 ) {
- areaNumRef_t *area;
- if ( frustumState == idInteraction::FRUSTUM_VALID ) {
- // retrieve all the areas the interaction frustum touches
- for ( areaReference_t *ref = entityDef->entityRefs; ref; ref = ref->ownerNext ) {
- area = entityDef->world->areaNumRefAllocator.Alloc();
- area->areaNum = ref->area->areaNum;
- area->next = frustumAreas;
- frustumAreas = area;
- }
- frustumAreas = tr.viewDef->renderWorld->FloodFrustumAreas( frustum, frustumAreas );
- frustumState = idInteraction::FRUSTUM_VALIDAREAS;
- }
- portalRect.Clear();
- for ( area = frustumAreas; area; area = area->next ) {
- portalRect.Union( entityDef->world->GetAreaScreenRect( area->areaNum ) );
- }
- portalRect.Intersect( lightDef->viewLight->scissorRect );
- } else {
- portalRect = lightDef->viewLight->scissorRect;
- }
- // early out if the interaction is not visible through any portals
- if ( portalRect.IsEmpty() ) {
- return portalRect;
- }
- // calculate bounds of the interaction frustum projected into the view frustum
- if ( lightDef->parms.pointLight ) {
- viewFrustum.ClippedProjectionBounds( frustum, idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ), projectionBounds );
- } else {
- viewFrustum.ClippedProjectionBounds( frustum, idBox( lightDef->frustumTris->bounds ), projectionBounds );
- }
- if ( projectionBounds.IsCleared() ) {
- return portalRect;
- }
- // derive a scissor rectangle from the projection bounds
- scissorRect = R_ScreenRectFromViewFrustumBounds( projectionBounds );
- // intersect with the portal crossing scissor rectangle
- scissorRect.Intersect( portalRect );
- if ( r_showInteractionScissors.GetInteger() > 0 ) {
- R_ShowColoredScreenRect( scissorRect, lightDef->index );
- }
- return scissorRect;
- }
- /*
- ===================
- idInteraction::CullInteractionByViewFrustum
- ===================
- */
- bool idInteraction::CullInteractionByViewFrustum( const idFrustum &viewFrustum ) {
- if ( !r_useInteractionCulling.GetBool() ) {
- return false;
- }
- if ( frustumState == idInteraction::FRUSTUM_INVALID ) {
- return false;
- }
- if ( frustumState == idInteraction::FRUSTUM_UNINITIALIZED ) {
- frustum.FromProjection( idBox( entityDef->referenceBounds, entityDef->parms.origin, entityDef->parms.axis ), lightDef->globalLightOrigin, MAX_WORLD_SIZE );
- if ( !frustum.IsValid() ) {
- frustumState = idInteraction::FRUSTUM_INVALID;
- return false;
- }
- if ( lightDef->parms.pointLight ) {
- frustum.ConstrainToBox( idBox( lightDef->parms.origin, lightDef->parms.lightRadius, lightDef->parms.axis ) );
- } else {
- frustum.ConstrainToBox( idBox( lightDef->frustumTris->bounds ) );
- }
- frustumState = idInteraction::FRUSTUM_VALID;
- }
- if ( !viewFrustum.IntersectsFrustum( frustum ) ) {
- return true;
- }
- if ( r_showInteractionFrustums.GetInteger() ) {
- static idVec4 colors[] = { colorRed, colorGreen, colorBlue, colorYellow, colorMagenta, colorCyan, colorWhite, colorPurple };
- tr.viewDef->renderWorld->DebugFrustum( colors[lightDef->index & 7], frustum, ( r_showInteractionFrustums.GetInteger() > 1 ) );
- if ( r_showInteractionFrustums.GetInteger() > 2 ) {
- tr.viewDef->renderWorld->DebugBox( colorWhite, idBox( entityDef->referenceBounds, entityDef->parms.origin, entityDef->parms.axis ) );
- }
- }
- return false;
- }
- /*
- ====================
- idInteraction::CreateInteraction
- Called when a entityDef and a lightDef are both present in a
- portalArea, and might be visible. Performs cull checking before doing the expensive
- computations.
- References tr.viewCount so lighting surfaces will only be created if the ambient surface is visible,
- otherwise it will be marked as deferred.
- The results of this are cached and valid until the light or entity change.
- ====================
- */
- void idInteraction::CreateInteraction( const idRenderModel *model ) {
- const idMaterial * lightShader = lightDef->lightShader;
- const idMaterial* shader;
- bool interactionGenerated;
- idBounds bounds;
- tr.pc.c_createInteractions++;
- bounds = model->Bounds( &entityDef->parms );
- // if it doesn't contact the light frustum, none of the surfaces will
- if ( R_CullLocalBox( bounds, entityDef->modelMatrix, 6, lightDef->frustum ) ) {
- MakeEmpty();
- return;
- }
- // use the turbo shadow path
- shadowGen_t shadowGen = SG_DYNAMIC;
- // really large models, like outside terrain meshes, should use
- // the more exactly culled static shadow path instead of the turbo shadow path.
- // FIXME: this is a HACK, we should probably have a material flag.
- if ( bounds[1][0] - bounds[0][0] > 3000 ) {
- shadowGen = SG_STATIC;
- }
- //
- // create slots for each of the model's surfaces
- //
- numSurfaces = model->NumSurfaces();
- surfaces = (surfaceInteraction_t *)R_ClearedStaticAlloc( sizeof( *surfaces ) * numSurfaces );
- interactionGenerated = false;
- // check each surface in the model
- for ( int c = 0 ; c < model->NumSurfaces() ; c++ ) {
- const modelSurface_t *surf;
- srfTriangles_t *tri;
-
- surf = model->Surface( c );
- tri = surf->geometry;
- if ( !tri ) {
- continue;
- }
- // determine the shader for this surface, possibly by skinning
- shader = surf->shader;
- shader = R_RemapShaderBySkin( shader, entityDef->parms.customSkin, entityDef->parms.customShader );
- if ( !shader ) {
- continue;
- }
- // try to cull each surface
- if ( R_CullLocalBox( tri->bounds, entityDef->modelMatrix, 6, lightDef->frustum ) ) {
- continue;
- }
- surfaceInteraction_t *sint = &surfaces[c];
- sint->shader = shader;
- // save the ambient tri pointer so we can reject lightTri interactions
- // when the ambient surface isn't in view, and we can get shared vertex
- // and shadow data from the source surface
- sint->ambientTris = tri;
- // "invisible ink" lights and shaders
- if ( shader->Spectrum() != lightShader->Spectrum() ) {
- continue;
- }
- // generate a lighted surface and add it
- if ( shader->ReceivesLighting() ) {
- if ( tri->ambientViewCount == tr.viewCount ) {
- sint->lightTris = R_CreateLightTris( entityDef, tri, lightDef, shader, sint->cullInfo );
- } else {
- // this will be calculated when sint->ambientTris is actually in view
- sint->lightTris = LIGHT_TRIS_DEFERRED;
- }
- interactionGenerated = true;
- }
- // if the interaction has shadows and this surface casts a shadow
- if ( HasShadows() && shader->SurfaceCastsShadow() && tri->silEdges != NULL ) {
- // if the light has an optimized shadow volume, don't create shadows for any models that are part of the base areas
- if ( lightDef->parms.prelightModel == NULL || !model->IsStaticWorldModel() || !r_useOptimizedShadows.GetBool() ) {
- // this is the only place during gameplay (outside the utilities) that R_CreateShadowVolume() is called
- sint->shadowTris = R_CreateShadowVolume( entityDef, tri, lightDef, shadowGen, sint->cullInfo );
- if ( sint->shadowTris ) {
- if ( shader->Coverage() != MC_OPAQUE || ( !r_skipSuppress.GetBool() && entityDef->parms.suppressSurfaceInViewID ) ) {
- // if any surface is a shadow-casting perforated or translucent surface, or the
- // base surface is suppressed in the view (world weapon shadows) we can't use
- // the external shadow optimizations because we can see through some of the faces
- sint->shadowTris->numShadowIndexesNoCaps = sint->shadowTris->numIndexes;
- sint->shadowTris->numShadowIndexesNoFrontCaps = sint->shadowTris->numIndexes;
- }
- }
- interactionGenerated = true;
- }
- }
- // free the cull information when it's no longer needed
- if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) {
- R_FreeInteractionCullInfo( sint->cullInfo );
- }
- }
- // if none of the surfaces generated anything, don't even bother checking?
- if ( !interactionGenerated ) {
- MakeEmpty();
- }
- }
- /*
- ======================
- R_PotentiallyInsideInfiniteShadow
- If we know that we are "off to the side" of an infinite shadow volume,
- we can draw it without caps in zpass mode
- ======================
- */
- static bool R_PotentiallyInsideInfiniteShadow( const srfTriangles_t *occluder,
- const idVec3 &localView, const idVec3 &localLight ) {
- idBounds exp;
- // expand the bounds to account for the near clip plane, because the
- // view could be mathematically outside, but if the near clip plane
- // chops a volume edge, the zpass rendering would fail.
- float znear = r_znear.GetFloat();
- if ( tr.viewDef->renderView.cramZNear ) {
- znear *= 0.25f;
- }
- float stretch = znear * 2; // in theory, should vary with FOV
- exp[0][0] = occluder->bounds[0][0] - stretch;
- exp[0][1] = occluder->bounds[0][1] - stretch;
- exp[0][2] = occluder->bounds[0][2] - stretch;
- exp[1][0] = occluder->bounds[1][0] + stretch;
- exp[1][1] = occluder->bounds[1][1] + stretch;
- exp[1][2] = occluder->bounds[1][2] + stretch;
- if ( exp.ContainsPoint( localView ) ) {
- return true;
- }
- if ( exp.ContainsPoint( localLight ) ) {
- return true;
- }
- // if the ray from localLight to localView intersects a face of the
- // expanded bounds, we will be inside the projection
- idVec3 ray = localView - localLight;
- // intersect the ray from the view to the light with the near side of the bounds
- for ( int axis = 0; axis < 3; axis++ ) {
- float d, frac;
- idVec3 hit;
- if ( localLight[axis] < exp[0][axis] ) {
- if ( localView[axis] < exp[0][axis] ) {
- continue;
- }
- d = exp[0][axis] - localLight[axis];
- frac = d / ray[axis];
- hit = localLight + frac * ray;
- hit[axis] = exp[0][axis];
- } else if ( localLight[axis] > exp[1][axis] ) {
- if ( localView[axis] > exp[1][axis] ) {
- continue;
- }
- d = exp[1][axis] - localLight[axis];
- frac = d / ray[axis];
- hit = localLight + frac * ray;
- hit[axis] = exp[1][axis];
- } else {
- continue;
- }
- if ( exp.ContainsPoint( hit ) ) {
- return true;
- }
- }
- // the view is definitely not inside the projected shadow
- return false;
- }
- /*
- ==================
- idInteraction::AddActiveInteraction
- Create and add any necessary light and shadow triangles
- If the model doesn't have any surfaces that need interactions
- with this type of light, it can be skipped, but we might need to
- instantiate the dynamic model to find out
- ==================
- */
- void idInteraction::AddActiveInteraction( void ) {
- viewLight_t * vLight;
- viewEntity_t * vEntity;
- idScreenRect shadowScissor;
- idScreenRect lightScissor;
- idVec3 localLightOrigin;
- idVec3 localViewOrigin;
- vLight = lightDef->viewLight;
- vEntity = entityDef->viewEntity;
- // do not waste time culling the interaction frustum if there will be no shadows
- if ( !HasShadows() ) {
- // use the entity scissor rectangle
- shadowScissor = vEntity->scissorRect;
- // culling does not seem to be worth it for static world models
- } else if ( entityDef->parms.hModel->IsStaticWorldModel() ) {
- // use the light scissor rectangle
- shadowScissor = vLight->scissorRect;
- } else {
- // try to cull the interaction
- // this will also cull the case where the light origin is inside the
- // view frustum and the entity bounds are outside the view frustum
- if ( CullInteractionByViewFrustum( tr.viewDef->viewFrustum ) ) {
- return;
- }
- // calculate the shadow scissor rectangle
- shadowScissor = CalcInteractionScissorRectangle( tr.viewDef->viewFrustum );
- }
- // get out before making the dynamic model if the shadow scissor rectangle is empty
- if ( shadowScissor.IsEmpty() ) {
- return;
- }
- // We will need the dynamic surface created to make interactions, even if the
- // model itself wasn't visible. This just returns a cached value after it
- // has been generated once in the view.
- idRenderModel *model = R_EntityDefDynamicModel( entityDef );
- if ( model == NULL || model->NumSurfaces() <= 0 ) {
- return;
- }
- // the dynamic model may have changed since we built the surface list
- if ( !IsDeferred() && entityDef->dynamicModelFrameCount != dynamicModelFrameCount ) {
- FreeSurfaces();
- }
- dynamicModelFrameCount = entityDef->dynamicModelFrameCount;
- // actually create the interaction if needed, building light and shadow surfaces as needed
- if ( IsDeferred() ) {
- CreateInteraction( model );
- }
- R_GlobalPointToLocal( vEntity->modelMatrix, lightDef->globalLightOrigin, localLightOrigin );
- R_GlobalPointToLocal( vEntity->modelMatrix, tr.viewDef->renderView.vieworg, localViewOrigin );
- // calculate the scissor as the intersection of the light and model rects
- // this is used for light triangles, but not for shadow triangles
- lightScissor = vLight->scissorRect;
- lightScissor.Intersect( vEntity->scissorRect );
- bool lightScissorsEmpty = lightScissor.IsEmpty();
- // for each surface of this entity / light interaction
- for ( int i = 0; i < numSurfaces; i++ ) {
- surfaceInteraction_t *sint = &surfaces[i];
- // see if the base surface is visible, we may still need to add shadows even if empty
- if ( !lightScissorsEmpty && sint->ambientTris && sint->ambientTris->ambientViewCount == tr.viewCount ) {
- // make sure we have created this interaction, which may have been deferred
- // on a previous use that only needed the shadow
- if ( sint->lightTris == LIGHT_TRIS_DEFERRED ) {
- sint->lightTris = R_CreateLightTris( vEntity->entityDef, sint->ambientTris, vLight->lightDef, sint->shader, sint->cullInfo );
- R_FreeInteractionCullInfo( sint->cullInfo );
- }
- srfTriangles_t *lightTris = sint->lightTris;
- if ( lightTris ) {
- // try to cull before adding
- // FIXME: this may not be worthwhile. We have already done culling on the ambient,
- // but individual surfaces may still be cropped somewhat more
- if ( !R_CullLocalBox( lightTris->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
- // make sure the original surface has its ambient cache created
- srfTriangles_t *tri = sint->ambientTris;
- if ( !tri->ambientCache ) {
- if ( !R_CreateAmbientCache( tri, sint->shader->ReceivesLighting() ) ) {
- // skip if we were out of vertex memory
- continue;
- }
- }
- // reference the original surface's ambient cache
- lightTris->ambientCache = tri->ambientCache;
- // touch the ambient surface so it won't get purged
- vertexCache.Touch( lightTris->ambientCache );
- // regenerate the lighting cache (for non-vertex program cards) if it has been purged
- if ( !lightTris->lightingCache ) {
- if ( !R_CreateLightingCache( entityDef, lightDef, lightTris ) ) {
- // skip if we are out of vertex memory
- continue;
- }
- }
- // touch the light surface so it won't get purged
- // (vertex program cards won't have a light cache at all)
- if ( lightTris->lightingCache ) {
- vertexCache.Touch( lightTris->lightingCache );
- }
- if ( !lightTris->indexCache && r_useIndexBuffers.GetBool() ) {
- vertexCache.Alloc( lightTris->indexes, lightTris->numIndexes * sizeof( lightTris->indexes[0] ), &lightTris->indexCache, true );
- }
- if ( lightTris->indexCache ) {
- vertexCache.Touch( lightTris->indexCache );
- }
- // add the surface to the light list
- const idMaterial *shader = sint->shader;
- R_GlobalShaderOverride( &shader );
- // there will only be localSurfaces if the light casts shadows and
- // there are surfaces with NOSELFSHADOW
- if ( sint->shader->Coverage() == MC_TRANSLUCENT ) {
- R_LinkLightSurf( &vLight->translucentInteractions, lightTris,
- vEntity, lightDef, shader, lightScissor, false );
- } else if ( !lightDef->parms.noShadows && sint->shader->TestMaterialFlag(MF_NOSELFSHADOW) ) {
- R_LinkLightSurf( &vLight->localInteractions, lightTris,
- vEntity, lightDef, shader, lightScissor, false );
- } else {
- R_LinkLightSurf( &vLight->globalInteractions, lightTris,
- vEntity, lightDef, shader, lightScissor, false );
- }
- }
- }
- }
- srfTriangles_t *shadowTris = sint->shadowTris;
- // the shadows will always have to be added, unless we can tell they
- // are from a surface in an unconnected area
- if ( shadowTris ) {
-
- // check for view specific shadow suppression (player shadows, etc)
- if ( !r_skipSuppress.GetBool() ) {
- if ( entityDef->parms.suppressShadowInViewID &&
- entityDef->parms.suppressShadowInViewID == tr.viewDef->renderView.viewID ) {
- continue;
- }
- if ( entityDef->parms.suppressShadowInLightID &&
- entityDef->parms.suppressShadowInLightID == lightDef->parms.lightId ) {
- continue;
- }
- }
- // cull static shadows that have a non-empty bounds
- // dynamic shadows that use the turboshadow code will not have valid
- // bounds, because the perspective projection extends them to infinity
- if ( r_useShadowCulling.GetBool() && !shadowTris->bounds.IsCleared() ) {
- if ( R_CullLocalBox( shadowTris->bounds, vEntity->modelMatrix, 5, tr.viewDef->frustum ) ) {
- continue;
- }
- }
- // copy the shadow vertexes to the vertex cache if they have been purged
- // if we are using shared shadowVertexes and letting a vertex program fix them up,
- // get the shadowCache from the parent ambient surface
- if ( !shadowTris->shadowVertexes ) {
- // the data may have been purged, so get the latest from the "home position"
- shadowTris->shadowCache = sint->ambientTris->shadowCache;
- }
- // if we have been purged, re-upload the shadowVertexes
- if ( !shadowTris->shadowCache ) {
- if ( shadowTris->shadowVertexes ) {
- // each interaction has unique vertexes
- R_CreatePrivateShadowCache( shadowTris );
- } else {
- R_CreateVertexProgramShadowCache( sint->ambientTris );
- shadowTris->shadowCache = sint->ambientTris->shadowCache;
- }
- // if we are out of vertex cache space, skip the interaction
- if ( !shadowTris->shadowCache ) {
- continue;
- }
- }
- // touch the shadow surface so it won't get purged
- vertexCache.Touch( shadowTris->shadowCache );
- if ( !shadowTris->indexCache && r_useIndexBuffers.GetBool() ) {
- vertexCache.Alloc( shadowTris->indexes, shadowTris->numIndexes * sizeof( shadowTris->indexes[0] ), &shadowTris->indexCache, true );
- vertexCache.Touch( shadowTris->indexCache );
- }
- // see if we can avoid using the shadow volume caps
- bool inside = R_PotentiallyInsideInfiniteShadow( sint->ambientTris, localViewOrigin, localLightOrigin );
- if ( sint->shader->TestMaterialFlag( MF_NOSELFSHADOW ) ) {
- R_LinkLightSurf( &vLight->localShadows,
- shadowTris, vEntity, lightDef, NULL, shadowScissor, inside );
- } else {
- R_LinkLightSurf( &vLight->globalShadows,
- shadowTris, vEntity, lightDef, NULL, shadowScissor, inside );
- }
- }
- }
- }
- /*
- ===================
- R_ShowInteractionMemory_f
- ===================
- */
- void R_ShowInteractionMemory_f( const idCmdArgs &args ) {
- int total = 0;
- int entities = 0;
- int interactions = 0;
- int deferredInteractions = 0;
- int emptyInteractions = 0;
- int lightTris = 0;
- int lightTriVerts = 0;
- int lightTriIndexes = 0;
- int shadowTris = 0;
- int shadowTriVerts = 0;
- int shadowTriIndexes = 0;
- for ( int i = 0; i < tr.primaryWorld->entityDefs.Num(); i++ ) {
- idRenderEntityLocal *def = tr.primaryWorld->entityDefs[i];
- if ( !def ) {
- continue;
- }
- if ( def->firstInteraction == NULL ) {
- continue;
- }
- entities++;
- for ( idInteraction *inter = def->firstInteraction; inter != NULL; inter = inter->entityNext ) {
- interactions++;
- total += inter->MemoryUsed();
- if ( inter->IsDeferred() ) {
- deferredInteractions++;
- continue;
- }
- if ( inter->IsEmpty() ) {
- emptyInteractions++;
- continue;
- }
- for ( int j = 0; j < inter->numSurfaces; j++ ) {
- surfaceInteraction_t *srf = &inter->surfaces[j];
- if ( srf->lightTris && srf->lightTris != LIGHT_TRIS_DEFERRED ) {
- lightTris++;
- lightTriVerts += srf->lightTris->numVerts;
- lightTriIndexes += srf->lightTris->numIndexes;
- }
- if ( srf->shadowTris ) {
- shadowTris++;
- shadowTriVerts += srf->shadowTris->numVerts;
- shadowTriIndexes += srf->shadowTris->numIndexes;
- }
- }
- }
- }
- common->Printf( "%i entities with %i total interactions totalling %ik\n", entities, interactions, total / 1024 );
- common->Printf( "%i deferred interactions, %i empty interactions\n", deferredInteractions, emptyInteractions );
- common->Printf( "%5i indexes %5i verts in %5i light tris\n", lightTriIndexes, lightTriVerts, lightTris );
- common->Printf( "%5i indexes %5i verts in %5i shadow tris\n", shadowTriIndexes, shadowTriVerts, shadowTris );
- }
|