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