1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "tr_local.h"
- // 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<<frustumNum;
- }
- }
- // if no faces have been defined for the shadow volume,
- // there won't be anything at all
- if ( numShadowIndexes == 0 ) {
- return NULL;
- }
- // this should have been prevented by the overflowed flag, so if it ever happens,
- // it is a code error
- if ( numShadowVerts > 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;
- }
|