2014-08-01 07:07:00 +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 "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 ), TAG_TOOLS );
|
|
|
|
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];
|
2014-08-02 13:09:25 +00:00
|
|
|
|
|
|
|
// RB begin
|
|
|
|
const idVec2 aST = a->GetTexCoord();
|
|
|
|
const idVec2 bST = b->GetTexCoord();
|
|
|
|
d0[3] = bST.x - aST.x;
|
|
|
|
d0[4] = bST.y - aST.y;
|
2014-08-01 07:07:00 +00:00
|
|
|
|
|
|
|
d1[0] = c->xyz[0] - a->xyz[0];
|
|
|
|
d1[1] = c->xyz[1] - a->xyz[1];
|
|
|
|
d1[2] = c->xyz[2] - a->xyz[2];
|
2014-08-02 13:09:25 +00:00
|
|
|
|
|
|
|
const idVec2 cST = c->GetTexCoord();
|
|
|
|
d1[3] = cST.x - aST.x;
|
|
|
|
d1[4] = cST.y - aST.y;
|
2014-08-01 07:07:00 +00:00
|
|
|
|
|
|
|
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;
|
2014-08-02 13:09:25 +00:00
|
|
|
texVec->v[0][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].GetTexCoord().x;
|
2014-08-01 07:07:00 +00:00
|
|
|
|
|
|
|
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;
|
2014-08-02 13:09:25 +00:00
|
|
|
texVec->v[1][3] = tri->v[0].xyz * texVec->v[0].ToVec3() - tri->v[0].GetTexCoord().y;
|
|
|
|
// RB end
|
2014-08-01 07:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
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
|
|
|
|
dv->xyz = *vec;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// calculate texture s/t from brush primitive texture matrix
|
|
|
|
idVec2 st;
|
|
|
|
st.x = ( dv->xyz * s->texVec.v[0].ToVec3() ) + s->texVec.v[0][3];
|
|
|
|
st.y = ( dv->xyz * s->texVec.v[1].ToVec3() ) + s->texVec.v[1][3];
|
|
|
|
|
|
|
|
dv->SetTexCoord( st );
|
|
|
|
|
|
|
|
// copy normal
|
|
|
|
dv->SetNormal( dmapGlobals.mapPlanes[s->planenum].Normal() );
|
|
|
|
if( dv->GetNormal().Length() < 0.9 || dv->GetNormal().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;
|
|
|
|
dv->xyz = *vec;
|
|
|
|
|
|
|
|
idVec2 st;
|
|
|
|
st.x = ( dv->xyz * s->texVec.v[0].ToVec3() ) + s->texVec.v[0][3];
|
|
|
|
st.y = ( dv->xyz * s->texVec.v[1].ToVec3() ) + s->texVec.v[1][3];
|
|
|
|
dv->SetTexCoord( st );
|
|
|
|
|
|
|
|
// copy normal
|
|
|
|
dv->SetNormal( dmapGlobals.mapPlanes[s->planenum].Normal() );
|
|
|
|
if( dv->GetNormal().Length() < 0.9f || dv->GetNormal().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] ), TAG_TOOLS );
|
|
|
|
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].SetNormal( tri->verts[tri->indexes[j + k]].GetNormal() * axis );
|
|
|
|
mapTri.v[k].SetTexCoord( tri->verts[tri->indexes[j + k]].GetTexCoord() );
|
|
|
|
}
|
|
|
|
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 )
|
|
|
|
{
|
2014-08-02 13:09:25 +00:00
|
|
|
// RB begin
|
|
|
|
oldInside->Split( light->frustumPlanes[i], 0, &outside[i], &inside );
|
|
|
|
delete oldInside;
|
|
|
|
// RB end
|
2014-08-01 07:07:00 +00:00
|
|
|
}
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
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.globalLightBounds ) )
|
|
|
|
{
|
|
|
|
|
|
|
|
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 ), TAG_TOOLS );
|
|
|
|
*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 ), TAG_TOOLS );
|
|
|
|
*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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 );
|
|
|
|
}
|
2014-08-02 13:09:25 +00:00
|
|
|
|
2014-08-01 07:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|