dhewm3/neo/tools/compilers/dmap/usurface.cpp

1047 lines
27 KiB
C++
Raw Normal View History

2011-11-22 21:28:15 +00:00
/*
===========================================================================
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 "dmap.h"
#define TEXTURE_OFFSET_EQUAL_EPSILON 0.005
#define TEXTURE_VECTOR_EQUAL_EPSILON 0.001
/*
===============
AddTriListToArea
The triList is appended to the apropriate optimzeGroup_t,
creating a new one if needed.
The entire list is assumed to come from the same planar primitive
===============
*/
static void AddTriListToArea( uEntity_t *e, mapTri_t *triList, int planeNum, int areaNum, textureVectors_t *texVec ) {
uArea_t *area;
optimizeGroup_t *group;
int i, j;
if ( !triList ) {
return;
}
area = &e->areas[areaNum];
for ( group = area->groups ; group ; group = group->nextGroup ) {
if ( group->material == triList->material
&& group->planeNum == planeNum
&& group->mergeGroup == triList->mergeGroup ) {
// check the texture vectors
for ( i = 0 ; i < 2 ; i++ ) {
for ( j = 0 ; j < 3 ; j++ ) {
if ( idMath::Fabs( texVec->v[i][j] - group->texVec.v[i][j] ) > TEXTURE_VECTOR_EQUAL_EPSILON ) {
break;
}
}
if ( j != 3 ) {
break;
}
if ( idMath::Fabs( texVec->v[i][3] - group->texVec.v[i][3] ) > TEXTURE_OFFSET_EQUAL_EPSILON ) {
break;
}
}
if ( i == 2 ) {
break; // exact match
} else {
// different texture offsets
i = 1; // just for debugger breakpoint
}
}
}
if ( !group ) {
group = (optimizeGroup_t *)Mem_Alloc( sizeof( *group ) );
memset( group, 0, sizeof( *group ) );
group->planeNum = planeNum;
group->mergeGroup = triList->mergeGroup;
group->material = triList->material;
group->nextGroup = area->groups;
group->texVec = *texVec;
area->groups = group;
}
group->triList = MergeTriLists( group->triList, triList );
}
/*
===================
TexVecForTri
===================
*/
static void TexVecForTri( textureVectors_t *texVec, mapTri_t *tri ) {
float area, inva;
idVec3 temp;
idVec5 d0, d1;
idDrawVert *a, *b, *c;
a = &tri->v[0];
b = &tri->v[1];
c = &tri->v[2];
d0[0] = b->xyz[0] - a->xyz[0];
d0[1] = b->xyz[1] - a->xyz[1];
d0[2] = b->xyz[2] - a->xyz[2];
d0[3] = b->st[0] - a->st[0];
d0[4] = b->st[1] - a->st[1];
d1[0] = c->xyz[0] - a->xyz[0];
d1[1] = c->xyz[1] - a->xyz[1];
d1[2] = c->xyz[2] - a->xyz[2];
d1[3] = c->st[0] - a->st[0];
d1[4] = c->st[1] - a->st[1];
area = d0[3] * d1[4] - d0[4] * d1[3];
inva = 1.0 / area;
temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
temp.Normalize();
texVec->v[0].ToVec3() = temp;
texVec->v[0][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].st[0];
temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
temp.Normalize();
texVec->v[1].ToVec3() = temp;
texVec->v[1][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].st[1];
}
/*
=================
TriListForSide
=================
*/
//#define SNAP_FLOAT_TO_INT 8
#define SNAP_FLOAT_TO_INT 256
#define SNAP_INT_TO_FLOAT (1.0/SNAP_FLOAT_TO_INT)
mapTri_t *TriListForSide( const side_t *s, const idWinding *w ) {
int i, j;
idDrawVert *dv;
mapTri_t *tri, *triList;
const idVec3 *vec;
const idMaterial *si;
si = s->material;
// skip any generated faces
if ( !si ) {
return NULL;
}
// don't create faces for non-visible sides
if ( !si->SurfaceCastsShadow() && !si->IsDrawn() ) {
return NULL;
}
if ( 1 ) {
// triangle fan using only the outer verts
// this gives the minimum triangle count,
// but may have some very distended triangles
triList = NULL;
for ( i = 2 ; i < w->GetNumPoints() ; i++ ) {
tri = AllocTri();
tri->material = si;
tri->next = triList;
triList = tri;
for ( j = 0 ; j < 3 ; j++ ) {
if ( j == 0 ) {
vec = &((*w)[0]).ToVec3();
} else if ( j == 1 ) {
vec = &((*w)[i-1]).ToVec3();
} else {
vec = &((*w)[i]).ToVec3();
}
dv = tri->v + j;
#if 0
// round the xyz to a given precision
for ( k = 0 ; k < 3 ; k++ ) {
dv->xyz[k] = SNAP_INT_TO_FLOAT * floor( vec[k] * SNAP_FLOAT_TO_INT + 0.5 );
}
#else
VectorCopy( *vec, dv->xyz );
#endif
// calculate texture s/t from brush primitive texture matrix
dv->st[0] = DotProduct( dv->xyz, s->texVec.v[0] ) + s->texVec.v[0][3];
dv->st[1] = DotProduct( dv->xyz, s->texVec.v[1] ) + s->texVec.v[1][3];
// copy normal
dv->normal = dmapGlobals.mapPlanes[s->planenum].Normal();
if ( dv->normal.Length() < 0.9 || dv->normal.Length() > 1.1 ) {
common->Error( "Bad normal in TriListForSide" );
}
}
}
} else {
// triangle fan from central point, more verts and tris, but less distended
// I use this when debugging some tjunction problems
triList = NULL;
for ( i = 0 ; i < w->GetNumPoints() ; i++ ) {
idVec3 midPoint;
tri = AllocTri();
tri->material = si;
tri->next = triList;
triList = tri;
for ( j = 0 ; j < 3 ; j++ ) {
if ( j == 0 ) {
vec = &midPoint;
midPoint = w->GetCenter();
} else if ( j == 1 ) {
vec = &((*w)[i]).ToVec3();
} else {
vec = &((*w)[(i+1)%w->GetNumPoints()]).ToVec3();
}
dv = tri->v + j;
VectorCopy( *vec, dv->xyz );
// calculate texture s/t from brush primitive texture matrix
dv->st[0] = DotProduct( dv->xyz, s->texVec.v[0] ) + s->texVec.v[0][3];
dv->st[1] = DotProduct( dv->xyz, s->texVec.v[1] ) + s->texVec.v[1][3];
// copy normal
dv->normal = dmapGlobals.mapPlanes[s->planenum].Normal();
if ( dv->normal.Length() < 0.9f || dv->normal.Length() > 1.1f ) {
common->Error( "Bad normal in TriListForSide" );
}
}
}
}
// set merge groups if needed, to prevent multiple sides from being
// merged into a single surface in the case of gui shaders, mirrors, and autosprites
if ( s->material->IsDiscrete() ) {
for ( tri = triList ; tri ; tri = tri->next ) {
tri->mergeGroup = (void *)s;
}
}
return triList;
}
//=================================================================================
/*
====================
ClipSideByTree_r
Adds non-opaque leaf fragments to the convex hull
====================
*/
static void ClipSideByTree_r( idWinding *w, side_t *side, node_t *node ) {
idWinding *front, *back;
if ( !w ) {
return;
}
if ( node->planenum != PLANENUM_LEAF ) {
if ( side->planenum == node->planenum ) {
ClipSideByTree_r( w, side, node->children[0] );
return;
}
if ( side->planenum == ( node->planenum ^ 1) ) {
ClipSideByTree_r( w, side, node->children[1] );
return;
}
w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
delete w;
ClipSideByTree_r( front, side, node->children[0] );
ClipSideByTree_r( back, side, node->children[1] );
return;
}
// if opaque leaf, don't add
if ( !node->opaque ) {
if ( !side->visibleHull ) {
side->visibleHull = w->Copy();
}
else {
side->visibleHull->AddToConvexHull( w, dmapGlobals.mapPlanes[ side->planenum ].Normal() );
}
}
delete w;
return;
}
/*
=====================
ClipSidesByTree
Creates side->visibleHull for all visible sides
The visible hull for a side will consist of the convex hull of
all points in non-opaque clusters, which allows overlaps
to be trimmed off automatically.
=====================
*/
void ClipSidesByTree( uEntity_t *e ) {
uBrush_t *b;
int i;
idWinding *w;
side_t *side;
primitive_t *prim;
common->Printf( "----- ClipSidesByTree -----\n");
for ( prim = e->primitives ; prim ; prim = prim->next ) {
b = prim->brush;
if ( !b ) {
// FIXME: other primitives!
continue;
}
for ( i = 0 ; i < b->numsides ; i++ ) {
side = &b->sides[i];
if ( !side->winding) {
continue;
}
w = side->winding->Copy();
side->visibleHull = NULL;
ClipSideByTree_r( w, side, e->tree->headnode );
// for debugging, we can choose to use the entire original side
// but we skip this if the side was completely clipped away
if ( side->visibleHull && dmapGlobals.noClipSides ) {
delete side->visibleHull;
side->visibleHull = side->winding->Copy();
}
}
}
}
//=================================================================================
/*
====================
ClipTriIntoTree_r
This is used for adding curve triangles
The winding will be freed before it returns
====================
*/
void ClipTriIntoTree_r( idWinding *w, mapTri_t *originalTri, uEntity_t *e, node_t *node ) {
idWinding *front, *back;
if ( !w ) {
return;
}
if ( node->planenum != PLANENUM_LEAF ) {
w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
delete w;
ClipTriIntoTree_r( front, originalTri, e, node->children[0] );
ClipTriIntoTree_r( back, originalTri, e, node->children[1] );
return;
}
// if opaque leaf, don't add
if ( !node->opaque && node->area >= 0 ) {
mapTri_t *list;
int planeNum;
idPlane plane;
textureVectors_t texVec;
list = WindingToTriList( w, originalTri );
PlaneForTri( originalTri, plane );
planeNum = FindFloatPlane( plane );
TexVecForTri( &texVec, originalTri );
AddTriListToArea( e, list, planeNum, node->area, &texVec );
}
delete w;
return;
}
//=============================================================
/*
====================
CheckWindingInAreas_r
Returns the area number that the winding is in, or
-2 if it crosses multiple areas.
====================
*/
static int CheckWindingInAreas_r( const idWinding *w, node_t *node ) {
idWinding *front, *back;
if ( !w ) {
return -1;
}
if ( node->planenum != PLANENUM_LEAF ) {
int a1, a2;
#if 0
if ( side->planenum == node->planenum ) {
return CheckWindingInAreas_r( w, node->children[0] );
}
if ( side->planenum == ( node->planenum ^ 1) ) {
return CheckWindingInAreas_r( w, node->children[1] );
}
#endif
w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
a1 = CheckWindingInAreas_r( front, node->children[0] );
delete front;
a2 = CheckWindingInAreas_r( back, node->children[1] );
delete back;
if ( a1 == -2 || a2 == -2 ) {
return -2; // different
}
if ( a1 == -1 ) {
return a2; // one solid
}
if ( a2 == -1 ) {
return a1; // one solid
}
if ( a1 != a2 ) {
return -2; // cross areas
}
return a1;
}
return node->area;
}
/*
====================
PutWindingIntoAreas_r
Clips a winding down into the bsp tree, then converts
the fragments to triangles and adds them to the area lists
====================
*/
static void PutWindingIntoAreas_r( uEntity_t *e, const idWinding *w, side_t *side, node_t *node ) {
idWinding *front, *back;
int area;
if ( !w ) {
return;
}
if ( node->planenum != PLANENUM_LEAF ) {
if ( side->planenum == node->planenum ) {
PutWindingIntoAreas_r( e, w, side, node->children[0] );
return;
}
if ( side->planenum == ( node->planenum ^ 1) ) {
PutWindingIntoAreas_r( e, w, side, node->children[1] );
return;
}
// see if we need to split it
// adding the "noFragment" flag to big surfaces like sky boxes
// will avoid potentially dicing them up into tons of triangles
// that take forever to optimize back together
if ( !dmapGlobals.fullCarve || side->material->NoFragment() ) {
area = CheckWindingInAreas_r( w, node );
if ( area >= 0 ) {
mapTri_t *tri;
// put in single area
tri = TriListForSide( side, w );
AddTriListToArea( e, tri, side->planenum, area, &side->texVec );
return;
}
}
w->Split( dmapGlobals.mapPlanes[ node->planenum ], ON_EPSILON, &front, &back );
PutWindingIntoAreas_r( e, front, side, node->children[0] );
if ( front ) {
delete front;
}
PutWindingIntoAreas_r( e, back, side, node->children[1] );
if ( back ) {
delete back;
}
return;
}
// if opaque leaf, don't add
if ( node->area >= 0 && !node->opaque ) {
mapTri_t *tri;
tri = TriListForSide( side, w );
AddTriListToArea( e, tri, side->planenum, node->area, &side->texVec );
}
}
/*
==================
AddMapTriToAreas
Used for curves and inlined models
==================
*/
void AddMapTriToAreas( mapTri_t *tri, uEntity_t *e ) {
int area;
idWinding *w;
// skip degenerate triangles from pinched curves
if ( MapTriArea( tri ) <= 0 ) {
return;
}
if ( dmapGlobals.fullCarve ) {
// always fragment into areas
w = WindingForTri( tri );
ClipTriIntoTree_r( w, tri, e, e->tree->headnode );
return;
}
w = WindingForTri( tri );
area = CheckWindingInAreas_r( w, e->tree->headnode );
delete w;
if ( area == -1 ) {
return;
}
if ( area >= 0 ) {
mapTri_t *newTri;
idPlane plane;
int planeNum;
textureVectors_t texVec;
// put in single area
newTri = CopyMapTri( tri );
newTri->next = NULL;
PlaneForTri( tri, plane );
planeNum = FindFloatPlane( plane );
TexVecForTri( &texVec, newTri );
AddTriListToArea( e, newTri, planeNum, area, &texVec );
} else {
// fragment into areas
w = WindingForTri( tri );
ClipTriIntoTree_r( w, tri, e, e->tree->headnode );
}
}
/*
=====================
PutPrimitivesInAreas
=====================
*/
void PutPrimitivesInAreas( uEntity_t *e ) {
uBrush_t *b;
int i;
side_t *side;
primitive_t *prim;
mapTri_t *tri;
common->Printf( "----- PutPrimitivesInAreas -----\n");
// allocate space for surface chains for each area
e->areas = (uArea_t *)Mem_Alloc( e->numAreas * sizeof( e->areas[0] ) );
memset( e->areas, 0, e->numAreas * sizeof( e->areas[0] ) );
// for each primitive, clip it to the non-solid leafs
// and divide it into different areas
for ( prim = e->primitives ; prim ; prim = prim->next ) {
b = prim->brush;
if ( !b ) {
// add curve triangles
for ( tri = prim->tris ; tri ; tri = tri->next ) {
AddMapTriToAreas( tri, e );
}
continue;
}
// clip in brush sides
for ( i = 0 ; i < b->numsides ; i++ ) {
side = &b->sides[i];
if ( !side->visibleHull ) {
continue;
}
PutWindingIntoAreas_r( e, side->visibleHull, side, e->tree->headnode );
}
}
// optionally inline some of the func_static models
if ( dmapGlobals.entityNum == 0 ) {
bool inlineAll = dmapGlobals.uEntities[0].mapEntity->epairs.GetBool( "inlineAllStatics" );
for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) {
uEntity_t *entity = &dmapGlobals.uEntities[eNum];
const char *className = entity->mapEntity->epairs.GetString( "classname" );
if ( idStr::Icmp( className, "func_static" ) ) {
continue;
}
if ( !entity->mapEntity->epairs.GetBool( "inline" ) && !inlineAll ) {
continue;
}
const char *modelName = entity->mapEntity->epairs.GetString( "model" );
if ( !modelName ) {
continue;
}
idRenderModel *model = renderModelManager->FindModel( modelName );
common->Printf( "inlining %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
idMat3 axis;
// get the rotation matrix in either full form, or single angle form
if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
float angle = entity->mapEntity->epairs.GetFloat( "angle" );
if ( angle != 0.0f ) {
axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
} else {
axis.Identity();
}
}
idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
const modelSurface_t *surface = model->Surface( i );
const srfTriangles_t *tri = surface->geometry;
mapTri_t mapTri;
memset( &mapTri, 0, sizeof( mapTri ) );
mapTri.material = surface->shader;
// don't let discretes (autosprites, etc) merge together
if ( mapTri.material->IsDiscrete() ) {
mapTri.mergeGroup = (void *)surface;
}
for ( int j = 0 ; j < tri->numIndexes ; j += 3 ) {
for ( int k = 0 ; k < 3 ; k++ ) {
idVec3 v = tri->verts[tri->indexes[j+k]].xyz;
mapTri.v[k].xyz = v * axis + origin;
mapTri.v[k].normal = tri->verts[tri->indexes[j+k]].normal * axis;
mapTri.v[k].st = tri->verts[tri->indexes[j+k]].st;
}
AddMapTriToAreas( &mapTri, e );
}
}
}
}
}
//============================================================================
/*
=================
ClipTriByLight
Carves a triangle by the frustom planes of a light, producing
a (possibly empty) list of triangles on the inside and outside.
The original triangle is not modified.
If no clipping is required, the result will be a copy of the original.
If clipping was required, the outside fragments will be planar clips, which
will benefit from re-optimization.
=================
*/
static void ClipTriByLight( const mapLight_t *light, const mapTri_t *tri,
mapTri_t **in, mapTri_t **out ) {
idWinding *inside, *oldInside;
idWinding *outside[6];
bool hasOutside;
int i;
*in = NULL;
*out = NULL;
// clip this winding to the light
inside = WindingForTri( tri );
hasOutside = false;
for ( i = 0 ; i < 6 ; i++ ) {
oldInside = inside;
if ( oldInside ) {
oldInside->Split( light->def.frustum[i], 0, &outside[i], &inside );
delete oldInside;
}
else {
outside[i] = NULL;
}
if ( outside[i] ) {
hasOutside = true;
}
}
if ( !inside ) {
// the entire winding is outside this light
// free the clipped fragments
for ( i = 0 ; i < 6 ; i++ ) {
if ( outside[i] ) {
delete outside[i];
}
}
*out = CopyMapTri( tri );
(*out)->next = NULL;
return;
}
if ( !hasOutside ) {
// the entire winding is inside this light
// free the inside copy
delete inside;
*in = CopyMapTri( tri );
(*in)->next = NULL;
return;
}
// the winding is split
*in = WindingToTriList( inside, tri );
delete inside;
// combine all the outside fragments
for ( i = 0 ; i < 6 ; i++ ) {
if ( outside[i] ) {
mapTri_t *list;
list = WindingToTriList( outside[i], tri );
delete outside[i];
*out = MergeTriLists( *out, list );
}
}
}
/*
=================
BoundOptimizeGroup
=================
*/
static void BoundOptimizeGroup( optimizeGroup_t *group ) {
group->bounds.Clear();
for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) {
group->bounds.AddPoint( tri->v[0].xyz );
group->bounds.AddPoint( tri->v[1].xyz );
group->bounds.AddPoint( tri->v[2].xyz );
}
}
/*
====================
BuildLightShadows
Build the beam tree and shadow volume surface for a light
====================
*/
static void BuildLightShadows( uEntity_t *e, mapLight_t *light ) {
int i;
optimizeGroup_t *group;
mapTri_t *tri;
mapTri_t *shadowers;
optimizeGroup_t *shadowerGroups;
idVec3 lightOrigin;
bool hasPerforatedSurface = false;
//
// build a group list of all the triangles that will contribute to
// the optimized shadow volume, leaving the original triangles alone
//
// shadowers will contain all the triangles that will contribute to the
// shadow volume
shadowerGroups = NULL;
lightOrigin = light->def.globalLightOrigin;
// if the light is no-shadows, don't add any surfaces
// to the beam tree at all
if ( !light->def.parms.noShadows
&& light->def.lightShader->LightCastsShadows() ) {
for ( i = 0 ; i < e->numAreas ; i++ ) {
for ( group = e->areas[i].groups ; group ; group = group->nextGroup ) {
// if the surface doesn't cast shadows, skip it
if ( !group->material->SurfaceCastsShadow() ) {
continue;
}
// if the group doesn't face away from the light, it
// won't contribute to the shadow volume
if ( dmapGlobals.mapPlanes[ group->planeNum ].Distance( lightOrigin ) > 0 ) {
continue;
}
// if the group bounds doesn't intersect the light bounds,
// skip it
if ( !group->bounds.IntersectsBounds( light->def.frustumTris->bounds ) ) {
continue;
}
// build up a list of the triangle fragments inside the
// light frustum
shadowers = NULL;
for ( tri = group->triList ; tri ; tri = tri->next ) {
mapTri_t *in, *out;
// clip it to the light frustum
ClipTriByLight( light, tri, &in, &out );
FreeTriList( out );
shadowers = MergeTriLists( shadowers, in );
}
// if we didn't get any out of this group, we don't
// need to create a new group in the shadower list
if ( !shadowers ) {
continue;
}
// find a group in shadowerGroups to add these to
// we will ignore everything but planenum, and we
// can merge across areas
optimizeGroup_t *check;
for ( check = shadowerGroups ; check ; check = check->nextGroup ) {
if ( check->planeNum == group->planeNum ) {
break;
}
}
if ( !check ) {
check = (optimizeGroup_t *)Mem_Alloc( sizeof( *check ) );
*check = *group;
check->triList = NULL;
check->nextGroup = shadowerGroups;
shadowerGroups = check;
}
// if any surface is a shadow-casting perforated or translucent surface, we
// can't use the face removal optimizations because we can see through
// some of the faces
if ( group->material->Coverage() != MC_OPAQUE ) {
hasPerforatedSurface = true;
}
check->triList = MergeTriLists( check->triList, shadowers );
}
}
}
// take the shadower group list and create a beam tree and shadow volume
light->shadowTris = CreateLightShadow( shadowerGroups, light );
if ( light->shadowTris && hasPerforatedSurface ) {
// can't ever remove front faces, because we can see through some of them
light->shadowTris->numShadowIndexesNoCaps = light->shadowTris->numShadowIndexesNoFrontCaps =
light->shadowTris->numIndexes;
}
// we don't need the original shadower triangles for anything else
FreeOptimizeGroupList( shadowerGroups );
}
/*
====================
CarveGroupsByLight
Divide each group into an inside group and an outside group, based
on which fragments are illuminated by the light's beam tree
====================
*/
static void CarveGroupsByLight( uEntity_t *e, mapLight_t *light ) {
int i;
optimizeGroup_t *group, *newGroup, *carvedGroups, *nextGroup;
mapTri_t *tri, *inside, *outside;
uArea_t *area;
for ( i = 0 ; i < e->numAreas ; i++ ) {
area = &e->areas[i];
carvedGroups = NULL;
// we will be either freeing or reassigning the groups as we go
for ( group = area->groups ; group ; group = nextGroup ) {
nextGroup = group->nextGroup;
// if the surface doesn't get lit, don't carve it up
if ( ( light->def.lightShader->IsFogLight() && !group->material->ReceivesFog() )
|| ( !light->def.lightShader->IsFogLight() && !group->material->ReceivesLighting() )
|| !group->bounds.IntersectsBounds( light->def.frustumTris->bounds ) ) {
group->nextGroup = carvedGroups;
carvedGroups = group;
continue;
}
if ( group->numGroupLights == MAX_GROUP_LIGHTS ) {
common->Error( "MAX_GROUP_LIGHTS around %f %f %f",
group->triList->v[0].xyz[0], group->triList->v[0].xyz[1], group->triList->v[0].xyz[2] );
}
// if the group doesn't face the light,
// it won't get carved at all
if ( !light->def.lightShader->LightEffectsBackSides() &&
!group->material->ReceivesLightingOnBackSides() &&
dmapGlobals.mapPlanes[ group->planeNum ].Distance( light->def.parms.origin ) <= 0 ) {
group->nextGroup = carvedGroups;
carvedGroups = group;
continue;
}
// split into lists for hit-by-light, and not-hit-by-light
inside = NULL;
outside = NULL;
for ( tri = group->triList ; tri ; tri = tri->next ) {
mapTri_t *in, *out;
ClipTriByLight( light, tri, &in, &out );
inside = MergeTriLists( inside, in );
outside = MergeTriLists( outside, out );
}
if ( inside ) {
newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) );
*newGroup = *group;
newGroup->groupLights[newGroup->numGroupLights] = light;
newGroup->numGroupLights++;
newGroup->triList = inside;
newGroup->nextGroup = carvedGroups;
carvedGroups = newGroup;
}
if ( outside ) {
newGroup = (optimizeGroup_t *)Mem_Alloc( sizeof( *newGroup ) );
*newGroup = *group;
newGroup->triList = outside;
newGroup->nextGroup = carvedGroups;
carvedGroups = newGroup;
}
// free the original
group->nextGroup = NULL;
FreeOptimizeGroupList( group );
}
// replace this area's group list with the new one
area->groups = carvedGroups;
}
}
/*
=====================
Prelight
Break optimize groups up into additional groups at light boundaries, so
optimization won't cross light bounds
=====================
*/
void Prelight( uEntity_t *e ) {
int i;
int start, end;
mapLight_t *light;
// don't prelight anything but the world entity
if ( dmapGlobals.entityNum != 0 ) {
return;
}
if ( dmapGlobals.shadowOptLevel > 0 ) {
common->Printf( "----- BuildLightShadows -----\n" );
start = Sys_Milliseconds();
// calc bounds for all the groups to speed things up
for ( i = 0 ; i < e->numAreas ; i++ ) {
uArea_t *area = &e->areas[i];
for ( optimizeGroup_t *group = area->groups ; group ; group = group->nextGroup ) {
BoundOptimizeGroup( group );
}
}
for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
light = dmapGlobals.mapLights[i];
BuildLightShadows( e, light );
}
end = Sys_Milliseconds();
common->Printf( "%5.1f seconds for BuildLightShadows\n", ( end - start ) / 1000.0 );
}
if ( !dmapGlobals.noLightCarve ) {
common->Printf( "----- CarveGroupsByLight -----\n" );
start = Sys_Milliseconds();
// now subdivide the optimize groups into additional groups for
// each light that illuminates them
for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
light = dmapGlobals.mapLights[i];
CarveGroupsByLight( e, light );
}
end = Sys_Milliseconds();
common->Printf( "%5.1f seconds for CarveGroupsByLight\n", ( end - start ) / 1000.0 );
}
}