mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2024-12-02 08:51:57 +00:00
277964f074
- Implemented soft shadows using PCF hardware shadow mapping The implementation uses sampler2DArrayShadow and PCF which usually requires Direct3D 10.1 however it is in the OpenGL 3.2 core so it should be widely supported. All 3 light types are supported which means parallel lights (sun) use scene independent cascaded shadow mapping. The implementation is very fast with single taps (400 fps average per scene on a GTX 660 ti OC) however I defaulted it to 16 taps so the shadows look really good which should you give stable 100 fps on todays hardware. The shadow filtering algorithm is based on Carmack's research which was released in the original Doom 3 GPL release draw_exp.cpp. - Changed interaction shaders to use Half-Lambert lighting like in HL2 to make the game less dark - Fixed some of the renderer debugging/development tools like r_showTris
969 lines
26 KiB
C++
969 lines
26 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 BFG Edition GPL Source Code
|
|
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
|
|
|
|
Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
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.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#pragma hdrstop
|
|
#include "precompiled.h"
|
|
|
|
#include "tr_local.h"
|
|
|
|
/*
|
|
===========================================================================
|
|
|
|
idInteraction implementation
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
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 )
|
|
{
|
|
SCOPED_PROFILE_EVENT( "R_CalcInteractionFacing" );
|
|
|
|
if( cullInfo.facing != NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
idVec3 localLightOrigin;
|
|
R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
|
|
|
|
const int numFaces = tri->numIndexes / 3;
|
|
cullInfo.facing = ( byte* ) R_StaticAlloc( ( numFaces + 1 ) * sizeof( cullInfo.facing[0] ), TAG_RENDER_INTERACTION );
|
|
|
|
// exact geometric cull against face
|
|
for( int i = 0, face = 0; i < tri->numIndexes; i += 3, face++ )
|
|
{
|
|
const idDrawVert& v0 = tri->verts[tri->indexes[i + 0]];
|
|
const idDrawVert& v1 = tri->verts[tri->indexes[i + 1]];
|
|
const idDrawVert& v2 = tri->verts[tri->indexes[i + 2]];
|
|
|
|
const idPlane plane( v0.xyz, v1.xyz, v2.xyz );
|
|
const float d = plane.Distance( localLightOrigin );
|
|
|
|
cullInfo.facing[face] = ( d >= 0.0f );
|
|
}
|
|
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 )
|
|
{
|
|
SCOPED_PROFILE_EVENT( "R_CalcInteractionCullBits" );
|
|
|
|
if( cullInfo.cullBits != NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
idPlane frustumPlanes[6];
|
|
idRenderMatrix::GetFrustumPlanes( frustumPlanes, light->baseLightProject, true, true );
|
|
|
|
int frontBits = 0;
|
|
|
|
// cull the triangle surface bounding box
|
|
for( int i = 0; i < 6; i++ )
|
|
{
|
|
R_GlobalPlaneToLocal( ent->modelMatrix, frustumPlanes[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] ), TAG_RENDER_INTERACTION );
|
|
memset( cullInfo.cullBits, 0, tri->numVerts * sizeof( cullInfo.cullBits[0] ) );
|
|
|
|
for( int i = 0; i < 6; i++ )
|
|
{
|
|
// if completely infront of this clipping plane
|
|
if( frontBits & ( 1 << i ) )
|
|
{
|
|
continue;
|
|
}
|
|
for( int j = 0; j < tri->numVerts; j++ )
|
|
{
|
|
float d = cullInfo.localClipPlanes[i].Distance( tri->verts[j].xyz );
|
|
cullInfo.cullBits[j] |= ( d < LIGHT_CLIP_EPSILON ) << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_CreateInteractionLightTris
|
|
|
|
This is only used for the static interaction case, dynamic interactions
|
|
just draw everything and let the GPU deal with it.
|
|
|
|
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_CreateInteractionLightTris( const idRenderEntityLocal* ent,
|
|
const srfTriangles_t* tri, const idRenderLightLocal* light,
|
|
const idMaterial* shader )
|
|
{
|
|
|
|
SCOPED_PROFILE_EVENT( "R_CreateInteractionLightTris" );
|
|
|
|
int i;
|
|
int numIndexes;
|
|
triIndex_t* indexes;
|
|
srfTriangles_t* newTri;
|
|
int c_backfaced;
|
|
int c_distance;
|
|
idBounds bounds;
|
|
bool includeBackFaces;
|
|
int faceNum;
|
|
|
|
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
|
|
srfCullInfo_t cullInfo = {};
|
|
|
|
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
|
|
newTri->indexes = tri->indexes;
|
|
newTri->indexCache = tri->indexCache;
|
|
// 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
|
|
newTri->numIndexes = numIndexes;
|
|
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;
|
|
}
|
|
|
|
// 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
|
|
newTri->numIndexes = numIndexes;
|
|
R_ResizeStaticTriSurfIndexes( newTri, numIndexes );
|
|
}
|
|
|
|
// free the cull information when it's no longer needed
|
|
R_FreeInteractionCullInfo( cullInfo );
|
|
|
|
if( !numIndexes )
|
|
{
|
|
R_FreeStaticTriSurf( newTri );
|
|
return NULL;
|
|
}
|
|
|
|
newTri->numIndexes = numIndexes;
|
|
|
|
newTri->bounds = bounds;
|
|
|
|
return newTri;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_CreateInteractionShadowVolume
|
|
|
|
Note that dangling edges outside the light frustum don't make silhouette planes because
|
|
a triangle outside the light frustum is considered facing and the "fake triangle" on
|
|
the outside of the dangling edge is also set to facing: cullInfo.facing[numFaces] = 1;
|
|
=====================
|
|
*/
|
|
static srfTriangles_t* R_CreateInteractionShadowVolume( const idRenderEntityLocal* ent,
|
|
const srfTriangles_t* tri, const idRenderLightLocal* light )
|
|
{
|
|
SCOPED_PROFILE_EVENT( "R_CreateInteractionShadowVolume" );
|
|
|
|
srfCullInfo_t cullInfo = {};
|
|
|
|
R_CalcInteractionFacing( ent, tri, light, cullInfo );
|
|
R_CalcInteractionCullBits( ent, tri, light, cullInfo );
|
|
|
|
int numFaces = tri->numIndexes / 3;
|
|
int numShadowingFaces = 0;
|
|
const byte* facing = cullInfo.facing;
|
|
|
|
// if all the triangles are inside the light frustum
|
|
if( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT )
|
|
{
|
|
|
|
// count the number of shadowing faces
|
|
for( int i = 0; i < numFaces; i++ )
|
|
{
|
|
numShadowingFaces += facing[i];
|
|
}
|
|
numShadowingFaces = numFaces - numShadowingFaces;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// make all triangles that are outside the light frustum "facing", so they won't cast shadows
|
|
const triIndex_t* indexes = tri->indexes;
|
|
byte* modifyFacing = cullInfo.facing;
|
|
const byte* cullBits = cullInfo.cullBits;
|
|
for( int i = 0, j = 0; i < tri->numIndexes; i += 3, j++ )
|
|
{
|
|
if( !modifyFacing[j] )
|
|
{
|
|
int i1 = indexes[i + 0];
|
|
int i2 = indexes[i + 1];
|
|
int i3 = indexes[i + 2];
|
|
if( cullBits[i1] & cullBits[i2] & cullBits[i3] )
|
|
{
|
|
modifyFacing[j] = 1;
|
|
}
|
|
else
|
|
{
|
|
numShadowingFaces++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !numShadowingFaces )
|
|
{
|
|
// no faces are inside the light frustum and still facing the right way
|
|
R_FreeInteractionCullInfo( cullInfo );
|
|
return NULL;
|
|
}
|
|
|
|
// shadowVerts will be NULL on these surfaces, so the shadowVerts will be taken from the ambient surface
|
|
srfTriangles_t* newTri = R_AllocStaticTriSurf();
|
|
|
|
newTri->numVerts = tri->numVerts * 2;
|
|
|
|
// alloc the max possible size
|
|
R_AllocStaticTriSurfIndexes( newTri, ( numShadowingFaces + tri->numSilEdges ) * 6 );
|
|
triIndex_t* tempIndexes = newTri->indexes;
|
|
triIndex_t* shadowIndexes = newTri->indexes;
|
|
|
|
// create new triangles along sil planes
|
|
const silEdge_t* sil = tri->silEdges;
|
|
for( int i = tri->numSilEdges; i > 0; i--, sil++ )
|
|
{
|
|
|
|
int f1 = facing[sil->p1];
|
|
int f2 = facing[sil->p2];
|
|
|
|
if( !( f1 ^ f2 ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int v1 = sil->v1 << 1;
|
|
int v2 = sil->v2 << 1;
|
|
|
|
// set the two triangle winding orders based on facing
|
|
// without using a poorly-predictable branch
|
|
|
|
shadowIndexes[0] = v1;
|
|
shadowIndexes[1] = v2 ^ f1;
|
|
shadowIndexes[2] = v2 ^ f2;
|
|
shadowIndexes[3] = v1 ^ f2;
|
|
shadowIndexes[4] = v1 ^ f1;
|
|
shadowIndexes[5] = v2 ^ 1;
|
|
|
|
shadowIndexes += 6;
|
|
}
|
|
|
|
int numShadowIndexes = shadowIndexes - tempIndexes;
|
|
|
|
// we aren't bothering to separate front and back caps on these
|
|
newTri->numIndexes = newTri->numShadowIndexesNoFrontCaps = numShadowIndexes + numShadowingFaces * 6;
|
|
newTri->numShadowIndexesNoCaps = numShadowIndexes;
|
|
newTri->shadowCapPlaneBits = SHADOW_CAP_INFINITE;
|
|
|
|
// decrease the size of the memory block to only store the used indexes
|
|
// R_ResizeStaticTriSurfIndexes( newTri, newTri->numIndexes );
|
|
|
|
// these have no effect, because they extend to infinity
|
|
newTri->bounds.Clear();
|
|
|
|
// put some faces on the model and some on the distant projection
|
|
const triIndex_t* indexes = tri->indexes;
|
|
shadowIndexes = newTri->indexes + numShadowIndexes;
|
|
for( int i = 0, j = 0; i < tri->numIndexes; i += 3, j++ )
|
|
{
|
|
if( facing[j] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int i0 = indexes[i + 0] << 1;
|
|
int i1 = indexes[i + 1] << 1;
|
|
int i2 = indexes[i + 2] << 1;
|
|
|
|
shadowIndexes[0] = i2;
|
|
shadowIndexes[1] = i1;
|
|
shadowIndexes[2] = i0;
|
|
shadowIndexes[3] = i0 ^ 1;
|
|
shadowIndexes[4] = i1 ^ 1;
|
|
shadowIndexes[5] = i2 ^ 1;
|
|
|
|
shadowIndexes += 6;
|
|
}
|
|
|
|
R_FreeInteractionCullInfo( cullInfo );
|
|
|
|
return newTri;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInteraction::idInteraction
|
|
===============
|
|
*/
|
|
idInteraction::idInteraction()
|
|
{
|
|
numSurfaces = 0;
|
|
surfaces = NULL;
|
|
entityDef = NULL;
|
|
lightDef = NULL;
|
|
lightNext = NULL;
|
|
lightPrev = NULL;
|
|
entityNext = NULL;
|
|
entityPrev = NULL;
|
|
staticInteraction = false;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInteraction::AllocAndLink
|
|
===============
|
|
*/
|
|
idInteraction* idInteraction::AllocAndLink( idRenderEntityLocal* edef, idRenderLightLocal* ldef )
|
|
{
|
|
if( edef == NULL || ldef == NULL )
|
|
{
|
|
common->Error( "idInteraction::AllocAndLink: NULL parm" );
|
|
return NULL;
|
|
}
|
|
|
|
idRenderWorldLocal* renderWorld = edef->world;
|
|
|
|
idInteraction* interaction = renderWorld->interactionAllocator.Alloc();
|
|
|
|
// link and initialize
|
|
interaction->lightDef = ldef;
|
|
interaction->entityDef = edef;
|
|
|
|
interaction->numSurfaces = -1; // not checked yet
|
|
interaction->surfaces = 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 != NULL )
|
|
{
|
|
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()
|
|
{
|
|
// anything regenerated is no longer an optimized static version
|
|
this->staticInteraction = false;
|
|
|
|
if( this->surfaces != NULL )
|
|
{
|
|
for( int i = 0; i < this->numSurfaces; i++ )
|
|
{
|
|
surfaceInteraction_t& srf = this->surfaces[i];
|
|
Mem_Free( srf.shadowIndexes );
|
|
srf.shadowIndexes = NULL;
|
|
}
|
|
R_StaticFree( this->surfaces );
|
|
this->surfaces = NULL;
|
|
}
|
|
this->numSurfaces = -1;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInteraction::Unlink
|
|
===============
|
|
*/
|
|
void idInteraction::Unlink()
|
|
{
|
|
|
|
// 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()
|
|
{
|
|
// clear the table pointer
|
|
idRenderWorldLocal* renderWorld = this->lightDef->world;
|
|
// RB: added check for NULL
|
|
if( renderWorld->interactionTable != NULL )
|
|
{
|
|
int index = this->lightDef->index * renderWorld->interactionTableWidth + this->entityDef->index;
|
|
if( renderWorld->interactionTable[index] != this && renderWorld->interactionTable[index] != INTERACTION_EMPTY )
|
|
{
|
|
common->Error( "idInteraction::UnlinkAndFree: interactionTable wasn't set" );
|
|
}
|
|
renderWorld->interactionTable[index] = NULL;
|
|
}
|
|
// RB end
|
|
|
|
Unlink();
|
|
|
|
FreeSurfaces();
|
|
|
|
// put it back on the free list
|
|
renderWorld->interactionAllocator.Free( this );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInteraction::MakeEmpty
|
|
|
|
Relinks the interaction at the end of both the light and entity chains
|
|
and adds the INTERACTION_EMPTY marker to the interactionTable.
|
|
|
|
It is necessary to keep the empty interaction so when entities or lights move
|
|
they can set all the interactionTable values to NULL.
|
|
===============
|
|
*/
|
|
void idInteraction::MakeEmpty()
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
// store the special marker in the interaction table
|
|
const int interactionIndex = lightDef->index * entityDef->world->interactionTableWidth + entityDef->index;
|
|
assert( entityDef->world->interactionTable[ interactionIndex ] == this );
|
|
entityDef->world->interactionTable[ interactionIndex ] = INTERACTION_EMPTY;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idInteraction::HasShadows
|
|
===============
|
|
*/
|
|
bool idInteraction::HasShadows() const
|
|
{
|
|
return !entityDef->parms.noShadow && lightDef->LightCastsShadows();
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CreateStaticInteraction
|
|
|
|
Called by idRenderWorldLocal::GenerateAllInteractions
|
|
======================
|
|
*/
|
|
void idInteraction::CreateStaticInteraction()
|
|
{
|
|
// note that it is a static interaction
|
|
staticInteraction = true;
|
|
const idRenderModel* model = entityDef->parms.hModel;
|
|
if( model == NULL || model->NumSurfaces() <= 0 || model->IsDynamicModel() != DM_STATIC )
|
|
{
|
|
MakeEmpty();
|
|
return;
|
|
}
|
|
|
|
const idBounds bounds = model->Bounds( &entityDef->parms );
|
|
|
|
// if it doesn't contact the light frustum, none of the surfaces will
|
|
if( R_CullModelBoundsToLight( lightDef, bounds, entityDef->modelRenderMatrix ) )
|
|
{
|
|
MakeEmpty();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// create slots for each of the model's surfaces
|
|
//
|
|
numSurfaces = model->NumSurfaces();
|
|
surfaces = ( surfaceInteraction_t* )R_ClearedStaticAlloc( sizeof( *surfaces ) * numSurfaces );
|
|
|
|
bool interactionGenerated = false;
|
|
|
|
// check each surface in the model
|
|
for( int c = 0 ; c < model->NumSurfaces() ; c++ )
|
|
{
|
|
const modelSurface_t* surf = model->Surface( c );
|
|
const srfTriangles_t* tri = surf->geometry;
|
|
if( tri == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// determine the shader for this surface, possibly by skinning
|
|
// Note that this will be wrong if customSkin/customShader are
|
|
// changed after map load time without invalidating the interaction!
|
|
const idMaterial* const shader = R_RemapShaderBySkin( surf->shader,
|
|
entityDef->parms.customSkin, entityDef->parms.customShader );
|
|
if( shader == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// try to cull each surface
|
|
if( R_CullModelBoundsToLight( lightDef, tri->bounds, entityDef->modelRenderMatrix ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
surfaceInteraction_t* sint = &surfaces[c];
|
|
|
|
// generate a set of indexes for the lit surfaces, culling away triangles that are
|
|
// not at least partially inside the light
|
|
if( shader->ReceivesLighting() )
|
|
{
|
|
srfTriangles_t* lightTris = R_CreateInteractionLightTris( entityDef, tri, lightDef, shader );
|
|
if( lightTris != NULL )
|
|
{
|
|
// make a static index cache
|
|
sint->numLightTrisIndexes = lightTris->numIndexes;
|
|
sint->lightTrisIndexCache = vertexCache.AllocStaticIndex( lightTris->indexes, ALIGN( lightTris->numIndexes * sizeof( lightTris->indexes[0] ), INDEX_CACHE_ALIGN ) );
|
|
|
|
interactionGenerated = true;
|
|
R_FreeStaticTriSurf( lightTris );
|
|
}
|
|
}
|
|
|
|
// 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_skipPrelightShadows.GetBool() )
|
|
{
|
|
srfTriangles_t* shadowTris = R_CreateInteractionShadowVolume( entityDef, tri, lightDef );
|
|
if( shadowTris != NULL )
|
|
{
|
|
// make a static index cache
|
|
sint->shadowIndexCache = vertexCache.AllocStaticIndex( shadowTris->indexes, ALIGN( shadowTris->numIndexes * sizeof( shadowTris->indexes[0] ), INDEX_CACHE_ALIGN ) );
|
|
sint->numShadowIndexes = shadowTris->numIndexes;
|
|
#if defined( KEEP_INTERACTION_CPU_DATA )
|
|
sint->shadowIndexes = shadowTris->indexes;
|
|
shadowTris->indexes = NULL;
|
|
#endif
|
|
if( shader->Coverage() != MC_OPAQUE )
|
|
{
|
|
// 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->numShadowIndexesNoCaps = shadowTris->numIndexes;
|
|
}
|
|
else
|
|
{
|
|
sint->numShadowIndexesNoCaps = shadowTris->numShadowIndexesNoCaps;
|
|
}
|
|
R_FreeStaticTriSurf( shadowTris );
|
|
}
|
|
interactionGenerated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if none of the surfaces generated anything, don't even bother checking?
|
|
if( !interactionGenerated )
|
|
{
|
|
MakeEmpty();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_ShowInteractionMemory_f
|
|
===================
|
|
*/
|
|
void R_ShowInteractionMemory_f( const idCmdArgs& args )
|
|
{
|
|
int entities = 0;
|
|
int interactions = 0;
|
|
int deferredInteractions = 0;
|
|
int emptyInteractions = 0;
|
|
int lightTris = 0;
|
|
int lightTriIndexes = 0;
|
|
int shadowTris = 0;
|
|
int shadowTriIndexes = 0;
|
|
int maxInteractionsForEntity = 0;
|
|
int maxInteractionsForLight = 0;
|
|
|
|
for( int i = 0; i < tr.primaryWorld->lightDefs.Num(); i++ )
|
|
{
|
|
idRenderLightLocal* light = tr.primaryWorld->lightDefs[i];
|
|
if( light == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
int numInteractionsForLight = 0;
|
|
for( idInteraction* inter = light->firstInteraction; inter != NULL; inter = inter->lightNext )
|
|
{
|
|
if( !inter->IsEmpty() )
|
|
{
|
|
numInteractionsForLight++;
|
|
}
|
|
}
|
|
if( numInteractionsForLight > maxInteractionsForLight )
|
|
{
|
|
maxInteractionsForLight = numInteractionsForLight;
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < tr.primaryWorld->entityDefs.Num(); i++ )
|
|
{
|
|
idRenderEntityLocal* def = tr.primaryWorld->entityDefs[i];
|
|
if( def == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
if( def->firstInteraction == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
entities++;
|
|
|
|
int numInteractionsForEntity = 0;
|
|
for( idInteraction* inter = def->firstInteraction; inter != NULL; inter = inter->entityNext )
|
|
{
|
|
interactions++;
|
|
|
|
if( !inter->IsEmpty() )
|
|
{
|
|
numInteractionsForEntity++;
|
|
}
|
|
|
|
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->numLightTrisIndexes )
|
|
{
|
|
lightTris++;
|
|
lightTriIndexes += srf->numLightTrisIndexes;
|
|
}
|
|
|
|
if( srf->numShadowIndexes )
|
|
{
|
|
shadowTris++;
|
|
shadowTriIndexes += srf->numShadowIndexes;
|
|
}
|
|
}
|
|
}
|
|
if( numInteractionsForEntity > maxInteractionsForEntity )
|
|
{
|
|
maxInteractionsForEntity = numInteractionsForEntity;
|
|
}
|
|
}
|
|
|
|
common->Printf( "%i entities with %i total interactions\n", entities, interactions );
|
|
common->Printf( "%i deferred interactions, %i empty interactions\n", deferredInteractions, emptyInteractions );
|
|
common->Printf( "%5i indexes in %5i light tris\n", lightTriIndexes, lightTris );
|
|
common->Printf( "%5i indexes in %5i shadow tris\n", shadowTriIndexes, shadowTris );
|
|
common->Printf( "%i maxInteractionsForEntity\n", maxInteractionsForEntity );
|
|
common->Printf( "%i maxInteractionsForLight\n", maxInteractionsForLight );
|
|
}
|