diff --git a/neo/doomdll.vcxproj b/neo/doomdll.vcxproj
index d02dff0b..b98fe891 100644
--- a/neo/doomdll.vcxproj
+++ b/neo/doomdll.vcxproj
@@ -298,7 +298,6 @@
-
@@ -1318,7 +1317,6 @@
-
@@ -1352,12 +1350,9 @@
-
-
-
NotUsing
@@ -2240,7 +2235,6 @@
-
diff --git a/neo/doomdll.vcxproj.filters b/neo/doomdll.vcxproj.filters
index bedcff7f..4004d189 100644
--- a/neo/doomdll.vcxproj.filters
+++ b/neo/doomdll.vcxproj.filters
@@ -225,9 +225,6 @@
Renderer
-
- Renderer
-
Renderer
@@ -746,9 +743,6 @@
Renderer
-
- Renderer
-
Renderer
@@ -848,12 +842,6 @@
Renderer
-
- Renderer
-
-
- Renderer
-
Renderer
@@ -863,9 +851,6 @@
Renderer
-
- Renderer
-
Renderer
@@ -1328,9 +1313,6 @@
Tools\Compilers\DMap
-
- Tools\Compilers\DMap
-
Tools\Compilers\DMap
diff --git a/neo/doomdll.vcxproj.user b/neo/doomdll.vcxproj.user
index 4fea10dc..aee7e7f9 100644
--- a/neo/doomdll.vcxproj.user
+++ b/neo/doomdll.vcxproj.user
@@ -17,7 +17,7 @@
WindowsLocalDebugger
+set r_fullscreen 0 +set r_mode 6
D:\projects\Doom3
- +set r_fullscreen 0 +set r_mode 6|
+ +set r_fullscreen 1 +set r_mode 6|+set r_fullscreen 0 +set r_mode 6|
D:\projects\Doom3\Doom3.exe
diff --git a/neo/renderer/Interaction.cpp b/neo/renderer/Interaction.cpp
deleted file mode 100644
index 6b03127e..00000000
--- a/neo/renderer/Interaction.cpp
+++ /dev/null
@@ -1,1308 +0,0 @@
-/*
-===========================================================================
-
-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 .
-
-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 "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(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<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(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 );
-}
diff --git a/neo/renderer/Interaction.h b/neo/renderer/Interaction.h
deleted file mode 100644
index 234e1753..00000000
--- a/neo/renderer/Interaction.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
-===========================================================================
-
-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 .
-
-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.
-
-===========================================================================
-*/
-
-#ifndef __INTERACTION_H__
-#define __INTERACTION_H__
-
-/*
-===============================================================================
-
- Interaction between entityDef surfaces and a lightDef.
-
- Interactions with no lightTris and no shadowTris are still
- valid, because they show that a given entityDef / lightDef
- do not interact, even though they share one or more areas.
-
-===============================================================================
-*/
-
-#define LIGHT_TRIS_DEFERRED ((srfTriangles_t *)-1)
-#define LIGHT_CULL_ALL_FRONT ((byte *)-1)
-#define LIGHT_CLIP_EPSILON 0.1f
-
-
-typedef struct {
- // For each triangle a byte set to 1 if facing the light origin.
- byte * facing;
-
- // For each vertex a byte with the bits [0-5] set if the
- // vertex is at the back side of the corresponding clip plane.
- // If the 'cullBits' pointer equals LIGHT_CULL_ALL_FRONT all
- // vertices are at the front of all the clip planes.
- byte * cullBits;
-
- // Clip planes in surface space used to calculate the cull bits.
- idPlane localClipPlanes[6];
-} srfCullInfo_t;
-
-
-typedef struct {
- // if lightTris == LIGHT_TRIS_DEFERRED, then the calculation of the
- // lightTris has been deferred, and must be done if ambientTris is visible
- srfTriangles_t * lightTris;
-
- // shadow volume triangle surface
- srfTriangles_t * shadowTris;
-
- // so we can check ambientViewCount before adding lightTris, and get
- // at the shared vertex and possibly shadowVertex caches
- srfTriangles_t * ambientTris;
-
- const idMaterial * shader;
-
- int expCulled; // only for the experimental shadow buffer renderer
-
- srfCullInfo_t cullInfo;
-} surfaceInteraction_t;
-
-
-typedef struct areaNumRef_s {
- struct areaNumRef_s * next;
- int areaNum;
-} areaNumRef_t;
-
-
-class idRenderEntityLocal;
-class idRenderLightLocal;
-
-class idInteraction {
-public:
- // this may be 0 if the light and entity do not actually intersect
- // -1 = an untested interaction
- int numSurfaces;
-
- // if there is a whole-entity optimized shadow hull, it will
- // be present as a surfaceInteraction_t with a NULL ambientTris, but
- // possibly having a shader to specify the shadow sorting order
- surfaceInteraction_t * surfaces;
-
- // get space from here, if NULL, it is a pre-generated shadow volume from dmap
- idRenderEntityLocal * entityDef;
- idRenderLightLocal * lightDef;
-
- idInteraction * lightNext; // for lightDef chains
- idInteraction * lightPrev;
- idInteraction * entityNext; // for entityDef chains
- idInteraction * entityPrev;
-
-public:
- idInteraction( void );
-
- // because these are generated and freed each game tic for active elements all
- // over the world, we use a custom pool allocater to avoid memory allocation overhead
- // and fragmentation
- static idInteraction * AllocAndLink( idRenderEntityLocal *edef, idRenderLightLocal *ldef );
-
- // unlinks from the entity and light, frees all surfaceInteractions,
- // and puts it back on the free list
- void UnlinkAndFree( void );
-
- // free the interaction surfaces
- void FreeSurfaces( void );
-
- // makes the interaction empty for when the light and entity do not actually intersect
- // all empty interactions are linked at the end of the light's and entity's interaction list
- void MakeEmpty( void );
-
- // returns true if the interaction is empty
- bool IsEmpty( void ) const { return ( numSurfaces == 0 ); }
-
- // returns true if the interaction is not yet completely created
- bool IsDeferred( void ) const { return ( numSurfaces == -1 ); }
-
- // returns true if the interaction has shadows
- bool HasShadows( void ) const;
-
- // counts up the memory used by all the surfaceInteractions, which
- // will be used to determine when we need to start purging old interactions
- int MemoryUsed( void );
-
- // makes sure all necessary light surfaces and shadow surfaces are created, and
- // calls R_LinkLightSurf() for each one
- void AddActiveInteraction( void );
-
-private:
- enum {
- FRUSTUM_UNINITIALIZED,
- FRUSTUM_INVALID,
- FRUSTUM_VALID,
- FRUSTUM_VALIDAREAS,
- } frustumState;
- idFrustum frustum; // frustum which contains the interaction
- areaNumRef_t * frustumAreas; // numbers of the areas the frustum touches
-
- int dynamicModelFrameCount; // so we can tell if a callback model animated
-
-private:
- // actually create the interaction
- void CreateInteraction( const idRenderModel *model );
-
- // unlink from entity and light lists
- void Unlink( void );
-
- // try to determine if the entire interaction, including shadows, is guaranteed
- // to be outside the view frustum
- bool CullInteractionByViewFrustum( const idFrustum &viewFrustum );
-
- // determine the minimum scissor rect that will include the interaction shadows
- // projected to the bounds of the light
- idScreenRect CalcInteractionScissorRectangle( const idFrustum &viewFrustum );
-};
-
-
-void R_CalcInteractionFacing( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo );
-void R_CalcInteractionCullBits( const idRenderEntityLocal *ent, const srfTriangles_t *tri, const idRenderLightLocal *light, srfCullInfo_t &cullInfo );
-void R_FreeInteractionCullInfo( srfCullInfo_t &cullInfo );
-
-void R_ShowInteractionMemory_f( const idCmdArgs &args );
-
-#endif /* !__INTERACTION_H__ */
diff --git a/neo/renderer/RenderEntity.cpp b/neo/renderer/RenderEntity.cpp
index 87a314fc..0f9abf26 100644
--- a/neo/renderer/RenderEntity.cpp
+++ b/neo/renderer/RenderEntity.cpp
@@ -49,8 +49,6 @@ idRenderEntityLocal::idRenderEntityLocal() {
decals = NULL;
overlay = NULL;
entityRefs = NULL;
- firstInteraction = NULL;
- lastInteraction = NULL;
dxrBottomAccelStruct = 0;
needsPortalSky = false;
}
@@ -108,8 +106,6 @@ idRenderLightLocal::idRenderLightLocal() {
viewLight = NULL;
references = NULL;
foggedPortals = NULL;
- firstInteraction = NULL;
- lastInteraction = NULL;
}
void idRenderLightLocal::FreeRenderLight() {
diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp
index 172f565b..956fa4a6 100644
--- a/neo/renderer/RenderSystem_init.cpp
+++ b/neo/renderer/RenderSystem_init.cpp
@@ -1864,7 +1864,6 @@ void R_InitCommands( void ) {
cmdSystem->AddCommand( "reportSurfaceAreas", R_ReportSurfaceAreas_f, CMD_FL_RENDERER, "lists all used materials sorted by surface area" );
cmdSystem->AddCommand( "reportImageDuplication", R_ReportImageDuplication_f, CMD_FL_RENDERER, "checks all referenced images for duplications" );
cmdSystem->AddCommand( "regenerateWorld", R_RegenerateWorld_f, CMD_FL_RENDERER, "regenerates all interactions" );
- cmdSystem->AddCommand( "showInteractionMemory", R_ShowInteractionMemory_f, CMD_FL_RENDERER, "shows memory used by interactions" );
cmdSystem->AddCommand( "showTriSurfMemory", R_ShowTriSurfMemory_f, CMD_FL_RENDERER, "shows memory used by triangle surfaces" );
cmdSystem->AddCommand( "vid_restart", R_VidRestart_f, CMD_FL_RENDERER, "restarts renderSystem" );
cmdSystem->AddCommand( "listRenderEntityDefs", R_ListRenderEntityDefs_f, CMD_FL_RENDERER, "lists the entity defs" );
diff --git a/neo/renderer/RenderWorld.cpp b/neo/renderer/RenderWorld.cpp
index 14abd50f..8d1fe7ee 100644
--- a/neo/renderer/RenderWorld.cpp
+++ b/neo/renderer/RenderWorld.cpp
@@ -55,13 +55,6 @@ void R_ListRenderLightDefs_f( const idCmdArgs &args ) {
continue;
}
- // count up the interactions
- int iCount = 0;
- for ( idInteraction *inter = ldef->firstInteraction; inter != NULL; inter = inter->lightNext ) {
- iCount++;
- }
- totalIntr += iCount;
-
// count up the references
int rCount = 0;
for ( areaReference_t *ref = ldef->references ; ref ; ref = ref->ownerNext ) {
@@ -69,7 +62,7 @@ void R_ListRenderLightDefs_f( const idCmdArgs &args ) {
}
totalRef += rCount;
- common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, ldef->lightShader->GetName());
+ //common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, ldef->lightShader->GetName());
active++;
}
@@ -99,13 +92,6 @@ void R_ListRenderEntityDefs_f( const idCmdArgs &args ) {
continue;
}
- // count up the interactions
- int iCount = 0;
- for ( idInteraction *inter = mdef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
- iCount++;
- }
- totalIntr += iCount;
-
// count up the references
int rCount = 0;
for ( areaReference_t *ref = mdef->entityRefs ; ref ; ref = ref->ownerNext ) {
@@ -113,7 +99,6 @@ void R_ListRenderEntityDefs_f( const idCmdArgs &args ) {
}
totalRef += rCount;
- common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, mdef->parms.hModel->Name());
active++;
}
@@ -130,8 +115,6 @@ idRenderWorldLocal::idRenderWorldLocal(dxrWorldId_t dxrWorldId) {
mapTimeStamp = FILE_NOT_FOUND_TIMESTAMP;
this->dxrWorldId = tr.dxrWorldHandles[dxrWorldId];
-
- generateAllInteractionsCalled = false;
areaNodes = NULL;
numAreaNodes = 0;
@@ -141,10 +124,6 @@ idRenderWorldLocal::idRenderWorldLocal(dxrWorldId_t dxrWorldId) {
doublePortals = NULL;
numInterAreaPortals = 0;
-
- interactionTable = 0;
- interactionTableWidth = 0;
- interactionTableHeight = 0;
}
/*
@@ -171,8 +150,6 @@ void idRenderWorldLocal::ResizeInteractionTable() {
// we overflowed the interaction table, so dump it
// we may want to resize this in the future if it turns out to be common
common->Printf( "idRenderWorldLocal::ResizeInteractionTable: overflowed interactionTableWidth, dumping\n" );
- R_StaticFree( interactionTable );
- interactionTable = NULL;
}
/*
@@ -185,9 +162,6 @@ qhandle_t idRenderWorldLocal::AddEntityDef( const renderEntity_t *re ){
int entityHandle = entityDefs.FindNull();
if ( entityHandle == -1 ) {
entityHandle = entityDefs.Append( NULL );
- if ( interactionTable && entityDefs.Num() > interactionTableWidth ) {
- ResizeInteractionTable();
- }
}
UpdateEntityDef( entityHandle, re );
@@ -381,9 +355,6 @@ qhandle_t idRenderWorldLocal::AddLightDef( const renderLight_t *rlight ) {
if ( lightHandle == -1 ) {
lightHandle = lightDefs.Append( NULL );
- if ( interactionTable && lightDefs.Num() > interactionTableHeight ) {
- ResizeInteractionTable();
- }
}
UpdateLightDef( lightHandle, rlight );
@@ -1469,8 +1440,6 @@ void idRenderWorldLocal::GenerateAllInteractions() {
int start = Sys_Milliseconds();
- generateAllInteractionsCalled = false;
-
// watch how much memory we allocate
tr.staticAllocCount = 0;
@@ -1490,37 +1459,6 @@ void idRenderWorldLocal::GenerateAllInteractions() {
int msec = end - start;
common->Printf( "idRenderWorld::GenerateAllInteractions, msec = %i, staticAllocCount = %i.\n", msec, tr.staticAllocCount );
-
-
- // build the interaction table
- if ( r_useInteractionTable.GetBool() ) {
- interactionTableWidth = entityDefs.Num() + 100;
- interactionTableHeight = lightDefs.Num() + 100;
- int size = interactionTableWidth * interactionTableHeight * sizeof( *interactionTable );
- interactionTable = (idInteraction **)R_ClearedStaticAlloc( size );
-
- int count = 0;
- for ( int i = 0 ; i < this->lightDefs.Num() ; i++ ) {
- idRenderLightLocal *ldef = this->lightDefs[i];
- if ( !ldef ) {
- continue;
- }
- idInteraction *inter;
- for ( inter = ldef->firstInteraction; inter != NULL; inter = inter->lightNext ) {
- idRenderEntityLocal *edef = inter->entityDef;
- int index = ldef->index * interactionTableWidth + edef->index;
-
- interactionTable[ index ] = inter;
- count++;
- }
- }
-
- common->Printf( "interactionTable size: %i bytes\n", size );
- common->Printf( "%i interaction take %i bytes\n", count, count * sizeof( idInteraction ) );
- }
-
- // entities flagged as noDynamicInteractions will no longer make any
- generateAllInteractionsCalled = true;
}
/*
@@ -1537,10 +1475,7 @@ void idRenderWorldLocal::FreeInteractions() {
if ( !def ) {
continue;
}
- // free all the interactions
- while ( def->firstInteraction != NULL ) {
- def->firstInteraction->UnlinkAndFree();
- }
+
}
}
diff --git a/neo/renderer/RenderWorld_load.cpp b/neo/renderer/RenderWorld_load.cpp
index 5481860a..4449406c 100644
--- a/neo/renderer/RenderWorld_load.cpp
+++ b/neo/renderer/RenderWorld_load.cpp
@@ -98,7 +98,7 @@ void idRenderWorldLocal::FreeWorld() {
localModels.Clear();
areaReferenceAllocator.Shutdown();
- interactionAllocator.Shutdown();
+ //interactionAllocator.Shutdown();
areaNumRefAllocator.Shutdown();
mapName = "";
@@ -454,13 +454,6 @@ dump all the interactions
void idRenderWorldLocal::FreeDefs() {
int i;
- generateAllInteractionsCalled = false;
-
- if ( interactionTable ) {
- R_StaticFree( interactionTable );
- interactionTable = NULL;
- }
-
// free all lightDefs
for ( i = 0 ; i < lightDefs.Num() ; i++ ) {
idRenderLightLocal *light;
diff --git a/neo/renderer/RenderWorld_local.h b/neo/renderer/RenderWorld_local.h
index a202c1b2..9ea4725d 100644
--- a/neo/renderer/RenderWorld_local.h
+++ b/neo/renderer/RenderWorld_local.h
@@ -79,6 +79,11 @@ struct dxrWorldModel_t {
uint32_t topAccelStruct = 0;
};
+typedef struct areaNumRef_s {
+ struct areaNumRef_s* next;
+ int areaNum;
+} areaNumRef_t;
+
class idRenderWorldLocal : public idRenderWorld {
public:
idRenderWorldLocal(dxrWorldId_t dxrWorldId);
@@ -159,21 +164,8 @@ public:
idList lightDefs;
idBlockAlloc areaReferenceAllocator;
- idBlockAlloc interactionAllocator;
idBlockAlloc areaNumRefAllocator;
- // all light / entity interactions are referenced here for fast lookup without
- // having to crawl the doubly linked lists. EnntityDefs are sequential for better
- // cache access, because the table is accessed by light in idRenderWorldLocal::CreateLightDefInteractions()
- // Growing this table is time consuming, so we add a pad value to the number
- // of entityDefs and lightDefs
- idInteraction ** interactionTable;
- int interactionTableWidth; // entityDefs
- int interactionTableHeight; // lightDefs
-
-
- bool generateAllInteractionsCalled;
-
//-----------------------
// RenderWorld_load.cpp
diff --git a/neo/renderer/draw_dx.cpp b/neo/renderer/draw_dx.cpp
index 4221884b..77174791 100644
--- a/neo/renderer/draw_dx.cpp
+++ b/neo/renderer/draw_dx.cpp
@@ -172,17 +172,11 @@ void RB_DXDrawInteractions(void)
continue;
}
- if (!vLight->localInteractions && !vLight->globalInteractions
- && !vLight->translucentInteractions)
- {
- continue;
- }
-
const renderLight_t& srcLight = vLight->lightDef->parms;
const float r = srcLight.shaderParms[SHADERPARM_RED];
const float g = srcLight.shaderParms[SHADERPARM_GREEN];
const float b = srcLight.shaderParms[SHADERPARM_BLUE];
- const float intensity = 1.0f;
+ const float intensity = 2.0f;
glRaytracingLight_t light = {};
bool supported = true;
diff --git a/neo/renderer/tr_light.cpp b/neo/renderer/tr_light.cpp
index 50bcf40b..2afaf490 100644
--- a/neo/renderer/tr_light.cpp
+++ b/neo/renderer/tr_light.cpp
@@ -544,7 +544,6 @@ void idRenderWorldLocal::CreateLightDefInteractions( idRenderLightLocal *ldef )
areaReference_t *lref;
idRenderEntityLocal *edef;
portalArea_t *area;
- idInteraction *inter;
for ( lref = ldef->references ; lref ; lref = lref->ownerNext ) {
area = lref->area;
@@ -576,53 +575,10 @@ void idRenderWorldLocal::CreateLightDefInteractions( idRenderLightLocal *ldef )
// some big outdoor meshes are flagged to not create any dynamic interactions
// when the level designer knows that nearby moving lights shouldn't actually hit them
- if ( edef->parms.noDynamicInteractions && edef->world->generateAllInteractionsCalled ) {
+ if ( edef->parms.noDynamicInteractions ) {
continue;
}
- // if any of the edef's interaction match this light, we don't
- // need to consider it.
- if ( r_useInteractionTable.GetBool() && this->interactionTable ) {
- // allocating these tables may take several megs on big maps, but it saves 3% to 5% of
- // the CPU time. The table is updated at interaction::AllocAndLink() and interaction::UnlinkAndFree()
- int index = ldef->index * this->interactionTableWidth + edef->index;
- inter = this->interactionTable[ index ];
- if ( inter ) {
- // if this entity wasn't in view already, the scissor rect will be empty,
- // so it will only be used for shadow casting
- if ( !inter->IsEmpty() ) {
- R_SetEntityDefViewEntity( edef );
- }
- continue;
- }
- } else {
- // scan the doubly linked lists, which may have several dozen entries
-
- // we could check either model refs or light refs for matches, but it is
- // assumed that there will be less lights in an area than models
- // so the entity chains should be somewhat shorter (they tend to be fairly close).
- for ( inter = edef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
- if ( inter->lightDef == ldef ) {
- break;
- }
- }
-
- // if we already have an interaction, we don't need to do anything
- if ( inter != NULL ) {
- // if this entity wasn't in view already, the scissor rect will be empty,
- // so it will only be used for shadow casting
- if ( !inter->IsEmpty() ) {
- R_SetEntityDefViewEntity( edef );
- }
- continue;
- }
- }
-
- //
- // create a new interaction, but don't do any work other than bbox to frustum culling
- //
- idInteraction *inter = idInteraction::AllocAndLink( edef, ldef );
-
// do a check of the entity reference bounds against the light frustum,
// trying to avoid creating a viewEntity if it hasn't been already
float modelMatrix[16];
@@ -636,7 +592,6 @@ void idRenderWorldLocal::CreateLightDefInteractions( idRenderLightLocal *ldef )
}
if ( R_CullLocalBox( edef->referenceBounds, m, 6, ldef->frustum ) ) {
- inter->MakeEmpty();
continue;
}
@@ -1471,7 +1426,6 @@ two or more lights.
*/
void R_AddModelSurfaces( void ) {
viewEntity_t *vEntity;
- idInteraction *inter, *next;
idRenderModel *model;
// clear the ambient surface list
@@ -1537,35 +1491,6 @@ void R_AddModelSurfaces( void ) {
tr.pc.c_shadowViewEntities++;
}
- //
- // for all the entity / light interactions on this entity, add them to the view
- //
- if ( tr.viewDef->isXraySubview ) {
- if ( vEntity->entityDef->parms.xrayIndex == 2 ) {
- for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
- next = inter->entityNext;
- if ( inter->lightDef->viewCount != tr.viewCount ) {
- continue;
- }
- inter->AddActiveInteraction();
- }
- }
- } else {
- // all empty interactions are at the end of the list so once the
- // first is encountered all the remaining interactions are empty
- for ( inter = vEntity->entityDef->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = next ) {
- next = inter->entityNext;
-
- // skip any lights that aren't currently visible
- // this is run after any lights that are turned off have already
- // been removed from the viewLights list, and had their viewCount cleared
- if ( inter->lightDef->viewCount != tr.viewCount ) {
- continue;
- }
- inter->AddActiveInteraction();
- }
- }
-
if ( vEntity->entityDef->parms.timeGroup ) {
tr.viewDef->floatTime = oldFloatTime;
tr.viewDef->renderView.time = oldTime;
diff --git a/neo/renderer/tr_lightrun.cpp b/neo/renderer/tr_lightrun.cpp
index ed0ee7be..a05fca4f 100644
--- a/neo/renderer/tr_lightrun.cpp
+++ b/neo/renderer/tr_lightrun.cpp
@@ -432,10 +432,6 @@ void R_DeriveLightData( idRenderLightLocal *light ) {
R_FreeLightDefFrustum( light );
light->frustumTris = R_PolytopeSurface( 6, light->frustum, light->frustumWindings );
-
- // a projected light will have one shadowFrustum, a point light will have
- // six unless the light center is outside the box
- R_MakeShadowFrustums( light );
}
/*
@@ -601,11 +597,6 @@ void R_FreeLightDefDerivedData( idRenderLightLocal *ldef ) {
dp->fogLight = NULL;
}
- // free all the interactions
- while ( ldef->firstInteraction != NULL ) {
- ldef->firstInteraction->UnlinkAndFree();
- }
-
// free all the references to the light
for ( lref = ldef->references ; lref ; lref = nextRef ) {
nextRef = lref->ownerNext;
@@ -653,11 +644,6 @@ void R_FreeEntityDefDerivedData( idRenderEntityLocal *def, bool keepDecals, bool
}
}
- // free all the interactions
- while ( def->firstInteraction != NULL ) {
- def->firstInteraction->UnlinkAndFree();
- }
-
// clear the dynamic model if present
if ( def->dynamicModel ) {
def->dynamicModel = NULL;
@@ -697,10 +683,6 @@ R_FreeEntityDefDerivedData
==================
*/
void R_ClearEntityDefDynamicModel( idRenderEntityLocal *def ) {
- // free all the interaction surfaces
- for( idInteraction *inter = def->firstInteraction; inter != NULL && !inter->IsEmpty(); inter = inter->entityNext ) {
- inter->FreeSurfaces();
- }
// clear the dynamic model if present
if ( def->dynamicModel ) {
diff --git a/neo/renderer/tr_local.h b/neo/renderer/tr_local.h
index 349a07a6..ccd5e0f1 100644
--- a/neo/renderer/tr_local.h
+++ b/neo/renderer/tr_local.h
@@ -95,8 +95,6 @@ SURFACES
#include "ModelDecal.h"
#include "ModelOverlay.h"
-#include "Interaction.h"
-
// drawSurf_t structures command the back end to render surfaces
// a given srfTriangles_t may be used with multiple viewEntity_t,
@@ -142,8 +140,8 @@ typedef struct areaReference_s {
struct areaReference_s *areaNext; // chain in the area
struct areaReference_s *areaPrev;
struct areaReference_s *ownerNext; // chain on either the entityDef or lightDef
- idRenderEntityLocal * entity; // only one of entity / light will be non-NULL
- idRenderLightLocal * light; // only one of entity / light will be non-NULL
+ class idRenderEntityLocal * entity; // only one of entity / light will be non-NULL
+ class idRenderLightLocal * light; // only one of entity / light will be non-NULL
struct portalArea_s * area; // so owners can find all the areas they are in
} areaReference_t;
@@ -227,8 +225,6 @@ public:
struct viewLight_s * viewLight;
areaReference_t * references; // each area the light is present in will have a lightRef
- idInteraction * firstInteraction; // doubly linked list
- idInteraction * lastInteraction;
struct doublePortal_s * foggedPortals;
};
@@ -283,8 +279,6 @@ public:
idRenderModelOverlay * overlay; // blood overlays on animated models
areaReference_t * entityRefs; // chain of all references
- idInteraction * firstInteraction; // doubly linked list
- idInteraction * lastInteraction;
bool needsPortalSky;
@@ -1387,79 +1381,6 @@ typedef enum {
PP_LIGHT_FALLOFF_TQ = 20 // only for NV programs
} programParameter_t;
-
-/*
-============================================================
-
-TR_STENCILSHADOWS
-
-"facing" should have one more element than tri->numIndexes / 3, which should be set to 1
-
-============================================================
-*/
-
-void R_MakeShadowFrustums( idRenderLightLocal *def );
-
-typedef enum {
- SG_DYNAMIC, // use infinite projections
- SG_STATIC, // clip to bounds
- SG_OFFLINE // perform very time consuming optimizations
-} shadowGen_t;
-
-srfTriangles_t *R_CreateShadowVolume( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- shadowGen_t optimize, srfCullInfo_t &cullInfo );
-
-/*
-============================================================
-
-TR_TURBOSHADOW
-
-Fast, non-clipped overshoot shadow volumes
-
-"facing" should have one more element than tri->numIndexes / 3, which should be set to 1
-calling this function may modify "facing" based on culling
-
-============================================================
-*/
-
-srfTriangles_t *R_CreateVertexProgramTurboShadowVolume( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- srfCullInfo_t &cullInfo );
-
-srfTriangles_t *R_CreateTurboShadowVolume( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- srfCullInfo_t &cullInfo );
-
-/*
-============================================================
-
-util/shadowopt3
-
-dmap time optimization of shadow volumes, called from R_CreateShadowVolume
-
-============================================================
-*/
-
-
-typedef struct {
- idVec3 *verts; // includes both front and back projections, caller should free
- int numVerts;
- glIndex_t *indexes; // caller should free
-
- // indexes must be sorted frontCap, rearCap, silPlanes so the caps can be removed
- // when the viewer is in a position that they don't need to see them
- int numFrontCapIndexes;
- int numRearCapIndexes;
- int numSilPlaneIndexes;
- int totalIndexes;
-} optimizedShadow_t;
-
-optimizedShadow_t SuperOptimizeOccluders( idVec4 *verts, glIndex_t *indexes, int numIndexes,
- idPlane projectionPlane, idVec3 projectionOrigin );
-
-void CleanupOptimizedShadowTris( srfTriangles_t *tri );
-
/*
============================================================
diff --git a/neo/renderer/tr_shadowbounds.cpp b/neo/renderer/tr_shadowbounds.cpp
deleted file mode 100644
index c9ca8ab2..00000000
--- a/neo/renderer/tr_shadowbounds.cpp
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
-===========================================================================
-
-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 .
-
-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 "precompiled.h"
-#pragma hdrstop
-
-#include "tr_local.h"
-
-
-
-// Compute conservative shadow bounds as the intersection
-// of the object's bounds' shadow volume and the light's bounds.
-//
-// --cass
-
-
-template
-struct MyArray
-{
- MyArray() : s(0) {}
-
- MyArray( const MyArray & cpy ) : s(cpy.s)
- {
- for(int i=0; i < s; i++)
- v[i] = cpy.v[i];
- }
-
- void push_back(const T & i) {
- v[s] = i;
- s++;
- //if(s > max_size)
- // max_size = int(s);
- }
-
- T & operator[](int i) {
- return v[i];
- }
-
- const T & operator[](int i) const {
- return v[i];
- }
-
- unsigned int size() const {
- return s;
- }
-
- void empty() {
- s = 0;
- }
-
- T v[N];
- int s;
-// static int max_size;
-};
-
-typedef MyArray MyArrayInt;
-//int MyArrayInt::max_size = 0;
-typedef MyArray MyArrayVec4;
-//int MyArrayVec4::max_size = 0;
-
-struct poly
-{
- MyArrayInt vi;
- MyArrayInt ni;
- idVec4 plane;
-};
-
-typedef MyArray MyArrayPoly;
-//int MyArrayPoly::max_size = 0;
-
-struct edge
-{
- int vi[2];
- int pi[2];
-};
-
-typedef MyArray MyArrayEdge;
-//int MyArrayEdge::max_size = 0;
-
-MyArrayInt four_ints(int a, int b, int c, int d)
-{
- MyArrayInt vi;
- vi.push_back(a);
- vi.push_back(b);
- vi.push_back(c);
- vi.push_back(d);
- return vi;
-}
-
-idVec3 homogeneous_difference(idVec4 a, idVec4 b)
-{
- idVec3 v;
- v.x = b.x * a.w - a.x * b.w;
- v.y = b.y * a.w - a.y * b.w;
- v.z = b.z * a.w - a.z * b.w;
- return v;
-}
-
-// handles positive w only
-idVec4 compute_homogeneous_plane(idVec4 a, idVec4 b, idVec4 c)
-{
- idVec4 v, t;
-
- if(a[3] == 0)
- { t = a; a = b; b = c; c = t; }
- if(a[3] == 0)
- { t = a; a = b; b = c; c = t; }
-
- // can't handle 3 infinite points
- if( a[3] == 0 )
- return v;
-
- idVec3 vb = homogeneous_difference(a, b);
- idVec3 vc = homogeneous_difference(a, c);
-
- idVec3 n = vb.Cross(vc);
- n.Normalize();
-
- v.x = n.x;
- v.y = n.y;
- v.z = n.z;
-
- v.w = - (n * idVec3(a.x, a.y, a.z)) / a.w ;
-
- return v;
-}
-
-struct polyhedron
-{
- MyArrayVec4 v;
- MyArrayPoly p;
- MyArrayEdge e;
-
- void add_quad( int va, int vb, int vc, int vd )
- {
- poly pg;
- pg.vi = four_ints(va, vb, vc, vd);
- pg.ni = four_ints(-1, -1, -1, -1);
- pg.plane = compute_homogeneous_plane(v[va], v[vb], v[vc]);
- p.push_back(pg);
- }
-
- void discard_neighbor_info()
- {
- for(unsigned int i = 0; i < p.size(); i++ )
- {
- MyArrayInt & ni = p[i].ni;
- for(unsigned int j = 0; j < ni.size(); j++)
- ni[j] = -1;
- }
- }
-
- void compute_neighbors()
- {
- e.empty();
-
- discard_neighbor_info();
-
- bool found;
- int P = p.size();
- // for each polygon
- for(int i = 0; i < P-1; i++ )
- {
- const MyArrayInt & vi = p[i].vi;
- MyArrayInt & ni = p[i].ni;
- int Si = vi.size();
-
- // for each edge of that polygon
- for(int ii=0; ii < Si; ii++)
- {
- int ii0 = ii;
- int ii1 = (ii+1) % Si;
-
- // continue if we've already found this neighbor
- if(ni[ii] != -1)
- continue;
- found = false;
- // check all remaining polygons
- for(int j = i+1; j < P; j++ )
- {
- const MyArrayInt & vj = p[j].vi;
- MyArrayInt & nj = p[j].ni;
- int Sj = vj.size();
-
- for( int jj = 0; jj < Sj; jj++ )
- {
- int jj0 = jj;
- int jj1 = (jj+1) % Sj;
- if(vi[ii0] == vj[jj1] && vi[ii1] == vj[jj0])
- {
- edge ed;
- ed.vi[0] = vi[ii0];
- ed.vi[1] = vi[ii1];
- ed.pi[0] = i;
- ed.pi[1] = j;
- e.push_back(ed);
- ni[ii] = j;
- nj[jj] = i;
- found = true;
- break;
- }
- else if ( vi[ii0] == vj[jj0] && vi[ii1] == vj[jj1] )
- {
- fprintf(stderr,"why am I here?\n");
- }
- }
- if( found )
- break;
- }
- }
- }
- }
-
- void recompute_planes()
- {
- // for each polygon
- for(unsigned int i = 0; i < p.size(); i++ )
- {
- p[i].plane = compute_homogeneous_plane(v[p[i].vi[0]], v[p[i].vi[1]], v[p[i].vi[2]]);
- }
- }
-
- void transform(const idMat4 & m)
- {
- for(unsigned int i=0; i < v.size(); i++ )
- v[i] = m * v[i];
- recompute_planes();
- }
-
-};
-
-// make a unit cube
-polyhedron PolyhedronFromBounds( const idBounds & b )
-{
-
-// 3----------2
-// |\ /|
-// | \ / |
-// | 7--6 |
-// | | | |
-// | 4--5 |
-// | / \ |
-// | / \ |
-// 0----------1
-//
-
- static polyhedron p;
-
- if( p.e.size() == 0 ) {
-
- p.v.push_back(idVec4( -1, -1, 1, 1));
- p.v.push_back(idVec4( 1, -1, 1, 1));
- p.v.push_back(idVec4( 1, 1, 1, 1));
- p.v.push_back(idVec4( -1, 1, 1, 1));
- p.v.push_back(idVec4( -1, -1, -1, 1));
- p.v.push_back(idVec4( 1, -1, -1, 1));
- p.v.push_back(idVec4( 1, 1, -1, 1));
- p.v.push_back(idVec4( -1, 1, -1, 1));
-
- p.add_quad( 0, 1, 2, 3 );
- p.add_quad( 7, 6, 5, 4 );
- p.add_quad( 1, 0, 4, 5 );
- p.add_quad( 2, 1, 5, 6 );
- p.add_quad( 3, 2, 6, 7 );
- p.add_quad( 0, 3, 7, 4 );
-
- p.compute_neighbors();
- p.recompute_planes();
- p.v.empty(); // no need to copy this data since it'll be replaced
- }
-
- polyhedron p2(p);
-
- const idVec3 & min = b[0];
- const idVec3 & max = b[1];
-
- p2.v.empty();
- p2.v.push_back(idVec4( min.x, min.y, max.z, 1));
- p2.v.push_back(idVec4( max.x, min.y, max.z, 1));
- p2.v.push_back(idVec4( max.x, max.y, max.z, 1));
- p2.v.push_back(idVec4( min.x, max.y, max.z, 1));
- p2.v.push_back(idVec4( min.x, min.y, min.z, 1));
- p2.v.push_back(idVec4( max.x, min.y, min.z, 1));
- p2.v.push_back(idVec4( max.x, max.y, min.z, 1));
- p2.v.push_back(idVec4( min.x, max.y, min.z, 1));
-
- p2.recompute_planes();
- return p2;
-}
-
-
-polyhedron make_sv(const polyhedron & oc, idVec4 light)
-{
- static polyhedron lut[64];
- int index = 0;
-
- for(unsigned int i = 0; i < 6; i++) {
- if( ( oc.p[i].plane * light ) > 0 )
- index |= 1< 0)
- {
- ph.p.push_back(oc.p[i]);
- }
- }
-
- if(ph.p.size() == 0)
- return ph = polyhedron();
-
- ph.compute_neighbors();
-
- MyArrayPoly vpg;
- int I = ph.p.size();
-
- for(int i=0; i < I; i++)
- {
- MyArrayInt & vi = ph.p[i].vi;
- MyArrayInt & ni = ph.p[i].ni;
- int S = vi.size();
-
- for(int j = 0; j < S; j++)
- {
- if( ni[j] == -1 )
- {
- poly pg;
- int a = vi[(j+1)%S];
- int b = vi[j];
- pg.vi = four_ints( a, b, b+V, a+V);
- pg.ni = four_ints(-1, -1, -1, -1);
- vpg.push_back(pg);
- }
- }
- }
- for(unsigned int i = 0; i < vpg.size(); i++)
- ph.p.push_back(vpg[i]);
-
- ph.compute_neighbors();
- ph.v.empty(); // no need to copy this data since it'll be replaced
- }
-
- polyhedron ph2 = lut[index];
-
- // initalize vertices
- ph2.v = oc.v;
- int V = ph2.v.size();
- for( int j = 0; j < V; j++ )
- {
- idVec3 proj = homogeneous_difference( light, ph2.v[j] );
- ph2.v.push_back( idVec4(proj.x, proj.y, proj.z, 0) );
- }
-
- // need to compute planes for the shadow volume (sv)
- ph2.recompute_planes();
-
- return ph2;
-}
-
-typedef MyArray MySegments;
-//int MySegments::max_size = 0;
-
-void polyhedron_edges(polyhedron & a, MySegments & e)
-{
- e.empty();
- if(a.e.size() == 0 && a.p.size() != 0)
- a.compute_neighbors();
-
- for(unsigned int i = 0; i < a.e.size(); i++)
- {
- e.push_back(a.v[a.e[i].vi[0]]);
- e.push_back(a.v[a.e[i].vi[1]]);
- }
-
-}
-
-// clip the segments of e by the planes of polyhedron a.
-void clip_segments(const polyhedron & ph, MySegments & is, MySegments & os)
-{
- const MyArrayPoly & p = ph.p;
-
- for(unsigned int i = 0; i < is.size(); i+=2 )
- {
- idVec4 a = is[i ];
- idVec4 b = is[i+1];
- idVec4 c;
-
- bool discard = false;
-
- for(unsigned int j = 0; j < p.size(); j++ )
- {
- float da = a * p[j].plane;
- float db = b * p[j].plane;
- float rdw = 1/(da - db);
-
- int code = 0;
- if( da > 0 )
- code = 2;
- if( db > 0 )
- code |= 1;
-
-
- switch ( code )
- {
- case 3:
- discard = true;
- break;
-
- case 2:
- c = -db * rdw * a + da * rdw * b;
- a = c;
- break;
-
- case 1:
- c = -db * rdw * a + da * rdw * b;
- b = c;
- break;
-
- case 0:
- break;
-
- default:
- common->Printf("bad clip code!\n");
- break;
- }
-
- if( discard )
- break;
- }
-
- if( ! discard )
- {
- os.push_back(a);
- os.push_back(b);
- }
- }
-
-}
-
-idMat4 make_idMat4(const float * m)
-{
- return idMat4( m[ 0], m[ 4], m[ 8], m[12],
- m[ 1], m[ 5], m[ 9], m[13],
- m[ 2], m[ 6], m[10], m[14],
- m[ 3], m[ 7], m[11], m[15] );
-}
-
-idVec3 v4to3(const idVec4 & v)
-{
- return idVec3(v.x/v.w, v.y/v.w, v.z/v.w);
-}
-
-void draw_polyhedron( const viewDef_t *viewDef, const polyhedron & p, idVec4 color )
-{
- for(unsigned int i = 0; i < p.e.size(); i++)
- {
- viewDef->renderWorld->DebugLine( color, v4to3(p.v[p.e[i].vi[0]]), v4to3(p.v[p.e[i].vi[1]]));
- }
-}
-
-void draw_segments( const viewDef_t *viewDef, const MySegments & s, idVec4 color )
-{
- for(unsigned int i = 0; i < s.size(); i+=2)
- {
- viewDef->renderWorld->DebugLine( color, v4to3(s[i]), v4to3(s[i+1]));
- }
-}
-
-void world_to_hclip( const viewDef_t *viewDef, const idVec4 &global, idVec4 &clip ) {
- int i;
- idVec4 view;
-
- for ( i = 0 ; i < 4 ; i ++ ) {
- view[i] =
- global[0] * viewDef->worldSpace.modelViewMatrix[ i + 0 * 4 ] +
- global[1] * viewDef->worldSpace.modelViewMatrix[ i + 1 * 4 ] +
- global[2] * viewDef->worldSpace.modelViewMatrix[ i + 2 * 4 ] +
- global[3] * viewDef->worldSpace.modelViewMatrix[ i + 3 * 4 ];
- }
-
-
- for ( i = 0 ; i < 4 ; i ++ ) {
- clip[i] =
- view[0] * viewDef->projectionMatrix[ i + 0 * 4 ] +
- view[1] * viewDef->projectionMatrix[ i + 1 * 4 ] +
- view[2] * viewDef->projectionMatrix[ i + 2 * 4 ] +
- view[3] * viewDef->projectionMatrix[ i + 3 * 4 ];
- }
-}
-
-idScreenRect R_CalcIntersectionScissor( const idRenderLightLocal * lightDef,
- const idRenderEntityLocal * entityDef,
- const viewDef_t * viewDef ) {
-
- idMat4 omodel = make_idMat4( entityDef->modelMatrix );
- idMat4 lmodel = make_idMat4( lightDef->modelMatrix );
-
- // compute light polyhedron
- polyhedron lvol = PolyhedronFromBounds( lightDef->frustumTris->bounds );
- // transform it into world space
- //lvol.transform( lmodel );
-
- // debug //
- if ( r_useInteractionScissors.GetInteger() == -2 ) {
- draw_polyhedron( viewDef, lvol, colorRed );
- }
-
- // compute object polyhedron
- polyhedron vol = PolyhedronFromBounds( entityDef->referenceBounds );
-
- //viewDef->renderWorld->DebugBounds( colorRed, lightDef->frustumTris->bounds );
- //viewDef->renderWorld->DebugBox( colorBlue, idBox( model->Bounds(), entityDef->parms.origin, entityDef->parms.axis ) );
-
- // transform it into world space
- vol.transform( omodel );
-
- // debug //
- if ( r_useInteractionScissors.GetInteger() == -2 ) {
- draw_polyhedron( viewDef, vol, colorBlue );
- }
-
- // transform light position into world space
- idVec4 lightpos = idVec4(lightDef->globalLightOrigin.x,
- lightDef->globalLightOrigin.y,
- lightDef->globalLightOrigin.z,
- 1.0f );
-
- // generate shadow volume "polyhedron"
- polyhedron sv = make_sv(vol, lightpos);
-
- MySegments in_segs, out_segs;
-
- // get shadow volume edges
- polyhedron_edges(sv, in_segs);
- // clip them against light bounds planes
- clip_segments(lvol, in_segs, out_segs);
-
- // get light bounds edges
- polyhedron_edges(lvol, in_segs);
- // clip them by the shadow volume
- clip_segments(sv, in_segs, out_segs);
-
- // debug //
- if ( r_useInteractionScissors.GetInteger() == -2 ) {
- draw_segments( viewDef, out_segs, colorGreen );
- }
-
- idBounds outbounds;
- outbounds.Clear();
- for( unsigned int i = 0; i < out_segs.size(); i++ ) {
-
- idVec4 v;
- world_to_hclip( viewDef, out_segs[i], v );
-
- if( v.w <= 0.0f ) {
- return lightDef->viewLight->scissorRect;
- }
-
- idVec3 rv(v.x, v.y, v.z);
- rv /= v.w;
-
- outbounds.AddPoint( rv );
- }
-
- // limit the bounds to avoid an inside out scissor rectangle due to floating point to short conversion
- if ( outbounds[0].x < -1.0f ) {
- outbounds[0].x = -1.0f;
- }
- if ( outbounds[1].x > 1.0f ) {
- outbounds[1].x = 1.0f;
- }
- if ( outbounds[0].y < -1.0f ) {
- outbounds[0].y = -1.0f;
- }
- if ( outbounds[1].y > 1.0f ) {
- outbounds[1].y = 1.0f;
- }
-
- float w2 = ( viewDef->viewport.x2 - viewDef->viewport.x1 + 1 ) / 2.0f;
- float x = viewDef->viewport.x1;
- float h2 = ( viewDef->viewport.y2 - viewDef->viewport.y1 + 1 ) / 2.0f;
- float y = viewDef->viewport.y1;
-
- idScreenRect rect;
- rect.x1 = outbounds[0].x * w2 + w2 + x;
- rect.x2 = outbounds[1].x * w2 + w2 + x;
- rect.y1 = outbounds[0].y * h2 + h2 + y;
- rect.y2 = outbounds[1].y * h2 + h2 + y;
- rect.Expand();
-
- rect.Intersect( lightDef->viewLight->scissorRect );
-
- // debug //
- if ( r_useInteractionScissors.GetInteger() == -2 && !rect.IsEmpty() ) {
- viewDef->renderWorld->DebugScreenRect( colorYellow, rect, viewDef );
- }
-
- return rect;
-}
diff --git a/neo/renderer/tr_stencilshadow.cpp b/neo/renderer/tr_stencilshadow.cpp
deleted file mode 100644
index 1bbe9d58..00000000
--- a/neo/renderer/tr_stencilshadow.cpp
+++ /dev/null
@@ -1,1395 +0,0 @@
-/*
-===========================================================================
-
-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 .
-
-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 "precompiled.h"
-#pragma hdrstop
-
-#include "tr_local.h"
-
-// tr_stencilShadow.c -- creaton of stencil shadow volumes
-
-/*
-
- Should we split shadow volume surfaces when they exceed max verts
- or max indexes?
-
- a problem is that the number of vertexes needed for the
- shadow volume will be twice the number in the original,
- and possibly up to 8/3 when near plane clipped.
-
- The maximum index count is 7x when not clipped and all
- triangles are completely discrete. Near plane clipping
- can increase this to 10x.
-
- The maximum expansions are always with discrete triangles.
- Meshes of triangles will result in less index expansion because
- there will be less silhouette edges, although it will always be
- greater than the source if a cap is present.
-
- can't just project onto a plane if some surface points are
- behind the light.
-
- The cases when a face is edge on to a light is robustly handled
- with closed volumes, because only a single one of it's neighbors
- will pass the edge test. It may be an issue with non-closed models.
-
- It is crucial that the shadow volumes be completely enclosed.
- The triangles identified as shadow sources will be projected
- directly onto the light far plane.
- The sil edges must be handled carefully.
- A partially clipped explicit sil edge will still generate a sil
- edge.
- EVERY new edge generated by clipping the triangles to the view
- will generate a sil edge.
-
- If a triangle has no points inside the frustum, it is completely
- culled away. If a sil edge is either in or on the frustum, it is
- added.
- If a triangle has no points outside the frustum, it does not
- need to be clipped.
-
-
-
- USING THE STENCIL BUFFER FOR SHADOWING
-
- basic triangle property
-
- view plane inside shadow volume problem
-
- quad triangulation issue
-
- issues with silhouette optimizations
-
- the shapes of shadow projections are poor for sphere or box culling
-
- the gouraud shading problem
-
-
- // epsilon culling rules:
-
-// the positive side of the frustum is inside
-d = tri->verts[i].xyz * frustum[j].Normal() + frustum[j][3];
-if ( d < LIGHT_CLIP_EPSILON ) {
- pointCull[i] |= ( 1 << j );
-}
-if ( d > -LIGHT_CLIP_EPSILON ) {
- pointCull[i] |= ( 1 << (6+j) );
-}
-
-If a low order bit is set, the point is on or outside the plane
-If a high order bit is set, the point is on or inside the plane
-If a low order bit is clear, the point is inside the plane (definately positive)
-If a high order bit is clear, the point is outside the plane (definately negative)
-
-
-*/
-
-#define TRIANGLE_CULLED(p1,p2,p3) ( pointCull[p1] & pointCull[p2] & pointCull[p3] & 0x3f )
-
-//#define TRIANGLE_CLIPPED(p1,p2,p3) ( ( pointCull[p1] | pointCull[p2] | pointCull[p3] ) & 0xfc0 )
-#define TRIANGLE_CLIPPED(p1,p2,p3) ( ( ( pointCull[p1] & pointCull[p2] & pointCull[p3] ) & 0xfc0 ) != 0xfc0 )
-
-// an edge that is on the plane is NOT culled
-#define EDGE_CULLED(p1,p2) ( ( pointCull[p1] ^ 0xfc0 ) & ( pointCull[p2] ^ 0xfc0 ) & 0xfc0 )
-
-#define EDGE_CLIPPED(p1,p2) ( ( pointCull[p1] & pointCull[p2] & 0xfc0 ) != 0xfc0 )
-
-// a point that is on the plane is NOT culled
-//#define POINT_CULLED(p1) ( ( pointCull[p1] ^ 0xfc0 ) & 0xfc0 )
-#define POINT_CULLED(p1) ( ( pointCull[p1] & 0xfc0 ) != 0xfc0 )
-
-//#define LIGHT_CLIP_EPSILON 0.001f
-#define LIGHT_CLIP_EPSILON 0.1f
-
-#define MAX_CLIP_SIL_EDGES 2048
-static int numClipSilEdges;
-static int clipSilEdges[MAX_CLIP_SIL_EDGES][2];
-
-// facing will be 0 if forward facing, 1 if backwards facing
-// grabbed with alloca
-static byte *globalFacing;
-
-// faceCastsShadow will be 1 if the face is in the projection
-// and facing the apropriate direction
-static byte *faceCastsShadow;
-
-static int *remap;
-
-#define MAX_SHADOW_INDEXES 0x18000
-#define MAX_SHADOW_VERTS 0x18000
-static int numShadowIndexes;
-static glIndex_t shadowIndexes[MAX_SHADOW_INDEXES];
-static int numShadowVerts;
-static idVec4 shadowVerts[MAX_SHADOW_VERTS];
-static bool overflowed;
-
-idPlane pointLightFrustums[6][6] = {
- {
- idPlane( 1,0,0,0 ),
- idPlane( 1,1,0,0 ),
- idPlane( 1,-1,0,0 ),
- idPlane( 1,0,1,0 ),
- idPlane( 1,0,-1,0 ),
- idPlane( -1,0,0,0 ),
- },
- {
- idPlane( -1,0,0,0 ),
- idPlane( -1,1,0,0 ),
- idPlane( -1,-1,0,0 ),
- idPlane( -1,0,1,0 ),
- idPlane( -1,0,-1,0 ),
- idPlane( 1,0,0,0 ),
- },
-
- {
- idPlane( 0,1,0,0 ),
- idPlane( 0,1,1,0 ),
- idPlane( 0,1,-1,0 ),
- idPlane( 1,1,0,0 ),
- idPlane( -1,1,0,0 ),
- idPlane( 0,-1,0,0 ),
- },
- {
- idPlane( 0,-1,0,0 ),
- idPlane( 0,-1,1,0 ),
- idPlane( 0,-1,-1,0 ),
- idPlane( 1,-1,0,0 ),
- idPlane( -1,-1,0,0 ),
- idPlane( 0,1,0,0 ),
- },
-
- {
- idPlane( 0,0,1,0 ),
- idPlane( 1,0,1,0 ),
- idPlane( -1,0,1,0 ),
- idPlane( 0,1,1,0 ),
- idPlane( 0,-1,1,0 ),
- idPlane( 0,0,-1,0 ),
- },
- {
- idPlane( 0,0,-1,0 ),
- idPlane( 1,0,-1,0 ),
- idPlane( -1,0,-1,0 ),
- idPlane( 0,1,-1,0 ),
- idPlane( 0,-1,-1,0 ),
- idPlane( 0,0,1,0 ),
- },
-};
-
-int c_caps, c_sils;
-
-static bool callOptimizer; // call the preprocessor optimizer after clipping occluders
-
-typedef struct {
- int frontCapStart;
- int rearCapStart;
- int silStart;
- int end;
-} indexRef_t;
-static indexRef_t indexRef[6];
-static int indexFrustumNumber; // which shadow generating side of a light the indexRef is for
-
-/*
-===============
-PointsOrdered
-
-To make sure the triangulations of the sil edges is consistant,
-we need to be able to order two points. We don't care about how
-they compare with any other points, just that when the same two
-points are passed in (in either order), they will always specify
-the same one as leading.
-
-Currently we need to have separate faces in different surfaces
-order the same way, so we must look at the actual coordinates.
-If surfaces are ever guaranteed to not have to edge match with
-other surfaces, we could just compare indexes.
-===============
-*/
-static bool PointsOrdered( const idVec3 &a, const idVec3 &b ) {
- float i, j;
-
- // vectors that wind up getting an equal hash value will
- // potentially cause a misorder, which can show as a couple
- // crack pixels in a shadow
-
- // scale by some odd numbers so -8, 8, 8 will not be equal
- // to 8, -8, 8
-
- // in the very rare case that these might be equal, all that would
- // happen is an oportunity for a tiny rasterization shadow crack
- i = a[0] + a[1]*127 + a[2]*1023;
- j = b[0] + b[1]*127 + b[2]*1023;
-
- return (bool)(i < j);
-}
-
-/*
-====================
-R_LightProjectionMatrix
-
-====================
-*/
-void R_LightProjectionMatrix( const idVec3 &origin, const idPlane &rearPlane, idVec4 mat[4] ) {
- idVec4 lv;
- float lg;
-
- // calculate the homogenious light vector
- lv.x = origin.x;
- lv.y = origin.y;
- lv.z = origin.z;
- lv.w = 1;
-
- lg = rearPlane.ToVec4() * lv;
-
- // outer product
- mat[0][0] = lg -rearPlane[0] * lv[0];
- mat[0][1] = -rearPlane[1] * lv[0];
- mat[0][2] = -rearPlane[2] * lv[0];
- mat[0][3] = -rearPlane[3] * lv[0];
-
- mat[1][0] = -rearPlane[0] * lv[1];
- mat[1][1] = lg -rearPlane[1] * lv[1];
- mat[1][2] = -rearPlane[2] * lv[1];
- mat[1][3] = -rearPlane[3] * lv[1];
-
- mat[2][0] = -rearPlane[0] * lv[2];
- mat[2][1] = -rearPlane[1] * lv[2];
- mat[2][2] = lg -rearPlane[2] * lv[2];
- mat[2][3] = -rearPlane[3] * lv[2];
-
- mat[3][0] = -rearPlane[0] * lv[3];
- mat[3][1] = -rearPlane[1] * lv[3];
- mat[3][2] = -rearPlane[2] * lv[3];
- mat[3][3] = lg -rearPlane[3] * lv[3];
-}
-
-/*
-===================
-R_ProjectPointsToFarPlane
-
-make a projected copy of the even verts into the odd spots
-that is on the far light clip plane
-===================
-*/
-static void R_ProjectPointsToFarPlane( const idRenderEntityLocal *ent, const idRenderLightLocal *light,
- const idPlane &lightPlaneLocal,
- int firstShadowVert, int numShadowVerts ) {
- idVec3 lv;
- idVec4 mat[4];
- int i;
- idVec4 *in;
-
- R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, lv );
- R_LightProjectionMatrix( lv, lightPlaneLocal, mat );
-
-#if 1
- // make a projected copy of the even verts into the odd spots
- in = &shadowVerts[firstShadowVert];
- for ( i = firstShadowVert ; i < numShadowVerts ; i+= 2, in += 2 ) {
- float w, oow;
-
- in[0].w = 1;
-
- w = in->ToVec3() * mat[3].ToVec3() + mat[3][3];
- if ( w == 0 ) {
- in[1] = in[0];
- continue;
- }
-
- oow = 1.0 / w;
- in[1].x = ( in->ToVec3() * mat[0].ToVec3() + mat[0][3] ) * oow;
- in[1].y = ( in->ToVec3() * mat[1].ToVec3() + mat[1][3] ) * oow;
- in[1].z = ( in->ToVec3() * mat[2].ToVec3() + mat[2][3] ) * oow;
- in[1].w = 1;
- }
-
-#else
- // messing with W seems to cause some depth precision problems
-
- // make a projected copy of the even verts into the odd spots
- in = &shadowVerts[firstShadowVert];
- for ( i = firstShadowVert ; i < numShadowVerts ; i+= 2, in += 2 ) {
- in[0].w = 1;
- in[1].x = *in * mat[0].ToVec3() + mat[0][3];
- in[1].y = *in * mat[1].ToVec3() + mat[1][3];
- in[1].z = *in * mat[2].ToVec3() + mat[2][3];
- in[1].w = *in * mat[3].ToVec3() + mat[3][3];
- }
-#endif
-}
-
-
-
-#define MAX_CLIPPED_POINTS 20
-typedef struct {
- int numVerts;
- idVec3 verts[MAX_CLIPPED_POINTS];
- int edgeFlags[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 *p1, *p2;
- idVec3 mid;
-
- in = &clipTris[inNum];
- out = &clipTris[inNum^1];
- counts[0] = counts[1] = counts[2] = 0;
-
- // determine sides for each point
- for ( i = 0 ; i < in->numVerts ; i++ ) {
- dot = plane.Distance( in->verts[i] );
- dists[i] = dot;
- if ( dot < -LIGHT_CLIP_EPSILON ) {
- sides[i] = SIDE_BACK;
- } else if ( dot > LIGHT_CLIP_EPSILON ) {
- sides[i] = SIDE_FRONT;
- } else {
- sides[i] = SIDE_ON;
- }
- counts[sides[i]]++;
- }
-
- // if none in front, it is completely clipped away
- if ( !counts[SIDE_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];
- in->edgeFlags[in->numVerts] = in->edgeFlags[0];
-
- out->numVerts = 0;
- for ( i = 0 ; i < in->numVerts ; i++ ) {
- p1 = &in->verts[i];
-
- if ( sides[i] != SIDE_BACK ) {
- out->verts[out->numVerts] = *p1;
- if ( sides[i] == SIDE_ON && sides[i+1] == SIDE_BACK ) {
- out->edgeFlags[out->numVerts] = 1;
- } else {
- out->edgeFlags[out->numVerts] = in->edgeFlags[i];
- }
- out->numVerts++;
- }
-
- if ( (sides[i] == SIDE_FRONT && sides[i+1] == SIDE_BACK)
- || (sides[i] == SIDE_BACK && sides[i+1] == SIDE_FRONT) ) {
- // generate a split point
- 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;
-
- // set the edge flag
- if ( sides[i+1] != SIDE_FRONT ) {
- out->edgeFlags[out->numVerts] = 1;
- } else {
- out->edgeFlags[out->numVerts] = in->edgeFlags[i];
- }
-
- 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;
- int base;
- clipTri_t pingPong[2], *ct;
- int p;
-
- pingPong[0].numVerts = 3;
- pingPong[0].edgeFlags[0] = 0;
- pingPong[0].edgeFlags[1] = 0;
- pingPong[0].edgeFlags[2] = 0;
- 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;
- }
- }
- }
- ct = &pingPong[p];
-
- // copy the clipped points out to shadowVerts
- if ( numShadowVerts + ct->numVerts * 2 > MAX_SHADOW_VERTS ) {
- overflowed = true;
- return false;
- }
-
- base = numShadowVerts;
- for ( i = 0 ; i < ct->numVerts ; i++ ) {
- shadowVerts[ base + i*2 ].ToVec3() = ct->verts[i];
- }
- numShadowVerts += ct->numVerts * 2;
-
- if ( numShadowIndexes + 3 * ( ct->numVerts - 2 ) > MAX_SHADOW_INDEXES ) {
- overflowed = true;
- return false;
- }
-
- for ( i = 2 ; i < ct->numVerts ; i++ ) {
- shadowIndexes[numShadowIndexes++] = base + i * 2;
- shadowIndexes[numShadowIndexes++] = base + ( i - 1 ) * 2;
- shadowIndexes[numShadowIndexes++] = base;
- }
-
- // any edges that were created by the clipping process will
- // have a silhouette quad created for it, because it is one
- // of the exterior bounds of the shadow volume
- for ( i = 0 ; i < ct->numVerts ; i++ ) {
- if ( ct->edgeFlags[i] ) {
- if ( numClipSilEdges == MAX_CLIP_SIL_EDGES ) {
- break;
- }
- clipSilEdges[ numClipSilEdges ][0] = base + i * 2;
- if ( i == ct->numVerts - 1 ) {
- clipSilEdges[ numClipSilEdges ][1] = base;
- } else {
- clipSilEdges[ numClipSilEdges ][1] = base + ( i + 1 ) * 2;
- }
- numClipSilEdges++;
- }
- }
-
- return true;
-}
-
-/*
-===================
-R_ClipLineToLight
-
-If neither point is clearly behind the clipping
-plane, the edge will be passed unmodified. A sil edge that
-is on a border plane must be drawn.
-
-If one point is clearly clipped by the plane and the
-other point is on the plane, it will be completely removed.
-===================
-*/
-static bool R_ClipLineToLight( const idVec3 &a, const idVec3 &b, const idPlane frustum[4],
- idVec3 &p1, idVec3 &p2 ) {
- float *clip;
- int j;
- float d1, d2;
- float f;
-
- p1 = a;
- p2 = b;
-
- // clip it
- for ( j = 0 ; j < 6 ; j++ ) {
- d1 = frustum[j].Distance( p1 );
- d2 = frustum[j].Distance( p2 );
-
- // if both on or in front, not clipped to this plane
- if ( d1 > -LIGHT_CLIP_EPSILON && d2 > -LIGHT_CLIP_EPSILON ) {
- continue;
- }
-
- // if one is behind and the other isn't clearly in front, the edge is clipped off
- if ( d1 <= -LIGHT_CLIP_EPSILON && d2 < LIGHT_CLIP_EPSILON ) {
- return false;
- }
- if ( d2 <= -LIGHT_CLIP_EPSILON && d1 < LIGHT_CLIP_EPSILON ) {
- return false;
- }
-
- // clip it, keeping the negative side
- if ( d1 < 0 ) {
- clip = p1.ToFloatPtr();
- } else {
- clip = p2.ToFloatPtr();
- }
-
-#if 0
- if ( idMath::Fabs(d1 - d2) < 0.001 ) {
- d2 = d1 - 0.1;
- }
-#endif
-
- f = d1 / ( d1 - d2 );
- clip[0] = p1[0] + f * ( p2[0] - p1[0] );
- clip[1] = p1[1] + f * ( p2[1] - p1[1] );
- clip[2] = p1[2] + f * ( p2[2] - p1[2] );
- }
-
- return true; // retain a fragment
-}
-
-
-/*
-==================
-R_AddClipSilEdges
-
-Add sil edges for each triangle clipped to the side of
-the frustum.
-
-Only done for simple projected lights, not point lights.
-==================
-*/
-static void R_AddClipSilEdges( void ) {
- int v1, v2;
- int v1_back, v2_back;
- int i;
-
- // don't allow it to overflow
- if ( numShadowIndexes + numClipSilEdges * 6 > MAX_SHADOW_INDEXES ) {
- overflowed = true;
- return;
- }
-
- for ( i = 0 ; i < numClipSilEdges ; i++ ) {
- v1 = clipSilEdges[i][0];
- v2 = clipSilEdges[i][1];
- v1_back = v1 + 1;
- v2_back = v2 + 1;
- if ( PointsOrdered( shadowVerts[ v1 ].ToVec3(), shadowVerts[ v2 ].ToVec3() ) ) {
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v1_back;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v2_back;
- shadowIndexes[numShadowIndexes++] = v1_back;
- } else {
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v2_back;
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2_back;
- shadowIndexes[numShadowIndexes++] = v1_back;
- }
- }
-}
-
-/*
-=================
-R_AddSilEdges
-
-Add quads from the front points to the projected points
-for each silhouette edge in the light
-=================
-*/
-static void R_AddSilEdges( const srfTriangles_t *tri, unsigned short *pointCull, const idPlane frustum[6] ) {
- int v1, v2;
- int i;
- silEdge_t *sil;
- int numPlanes;
-
- numPlanes = tri->numIndexes / 3;
-
- // add sil edges for any true silhouette boundaries on the surface
- for ( i = 0 ; i < tri->numSilEdges ; i++ ) {
- sil = tri->silEdges + i;
- if ( sil->p1 < 0 || sil->p1 > numPlanes || sil->p2 < 0 || sil->p2 > numPlanes ) {
- common->Error( "Bad sil planes" );
- }
-
- // an edge will be a silhouette edge if the face on one side
- // casts a shadow, but the face on the other side doesn't.
- // "casts a shadow" means that it has some surface in the projection,
- // not just that it has the correct facing direction
- // This will cause edges that are exactly on the frustum plane
- // to be considered sil edges if the face inside casts a shadow.
- if ( !( faceCastsShadow[ sil->p1 ] ^ faceCastsShadow[ sil->p2 ] ) ) {
- continue;
- }
-
- // if the edge is completely off the negative side of
- // a frustum plane, don't add it at all. This can still
- // happen even if the face is visible and casting a shadow
- // if it is partially clipped
- if ( EDGE_CULLED( sil->v1, sil->v2 ) ) {
- continue;
- }
-
- // see if the edge needs to be clipped
- if ( EDGE_CLIPPED( sil->v1, sil->v2 ) ) {
- if ( numShadowVerts + 4 > MAX_SHADOW_VERTS ) {
- overflowed = true;
- return;
- }
- v1 = numShadowVerts;
- v2 = v1 + 2;
- if ( !R_ClipLineToLight( tri->verts[ sil->v1 ].xyz, tri->verts[ sil->v2 ].xyz,
- frustum, shadowVerts[v1].ToVec3(), shadowVerts[v2].ToVec3() ) ) {
- continue; // clipped away
- }
-
- numShadowVerts += 4;
- } else {
- // use the entire edge
- v1 = remap[ sil->v1 ];
- v2 = remap[ sil->v2 ];
- if ( v1 < 0 || v2 < 0 ) {
- common->Error( "R_AddSilEdges: bad remap[]" );
- }
- }
-
- // don't overflow
- if ( numShadowIndexes + 6 > MAX_SHADOW_INDEXES ) {
- overflowed = true;
- return;
- }
-
- // we need to choose the correct way of triangulating the silhouette quad
- // consistantly between any two points, no matter which order they are specified.
- // If this wasn't done, slight rasterization cracks would show in the shadow
- // volume when two sil edges were exactly coincident
- if ( faceCastsShadow[ sil->p2 ] ) {
- if ( PointsOrdered( shadowVerts[ v1 ].ToVec3(), shadowVerts[ v2 ].ToVec3() ) ) {
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v1+1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v1+1;
- shadowIndexes[numShadowIndexes++] = v2+1;
- } else {
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2+1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v1+1;
- shadowIndexes[numShadowIndexes++] = v2+1;
- }
- } else {
- if ( PointsOrdered( shadowVerts[ v1 ].ToVec3(), shadowVerts[ v2 ].ToVec3() ) ) {
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v1+1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v2+1;
- shadowIndexes[numShadowIndexes++] = v1+1;
- } else {
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2;
- shadowIndexes[numShadowIndexes++] = v2+1;
- shadowIndexes[numShadowIndexes++] = v1;
- shadowIndexes[numShadowIndexes++] = v2+1;
- shadowIndexes[numShadowIndexes++] = v1+1;
- }
- }
- }
-}
-
-/*
-================
-R_CalcPointCull
-
-Also inits the remap[] array to all -1
-================
-*/
-static void R_CalcPointCull( const srfTriangles_t *tri, const idPlane frustum[6], unsigned short *pointCull ) {
- int i;
- int frontBits;
- float *planeSide;
- byte *side1, *side2;
-
- SIMDProcessor->Memset( remap, -1, tri->numVerts * sizeof( remap[0] ) );
-
- for ( frontBits = 0, i = 0; i < 6; i++ ) {
- // get front bits for the whole surface
- if ( tri->bounds.PlaneDistance( frustum[i] ) >= LIGHT_CLIP_EPSILON ) {
- frontBits |= 1<<(i+6);
- }
- }
-
- // initialize point cull
- for ( i = 0; i < tri->numVerts; i++ ) {
- pointCull[i] = frontBits;
- }
-
- // if the surface is not completely inside the light frustum
- if ( frontBits == ( ( ( 1 << 6 ) - 1 ) ) << 6 ) {
- return;
- }
-
- planeSide = (float *) _alloca16( tri->numVerts * sizeof( float ) );
- side1 = (byte *) _alloca16( tri->numVerts * sizeof( byte ) );
- side2 = (byte *) _alloca16( tri->numVerts * sizeof( byte ) );
- SIMDProcessor->Memset( side1, 0, tri->numVerts * sizeof( byte ) );
- SIMDProcessor->Memset( side2, 0, tri->numVerts * sizeof( byte ) );
-
- for ( i = 0; i < 6; i++ ) {
-
- if ( frontBits & (1<<(i+6)) ) {
- continue;
- }
-
- SIMDProcessor->Dot( planeSide, frustum[i], tri->verts, tri->numVerts );
- SIMDProcessor->CmpLT( side1, i, planeSide, LIGHT_CLIP_EPSILON, tri->numVerts );
- SIMDProcessor->CmpGT( side2, i, planeSide, -LIGHT_CLIP_EPSILON, tri->numVerts );
- }
- for ( i = 0; i < tri->numVerts; i++ ) {
- pointCull[i] |= side1[i] | (side2[i] << 6);
- }
-}
-
-/*
-=================
-R_CreateShadowVolumeInFrustum
-
-Adds new verts and indexes to the shadow volume.
-
-If the frustum completely defines the projected light,
-makeClippedPlanes should be true, which will cause sil quads to
-be added along all clipped edges.
-
-If the frustum is just part of a point light, clipped planes don't
-need to be added.
-=================
-*/
-static void R_CreateShadowVolumeInFrustum( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri,
- const idRenderLightLocal *light,
- const idVec3 lightOrigin,
- const idPlane frustum[6],
- const idPlane &farPlane,
- bool makeClippedPlanes ) {
- int i;
- int numTris;
- unsigned short *pointCull;
- int numCapIndexes;
- int firstShadowIndex;
- int firstShadowVert;
- int cullBits;
-
- pointCull = (unsigned short *)_alloca16( tri->numVerts * sizeof( pointCull[0] ) );
-
- // test the vertexes for inside the light frustum, which will allow
- // us to completely cull away some triangles from consideration.
- R_CalcPointCull( tri, frustum, pointCull );
-
- // this may not be the first frustum added to the volume
- firstShadowIndex = numShadowIndexes;
- firstShadowVert = numShadowVerts;
-
- // decide which triangles front shadow volumes, clipping as needed
- numClipSilEdges = 0;
- numTris = tri->numIndexes / 3;
- for ( i = 0 ; i < numTris ; i++ ) {
- int i1, i2, i3;
-
- faceCastsShadow[i] = 0; // until shown otherwise
-
- // if it isn't facing the right way, don't add it
- // to the shadow volume
- if ( globalFacing[i] ) {
- continue;
- }
-
- i1 = tri->silIndexes[ i*3 + 0 ];
- i2 = tri->silIndexes[ i*3 + 1 ];
- i3 = tri->silIndexes[ i*3 + 2 ];
-
- // if all the verts are off one side of the frustum,
- // don't add any of them
- if ( TRIANGLE_CULLED( i1, i2, i3 ) ) {
- continue;
- }
-
- // make sure the verts that are not on the negative sides
- // of the frustum are copied over.
- // we need to get the original verts even from clipped triangles
- // so the edges reference correctly, because an edge may be unclipped
- // even when a triangle is clipped.
- if ( numShadowVerts + 6 > MAX_SHADOW_VERTS ) {
- overflowed = true;
- return;
- }
-
- if ( !POINT_CULLED(i1) && remap[i1] == -1 ) {
- remap[i1] = numShadowVerts;
- shadowVerts[ numShadowVerts ].ToVec3() = tri->verts[i1].xyz;
- numShadowVerts+=2;
- }
- if ( !POINT_CULLED(i2) && remap[i2] == -1 ) {
- remap[i2] = numShadowVerts;
- shadowVerts[ numShadowVerts ].ToVec3() = tri->verts[i2].xyz;
- numShadowVerts+=2;
- }
- if ( !POINT_CULLED(i3) && remap[i3] == -1 ) {
- remap[i3] = numShadowVerts;
- shadowVerts[ numShadowVerts ].ToVec3() = tri->verts[i3].xyz;
- numShadowVerts+=2;
- }
-
- // clip the triangle if any points are on the negative sides
- if ( TRIANGLE_CLIPPED( i1, i2, i3 ) ) {
- cullBits = ( ( pointCull[ i1 ] ^ 0xfc0 ) | ( pointCull[ i2 ] ^ 0xfc0 ) | ( pointCull[ i3 ] ^ 0xfc0 ) ) >> 6;
- // this will also define clip edges that will become
- // silhouette planes
- if ( R_ClipTriangleToLight( tri->verts[i1].xyz, tri->verts[i2].xyz,
- tri->verts[i3].xyz, cullBits, frustum ) ) {
- faceCastsShadow[i] = 1;
- }
- } else {
- // instead of overflowing or drawing a streamer shadow, don't draw a shadow at all
- if ( numShadowIndexes + 3 > MAX_SHADOW_INDEXES ) {
- overflowed = true;
- return;
- }
- if ( remap[i1] == -1 || remap[i2] == -1 || remap[i3] == -1 ) {
- common->Error( "R_CreateShadowVolumeInFrustum: bad remap[]" );
- }
- shadowIndexes[numShadowIndexes++] = remap[i3];
- shadowIndexes[numShadowIndexes++] = remap[i2];
- shadowIndexes[numShadowIndexes++] = remap[i1];
- faceCastsShadow[i] = 1;
- }
- }
-
- // add indexes for the back caps, which will just be reversals of the
- // front caps using the back vertexes
- numCapIndexes = numShadowIndexes - firstShadowIndex;
-
- // if no faces have been defined for the shadow volume,
- // there won't be anything at all
- if ( numCapIndexes == 0 ) {
- return;
- }
-
- //--------------- off-line processing ------------------
-
- // if we are running from dmap, perform the (very) expensive shadow optimizations
- // to remove internal sil edges and optimize the caps
- if ( callOptimizer ) {
- optimizedShadow_t opt;
-
- // project all of the vertexes to the shadow plane, generating
- // an equal number of back vertexes
-// R_ProjectPointsToFarPlane( ent, light, farPlane, firstShadowVert, numShadowVerts );
-
- opt = SuperOptimizeOccluders( shadowVerts, shadowIndexes + firstShadowIndex, numCapIndexes, farPlane, lightOrigin );
-
- // pull off the non-optimized data
- numShadowIndexes = firstShadowIndex;
- numShadowVerts = firstShadowVert;
-
- // add the optimized data
- if ( numShadowIndexes + opt.totalIndexes > MAX_SHADOW_INDEXES
- || numShadowVerts + opt.numVerts > MAX_SHADOW_VERTS ) {
- overflowed = true;
- common->Printf( "WARNING: overflowed MAX_SHADOW tables, shadow discarded\n" );
- Mem_Free( opt.verts );
- Mem_Free( opt.indexes );
- return;
- }
-
- for ( i = 0 ; i < opt.numVerts ; i++ ) {
- shadowVerts[numShadowVerts+i][0] = opt.verts[i][0];
- shadowVerts[numShadowVerts+i][1] = opt.verts[i][1];
- shadowVerts[numShadowVerts+i][2] = opt.verts[i][2];
- shadowVerts[numShadowVerts+i][3] = 1;
- }
- for ( i = 0 ; i < opt.totalIndexes ; i++ ) {
- int index = opt.indexes[i];
- if ( index < 0 || index > opt.numVerts ) {
- common->Error( "optimized shadow index out of range" );
- }
- shadowIndexes[numShadowIndexes+i] = index + numShadowVerts;
- }
-
- numShadowVerts += opt.numVerts;
- numShadowIndexes += opt.totalIndexes;
-
- // note the index distribution so we can sort all the caps after all the sils
- indexRef[indexFrustumNumber].frontCapStart = firstShadowIndex;
- indexRef[indexFrustumNumber].rearCapStart = firstShadowIndex+opt.numFrontCapIndexes;
- indexRef[indexFrustumNumber].silStart = firstShadowIndex+opt.numFrontCapIndexes+opt.numRearCapIndexes;
- indexRef[indexFrustumNumber].end = numShadowIndexes;
- indexFrustumNumber++;
-
- Mem_Free( opt.verts );
- Mem_Free( opt.indexes );
- return;
- }
-
- //--------------- real-time processing ------------------
-
- // the dangling edge "face" is never considered to cast a shadow,
- // so any face with dangling edges that casts a shadow will have
- // it's dangling sil edge trigger a sil plane
- faceCastsShadow[numTris] = 0;
-
- // instead of overflowing or drawing a streamer shadow, don't draw a shadow at all
- // if we ran out of space
- if ( numShadowIndexes + numCapIndexes > MAX_SHADOW_INDEXES ) {
- overflowed = true;
- return;
- }
- for ( i = 0 ; i < numCapIndexes ; i += 3 ) {
- shadowIndexes[ numShadowIndexes + i + 0 ] = shadowIndexes[ firstShadowIndex + i + 2 ] + 1;
- shadowIndexes[ numShadowIndexes + i + 1 ] = shadowIndexes[ firstShadowIndex + i + 1 ] + 1;
- shadowIndexes[ numShadowIndexes + i + 2 ] = shadowIndexes[ firstShadowIndex + i + 0 ] + 1;
- }
- numShadowIndexes += numCapIndexes;
-
-c_caps += numCapIndexes * 2;
-
-int preSilIndexes = numShadowIndexes;
-
- // if any triangles were clipped, we will have a list of edges
- // on the frustum which must now become sil edges
- if ( makeClippedPlanes ) {
- R_AddClipSilEdges();
- }
-
- // any edges that are a transition between a shadowing and
- // non-shadowing triangle will cast a silhouette edge
- R_AddSilEdges( tri, pointCull, frustum );
-
-c_sils += numShadowIndexes - preSilIndexes;
-
- // project all of the vertexes to the shadow plane, generating
- // an equal number of back vertexes
- R_ProjectPointsToFarPlane( ent, light, farPlane, firstShadowVert, numShadowVerts );
-
- // note the index distribution so we can sort all the caps after all the sils
- indexRef[indexFrustumNumber].frontCapStart = firstShadowIndex;
- indexRef[indexFrustumNumber].rearCapStart = firstShadowIndex+numCapIndexes;
- indexRef[indexFrustumNumber].silStart = preSilIndexes;
- indexRef[indexFrustumNumber].end = numShadowIndexes;
- indexFrustumNumber++;
-}
-
-/*
-===================
-R_MakeShadowFrustums
-
-Called at definition derivation time
-===================
-*/
-void R_MakeShadowFrustums( idRenderLightLocal *light ) {
- int i, j;
-
- if ( light->parms.pointLight ) {
-#if 0
- idVec3 adjustedRadius;
-
- // increase the light radius to cover any origin offsets.
- // this will cause some shadows to extend out of the exact light
- // volume, but is simpler than adjusting all the frustums
- adjustedRadius[0] = light->parms.lightRadius[0] + idMath::Fabs( light->parms.lightCenter[0] );
- adjustedRadius[1] = light->parms.lightRadius[1] + idMath::Fabs( light->parms.lightCenter[1] );
- adjustedRadius[2] = light->parms.lightRadius[2] + idMath::Fabs( light->parms.lightCenter[2] );
-
- light->numShadowFrustums = 0;
- // a point light has to project against six planes
- for ( i = 0 ; i < 6 ; i++ ) {
- shadowFrustum_t *frust = &light->shadowFrustums[ light->numShadowFrustums ];
-
- frust->numPlanes = 6;
- frust->makeClippedPlanes = false;
- for ( j = 0 ; j < 6 ; j++ ) {
- idPlane &plane = frust->planes[j];
- plane[0] = pointLightFrustums[i][j][0] / adjustedRadius[0];
- plane[1] = pointLightFrustums[i][j][1] / adjustedRadius[1];
- plane[2] = pointLightFrustums[i][j][2] / adjustedRadius[2];
- plane.Normalize();
- plane[3] = -( plane.Normal() * light->globalLightOrigin );
- if ( j == 5 ) {
- plane[3] += adjustedRadius[i>>1];
- }
- }
-
- light->numShadowFrustums++;
- }
-#else
- // exact projection,taking into account asymetric frustums when
- // globalLightOrigin isn't centered
-
- static int faceCorners[6][4] = {
- { 7, 5, 1, 3 }, // positive X side
- { 4, 6, 2, 0 }, // negative X side
- { 6, 7, 3, 2 }, // positive Y side
- { 5, 4, 0, 1 }, // negative Y side
- { 6, 4, 5, 7 }, // positive Z side
- { 3, 1, 0, 2 } // negative Z side
- };
- static int faceEdgeAdjacent[6][4] = {
- { 4, 4, 2, 2 }, // positive X side
- { 7, 7, 1, 1 }, // negative X side
- { 5, 5, 0, 0 }, // positive Y side
- { 6, 6, 3, 3 }, // negative Y side
- { 0, 0, 3, 3 }, // positive Z side
- { 5, 5, 6, 6 } // negative Z side
- };
-
- bool centerOutside = false;
-
- // if the light center of projection is outside the light bounds,
- // we will need to build the planes a little differently
- if ( fabs( light->parms.lightCenter[0] ) > light->parms.lightRadius[0]
- || fabs( light->parms.lightCenter[1] ) > light->parms.lightRadius[1]
- || fabs( light->parms.lightCenter[2] ) > light->parms.lightRadius[2] ) {
- centerOutside = true;
- }
-
- // make the corners
- idVec3 corners[8];
-
- for ( i = 0 ; i < 8 ; i++ ) {
- idVec3 temp;
- for ( j = 0 ; j < 3 ; j++ ) {
- if ( i & ( 1 << j ) ) {
- temp[j] = light->parms.lightRadius[j];
- } else {
- temp[j] = -light->parms.lightRadius[j];
- }
- }
-
- // transform to global space
- corners[i] = light->parms.origin + light->parms.axis * temp;
- }
-
- light->numShadowFrustums = 0;
- for ( int side = 0 ; side < 6 ; side++ ) {
- shadowFrustum_t *frust = &light->shadowFrustums[ light->numShadowFrustums ];
- idVec3 &p1 = corners[faceCorners[side][0]];
- idVec3 &p2 = corners[faceCorners[side][1]];
- idVec3 &p3 = corners[faceCorners[side][2]];
- idPlane backPlane;
-
- // plane will have positive side inward
- backPlane.FromPoints( p1, p2, p3 );
-
- // if center of projection is on the wrong side, skip
- float d = backPlane.Distance( light->globalLightOrigin );
- if ( d < 0 ) {
- continue;
- }
-
- frust->numPlanes = 6;
- frust->planes[5] = backPlane;
- frust->planes[4] = backPlane; // we don't really need the extra plane
-
- // make planes with positive side facing inwards in light local coordinates
- for ( int edge = 0 ; edge < 4 ; edge++ ) {
- idVec3 &p1 = corners[faceCorners[side][edge]];
- idVec3 &p2 = corners[faceCorners[side][(edge+1)&3]];
-
- // create a plane that goes through the center of projection
- frust->planes[edge].FromPoints( p2, p1, light->globalLightOrigin );
-
- // see if we should use an adjacent plane instead
- if ( centerOutside ) {
- idVec3 &p3 = corners[faceEdgeAdjacent[side][edge]];
- idPlane sidePlane;
-
- sidePlane.FromPoints( p2, p1, p3 );
- d = sidePlane.Distance( light->globalLightOrigin );
- if ( d < 0 ) {
- // use this plane instead of the edged plane
- frust->planes[edge] = sidePlane;
- }
- // we can't guarantee a neighbor, so add sill planes at edge
- light->shadowFrustums[ light->numShadowFrustums ].makeClippedPlanes = true;
- }
- }
- light->numShadowFrustums++;
- }
-
-#endif
- return;
- }
-
- // projected light
-
- light->numShadowFrustums = 1;
- shadowFrustum_t *frust = &light->shadowFrustums[ 0 ];
-
- // flip and transform the frustum planes so the positive side faces
- // inward in local coordinates
-
- // it is important to clip against even the near clip plane, because
- // many projected lights that are faking area lights will have their
- // origin behind solid surfaces.
- for ( i = 0 ; i < 6 ; i++ ) {
- idPlane &plane = frust->planes[i];
-
- plane.SetNormal( -light->frustum[i].Normal() );
- plane.SetDist( -light->frustum[i].Dist() );
- }
-
- frust->numPlanes = 6;
-
- frust->makeClippedPlanes = true;
- // projected lights don't have shared frustums, so any clipped edges
- // right on the planes must have a sil plane created for them
-}
-
-/*
-=================
-R_CreateShadowVolume
-
-The returned surface will have a valid bounds and radius for culling.
-
-Triangles are clipped to the light frustum before projecting.
-
-A single triangle can clip to as many as 7 vertexes, so
-the worst case expansion is 2*(numindexes/3)*7 verts when counting both
-the front and back caps, although it will usually only be a modest
-increase in vertexes for closed modesl
-
-The worst case index count is much larger, when the 7 vertex clipped triangle
-needs 15 indexes for the front, 15 for the back, and 42 (a quad on seven sides)
-for the sides, for a total of 72 indexes from the original 3. Ouch.
-
-NULL may be returned if the surface doesn't create a shadow volume at all,
-as with a single face that the light is behind.
-
-If an edge is within an epsilon of the border of the volume, it must be treated
-as if it is clipped for triangles, generating a new sil edge, and act
-as if it was culled for edges, because the sil edge will have been
-generated by the triangle irregardless of if it actually was a sil edge.
-=================
-*/
-srfTriangles_t *R_CreateShadowVolume( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- shadowGen_t optimize, srfCullInfo_t &cullInfo ) {
- int i, j;
- idVec3 lightOrigin;
- srfTriangles_t *newTri;
- int capPlaneBits;
-
- if ( !r_shadows.GetBool() ) {
- return NULL;
- }
-
- if ( tri->numSilEdges == 0 || tri->numIndexes == 0 || tri->numVerts == 0 ) {
- return NULL;
- }
-
- if ( tri->numIndexes < 0 ) {
- common->Error( "R_CreateShadowVolume: tri->numIndexes = %i", tri->numIndexes );
- }
-
- if ( tri->numVerts < 0 ) {
- common->Error( "R_CreateShadowVolume: tri->numVerts = %i", tri->numVerts );
- }
-
- tr.pc.c_createShadowVolumes++;
-
- // use the fast infinite projection in dynamic situations, which
- // trades somewhat more overdraw and no cap optimizations for
- // a very simple generation process
- if ( optimize == SG_DYNAMIC && r_useTurboShadow.GetBool() ) {
- if ( tr.backEndRendererHasVertexPrograms && r_useShadowVertexProgram.GetBool() ) {
- return R_CreateVertexProgramTurboShadowVolume( ent, tri, light, cullInfo );
- } else {
- return R_CreateTurboShadowVolume( ent, tri, light, cullInfo );
- }
- }
-
- R_CalcInteractionFacing( ent, tri, light, cullInfo );
-
- int numFaces = tri->numIndexes / 3;
- int allFront = 1;
- for ( i = 0; i < numFaces && allFront; i++ ) {
- allFront &= cullInfo.facing[i];
- }
- if ( allFront ) {
- // if no faces are the right direction, don't make a shadow at all
- return NULL;
- }
-
- // clear the shadow volume
- numShadowIndexes = 0;
- numShadowVerts = 0;
- overflowed = false;
- indexFrustumNumber = 0;
- capPlaneBits = 0;
- callOptimizer = (optimize == SG_OFFLINE);
-
- // the facing information will be the same for all six projections
- // from a point light, as well as for any directed lights
- globalFacing = cullInfo.facing;
- faceCastsShadow = (byte *)_alloca16( tri->numIndexes / 3 + 1 ); // + 1 for fake dangling edge face
- remap = (int *)_alloca16( tri->numVerts * sizeof( remap[0] ) );
-
- R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, lightOrigin );
-
- // run through all the shadow frustums, which is one for a projected light,
- // and usually six for a point light, but point lights with centers outside
- // the box may have less
- for ( int frustumNum = 0 ; frustumNum < light->numShadowFrustums ; frustumNum++ ) {
- const shadowFrustum_t *frust = &light->shadowFrustums[frustumNum];
- ALIGN16( idPlane frustum[6] );
-
- // transform the planes into entity space
- // we could share and reverse some of the planes between frustums for a minor
- // speed increase
-
- // the cull test is redundant for a single shadow frustum projected light, because
- // the surface has already been checked against the main light frustums
-
- for ( j = 0 ; j < frust->numPlanes ; j++ ) {
- R_GlobalPlaneToLocal( ent->modelMatrix, frust->planes[j], frustum[j] );
-
- // try to cull the entire surface against this frustum
- float d = tri->bounds.PlaneDistance( frustum[j] );
- if ( d < -LIGHT_CLIP_EPSILON ) {
- break;
- }
- }
- if ( j != frust->numPlanes ) {
- continue;
- }
- // we need to check all the triangles
- int oldFrustumNumber = indexFrustumNumber;
-
- R_CreateShadowVolumeInFrustum( ent, tri, light, lightOrigin, frustum, frustum[5], frust->makeClippedPlanes );
-
- // if we couldn't make a complete shadow volume, it is better to
- // not draw one at all, avoiding streamer problems
- if ( overflowed ) {
- return NULL;
- }
-
- if ( indexFrustumNumber != oldFrustumNumber ) {
- // note that we have caps projected against this frustum,
- // which may allow us to skip drawing the caps if all projected
- // planes face away from the viewer and the viewer is outside the light volume
- capPlaneBits |= 1< MAX_SHADOW_VERTS || numShadowIndexes > MAX_SHADOW_INDEXES ) {
- common->FatalError( "Shadow volume exceeded allocation" );
- }
-
- // allocate a new surface for the shadow volume
- newTri = R_AllocStaticTriSurf();
-
- // we might consider setting this, but it would only help for
- // large lights that are partially off screen
- newTri->bounds.Clear();
-
- // copy off the verts and indexes
- newTri->numVerts = numShadowVerts;
- newTri->numIndexes = numShadowIndexes;
-
- // the shadow verts will go into a main memory buffer as well as a vertex
- // cache buffer, so they can be copied back if they are purged
- R_AllocStaticTriSurfShadowVerts( newTri, newTri->numVerts );
- SIMDProcessor->Memcpy( newTri->shadowVertexes, shadowVerts, newTri->numVerts * sizeof( newTri->shadowVertexes[0] ) );
-
- R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes );
-
- if ( 1 /* sortCapIndexes */ ) {
- newTri->shadowCapPlaneBits = capPlaneBits;
-
- // copy the sil indexes first
- newTri->numShadowIndexesNoCaps = 0;
- for ( i = 0 ; i < indexFrustumNumber ; i++ ) {
- int c = indexRef[i].end - indexRef[i].silStart;
- SIMDProcessor->Memcpy( newTri->indexes+newTri->numShadowIndexesNoCaps,
- shadowIndexes+indexRef[i].silStart, c * sizeof( newTri->indexes[0] ) );
- newTri->numShadowIndexesNoCaps += c;
- }
- // copy rear cap indexes next
- newTri->numShadowIndexesNoFrontCaps = newTri->numShadowIndexesNoCaps;
- for ( i = 0 ; i < indexFrustumNumber ; i++ ) {
- int c = indexRef[i].silStart - indexRef[i].rearCapStart;
- SIMDProcessor->Memcpy( newTri->indexes+newTri->numShadowIndexesNoFrontCaps,
- shadowIndexes+indexRef[i].rearCapStart, c * sizeof( newTri->indexes[0] ) );
- newTri->numShadowIndexesNoFrontCaps += c;
- }
- // copy front cap indexes last
- newTri->numIndexes = newTri->numShadowIndexesNoFrontCaps;
- for ( i = 0 ; i < indexFrustumNumber ; i++ ) {
- int c = indexRef[i].rearCapStart - indexRef[i].frontCapStart;
- SIMDProcessor->Memcpy( newTri->indexes+newTri->numIndexes,
- shadowIndexes+indexRef[i].frontCapStart, c * sizeof( newTri->indexes[0] ) );
- newTri->numIndexes += c;
- }
-
- } else {
- newTri->shadowCapPlaneBits = 63; // we don't have optimized index lists
- SIMDProcessor->Memcpy( newTri->indexes, shadowIndexes, newTri->numIndexes * sizeof( newTri->indexes[0] ) );
- }
-
- if ( optimize == SG_OFFLINE ) {
- CleanupOptimizedShadowTris( newTri );
- }
-
- return newTri;
-}
diff --git a/neo/renderer/tr_trisurf.cpp b/neo/renderer/tr_trisurf.cpp
index 06aea3d7..78ee66d8 100644
--- a/neo/renderer/tr_trisurf.cpp
+++ b/neo/renderer/tr_trisurf.cpp
@@ -294,11 +294,6 @@ int R_TriSurfMemory( const srfTriangles_t *tri ) {
return total;
}
- // used as a flag in interations
- if ( tri == LIGHT_TRIS_DEFERRED ) {
- return total;
- }
-
if ( tri->shadowVertexes != NULL ) {
total += tri->numVerts * sizeof( tri->shadowVertexes[0] );
} else if ( tri->verts != NULL ) {
diff --git a/neo/renderer/tr_turboshadow.cpp b/neo/renderer/tr_turboshadow.cpp
deleted file mode 100644
index 50f2c1d6..00000000
--- a/neo/renderer/tr_turboshadow.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
-===========================================================================
-
-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 .
-
-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 "precompiled.h"
-#pragma hdrstop
-
-#include "tr_local.h"
-
-int c_turboUsedVerts;
-int c_turboUnusedVerts;
-
-
-/*
-=====================
-R_CreateVertexProgramTurboShadowVolume
-
-are dangling edges that are outside the light frustum still making planes?
-=====================
-*/
-srfTriangles_t *R_CreateVertexProgramTurboShadowVolume( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- srfCullInfo_t &cullInfo ) {
- int i, j;
- srfTriangles_t *newTri;
- silEdge_t *sil;
- const glIndex_t *indexes;
- const byte *facing;
-
- R_CalcInteractionFacing( ent, tri, light, cullInfo );
- if ( r_useShadowProjectedCull.GetBool() ) {
- R_CalcInteractionCullBits( ent, tri, light, cullInfo );
- }
-
- int numFaces = tri->numIndexes / 3;
- int numShadowingFaces = 0;
- facing = cullInfo.facing;
-
- // if all the triangles are inside the light frustum
- if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT || !r_useShadowProjectedCull.GetBool() ) {
-
- // count the number of shadowing faces
- for ( 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
- indexes = tri->indexes;
- byte *modifyFacing = cullInfo.facing;
- const byte *cullBits = cullInfo.cullBits;
- for ( j = i = 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
- return NULL;
- }
-
- // shadowVerts will be NULL on these surfaces, so the shadowVerts will be taken from the ambient surface
- newTri = R_AllocStaticTriSurf();
-
- newTri->numVerts = tri->numVerts * 2;
-
- // alloc the max possible size
-#ifdef USE_TRI_DATA_ALLOCATOR
- R_AllocStaticTriSurfIndexes( newTri, ( numShadowingFaces + tri->numSilEdges ) * 6 );
- glIndex_t *tempIndexes = newTri->indexes;
- glIndex_t *shadowIndexes = newTri->indexes;
-#else
- glIndex_t *tempIndexes = (glIndex_t *)_alloca16( tri->numSilEdges * 6 * sizeof( tempIndexes[0] ) );
- glIndex_t *shadowIndexes = tempIndexes;
-#endif
-
- // create new triangles along sil planes
- for ( sil = tri->silEdges, 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;
-
-#ifdef USE_TRI_DATA_ALLOCATOR
- // decrease the size of the memory block to only store the used indexes
- R_ResizeStaticTriSurfIndexes( newTri, newTri->numIndexes );
-#else
- // allocate memory for the indexes
- R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes );
- // copy the indexes we created for the sil planes
- SIMDProcessor->Memcpy( newTri->indexes, tempIndexes, numShadowIndexes * sizeof( tempIndexes[0] ) );
-#endif
-
- // these have no effect, because they extend to infinity
- newTri->bounds.Clear();
-
- // put some faces on the model and some on the distant projection
- indexes = tri->indexes;
- shadowIndexes = newTri->indexes + numShadowIndexes;
- for ( i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) {
- if ( facing[j] ) {
- continue;
- }
-
- int i0 = indexes[i+0] << 1;
- shadowIndexes[2] = i0;
- shadowIndexes[3] = i0 ^ 1;
- int i1 = indexes[i+1] << 1;
- shadowIndexes[1] = i1;
- shadowIndexes[4] = i1 ^ 1;
- int i2 = indexes[i+2] << 1;
- shadowIndexes[0] = i2;
- shadowIndexes[5] = i2 ^ 1;
-
- shadowIndexes += 6;
- }
-
- return newTri;
-}
-
-/*
-=====================
-R_CreateTurboShadowVolume
-=====================
-*/
-srfTriangles_t *R_CreateTurboShadowVolume( const idRenderEntityLocal *ent,
- const srfTriangles_t *tri, const idRenderLightLocal *light,
- srfCullInfo_t &cullInfo ) {
- int i, j;
- idVec3 localLightOrigin;
- srfTriangles_t *newTri;
- silEdge_t *sil;
- const glIndex_t *indexes;
- const byte *facing;
-
- R_CalcInteractionFacing( ent, tri, light, cullInfo );
- if ( r_useShadowProjectedCull.GetBool() ) {
- R_CalcInteractionCullBits( ent, tri, light, cullInfo );
- }
-
- int numFaces = tri->numIndexes / 3;
- int numShadowingFaces = 0;
- facing = cullInfo.facing;
-
- // if all the triangles are inside the light frustum
- if ( cullInfo.cullBits == LIGHT_CULL_ALL_FRONT || !r_useShadowProjectedCull.GetBool() ) {
-
- // count the number of shadowing faces
- for ( 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
- indexes = tri->indexes;
- byte *modifyFacing = cullInfo.facing;
- const byte *cullBits = cullInfo.cullBits;
- for ( j = i = 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
- return NULL;
- }
-
- newTri = R_AllocStaticTriSurf();
-
-#ifdef USE_TRI_DATA_ALLOCATOR
- R_AllocStaticTriSurfShadowVerts( newTri, tri->numVerts * 2 );
- shadowCache_t *shadowVerts = newTri->shadowVertexes;
-#else
- shadowCache_t *shadowVerts = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowVerts[0] ) );
-#endif
-
- R_GlobalPointToLocal( ent->modelMatrix, light->globalLightOrigin, localLightOrigin );
-
- int *vertRemap = (int *)_alloca16( tri->numVerts * sizeof( vertRemap[0] ) );
-
- SIMDProcessor->Memset( vertRemap, -1, tri->numVerts * sizeof( vertRemap[0] ) );
-
- for ( i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) {
- if ( facing[j] ) {
- continue;
- }
- // this may pull in some vertexes that are outside
- // the frustum, because they connect to vertexes inside
- vertRemap[tri->silIndexes[i+0]] = 0;
- vertRemap[tri->silIndexes[i+1]] = 0;
- vertRemap[tri->silIndexes[i+2]] = 0;
- }
-
- newTri->numVerts = SIMDProcessor->CreateShadowCache( &shadowVerts->xyz, vertRemap, localLightOrigin, tri->verts, tri->numVerts );
-
- c_turboUsedVerts += newTri->numVerts;
- c_turboUnusedVerts += tri->numVerts * 2 - newTri->numVerts;
-
-#ifdef USE_TRI_DATA_ALLOCATOR
- R_ResizeStaticTriSurfShadowVerts( newTri, newTri->numVerts );
-#else
- R_AllocStaticTriSurfShadowVerts( newTri, newTri->numVerts );
- SIMDProcessor->Memcpy( newTri->shadowVertexes, shadowVerts, newTri->numVerts * sizeof( shadowVerts[0] ) );
-#endif
-
- // alloc the max possible size
-#ifdef USE_TRI_DATA_ALLOCATOR
- R_AllocStaticTriSurfIndexes( newTri, ( numShadowingFaces + tri->numSilEdges ) * 6 );
- glIndex_t *tempIndexes = newTri->indexes;
- glIndex_t *shadowIndexes = newTri->indexes;
-#else
- glIndex_t *tempIndexes = (glIndex_t *)_alloca16( tri->numSilEdges * 6 * sizeof( tempIndexes[0] ) );
- glIndex_t *shadowIndexes = tempIndexes;
-#endif
-
- // create new triangles along sil planes
- for ( sil = tri->silEdges, i = tri->numSilEdges; i > 0; i--, sil++ ) {
-
- int f1 = facing[sil->p1];
- int f2 = facing[sil->p2];
-
- if ( !( f1 ^ f2 ) ) {
- continue;
- }
-
- int v1 = vertRemap[sil->v1];
- int v2 = vertRemap[sil->v2];
-
- // 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;
-
-#ifdef USE_TRI_DATA_ALLOCATOR
- // decrease the size of the memory block to only store the used indexes
- R_ResizeStaticTriSurfIndexes( newTri, newTri->numIndexes );
-#else
- // allocate memory for the indexes
- R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes );
- // copy the indexes we created for the sil planes
- SIMDProcessor->Memcpy( newTri->indexes, tempIndexes, numShadowIndexes * sizeof( tempIndexes[0] ) );
-#endif
-
- // these have no effect, because they extend to infinity
- newTri->bounds.Clear();
-
- // put some faces on the model and some on the distant projection
- indexes = tri->silIndexes;
- shadowIndexes = newTri->indexes + numShadowIndexes;
- for ( i = 0, j = 0; i < tri->numIndexes; i += 3, j++ ) {
- if ( facing[j] ) {
- continue;
- }
-
- int i0 = vertRemap[indexes[i+0]];
- shadowIndexes[2] = i0;
- shadowIndexes[3] = i0 ^ 1;
- int i1 = vertRemap[indexes[i+1]];
- shadowIndexes[1] = i1;
- shadowIndexes[4] = i1 ^ 1;
- int i2 = vertRemap[indexes[i+2]];
- shadowIndexes[0] = i2;
- shadowIndexes[5] = i2 ^ 1;
-
- shadowIndexes += 6;
- }
-
- return newTri;
-}
diff --git a/neo/tools/compilers/dmap/shadowopt3.cpp b/neo/tools/compilers/dmap/shadowopt3.cpp
deleted file mode 100644
index 1fd5497a..00000000
--- a/neo/tools/compilers/dmap/shadowopt3.cpp
+++ /dev/null
@@ -1,1276 +0,0 @@
-/*
-===========================================================================
-
-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 .
-
-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 "precompiled.h"
-#pragma hdrstop
-
-#include "dmap.h"
-#include "../../../renderer/tr_local.h"
-
-/*
-
- given a set of faces that are clipped to the required frustum
-
- make 2D projection for each vertex
-
- for each edge
- add edge, generating new points at each edge intersection
-
- ?add all additional edges to make a full triangulation
-
- make full triangulation
-
- for each triangle
- find midpoint
- find original triangle with midpoint closest to view
- annotate triangle with that data
- project all vertexes to that plane
- output the triangle as a front cap
-
- snap all vertexes
- make a back plane projection for all vertexes
-
- for each edge
- if one side doesn't have a triangle
- make a sil edge to back plane projection
- continue
- if triangles on both sides have two verts in common
- continue
- make a sil edge from one triangle to the other
-
-
-
-
- classify triangles on common planes, so they can be optimized
-
- what about interpenetrating triangles???
-
- a perfect shadow volume will have every edge exactly matched with
- an opposite, and no two triangles covering the same area on either
- the back projection or a silhouette edge.
-
- Optimizing the triangles on the projected plane can give a significant
- improvement, but the quadratic time nature of the optimization process
- probably makes it untenable.
-
- There exists some small room for further triangle count optimizations of the volumes
- by collapsing internal surface geometry in some cases, or allowing original triangles
- to extend outside the exactly light frustum without being clipped, but it probably
- isn't worth it.
-
- Triangle count optimizations at the expense of a slight fill rate cost
- may be apropriate in some cases.
-
-
- Perform the complete clipping on all triangles
- for each vertex
- project onto the apropriate plane and mark plane bit as in use
-for each triangle
- if points project onto different planes, clip
-*/
-
-
-typedef struct {
- idVec3 v[3];
- idVec3 edge[3]; // positive side is inside the triangle
- glIndex_t index[3];
- idPlane plane; // positive side is forward for the triangle, which is away from the light
- int planeNum; // from original triangle, not calculated from the clipped verts
-} shadowTri_t;
-
-static const int MAX_SHADOW_TRIS = 32768;
-
-static shadowTri_t outputTris[MAX_SHADOW_TRIS];
-static int numOutputTris;
-
-typedef struct shadowOptEdge_s {
- glIndex_t index[2];
- struct shadowOptEdge_s *nextEdge;
-} shadowOptEdge_t;
-
-static const int MAX_SIL_EDGES = MAX_SHADOW_TRIS*3;
-static shadowOptEdge_t silEdges[MAX_SIL_EDGES];
-static int numSilEdges;
-
-typedef struct silQuad_s {
- int nearV[2];
- int farV[2]; // will always be a projection of near[]
- struct silQuad_s *nextQuad;
-} silQuad_t;
-
-static const int MAX_SIL_QUADS = MAX_SHADOW_TRIS*3;
-static silQuad_t silQuads[MAX_SIL_QUADS];
-static int numSilQuads;
-
-
-typedef struct {
- idVec3 normal; // all sil planes go through the projection origin
- shadowOptEdge_t *edges;
- silQuad_t *fragmentedQuads;
-} silPlane_t;
-
-static float EDGE_PLANE_EPSILON = 0.1f;
-static float UNIQUE_EPSILON = 0.1f;
-
-static int numSilPlanes;
-static silPlane_t *silPlanes;
-
-// the uniqued verts are still in projection centered space, not global space
-static int numUniqued;
-static int numUniquedBeforeProjection;
-static int maxUniqued;
-static idVec3 *uniqued;
-
-static optimizedShadow_t ret;
-static int maxRetIndexes;
-
-static int FindUniqueVert( idVec3 &v );
-
-//=====================================================================================
-
-/*
-=================
-CreateEdgesForTri
-=================
-*/
-static void CreateEdgesForTri( shadowTri_t *tri ) {
- for ( int j = 0 ; j < 3 ; j++ ) {
- idVec3 &v1 = tri->v[j];
- idVec3 &v2 = tri->v[(j+1)%3];
-
- tri->edge[j].Cross( v2, v1 );
- tri->edge[j].Normalize();
- }
-}
-
-
-static const float EDGE_EPSILON = 0.1f;
-
-static bool TriOutsideTri( const shadowTri_t *a, const shadowTri_t *b ) {
-#if 0
- if ( a->v[0] * b->edge[0] <= EDGE_EPSILON
- && a->v[1] * b->edge[0] <= EDGE_EPSILON
- && a->v[2] * b->edge[0] <= EDGE_EPSILON ) {
- return true;
- }
- if ( a->v[0] * b->edge[1] <= EDGE_EPSILON
- && a->v[1] * b->edge[1] <= EDGE_EPSILON
- && a->v[2] * b->edge[1] <= EDGE_EPSILON ) {
- return true;
- }
- if ( a->v[0] * b->edge[2] <= EDGE_EPSILON
- && a->v[1] * b->edge[2] <= EDGE_EPSILON
- && a->v[2] * b->edge[2] <= EDGE_EPSILON ) {
- return true;
- }
-#else
- for ( int i = 0 ; i < 3 ; i++ ) {
- int j;
- for ( j = 0 ; j < 3 ; j++ ) {
- float d = a->v[j] * b->edge[i];
- if ( d > EDGE_EPSILON ) {
- break;
- }
- }
- if ( j == 3 ) {
- return true;
- }
- }
-#endif
- return false;
-}
-
-static bool TriBehindTri( const shadowTri_t *a, const shadowTri_t *b ) {
- float d;
-
- d = b->plane.Distance( a->v[0] );
- if ( d > 0 ) {
- return true;
- }
- d = b->plane.Distance( a->v[1] );
- if ( d > 0 ) {
- return true;
- }
- d = b->plane.Distance( a->v[2] );
- if ( d > 0 ) {
- return true;
- }
-
- return false;
-}
-
-/*
-===================
-ClipTriangle_r
-===================
-*/
-static int c_removedFragments;
-static void ClipTriangle_r( const shadowTri_t *tri, int startTri, int skipTri, int numTris, const shadowTri_t *tris ) {
- // create edge planes for this triangle
-
- // compare against all the other triangles
- for ( int i = startTri ; i < numTris ; i++ ) {
- if ( i == skipTri ) {
- continue;
- }
- const shadowTri_t *other = &tris[i];
-
- if ( TriOutsideTri( tri, other ) ) {
- continue;
- }
- if ( TriOutsideTri( other, tri ) ) {
- continue;
- }
- // they overlap to some degree
-
- // if other is behind tri, it doesn't clip it
- if ( !TriBehindTri( tri, other ) ) {
- continue;
- }
-
- // clip it
- idWinding *w = new idWinding( tri->v, 3 );
-
- for ( int j = 0 ; j < 4 && w ; j++ ) {
- idWinding *front, *back;
-
- // keep any portion in front of other's plane
- if ( j == 0 ) {
- w->Split( other->plane, ON_EPSILON, &front, &back );
- } else {
- w->Split( idPlane( other->edge[j-1], 0.0f ), ON_EPSILON, &front, &back );
- }
- if ( back ) {
- // recursively clip these triangles to all subsequent triangles
- for ( int k = 2 ; k < back->GetNumPoints() ; k++ ) {
- shadowTri_t fragment = *tri;
-
- fragment.v[0] = (*back)[0].ToVec3();
- fragment.v[1] = (*back)[k-1].ToVec3();
- fragment.v[2] = (*back)[k].ToVec3();
- CreateEdgesForTri( &fragment );
- ClipTriangle_r( &fragment, i + 1, skipTri, numTris, tris );
- }
- delete back;
- }
-
- delete w;
- w = front;
- }
- if ( w ) {
- delete w;
- }
-
- c_removedFragments++;
- // any fragments will have been added recursively
- return;
- }
-
- // this fragment is frontmost, so add it to the output list
- if ( numOutputTris == MAX_SHADOW_TRIS ) {
- common->Error( "numOutputTris == MAX_SHADOW_TRIS" );
- }
-
- outputTris[numOutputTris] = *tri;
- numOutputTris++;
-}
-
-
-/*
-====================
-ClipOccluders
-
-Generates outputTris by clipping all the triangles against each other,
-retaining only those closest to the projectionOrigin
-====================
-*/
-static void ClipOccluders( idVec4 *verts, glIndex_t *indexes, int numIndexes,
- idVec3 projectionOrigin ) {
- int numTris = numIndexes / 3;
- int i;
- shadowTri_t *tris = (shadowTri_t *)_alloca( numTris * sizeof( *tris ) );
- shadowTri_t *tri;
-
- common->Printf( "ClipOccluders: %i triangles\n", numTris );
-
- for ( i = 0 ; i < numTris ; i++ ) {
- tri = &tris[i];
-
- // the indexes are in reversed order from tr_stencilshadow
- tri->v[0] = verts[indexes[i*3+2]].ToVec3() - projectionOrigin;
- tri->v[1] = verts[indexes[i*3+1]].ToVec3() - projectionOrigin;
- tri->v[2] = verts[indexes[i*3+0]].ToVec3() - projectionOrigin;
-
- idVec3 d1 = tri->v[1] - tri->v[0];
- idVec3 d2 = tri->v[2] - tri->v[0];
-
- tri->plane.ToVec4().ToVec3().Cross( d2, d1 );
- tri->plane.ToVec4().ToVec3().Normalize();
- tri->plane[3] = - ( tri->v[0] * tri->plane.ToVec4().ToVec3() );
-
- // get the plane number before any clipping
- // we should avoid polluting the regular dmap planes with these
- // that are offset from the light origin...
- tri->planeNum = FindFloatPlane( tri->plane );
-
- CreateEdgesForTri( tri );
- }
-
- // clear our output buffer
- numOutputTris = 0;
-
- // for each triangle, clip against all other triangles
- int numRemoved = 0;
- int numComplete = 0;
- int numFragmented = 0;
-
- for ( i = 0 ; i < numTris ; i++ ) {
- int oldOutput = numOutputTris;
- c_removedFragments = 0;
- ClipTriangle_r( &tris[i], 0, i, numTris, tris );
- if ( numOutputTris == oldOutput ) {
- numRemoved++; // completely unused
- } else if ( c_removedFragments == 0 ) {
- // the entire triangle is visible
- numComplete++;
- shadowTri_t *out = &outputTris[oldOutput];
- *out = tris[i];
- numOutputTris = oldOutput+1;
- } else {
- numFragmented++;
- // we made at least one fragment
-
- // if we are at the low optimization level, just use a single
- // triangle if it produced any fragments
- if ( dmapGlobals.shadowOptLevel == SO_CULL_OCCLUDED ) {
- shadowTri_t *out = &outputTris[oldOutput];
- *out = tris[i];
- numOutputTris = oldOutput+1;
- }
- }
- }
- common->Printf( "%i triangles completely invisible\n", numRemoved );
- common->Printf( "%i triangles completely visible\n", numComplete );
- common->Printf( "%i triangles fragmented\n", numFragmented );
- common->Printf( "%i shadowing fragments before optimization\n", numOutputTris );
-}
-
-//=====================================================================================
-
-/*
-================
-OptimizeOutputTris
-================
-*/
-static void OptimizeOutputTris( void ) {
- int i;
-
- // optimize the clipped surfaces
- optimizeGroup_t *optGroups = NULL;
- optimizeGroup_t *checkGroup;
-
- for ( i = 0 ; i < numOutputTris ; i++ ) {
- shadowTri_t *tri = &outputTris[i];
-
- int planeNum = tri->planeNum;
-
- // add it to an optimize group
- for ( checkGroup = optGroups ; checkGroup ; checkGroup = checkGroup->nextGroup ) {
- if ( checkGroup->planeNum == planeNum ) {
- break;
- }
- }
- if ( !checkGroup ) {
- // create a new optGroup
- checkGroup = (optimizeGroup_t *)Mem_ClearedAlloc( sizeof( *checkGroup ) );
- checkGroup->planeNum = planeNum;
- checkGroup->nextGroup = optGroups;
- optGroups = checkGroup;
- }
-
- // create a mapTri for the optGroup
- mapTri_t *mtri = (mapTri_t *)Mem_ClearedAlloc( sizeof( *mtri ) );
- mtri->v[0].xyz = tri->v[0];
- mtri->v[1].xyz = tri->v[1];
- mtri->v[2].xyz = tri->v[2];
- mtri->next = checkGroup->triList;
- checkGroup->triList = mtri;
- }
-
- OptimizeGroupList( optGroups );
-
- numOutputTris = 0;
- for ( checkGroup = optGroups ; checkGroup ; checkGroup = checkGroup->nextGroup ) {
- for ( mapTri_t *mtri = checkGroup->triList ; mtri ; mtri = mtri->next ) {
- shadowTri_t *tri = &outputTris[numOutputTris];
- numOutputTris++;
- tri->v[0] = mtri->v[0].xyz;
- tri->v[1] = mtri->v[1].xyz;
- tri->v[2] = mtri->v[2].xyz;
- }
- }
- FreeOptimizeGroupList( optGroups );
-}
-
-//==================================================================================
-
-static int EdgeSort( const void *a, const void *b ) {
- if ( *(unsigned *)a < *(unsigned *)b ) {
- return -1;
- }
- if ( *(unsigned *)a > *(unsigned *)b ) {
- return 1;
- }
- return 0;
-}
-
-/*
-=====================
-GenerateSilEdges
-
-Output tris must be tjunction fixed and vertex uniqued
-A edge that is not exactly matched is a silhouette edge
-We could skip this and rely completely on the matched quad removal
-for all sil edges, but this will avoid the bulk of the checks.
-=====================
-*/
-static void GenerateSilEdges( void ) {
- int i, j;
-
- unsigned *edges = (unsigned *)_alloca( (numOutputTris*3+1)*sizeof(*edges) );
- int numEdges = 0;
-
- numSilEdges = 0;
-
- for ( i = 0 ; i < numOutputTris ; i++ ) {
- int a = outputTris[i].index[0];
- int b = outputTris[i].index[1];
- int c = outputTris[i].index[2];
- if ( a == b || a == c || b == c ) {
- continue; // degenerate
- }
-
- for ( j = 0 ; j < 3 ; j++ ) {
- int v1, v2;
-
- v1 = outputTris[i].index[j];
- v2 = outputTris[i].index[(j+1)%3];
- if ( v1 == v2 ) {
- continue; // degenerate
- }
- if ( v1 > v2 ) {
- edges[numEdges] = ( v1 << 16 ) | ( v2 << 1 );
- } else {
- edges[numEdges] = ( v2 << 16 ) | ( v1 << 1 ) | 1;
- }
- numEdges++;
- }
- }
-
- qsort( edges, numEdges, sizeof( edges[0] ), EdgeSort );
- edges[numEdges] = -1; // force the last to make an edge if no matched to previous
-
- for ( i = 0 ; i < numEdges ; i++ ) {
- if ( ( edges[i] ^ edges[i+1] ) == 1 ) {
- // skip the next one, because we matched and
- // removed both
- i++;
- continue;
- }
- // this is an unmatched edge, so we need to generate a sil plane
- int v1, v2;
- if ( edges[i] & 1 ) {
- v2 = edges[i] >> 16;
- v1 = ( edges[i] >> 1 ) & 0x7fff;
- } else {
- v1 = edges[i] >> 16;
- v2 = ( edges[i] >> 1 ) & 0x7fff;
- }
-
- if ( numSilEdges == MAX_SIL_EDGES ) {
- common->Error( "numSilEdges == MAX_SIL_EDGES" );
- }
- silEdges[numSilEdges].index[0] = v1;
- silEdges[numSilEdges].index[1] = v2;
- numSilEdges++;
- }
-}
-
-//==================================================================================
-
-/*
-=====================
-GenerateSilPlanes
-
-Groups the silEdges into common planes
-=====================
-*/
-void GenerateSilPlanes( void ) {
- numSilPlanes = 0;
- silPlanes = (silPlane_t *)Mem_Alloc( sizeof( *silPlanes ) * numSilEdges );
-
- // identify the silPlanes
- numSilPlanes = 0;
- for ( int i = 0 ; i < numSilEdges ; i++ ) {
- if ( silEdges[i].index[0] == silEdges[i].index[1] ) {
- continue; // degenerate
- }
-
- idVec3 &v1 = uniqued[silEdges[i].index[0]];
- idVec3 &v2 = uniqued[silEdges[i].index[1]];
-
- // search for an existing plane
- int j;
- for ( j = 0 ; j < numSilPlanes ; j++ ) {
- float d = v1 * silPlanes[j].normal;
- float d2 = v2 * silPlanes[j].normal;
-
- if ( fabs( d ) < EDGE_PLANE_EPSILON
- && fabs( d2 ) < EDGE_PLANE_EPSILON ) {
- silEdges[i].nextEdge = silPlanes[j].edges;
- silPlanes[j].edges = &silEdges[i];
- break;
- }
- }
-
- if ( j == numSilPlanes ) {
- // create a new silPlane
- silPlanes[j].normal.Cross( v2, v1 );
- silPlanes[j].normal.Normalize();
- silEdges[i].nextEdge = NULL;
- silPlanes[j].edges = &silEdges[i];
- silPlanes[j].fragmentedQuads = NULL;
- numSilPlanes++;
- }
- }
-}
-
-//==================================================================================
-
-/*
-=============
-SaveQuad
-=============
-*/
-static void SaveQuad( silPlane_t *silPlane, silQuad_t &quad ) {
- // this fragment is a final fragment
- if ( numSilQuads == MAX_SIL_QUADS ) {
- common->Error( "numSilQuads == MAX_SIL_QUADS" );
- }
- silQuads[numSilQuads] = quad;
- silQuads[numSilQuads].nextQuad = silPlane->fragmentedQuads;
- silPlane->fragmentedQuads = &silQuads[numSilQuads];
- numSilQuads++;
-}
-
-
-/*
-===================
-FragmentSilQuad
-
-Clip quads, or reconstruct?
-Generate them T-junction free, or require another pass of fix-tjunc?
-Call optimizer on a per-sil-plane basis?
- will this ever introduce tjunctions with the front faces?
- removal of planes can allow the rear projection to be farther optimized
-
-For quad clipping
- PlaneThroughEdge
-
-quad clipping introduces new vertexes
-
-Cannot just fragment edges, must emit full indexes
-
-what is the bounds on max indexes?
- the worst case is that all edges but one carve an existing edge in the middle,
- giving twice the input number of indexes (I think)
-
-can we avoid knowing about projected positions and still optimize?
-
-Fragment all edges first
-Introduces T-junctions
-create additional silEdges, linked to silPlanes
-
-In theory, we should never have more than one edge clipping a given
-fragment, but it is more robust if we check them all
-===================
-*/
-static void FragmentSilQuad( silQuad_t quad, silPlane_t *silPlane,
- shadowOptEdge_t *startEdge, shadowOptEdge_t *skipEdge ) {
- if ( quad.nearV[0] == quad.nearV[1] ) {
- return;
- }
-
- for ( shadowOptEdge_t *check = startEdge ; check ; check = check->nextEdge ) {
- if ( check == skipEdge ) {
- // don't clip against self
- continue;
- }
-
- if ( check->index[0] == check->index[1] ) {
- continue;
- }
-
- // make planes through both points of check
- for ( int i = 0 ; i < 2 ; i++ ) {
- idVec3 plane;
-
- plane.Cross( uniqued[check->index[i]], silPlane->normal );
- plane.Normalize();
-
- if ( plane.Length() < 0.9 ) {
- continue;
- }
-
- // if the other point on check isn't on the negative side of the plane,
- // flip the plane
- if ( uniqued[check->index[!i]] * plane > 0 ) {
- plane = -plane;
- }
-
- float d1 = uniqued[quad.nearV[0]] * plane;
- float d2 = uniqued[quad.nearV[1]] * plane;
-
- float d3 = uniqued[quad.farV[0]] * plane;
- float d4 = uniqued[quad.farV[1]] * plane;
-
- // it is better to conservatively NOT split the quad, which, at worst,
- // will leave some extra overdraw
-
- // if the plane divides the incoming edge, split it and recurse
- // with the outside fraction before continuing with the inside fraction
- if ( ( d1 > EDGE_PLANE_EPSILON && d3 > EDGE_PLANE_EPSILON && d2 < -EDGE_PLANE_EPSILON && d4 < -EDGE_PLANE_EPSILON )
- || ( d2 > EDGE_PLANE_EPSILON && d4 > EDGE_PLANE_EPSILON && d1 < -EDGE_PLANE_EPSILON && d3 < -EDGE_PLANE_EPSILON ) ) {
- float f = d1 / ( d1 - d2 );
- float f2 = d3 / ( d3 - d4 );
-f = f2;
- if ( f <= 0.0001 || f >= 0.9999 ) {
- common->Error( "Bad silQuad fraction" );
- }
-
- // finding uniques may be causing problems here
- idVec3 nearMid = (1-f) * uniqued[quad.nearV[0]] + f * uniqued[quad.nearV[1]];
- int nearMidIndex = FindUniqueVert( nearMid );
- idVec3 farMid = (1-f) * uniqued[quad.farV[0]] + f * uniqued[quad.farV[1]];
- int farMidIndex = FindUniqueVert( farMid );
-
- silQuad_t clipped = quad;
-
- if ( d1 > EDGE_PLANE_EPSILON ) {
- clipped.nearV[1] = nearMidIndex;
- clipped.farV[1] = farMidIndex;
- FragmentSilQuad( clipped, silPlane, check->nextEdge, skipEdge );
- quad.nearV[0] = nearMidIndex;
- quad.farV[0] = farMidIndex;
- } else {
- clipped.nearV[0] = nearMidIndex;
- clipped.farV[0] = farMidIndex;
- FragmentSilQuad( clipped, silPlane, check->nextEdge, skipEdge );
- quad.nearV[1] = nearMidIndex;
- quad.farV[1] = farMidIndex;
- }
- }
- }
-
- // make a plane through the line of check
- idPlane separate;
-
- idVec3 dir = uniqued[check->index[1]] - uniqued[check->index[0]];
- separate.Normal().Cross( dir, silPlane->normal );
- separate.Normal().Normalize();
- separate.ToVec4()[3] = -(uniqued[check->index[1]] * separate.Normal());
-
- // this may miss a needed separation when the quad would be
- // clipped into a triangle and a quad
- float d1 = separate.Distance( uniqued[quad.nearV[0]] );
- float d2 = separate.Distance( uniqued[quad.farV[0]] );
-
- if ( ( d1 < EDGE_PLANE_EPSILON && d2 < EDGE_PLANE_EPSILON )
- || ( d1 > -EDGE_PLANE_EPSILON && d2 > -EDGE_PLANE_EPSILON ) ) {
- continue;
- }
-
- // split the quad at this plane
- float f = d1 / ( d1 - d2 );
- idVec3 mid0 = (1-f) * uniqued[quad.nearV[0]] + f * uniqued[quad.farV[0]];
- int mid0Index = FindUniqueVert( mid0 );
-
- d1 = separate.Distance( uniqued[quad.nearV[1]] );
- d2 = separate.Distance( uniqued[quad.farV[1]] );
- f = d1 / ( d1 - d2 );
- if ( f < 0 || f > 1 ) {
- continue;
- }
-
- idVec3 mid1 = (1-f) * uniqued[quad.nearV[1]] + f * uniqued[quad.farV[1]];
- int mid1Index = FindUniqueVert( mid1 );
-
- silQuad_t clipped = quad;
-
- clipped.nearV[0] = mid0Index;
- clipped.nearV[1] = mid1Index;
- FragmentSilQuad( clipped, silPlane, check->nextEdge, skipEdge );
- quad.farV[0] = mid0Index;
- quad.farV[1] = mid1Index;
- }
-
- SaveQuad( silPlane, quad );
-}
-
-
-/*
-===============
-FragmentSilQuads
-===============
-*/
-static void FragmentSilQuads( void ) {
- // group the edges into common planes
- GenerateSilPlanes();
-
- numSilQuads = 0;
-
- // fragment overlapping edges
- for ( int i = 0 ; i < numSilPlanes ; i++ ) {
- silPlane_t *sil = &silPlanes[i];
-
- for ( shadowOptEdge_t *e1 = sil->edges ; e1 ; e1 = e1->nextEdge ) {
- silQuad_t quad;
-
- quad.nearV[0] = e1->index[0];
- quad.nearV[1] = e1->index[1];
- if ( e1->index[0] == e1->index[1] ) {
- common->Error( "FragmentSilQuads: degenerate edge" );
- }
- quad.farV[0] = e1->index[0] + numUniquedBeforeProjection;
- quad.farV[1] = e1->index[1] + numUniquedBeforeProjection;
- FragmentSilQuad( quad, sil, sil->edges, e1 );
- }
- }
-}
-
-//=======================================================================
-
-/*
-=====================
-EmitFragmentedSilQuads
-
-=====================
-*/
-static void EmitFragmentedSilQuads( void ) {
- int i, j, k;
- mapTri_t *mtri;
-
- for ( i = 0 ; i < numSilPlanes ; i++ ) {
- silPlane_t *sil = &silPlanes[i];
-
- // prepare for optimizing the sil quads on each side of the sil plane
- optimizeGroup_t groups[2];
- memset( &groups, 0, sizeof( groups ) );
- idPlane planes[2];
- planes[0].Normal() = sil->normal;
- planes[0][3] = 0;
- planes[1] = -planes[0];
- groups[0].planeNum = FindFloatPlane( planes[0] );
- groups[1].planeNum = FindFloatPlane( planes[1] );
-
- // emit the quads that aren't matched
- for ( silQuad_t *f1 = sil->fragmentedQuads ; f1 ; f1 = f1->nextQuad ) {
- silQuad_t *f2;
- for ( f2 = sil->fragmentedQuads ; f2 ; f2 = f2->nextQuad ) {
- if ( f2 == f1 ) {
- continue;
- }
- // in theory, this is sufficient, but we might
- // have some cases of tripple+ matching, or unclipped rear projections
- if ( f1->nearV[0] == f2->nearV[1] && f1->nearV[1] == f2->nearV[0] ) {
- break;
- }
- }
- // if we went through all the quads without finding a match, emit the quad
- if ( !f2 ) {
- optimizeGroup_t *gr;
- idVec3 v1, v2, normal;
-
- mtri = (mapTri_t *)Mem_ClearedAlloc( sizeof( *mtri ) );
- mtri->v[0].xyz = uniqued[f1->nearV[0]];
- mtri->v[1].xyz = uniqued[f1->nearV[1]];
- mtri->v[2].xyz = uniqued[f1->farV[1]];
-
- v1 = mtri->v[1].xyz - mtri->v[0].xyz;
- v2 = mtri->v[2].xyz - mtri->v[0].xyz;
- normal.Cross( v2, v1 );
-
- if ( normal * planes[0].Normal() > 0 ) {
- gr = &groups[0];
- } else {
- gr = &groups[1];
- }
-
- mtri->next = gr->triList;
- gr->triList = mtri;
-
- mtri = (mapTri_t *)Mem_ClearedAlloc( sizeof( *mtri ) );
- mtri->v[0].xyz = uniqued[f1->farV[0]];
- mtri->v[1].xyz = uniqued[f1->nearV[0]];
- mtri->v[2].xyz = uniqued[f1->farV[1]];
-
- mtri->next = gr->triList;
- gr->triList = mtri;
-
-#if 0
- // emit a sil quad all the way to the projection plane
- int index = ret.totalIndexes;
- if ( index + 6 > maxRetIndexes ) {
- common->Error( "maxRetIndexes exceeded" );
- }
- ret.indexes[index+0] = f1->nearV[0];
- ret.indexes[index+1] = f1->nearV[1];
- ret.indexes[index+2] = f1->farV[1];
- ret.indexes[index+3] = f1->farV[0];
- ret.indexes[index+4] = f1->nearV[0];
- ret.indexes[index+5] = f1->farV[1];
- ret.totalIndexes += 6;
-#endif
- }
- }
-
-
- // optimize
- for ( j = 0 ; j < 2 ; j++ ) {
- if ( !groups[j].triList ) {
- continue;
- }
- if ( dmapGlobals.shadowOptLevel == SO_SIL_OPTIMIZE ) {
- OptimizeGroupList( &groups[j] );
- }
- // add as indexes
- for ( mtri = groups[j].triList ; mtri ; mtri = mtri->next ) {
- for ( k = 0 ; k < 3 ; k++ ) {
- if ( ret.totalIndexes == maxRetIndexes ) {
- common->Error( "maxRetIndexes exceeded" );
- }
- ret.indexes[ret.totalIndexes] = FindUniqueVert( mtri->v[k].xyz );
- ret.totalIndexes++;
- }
- }
- FreeTriList( groups[j].triList );
- }
- }
-
- // we don't need the silPlane grouping anymore
- Mem_Free( silPlanes );
-}
-
-/*
-=================
-EmitUnoptimizedSilEdges
-=================
-*/
-static void EmitUnoptimizedSilEdges( void ) {
- int i;
-
- for ( i = 0 ; i < numSilEdges ; i++ ) {
- int v1 = silEdges[i].index[0];
- int v2 = silEdges[i].index[1];
- int index = ret.totalIndexes;
- ret.indexes[index+0] = v1;
- ret.indexes[index+1] = v2;
- ret.indexes[index+2] = v2+numUniquedBeforeProjection;
- ret.indexes[index+3] = v1+numUniquedBeforeProjection;
- ret.indexes[index+4] = v1;
- ret.indexes[index+5] = v2+numUniquedBeforeProjection;
- ret.totalIndexes += 6;
- }
-}
-
-//==================================================================================
-
-/*
-================
-FindUniqueVert
-================
-*/
-static int FindUniqueVert( idVec3 &v ) {
- int k;
-
- for ( k = 0 ; k < numUniqued ; k++ ) {
- idVec3 &check = uniqued[k];
- if ( fabs( v[0] - check[0] ) < UNIQUE_EPSILON
- && fabs( v[1] - check[1] ) < UNIQUE_EPSILON
- && fabs( v[2] - check[2] ) < UNIQUE_EPSILON ) {
- return k;
- }
- }
- if ( numUniqued == maxUniqued ) {
- common->Error( "FindUniqueVert: numUniqued == maxUniqued" );
- }
- uniqued[numUniqued] = v;
- numUniqued++;
-
- return k;
-}
-
-/*
-===================
-UniqueVerts
-
-Snaps all triangle verts together, setting tri->index[]
-and generating numUniqued and uniqued.
-These are still in projection-centered space, not global space
-===================
-*/
-static void UniqueVerts( void ) {
- int i, j;
-
- // we may add to uniqued later when splitting sil edges, so leave
- // some extra room
- maxUniqued = 100000; // numOutputTris * 10 + 1000;
- uniqued = (idVec3 *)Mem_Alloc( sizeof( *uniqued ) * maxUniqued );
- numUniqued = 0;
-
- for ( i = 0 ; i < numOutputTris ; i++ ) {
- for ( j = 0 ; j < 3 ; j++ ) {
- outputTris[i].index[j] = FindUniqueVert( outputTris[i].v[j] );
- }
- }
-}
-
-/*
-======================
-ProjectUniqued
-======================
-*/
-static void ProjectUniqued( idVec3 projectionOrigin, idPlane projectionPlane ) {
- // calculate the projection
- idVec4 mat[4];
-
- R_LightProjectionMatrix( projectionOrigin, projectionPlane, mat );
-
- if ( numUniqued * 2 > maxUniqued ) {
- common->Error( "ProjectUniqued: numUniqued * 2 > maxUniqued" );
- }
-
- // this is goofy going back and forth between the spaces,
- // but I don't want to change R_LightProjectionMatrix righ tnow...
- for ( int i = 0 ; i < numUniqued ; i++ ) {
- // put the vert back in global space, instead of light centered space
- idVec3 in = uniqued[i] + projectionOrigin;
-
- // project to far plane
- float w, oow;
- idVec3 out;
-
- w = in * mat[3].ToVec3() + mat[3][3];
-
- oow = 1.0 / w;
- out.x = ( in * mat[0].ToVec3() + mat[0][3] ) * oow;
- out.y = ( in * mat[1].ToVec3() + mat[1][3] ) * oow;
- out.z = ( in * mat[2].ToVec3() + mat[2][3] ) * oow;
-
- uniqued[numUniqued+i] = out - projectionOrigin;
- }
- numUniqued *= 2;
-}
-
-/*
-====================
-SuperOptimizeOccluders
-
-This is the callback from the renderer shadow generation routine, after
-verts have been culled against individual frustums of point lights
-
-====================
-*/
-optimizedShadow_t SuperOptimizeOccluders( idVec4 *verts, glIndex_t *indexes, int numIndexes,
- idPlane projectionPlane, idVec3 projectionOrigin )
-{
- memset( &ret, 0, sizeof( ret ) );
-
- // generate outputTris, removing fragments that are occluded by closer fragments
- ClipOccluders( verts, indexes, numIndexes, projectionOrigin );
-
- if ( dmapGlobals.shadowOptLevel >= SO_CULL_OCCLUDED ) {
- OptimizeOutputTris();
- }
-
- // match up common verts
- UniqueVerts();
-
- // now that we have uniqued the vertexes, we can find unmatched
- // edges, which are silhouette planes
- GenerateSilEdges();
-
- // generate the projected verts
- numUniquedBeforeProjection = numUniqued;
- ProjectUniqued( projectionOrigin, projectionPlane );
-
- // fragment the sil edges where the overlap,
- // possibly generating some additional unique verts
- if ( dmapGlobals.shadowOptLevel >= SO_CLIP_SILS ) {
- FragmentSilQuads();
- }
-
- // indexes for face and projection caps
- ret.numFrontCapIndexes = numOutputTris * 3;
- ret.numRearCapIndexes = numOutputTris * 3;
- if ( dmapGlobals.shadowOptLevel >= SO_CLIP_SILS ) {
- ret.numSilPlaneIndexes = numSilQuads * 12; // this is the worst case with clipping
- } else {
- ret.numSilPlaneIndexes = numSilEdges * 6; // this is the worst case with clipping
- }
-
- ret.totalIndexes = 0;
-
- maxRetIndexes = ret.numFrontCapIndexes + ret.numRearCapIndexes + ret.numSilPlaneIndexes;
-
- ret.indexes = (glIndex_t *)Mem_Alloc( maxRetIndexes * sizeof( ret.indexes[0] ) );
- for ( int i = 0 ; i < numOutputTris ; i++ ) {
- // flip the indexes so the surface triangle faces outside the shadow volume
- ret.indexes[i*3+0] = outputTris[i].index[2];
- ret.indexes[i*3+1] = outputTris[i].index[1];
- ret.indexes[i*3+2] = outputTris[i].index[0];
-
- ret.indexes[(numOutputTris+i)*3+0] = numUniquedBeforeProjection + outputTris[i].index[0];
- ret.indexes[(numOutputTris+i)*3+1] = numUniquedBeforeProjection + outputTris[i].index[1];
- ret.indexes[(numOutputTris+i)*3+2] = numUniquedBeforeProjection + outputTris[i].index[2];
- }
- // emit the sil planes
- ret.totalIndexes = ret.numFrontCapIndexes + ret.numRearCapIndexes;
-
- if ( dmapGlobals.shadowOptLevel >= SO_CLIP_SILS ) {
- // re-optimize the sil planes, cutting
- EmitFragmentedSilQuads();
- } else {
- // indexes for silhouette edges
- EmitUnoptimizedSilEdges();
- }
-
- // we have all the verts now
- // create twice the uniqued verts
- ret.numVerts = numUniqued;
- ret.verts = (idVec3 *)Mem_Alloc( ret.numVerts * sizeof( ret.verts[0] ) );
- for ( int i = 0 ; i < numUniqued ; i++ ) {
- // put the vert back in global space, instead of light centered space
- ret.verts[i] = uniqued[i] + projectionOrigin;
- }
-
- // set the final index count
- ret.numSilPlaneIndexes = ret.totalIndexes - (ret.numFrontCapIndexes + ret.numRearCapIndexes);
-
- // free out local data
- Mem_Free( uniqued );
-
- return ret;
-}
-
-/*
-=================
-RemoveDegenerateTriangles
-=================
-*/
-static void RemoveDegenerateTriangles( srfTriangles_t *tri ) {
- int c_removed;
- int i;
- int a, b, c;
-
- // check for completely degenerate triangles
- c_removed = 0;
- for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
- a = tri->indexes[i];
- b = tri->indexes[i+1];
- c = tri->indexes[i+2];
- if ( a == b || a == c || b == c ) {
- c_removed++;
- memmove( tri->indexes + i, tri->indexes + i + 3, ( tri->numIndexes - i - 3 ) * sizeof( tri->indexes[0] ) );
- tri->numIndexes -= 3;
- if ( i < tri->numShadowIndexesNoCaps ) {
- tri->numShadowIndexesNoCaps -= 3;
- }
- if ( i < tri->numShadowIndexesNoFrontCaps ) {
- tri->numShadowIndexesNoFrontCaps -= 3;
- }
- i -= 3;
- }
- }
-
- // this doesn't free the memory used by the unused verts
-
- if ( c_removed ) {
- common->Printf( "removed %i degenerate triangles from shadow\n", c_removed );
- }
-}
-
-/*
-====================
-CleanupOptimizedShadowTris
-
-Uniques all verts across the frustums
-removes matched sil quads at frustum seams
-removes degenerate tris
-====================
-*/
-void CleanupOptimizedShadowTris( srfTriangles_t *tri ) {
- int i;
-
- // unique all the verts
- maxUniqued = tri->numVerts;
- uniqued = (idVec3 *)_alloca( sizeof( *uniqued ) * maxUniqued );
- numUniqued = 0;
-
- glIndex_t *remap = (glIndex_t *)_alloca( sizeof( *remap ) * tri->numVerts );
-
- for ( i = 0 ; i < tri->numIndexes ; i++ ) {
- if ( tri->indexes[i] > tri->numVerts || tri->indexes[i] < 0 ) {
- common->Error( "CleanupOptimizedShadowTris: index out of range" );
- }
- }
-
- for ( i = 0 ; i < tri->numVerts ; i++ ) {
- remap[i] = FindUniqueVert( tri->shadowVertexes[i].xyz.ToVec3() );
- }
- tri->numVerts = numUniqued;
- for ( i = 0 ; i < tri->numVerts ; i++ ) {
- tri->shadowVertexes[i].xyz.ToVec3() = uniqued[i];
- tri->shadowVertexes[i].xyz[3] = 1;
- }
-
- for ( i = 0 ; i < tri->numIndexes ; i++ ) {
- tri->indexes[i] = remap[tri->indexes[i]];
- }
-
- // remove matched quads
- int numSilIndexes = tri->numShadowIndexesNoCaps;
- for ( int i = 0 ; i < numSilIndexes ; i+=6 ) {
- int j;
- for ( j = i+6 ; j < numSilIndexes ; j+=6 ) {
- // if there is a reversed quad match, we can throw both of them out
- // this is not a robust check, it relies on the exact ordering of
- // quad indexes
- if ( tri->indexes[i+0] == tri->indexes[j+1]
- && tri->indexes[i+1] == tri->indexes[j+0]
- && tri->indexes[i+2] == tri->indexes[j+3]
- && tri->indexes[i+3] == tri->indexes[j+5]
- && tri->indexes[i+4] == tri->indexes[j+1]
- && tri->indexes[i+5] == tri->indexes[j+3] ) {
- break;
- }
- }
- if ( j == numSilIndexes ) {
- continue;
- }
- int k;
- // remove first quad
- for ( k = i+6 ; k < j ; k++ ) {
- tri->indexes[k-6] = tri->indexes[k];
- }
- // remove second quad
- for ( k = j+6 ; k < tri->numIndexes ; k++ ) {
- tri->indexes[k-12] = tri->indexes[k];
- }
- numSilIndexes -= 12;
- i -= 6;
- }
-
- int removed = tri->numShadowIndexesNoCaps - numSilIndexes;
-
- tri->numIndexes -= removed;
- tri->numShadowIndexesNoCaps -= removed;
- tri->numShadowIndexesNoFrontCaps -= removed;
-
- // remove degenerates after we have removed quads, so the double
- // triangle pairing isn't disturbed
- RemoveDegenerateTriangles( tri );
-}
-
-/*
-========================
-CreateLightShadow
-
-This is called from dmap in util/surface.cpp
-shadowerGroups should be exactly clipped to the light frustum before calling.
-shadowerGroups is optimized by this function, but the contents can be freed, because the returned
-lightShadow_t list is a further culling and optimization of the data.
-========================
-*/
-srfTriangles_t *CreateLightShadow( optimizeGroup_t *shadowerGroups, const mapLight_t *light ) {;
-
- common->Printf( "----- CreateLightShadow %p -----\n", light );
-
- // optimize all the groups
- OptimizeGroupList( shadowerGroups );
-
- // combine all the triangles into one list
- mapTri_t *combined;
-
- combined = NULL;
- for ( optimizeGroup_t *group = shadowerGroups ; group ; group = group->nextGroup ) {
- combined = MergeTriLists( combined, CopyTriList( group->triList ) );
- }
-
- if ( !combined ) {
- return NULL;
- }
-
- // find uniqued vertexes
- srfTriangles_t *occluders = ShareMapTriVerts( combined );
-
- FreeTriList( combined );
-
- // find silhouette information for the triSurf
- R_CleanupTriangles( occluders, false, true, false );
-
- // let the renderer build the shadow volume normally
- idRenderEntityLocal space;
-
- space.modelMatrix[0] = 1;
- space.modelMatrix[5] = 1;
- space.modelMatrix[10] = 1;
- space.modelMatrix[15] = 1;
-
- srfCullInfo_t cullInfo;
- memset( &cullInfo, 0, sizeof( cullInfo ) );
-
- // call the normal shadow creation, but with the superOptimize flag set, which will
- // call back to SuperOptimizeOccluders after clipping the triangles to each frustum
- srfTriangles_t *shadowTris;
- if ( dmapGlobals.shadowOptLevel == SO_MERGE_SURFACES ) {
- shadowTris = R_CreateShadowVolume( &space, occluders, &light->def, SG_STATIC, cullInfo );
- } else {
- shadowTris = R_CreateShadowVolume( &space, occluders, &light->def, SG_OFFLINE, cullInfo );
- }
- R_FreeStaticTriSurf( occluders );
-
- R_FreeInteractionCullInfo( cullInfo );
-
- if ( shadowTris ) {
- dmapGlobals.totalShadowTriangles += shadowTris->numIndexes / 3;
- dmapGlobals.totalShadowVerts += shadowTris->numVerts / 3;
- }
-
- return shadowTris;
-}
diff --git a/neo/tools/compilers/dmap/usurface.cpp b/neo/tools/compilers/dmap/usurface.cpp
index 52297161..44d578d5 100644
--- a/neo/tools/compilers/dmap/usurface.cpp
+++ b/neo/tools/compilers/dmap/usurface.cpp
@@ -885,7 +885,7 @@ static void BuildLightShadows( uEntity_t *e, mapLight_t *light ) {
}
// take the shadower group list and create a beam tree and shadow volume
- light->shadowTris = CreateLightShadow( shadowerGroups, light );
+ //light->shadowTris = CreateLightShadow( shadowerGroups, light );
if ( light->shadowTris && hasPerforatedSurface ) {
// can't ever remove front faces, because we can see through some of them