2025-05-17 14:15:27 -04:00
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
2008-09-15 01:07:45 -05:00
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// faces.c
# include "vbsp.h"
# include "utlvector.h"
# include "utilmatlib.h"
# include <float.h>
# include "mstristrip.h"
# include "tier1/strtools.h"
# include "materialpatch.h"
/*
some faces will be removed before saving , but still form nodes :
the insides of sky volumes
meeting planes of different water current volumes
*/
// undefine for dumb linear searches
# define USE_HASHING
# define INTEGRAL_EPSILON 0.01
# define POINT_EPSILON 0.1
# define OFF_EPSILON 0.25
int c_merge ;
int c_subdivide ;
int c_totalverts ;
int c_uniqueverts ;
int c_degenerate ;
int c_tjunctions ;
int c_faceoverflows ;
int c_facecollapse ;
int c_badstartverts ;
# define MAX_SUPERVERTS 512
int superverts [ MAX_SUPERVERTS ] ;
int numsuperverts ;
face_t * edgefaces [ MAX_MAP_EDGES ] [ 2 ] ;
int firstmodeledge = 1 ;
int firstmodelface ;
int c_tryedges ;
Vector edge_dir ;
Vector edge_start ;
vec_t edge_len ;
int num_edge_verts ;
int edge_verts [ MAX_MAP_VERTS ] ;
float g_maxLightmapDimension = 32 ;
face_t * NewFaceFromFace ( face_t * f ) ;
// Used to speed up GetEdge2(). Holds a list of edges connected to each vert.
CUtlVector < int > g_VertEdgeList [ MAX_MAP_VERTS ] ;
//===========================================================================
typedef struct hashvert_s
{
struct hashvert_s * next ;
int num ;
} hashvert_t ;
# define HASH_BITS 7
# define HASH_SIZE (COORD_EXTENT>>HASH_BITS)
int vertexchain [ MAX_MAP_VERTS ] ; // the next vertex in a hash chain
int hashverts [ HASH_SIZE * HASH_SIZE ] ; // a vertex number, or 0 for no verts
//face_t *edgefaces[MAX_MAP_EDGES][2];
//============================================================================
unsigned HashVec ( Vector & vec )
{
int x , y ;
x = ( MAX_COORD_INTEGER + ( int ) ( vec [ 0 ] + 0.5 ) ) > > HASH_BITS ;
y = ( MAX_COORD_INTEGER + ( int ) ( vec [ 1 ] + 0.5 ) ) > > HASH_BITS ;
if ( x < 0 | | x > = HASH_SIZE | | y < 0 | | y > = HASH_SIZE )
Error ( " HashVec: point outside valid range " ) ;
return y * HASH_SIZE + x ;
}
# ifdef USE_HASHING
/*
= = = = = = = = = = = = =
GetVertex
Uses hashing
= = = = = = = = = = = = =
*/
int GetVertexnum ( Vector & in )
{
int h ;
int i ;
Vector vert ;
int vnum ;
c_totalverts + + ;
for ( i = 0 ; i < 3 ; i + + )
{
if ( fabs ( in [ i ] - ( int ) ( in [ i ] + 0.5 ) ) < INTEGRAL_EPSILON )
vert [ i ] = ( int ) ( in [ i ] + 0.5 ) ;
else
vert [ i ] = in [ i ] ;
}
h = HashVec ( vert ) ;
for ( vnum = hashverts [ h ] ; vnum ; vnum = vertexchain [ vnum ] )
{
Vector & p = dvertexes [ vnum ] . point ;
if ( fabs ( p [ 0 ] - vert [ 0 ] ) < POINT_EPSILON
& & fabs ( p [ 1 ] - vert [ 1 ] ) < POINT_EPSILON
& & fabs ( p [ 2 ] - vert [ 2 ] ) < POINT_EPSILON )
return vnum ;
}
// emit a vertex
if ( numvertexes = = MAX_MAP_VERTS )
Error ( " Too many unique verts, max = %d (map has too much brush geometry) \n " , MAX_MAP_VERTS ) ;
dvertexes [ numvertexes ] . point [ 0 ] = vert [ 0 ] ;
dvertexes [ numvertexes ] . point [ 1 ] = vert [ 1 ] ;
dvertexes [ numvertexes ] . point [ 2 ] = vert [ 2 ] ;
vertexchain [ numvertexes ] = hashverts [ h ] ;
hashverts [ h ] = numvertexes ;
c_uniqueverts + + ;
numvertexes + + ;
return numvertexes - 1 ;
}
# else
/*
= = = = = = = = = = = = = = = = = =
GetVertexnum
Dumb linear search
= = = = = = = = = = = = = = = = = =
*/
int GetVertexnum ( Vector & v )
{
int i , j ;
dvertex_t * dv ;
vec_t d ;
c_totalverts + + ;
// make really close values exactly integral
for ( i = 0 ; i < 3 ; i + + )
{
if ( fabs ( v [ i ] - ( int ) ( v [ i ] + 0.5 ) ) < INTEGRAL_EPSILON )
v [ i ] = ( int ) ( v [ i ] + 0.5 ) ;
if ( v [ i ] < MIN_COORD_INTEGER | | v [ i ] > MAX_COORD_INTEGER )
Error ( " GetVertexnum: outside world, vertex %.1f %.1f %.1f " , v . x , v . y , v . z ) ;
}
// search for an existing vertex match
for ( i = 0 , dv = dvertexes ; i < numvertexes ; i + + , dv + + )
{
for ( j = 0 ; j < 3 ; j + + )
{
d = v [ j ] - dv - > point [ j ] ;
if ( d > POINT_EPSILON | | d < - POINT_EPSILON )
break ;
}
if ( j = = 3 )
return i ; // a match
}
// new point
if ( numvertexes = = MAX_MAP_VERTS )
Error ( " Too many unique verts, max = %d (map has too much brush geometry) \n " , MAX_MAP_VERTS ) ;
VectorCopy ( v , dv - > point ) ;
numvertexes + + ;
c_uniqueverts + + ;
return numvertexes - 1 ;
}
# endif
/*
= = = = = = = = = = = = = = = = = =
FaceFromSuperverts
The faces vertexes have beeb added to the superverts [ ] array ,
and there may be more there than can be held in a face ( MAXEDGES ) .
If less , the faces vertexnums [ ] will be filled in , otherwise
face will reference a tree of split [ ] faces until all of the
vertexnums can be added .
superverts [ base ] will become face - > vertexnums [ 0 ] , and the others
will be circularly filled in .
= = = = = = = = = = = = = = = = = =
*/
void FaceFromSuperverts ( face_t * * pListHead , face_t * f , int base )
{
face_t * newf ;
int remaining ;
int i ;
remaining = numsuperverts ;
while ( remaining > MAXEDGES )
{ // must split into two faces, because of vertex overload
c_faceoverflows + + ;
newf = NewFaceFromFace ( f ) ;
f - > split [ 0 ] = newf ;
newf - > next = * pListHead ;
* pListHead = newf ;
newf - > numpoints = MAXEDGES ;
for ( i = 0 ; i < MAXEDGES ; i + + )
newf - > vertexnums [ i ] = superverts [ ( i + base ) % numsuperverts ] ;
f - > split [ 1 ] = NewFaceFromFace ( f ) ;
f = f - > split [ 1 ] ;
f - > next = * pListHead ;
* pListHead = f ;
remaining - = ( MAXEDGES - 2 ) ;
base = ( base + MAXEDGES - 1 ) % numsuperverts ;
}
// copy the vertexes back to the face
f - > numpoints = remaining ;
for ( i = 0 ; i < remaining ; i + + )
f - > vertexnums [ i ] = superverts [ ( i + base ) % numsuperverts ] ;
}
/*
= = = = = = = = = = = = = = = = = =
EmitFaceVertexes
= = = = = = = = = = = = = = = = = =
*/
void EmitFaceVertexes ( face_t * * pListHead , face_t * f )
{
winding_t * w ;
int i ;
if ( f - > merged | | f - > split [ 0 ] | | f - > split [ 1 ] )
return ;
w = f - > w ;
for ( i = 0 ; i < w - > numpoints ; i + + )
{
if ( noweld )
{ // make every point unique
if ( numvertexes = = MAX_MAP_VERTS )
Error ( " Too many unique verts, max = %d (map has too much brush geometry) \n " , MAX_MAP_VERTS ) ;
superverts [ i ] = numvertexes ;
VectorCopy ( w - > p [ i ] , dvertexes [ numvertexes ] . point ) ;
numvertexes + + ;
c_uniqueverts + + ;
c_totalverts + + ;
}
else
superverts [ i ] = GetVertexnum ( w - > p [ i ] ) ;
}
numsuperverts = w - > numpoints ;
// this may fragment the face if > MAXEDGES
FaceFromSuperverts ( pListHead , f , 0 ) ;
}
/*
= = = = = = = = = = = = = = = = = =
EmitNodeFaceVertexes_r
= = = = = = = = = = = = = = = = = =
*/
void EmitNodeFaceVertexes_r ( node_t * node )
{
int i ;
face_t * f ;
if ( node - > planenum = = PLANENUM_LEAF )
{
// leaf faces are emitted in second pass
return ;
}
for ( f = node - > faces ; f ; f = f - > next )
{
EmitFaceVertexes ( & node - > faces , f ) ;
}
for ( i = 0 ; i < 2 ; i + + )
{
EmitNodeFaceVertexes_r ( node - > children [ i ] ) ;
}
}
void EmitLeafFaceVertexes ( face_t * * ppLeafFaceList )
{
face_t * f = * ppLeafFaceList ;
while ( f )
{
EmitFaceVertexes ( ppLeafFaceList , f ) ;
f = f - > next ;
}
}
# ifdef USE_HASHING
/*
= = = = = = = = = =
FindEdgeVerts
Uses the hash tables to cut down to a small number
= = = = = = = = = =
*/
void FindEdgeVerts ( Vector & v1 , Vector & v2 )
{
int x1 , x2 , y1 , y2 , t ;
int x , y ;
int vnum ;
#if 0
{
int i ;
num_edge_verts = numvertexes - 1 ;
for ( i = 0 ; i < numvertexes - 1 ; i + + )
edge_verts [ i ] = i + 1 ;
}
# endif
x1 = ( MAX_COORD_INTEGER + ( int ) ( v1 [ 0 ] + 0.5 ) ) > > HASH_BITS ;
y1 = ( MAX_COORD_INTEGER + ( int ) ( v1 [ 1 ] + 0.5 ) ) > > HASH_BITS ;
x2 = ( MAX_COORD_INTEGER + ( int ) ( v2 [ 0 ] + 0.5 ) ) > > HASH_BITS ;
y2 = ( MAX_COORD_INTEGER + ( int ) ( v2 [ 1 ] + 0.5 ) ) > > HASH_BITS ;
if ( x1 > x2 )
{
t = x1 ;
x1 = x2 ;
x2 = t ;
}
if ( y1 > y2 )
{
t = y1 ;
y1 = y2 ;
y2 = t ;
}
#if 0
x1 - - ;
x2 + + ;
y1 - - ;
y2 + + ;
if ( x1 < 0 )
x1 = 0 ;
if ( x2 > = HASH_SIZE )
x2 = HASH_SIZE ;
if ( y1 < 0 )
y1 = 0 ;
if ( y2 > = HASH_SIZE )
y2 = HASH_SIZE ;
# endif
num_edge_verts = 0 ;
for ( x = x1 ; x < = x2 ; x + + )
{
for ( y = y1 ; y < = y2 ; y + + )
{
for ( vnum = hashverts [ y * HASH_SIZE + x ] ; vnum ; vnum = vertexchain [ vnum ] )
{
edge_verts [ num_edge_verts + + ] = vnum ;
}
}
}
}
# else
/*
= = = = = = = = = =
FindEdgeVerts
Forced a dumb check of everything
= = = = = = = = = =
*/
void FindEdgeVerts ( Vector & v1 , Vector & v2 )
{
int i ;
num_edge_verts = numvertexes - 1 ;
for ( i = 0 ; i < num_edge_verts ; i + + )
edge_verts [ i ] = i + 1 ;
}
# endif
/*
= = = = = = = = = =
TestEdge
Can be recursively reentered
= = = = = = = = = =
*/
void TestEdge ( vec_t start , vec_t end , int p1 , int p2 , int startvert )
{
int j , k ;
vec_t dist ;
Vector delta ;
Vector exact ;
Vector off ;
vec_t error ;
Vector p ;
if ( p1 = = p2 )
{
c_degenerate + + ;
return ; // degenerate edge
}
for ( k = startvert ; k < num_edge_verts ; k + + )
{
j = edge_verts [ k ] ;
if ( j = = p1 | | j = = p2 )
continue ;
VectorCopy ( dvertexes [ j ] . point , p ) ;
VectorSubtract ( p , edge_start , delta ) ;
dist = DotProduct ( delta , edge_dir ) ;
if ( dist < = start | | dist > = end )
continue ; // off an end
VectorMA ( edge_start , dist , edge_dir , exact ) ;
VectorSubtract ( p , exact , off ) ;
error = off . Length ( ) ;
if ( error > OFF_EPSILON )
continue ; // not on the edge
// break the edge
c_tjunctions + + ;
TestEdge ( start , dist , p1 , j , k + 1 ) ;
TestEdge ( dist , end , j , p2 , k + 1 ) ;
return ;
}
// the edge p1 to p2 is now free of tjunctions
if ( numsuperverts > = MAX_SUPERVERTS )
Error ( " Edge with too many vertices due to t-junctions. Max %d verts along an edge! \n " , MAX_SUPERVERTS ) ;
superverts [ numsuperverts ] = p1 ;
numsuperverts + + ;
}
// stores the edges that each vert is part of
struct face_vert_table_t
{
face_vert_table_t ( )
{
edge0 = - 1 ;
edge1 = - 1 ;
}
void AddEdge ( int edge )
{
if ( edge0 = = - 1 )
{
edge0 = edge ;
}
else
{
// can only have two edges
Assert ( edge1 = = - 1 ) ;
edge1 = edge ;
}
}
bool HasEdge ( int edge ) const
{
if ( edge > = 0 )
{
if ( edge0 = = edge | | edge1 = = edge )
return true ;
}
return false ;
}
int edge0 ;
int edge1 ;
} ;
// if these two verts share an edge, they must be collinear
bool IsDiagonal ( const face_vert_table_t & v0 , const face_vert_table_t & v1 )
{
if ( v1 . HasEdge ( v0 . edge0 ) | | v1 . HasEdge ( v0 . edge1 ) )
return false ;
return true ;
}
void Triangulate_r ( CUtlVector < int > & out , const CUtlVector < int > & inIndices , const CUtlVector < face_vert_table_t > & poly )
{
Assert ( inIndices . Count ( ) > 2 ) ;
// one triangle left, return
if ( inIndices . Count ( ) = = 3 )
{
for ( int i = 0 ; i < inIndices . Count ( ) ; i + + )
{
out . AddToTail ( inIndices [ i ] ) ;
}
return ;
}
// check each pair of verts and see if they are diagonal (not on a shared edge)
// if so, split & recurse
for ( int i = 0 ; i < inIndices . Count ( ) ; i + + )
{
int count = inIndices . Count ( ) ;
// i + count is myself, i + count-1 is previous, so we need to stop at i+count-2
for ( int j = 2 ; j < count - 1 ; j + + )
{
// if these two form a diagonal, split the poly along
// the diagonal and triangulate the two sub-polys
int index = inIndices [ i ] ;
int nextArray = ( i + j ) % count ;
int nextIndex = inIndices [ nextArray ] ;
if ( IsDiagonal ( poly [ index ] , poly [ nextIndex ] ) )
{
// add the poly up to the diagonal
CUtlVector < int > in1 ;
for ( int k = i ; k ! = nextArray ; k = ( k + 1 ) % count )
{
in1 . AddToTail ( inIndices [ k ] ) ;
}
in1 . AddToTail ( nextIndex ) ;
// add the rest of the poly starting with the diagonal
CUtlVector < int > in2 ;
in2 . AddToTail ( index ) ;
for ( int l = nextArray ; l ! = i ; l = ( l + 1 ) % count )
{
in2 . AddToTail ( inIndices [ l ] ) ;
}
// triangulate the sub-polys
Triangulate_r ( out , in1 , poly ) ;
Triangulate_r ( out , in2 , poly ) ;
return ;
}
}
}
// didn't find a diagonal
Assert ( 0 ) ;
}
/*
= = = = = = = = = = = = = = = = = =
FixFaceEdges
= = = = = = = = = = = = = = = = = =
*/
void FixFaceEdges ( face_t * * pList , face_t * f )
{
int p1 , p2 ;
int i ;
Vector e2 ;
vec_t len ;
int count [ MAX_SUPERVERTS ] , start [ MAX_SUPERVERTS ] ;
int base ;
if ( f - > merged | | f - > split [ 0 ] | | f - > split [ 1 ] )
return ;
numsuperverts = 0 ;
int originalPoints = f - > numpoints ;
for ( i = 0 ; i < f - > numpoints ; i + + )
{
p1 = f - > vertexnums [ i ] ;
p2 = f - > vertexnums [ ( i + 1 ) % f - > numpoints ] ;
VectorCopy ( dvertexes [ p1 ] . point , edge_start ) ;
VectorCopy ( dvertexes [ p2 ] . point , e2 ) ;
FindEdgeVerts ( edge_start , e2 ) ;
VectorSubtract ( e2 , edge_start , edge_dir ) ;
len = VectorNormalize ( edge_dir ) ;
start [ i ] = numsuperverts ;
TestEdge ( 0 , len , p1 , p2 , 0 ) ;
count [ i ] = numsuperverts - start [ i ] ;
}
if ( numsuperverts < 3 )
{ // entire face collapsed
f - > numpoints = 0 ;
c_facecollapse + + ;
return ;
}
// we want to pick a vertex that doesn't have tjunctions
// on either side, which can cause artifacts on trifans,
// especially underwater
for ( i = 0 ; i < f - > numpoints ; i + + )
{
if ( count [ i ] = = 1 & & count [ ( i + f - > numpoints - 1 ) % f - > numpoints ] = = 1 )
break ;
}
if ( i = = f - > numpoints )
{
f - > badstartvert = true ;
c_badstartverts + + ;
base = 0 ;
}
else
{ // rotate the vertex order
base = start [ i ] ;
}
// this may fragment the face if > MAXEDGES
FaceFromSuperverts ( pList , f , base ) ;
// if this is the world, then re-triangulate to sew cracks
if ( f - > badstartvert & & entity_num = = 0 )
{
CUtlVector < face_vert_table_t > poly ;
CUtlVector < int > inIndices ;
CUtlVector < int > outIndices ;
poly . AddMultipleToTail ( numsuperverts ) ;
for ( i = 0 ; i < originalPoints ; i + + )
{
// edge may not have output any points. Don't mark
if ( ! count [ i ] )
continue ;
// mark each edge the point is a member of
// we'll use this as a fast "is collinear" test
for ( int j = 0 ; j < = count [ i ] ; j + + )
{
int polyIndex = ( start [ i ] + j ) % numsuperverts ;
poly [ polyIndex ] . AddEdge ( i ) ;
}
}
for ( i = 0 ; i < numsuperverts ; i + + )
{
inIndices . AddToTail ( i ) ;
}
Triangulate_r ( outIndices , inIndices , poly ) ;
dprimitive_t & newPrim = g_primitives [ g_numprimitives ] ;
f - > firstPrimID = g_numprimitives ;
g_numprimitives + + ;
f - > numPrims = 1 ;
newPrim . firstIndex = g_numprimindices ;
newPrim . firstVert = g_numprimverts ;
newPrim . indexCount = outIndices . Count ( ) ;
newPrim . vertCount = 0 ;
newPrim . type = PRIM_TRILIST ;
g_numprimindices + = newPrim . indexCount ;
if ( g_numprimitives > MAX_MAP_PRIMITIVES | | g_numprimindices > MAX_MAP_PRIMINDICES )
{
Error ( " Too many t-junctions to fix up! (%d prims, max %d :: %d indices, max %d) \n " , g_numprimitives , MAX_MAP_PRIMITIVES , g_numprimindices , MAX_MAP_PRIMINDICES ) ;
}
for ( i = 0 ; i < outIndices . Count ( ) ; i + + )
{
g_primindices [ newPrim . firstIndex + i ] = outIndices [ i ] ;
}
}
}
/*
= = = = = = = = = = = = = = = = = =
FixEdges_r
= = = = = = = = = = = = = = = = = =
*/
void FixEdges_r ( node_t * node )
{
int i ;
face_t * f ;
if ( node - > planenum = = PLANENUM_LEAF )
{
return ;
}
for ( f = node - > faces ; f ; f = f - > next )
FixFaceEdges ( & node - > faces , f ) ;
for ( i = 0 ; i < 2 ; i + + )
FixEdges_r ( node - > children [ i ] ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Fix the t-junctions on detail faces
//-----------------------------------------------------------------------------
void FixLeafFaceEdges ( face_t * * ppLeafFaceList )
{
face_t * f ;
for ( f = * ppLeafFaceList ; f ; f = f - > next )
{
FixFaceEdges ( ppLeafFaceList , f ) ;
}
}
/*
= = = = = = = = = = =
FixTjuncs
= = = = = = = = = = =
*/
face_t * FixTjuncs ( node_t * headnode , face_t * pLeafFaceList )
{
// snap and merge all vertexes
qprintf ( " ---- snap verts ---- \n " ) ;
memset ( hashverts , 0 , sizeof ( hashverts ) ) ;
memset ( vertexchain , 0 , sizeof ( vertexchain ) ) ;
c_totalverts = 0 ;
c_uniqueverts = 0 ;
c_faceoverflows = 0 ;
EmitNodeFaceVertexes_r ( headnode ) ;
// UNDONE: This count is wrong with tjuncs off on details - since
// break edges on tjunctions
qprintf ( " ---- tjunc ---- \n " ) ;
c_tryedges = 0 ;
c_degenerate = 0 ;
c_facecollapse = 0 ;
c_tjunctions = 0 ;
if ( g_bAllowDetailCracks )
{
FixEdges_r ( headnode ) ;
EmitLeafFaceVertexes ( & pLeafFaceList ) ;
FixLeafFaceEdges ( & pLeafFaceList ) ;
}
else
{
EmitLeafFaceVertexes ( & pLeafFaceList ) ;
if ( ! notjunc )
{
FixEdges_r ( headnode ) ;
FixLeafFaceEdges ( & pLeafFaceList ) ;
}
}
qprintf ( " %i unique from %i \n " , c_uniqueverts , c_totalverts ) ;
qprintf ( " %5i edges degenerated \n " , c_degenerate ) ;
qprintf ( " %5i faces degenerated \n " , c_facecollapse ) ;
qprintf ( " %5i edges added by tjunctions \n " , c_tjunctions ) ;
qprintf ( " %5i faces added by tjunctions \n " , c_faceoverflows ) ;
qprintf ( " %5i bad start verts \n " , c_badstartverts ) ;
return pLeafFaceList ;
}
//========================================================
int c_faces ;
face_t * AllocFace ( void )
{
static int s_FaceId = 0 ;
face_t * f ;
f = ( face_t * ) malloc ( sizeof ( * f ) ) ;
memset ( f , 0 , sizeof ( * f ) ) ;
f - > id = s_FaceId ;
+ + s_FaceId ;
c_faces + + ;
return f ;
}
face_t * NewFaceFromFace ( face_t * f )
{
face_t * newf ;
newf = AllocFace ( ) ;
* newf = * f ;
newf - > merged = NULL ;
newf - > split [ 0 ] = newf - > split [ 1 ] = NULL ;
newf - > w = NULL ;
return newf ;
}
void FreeFace ( face_t * f )
{
if ( f - > w )
FreeWinding ( f - > w ) ;
free ( f ) ;
c_faces - - ;
}
void FreeFaceList ( face_t * pFaces )
{
while ( pFaces )
{
face_t * next = pFaces - > next ;
FreeFace ( pFaces ) ;
pFaces = next ;
}
}
//========================================================
void GetEdge2_InitOptimizedList ( )
{
for ( int i = 0 ; i < MAX_MAP_VERTS ; i + + )
g_VertEdgeList [ i ] . RemoveAll ( ) ;
}
void IntSort ( CUtlVector < int > & theList )
{
for ( int i = 0 ; i < theList . Size ( ) - 1 ; i + + )
{
if ( theList [ i ] > theList [ i + 1 ] )
{
int temp = theList [ i ] ;
theList [ i ] = theList [ i + 1 ] ;
theList [ i + 1 ] = temp ;
if ( i > 0 )
i - = 2 ;
else
i = - 1 ;
}
}
}
int AddEdge ( int v1 , int v2 , face_t * f )
{
if ( numedges > = MAX_MAP_EDGES )
Error ( " Too many edges in map, max == %d " , MAX_MAP_EDGES ) ;
g_VertEdgeList [ v1 ] . AddToTail ( numedges ) ;
g_VertEdgeList [ v2 ] . AddToTail ( numedges ) ;
IntSort ( g_VertEdgeList [ v1 ] ) ;
IntSort ( g_VertEdgeList [ v2 ] ) ;
dedge_t * edge = & dedges [ numedges ] ;
numedges + + ;
edge - > v [ 0 ] = v1 ;
edge - > v [ 1 ] = v2 ;
edgefaces [ numedges - 1 ] [ 0 ] = f ;
return numedges - 1 ;
}
/*
= = = = = = = = = = = = = = = = = =
GetEdge
Called by writebsp .
Don ' t allow four way edges
= = = = = = = = = = = = = = = = = =
*/
int GetEdge2 ( int v1 , int v2 , face_t * f )
{
dedge_t * edge ;
c_tryedges + + ;
if ( ! noshare )
{
// Check all edges connected to v1.
CUtlVector < int > & theList = g_VertEdgeList [ v1 ] ;
for ( int i = 0 ; i < theList . Size ( ) ; i + + )
{
int iEdge = theList [ i ] ;
edge = & dedges [ iEdge ] ;
if ( v1 = = edge - > v [ 1 ] & & v2 = = edge - > v [ 0 ] & & edgefaces [ iEdge ] [ 0 ] - > contents = = f - > contents )
{
if ( edgefaces [ iEdge ] [ 1 ] )
continue ;
edgefaces [ iEdge ] [ 1 ] = f ;
return - iEdge ;
}
}
}
return AddEdge ( v1 , v2 , f ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
FACE MERGING
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# define CONTINUOUS_EPSILON 0.001
/*
= = = = = = = = = = = = =
TryMergeWinding
If two polygons share a common edge and the edges that meet at the
common points are both inside the other polygons , merge them
Returns NULL if the faces couldn ' t be merged , or the new face .
The originals will NOT be freed .
= = = = = = = = = = = = =
*/
winding_t * TryMergeWinding ( winding_t * f1 , winding_t * f2 , Vector & planenormal )
{
Vector * p1 , * p2 , * p3 , * p4 , * back ;
winding_t * newf ;
int i , j , k , l ;
Vector normal , delta ;
vec_t dot ;
qboolean keep1 , keep2 ;
//
// find a common edge
//
p1 = p2 = NULL ; // stop compiler warning
j = 0 ; //
for ( i = 0 ; i < f1 - > numpoints ; i + + )
{
p1 = & f1 - > p [ i ] ;
p2 = & f1 - > p [ ( i + 1 ) % f1 - > numpoints ] ;
for ( j = 0 ; j < f2 - > numpoints ; j + + )
{
p3 = & f2 - > p [ j ] ;
p4 = & f2 - > p [ ( j + 1 ) % f2 - > numpoints ] ;
for ( k = 0 ; k < 3 ; k + + )
{
if ( fabs ( ( * p1 ) [ k ] - ( * p4 ) [ k ] ) > EQUAL_EPSILON )
break ;
if ( fabs ( ( * p2 ) [ k ] - ( * p3 ) [ k ] ) > EQUAL_EPSILON )
break ;
}
if ( k = = 3 )
break ;
}
if ( j < f2 - > numpoints )
break ;
}
if ( i = = f1 - > numpoints )
return NULL ; // no matching edges
//
// check slope of connected lines
// if the slopes are colinear, the point can be removed
//
back = & f1 - > p [ ( i + f1 - > numpoints - 1 ) % f1 - > numpoints ] ;
VectorSubtract ( * p1 , * back , delta ) ;
CrossProduct ( planenormal , delta , normal ) ;
VectorNormalize ( normal ) ;
back = & f2 - > p [ ( j + 2 ) % f2 - > numpoints ] ;
VectorSubtract ( * back , * p1 , delta ) ;
dot = DotProduct ( delta , normal ) ;
if ( dot > CONTINUOUS_EPSILON )
return NULL ; // not a convex polygon
keep1 = ( qboolean ) ( dot < - CONTINUOUS_EPSILON ) ;
back = & f1 - > p [ ( i + 2 ) % f1 - > numpoints ] ;
VectorSubtract ( * back , * p2 , delta ) ;
CrossProduct ( planenormal , delta , normal ) ;
VectorNormalize ( normal ) ;
back = & f2 - > p [ ( j + f2 - > numpoints - 1 ) % f2 - > numpoints ] ;
VectorSubtract ( * back , * p2 , delta ) ;
dot = DotProduct ( delta , normal ) ;
if ( dot > CONTINUOUS_EPSILON )
return NULL ; // not a convex polygon
keep2 = ( qboolean ) ( dot < - CONTINUOUS_EPSILON ) ;
//
// build the new polygon
//
newf = AllocWinding ( f1 - > numpoints + f2 - > numpoints ) ;
// copy first polygon
for ( k = ( i + 1 ) % f1 - > numpoints ; k ! = i ; k = ( k + 1 ) % f1 - > numpoints )
{
if ( k = = ( i + 1 ) % f1 - > numpoints & & ! keep2 )
continue ;
VectorCopy ( f1 - > p [ k ] , newf - > p [ newf - > numpoints ] ) ;
newf - > numpoints + + ;
}
// copy second polygon
for ( l = ( j + 1 ) % f2 - > numpoints ; l ! = j ; l = ( l + 1 ) % f2 - > numpoints )
{
if ( l = = ( j + 1 ) % f2 - > numpoints & & ! keep1 )
continue ;
VectorCopy ( f2 - > p [ l ] , newf - > p [ newf - > numpoints ] ) ;
newf - > numpoints + + ;
}
return newf ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool OverlaysAreEqual ( face_t * f1 , face_t * f2 )
{
// Check the overlay ids - see if they are the same.
if ( f1 - > originalface - > aOverlayIds . Count ( ) ! = f2 - > originalface - > aOverlayIds . Count ( ) )
return false ;
int nOverlayCount = f1 - > originalface - > aOverlayIds . Count ( ) ;
for ( int iOverlay = 0 ; iOverlay < nOverlayCount ; + + iOverlay )
{
int nOverlayId = f1 - > originalface - > aOverlayIds [ iOverlay ] ;
if ( f2 - > originalface - > aOverlayIds . Find ( nOverlayId ) = = - 1 )
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool FaceOnWaterBrush ( face_t * face )
{
side_t * pSide = face - > originalface ;
if ( ! pSide )
return false ;
if ( pSide - > contents & ( CONTENTS_WATER | CONTENTS_SLIME ) )
return true ;
return false ;
}
/*
= = = = = = = = = = = = =
TryMerge
If two polygons share a common edge and the edges that meet at the
common points are both inside the other polygons , merge them
Returns NULL if the faces couldn ' t be merged , or the new face .
The originals will NOT be freed .
= = = = = = = = = = = = =
*/
face_t * TryMerge ( face_t * f1 , face_t * f2 , Vector & planenormal )
{
face_t * newf ;
winding_t * nw ;
if ( ! f1 - > w | | ! f2 - > w )
return NULL ;
if ( f1 - > texinfo ! = f2 - > texinfo )
return NULL ;
if ( f1 - > planenum ! = f2 - > planenum ) // on front and back sides
return NULL ;
if ( f1 - > contents ! = f2 - > contents )
return NULL ;
if ( f1 - > originalface - > smoothingGroups ! = f2 - > originalface - > smoothingGroups )
return NULL ;
if ( ! OverlaysAreEqual ( f1 , f2 ) )
return NULL ;
if ( nomergewater & & ( FaceOnWaterBrush ( f1 ) | | FaceOnWaterBrush ( f2 ) ) )
return NULL ;
nw = TryMergeWinding ( f1 - > w , f2 - > w , planenormal ) ;
if ( ! nw )
return NULL ;
c_merge + + ;
newf = NewFaceFromFace ( f1 ) ;
newf - > w = nw ;
f1 - > merged = newf ;
f2 - > merged = newf ;
return newf ;
}
/*
= = = = = = = = = = = = = = =
MergeFaceList
= = = = = = = = = = = = = = =
*/
void MergeFaceList ( face_t * * pList )
{
face_t * f1 , * f2 , * end ;
face_t * merged ;
plane_t * plane ;
merged = NULL ;
for ( f1 = * pList ; f1 ; f1 = f1 - > next )
{
if ( f1 - > merged | | f1 - > split [ 0 ] | | f1 - > split [ 1 ] )
continue ;
for ( f2 = * pList ; f2 ! = f1 ; f2 = f2 - > next )
{
if ( f2 - > merged | | f2 - > split [ 0 ] | | f2 - > split [ 1 ] )
continue ;
plane = & mapplanes [ f1 - > planenum ] ;
merged = TryMerge ( f1 , f2 , plane - > normal ) ;
if ( ! merged )
continue ;
// add merged to the end of the face list
// so it will be checked against all the faces again
for ( end = * pList ; end - > next ; end = end - > next )
;
merged - > next = NULL ;
end - > next = merged ;
break ;
}
}
}
//=====================================================================
/*
= = = = = = = = = = = = = = =
SubdivideFace
Chop up faces that are larger than we want in the surface cache
= = = = = = = = = = = = = = =
*/
void SubdivideFace ( face_t * * pFaceList , face_t * f )
{
float mins , maxs ;
vec_t v ;
vec_t luxelsPerWorldUnit ;
int axis , i ;
texinfo_t * tex ;
Vector temp ;
vec_t dist ;
winding_t * w , * frontw , * backw ;
if ( f - > merged | | f - > split [ 0 ] | | f - > split [ 1 ] )
return ;
// special (non-surface cached) faces don't need subdivision
tex = & texinfo [ f - > texinfo ] ;
if ( tex - > flags & SURF_NOLIGHT )
{
return ;
}
for ( axis = 0 ; axis < 2 ; axis + + )
{
while ( 1 )
{
mins = 999999 ;
maxs = - 999999 ;
VECTOR_COPY ( tex - > lightmapVecsLuxelsPerWorldUnits [ axis ] , temp ) ;
w = f - > w ;
for ( i = 0 ; i < w - > numpoints ; i + + )
{
v = DotProduct ( w - > p [ i ] , temp ) ;
if ( v < mins )
mins = v ;
if ( v > maxs )
maxs = v ;
}
#if 0
if ( maxs - mins < = 0 )
Error ( " zero extents " ) ;
# endif
if ( maxs - mins < = g_maxLightmapDimension )
break ;
// split it
c_subdivide + + ;
luxelsPerWorldUnit = VectorNormalize ( temp ) ;
dist = ( mins + g_maxLightmapDimension - 1 ) / luxelsPerWorldUnit ;
ClipWindingEpsilon ( w , temp , dist , ON_EPSILON , & frontw , & backw ) ;
if ( ! frontw | | ! backw )
Error ( " SubdivideFace: didn't split the polygon " ) ;
f - > split [ 0 ] = NewFaceFromFace ( f ) ;
f - > split [ 0 ] - > w = frontw ;
f - > split [ 0 ] - > next = * pFaceList ;
* pFaceList = f - > split [ 0 ] ;
f - > split [ 1 ] = NewFaceFromFace ( f ) ;
f - > split [ 1 ] - > w = backw ;
f - > split [ 1 ] - > next = * pFaceList ;
* pFaceList = f - > split [ 1 ] ;
SubdivideFace ( pFaceList , f - > split [ 0 ] ) ;
SubdivideFace ( pFaceList , f - > split [ 1 ] ) ;
return ;
}
}
}
void SubdivideFaceList ( face_t * * pFaceList )
{
face_t * f ;
for ( f = * pFaceList ; f ; f = f - > next )
{
SubdivideFace ( pFaceList , f ) ;
}
}
//-----------------------------------------------------------------------------
// Assigns the bottom material to the bottom face
//-----------------------------------------------------------------------------
static bool AssignBottomWaterMaterialToFace ( face_t * f )
{
// NOTE: This happens *after* cubemap fixup occurs, so we need to get the
// fixed-up bottom material for this
texinfo_t * pTexInfo = & texinfo [ f - > texinfo ] ;
dtexdata_t * pTexData = GetTexData ( pTexInfo - > texdata ) ;
const char * pMaterialName = TexDataStringTable_GetString ( pTexData - > nameStringTableID ) ;
char pBottomMatName [ 512 ] ;
if ( ! GetValueFromPatchedMaterial ( pMaterialName , " $bottommaterial " , pBottomMatName , 512 ) )
{
if ( ! Q_stristr ( pMaterialName , " nodraw " ) & & ! Q_stristr ( pMaterialName , " toolsskip " ) )
{
Warning ( " error: material %s doesn't have a $bottommaterial \n " , pMaterialName ) ;
}
return false ;
}
//Assert( mapplanes[f->planenum].normal.z < 0 );
texinfo_t newTexInfo ;
newTexInfo . flags = pTexInfo - > flags ;
int j , k ;
for ( j = 0 ; j < 2 ; j + + )
{
for ( k = 0 ; k < 4 ; k + + )
{
newTexInfo . textureVecsTexelsPerWorldUnits [ j ] [ k ] = pTexInfo - > textureVecsTexelsPerWorldUnits [ j ] [ k ] ;
newTexInfo . lightmapVecsLuxelsPerWorldUnits [ j ] [ k ] = pTexInfo - > lightmapVecsLuxelsPerWorldUnits [ j ] [ k ] ;
}
}
newTexInfo . texdata = FindOrCreateTexData ( pBottomMatName ) ;
f - > texinfo = FindOrCreateTexInfo ( newTexInfo ) ;
return true ;
}
//===========================================================================
int c_nodefaces ;
static void SubdivideFaceBySubdivSize ( face_t * f , float subdivsize ) ;
void SubdivideFaceBySubdivSize ( face_t * f ) ;
/*
= = = = = = = = = = = =
FaceFromPortal
= = = = = = = = = = = =
*/
extern int FindOrCreateTexInfo ( const texinfo_t & searchTexInfo ) ;
face_t * FaceFromPortal ( portal_t * p , int pside )
{
face_t * f ;
side_t * side ;
int deltaContents ;
// portal does not bridge different visible contents
side = p - > side ;
if ( ! side )
return NULL ;
// allocate a new face
f = AllocFace ( ) ;
// save the original "side" from the map brush -- portal->side
// see FindPortalSide(...)
f - > originalface = side ;
//
// save material info
//
f - > texinfo = side - > texinfo ;
f - > dispinfo = - 1 ; // all faces with displacement info are created elsewhere
f - > smoothingGroups = side - > smoothingGroups ;
// save plane info
f - > planenum = ( side - > planenum & ~ 1 ) | pside ;
if ( entity_num ! = 0 )
{
// the brush model renderer doesn't use PLANEBACK, so write the real plane
// inside water faces can be flipped because they are generated on the inside of the brush
if ( p - > nodes [ pside ] - > contents & ( CONTENTS_WATER | CONTENTS_SLIME ) )
{
f - > planenum = ( side - > planenum & ~ 1 ) | pside ;
}
else
{
f - > planenum = side - > planenum ;
}
}
// save portal info
f - > portal = p ;
f - > fogVolumeLeaf = NULL ;
deltaContents = VisibleContents ( p - > nodes [ ! pside ] - > contents ^ p - > nodes [ pside ] - > contents ) ;
// don't show insides of windows or grates
if ( ( ( p - > nodes [ pside ] - > contents & CONTENTS_WINDOW ) & & deltaContents = = CONTENTS_WINDOW ) | |
( ( p - > nodes [ pside ] - > contents & CONTENTS_GRATE ) & & deltaContents = = CONTENTS_GRATE ) )
{
FreeFace ( f ) ;
return NULL ;
}
if ( p - > nodes [ pside ] - > contents & MASK_WATER )
{
f - > fogVolumeLeaf = p - > nodes [ pside ] ;
}
else if ( p - > nodes [ ! pside ] - > contents & MASK_WATER )
{
f - > fogVolumeLeaf = p - > nodes [ ! pside ] ;
}
// If it's the underside of water, we need to figure out what material to use, etc.
if ( ( p - > nodes [ pside ] - > contents & CONTENTS_WATER ) & & deltaContents = = CONTENTS_WATER )
{
if ( ! AssignBottomWaterMaterialToFace ( f ) )
{
FreeFace ( f ) ;
return NULL ;
}
}
//
// generate the winding for the face and save face contents
//
if ( pside )
{
f - > w = ReverseWinding ( p - > winding ) ;
f - > contents = p - > nodes [ 1 ] - > contents ;
}
else
{
f - > w = CopyWinding ( p - > winding ) ;
f - > contents = p - > nodes [ 0 ] - > contents ;
}
f - > numPrims = 0 ;
f - > firstPrimID = 0 ;
// return the created face
return f ;
}
/*
= = = = = = = = = = = = = = =
MakeFaces_r
If a portal will make a visible face ,
mark the side that originally created it
solid / empty : solid
solid / water : solid
water / empty : water
water / water : none
= = = = = = = = = = = = = = =
*/
void MakeFaces_r ( node_t * node )
{
portal_t * p ;
int s ;
// recurse down to leafs
if ( node - > planenum ! = PLANENUM_LEAF )
{
MakeFaces_r ( node - > children [ 0 ] ) ;
MakeFaces_r ( node - > children [ 1 ] ) ;
// merge together all visible faces on the node
if ( ! nomerge )
MergeFaceList ( & node - > faces ) ;
if ( ! nosubdiv )
SubdivideFaceList ( & node - > faces ) ;
return ;
}
// solid leafs never have visible faces
if ( node - > contents & CONTENTS_SOLID )
return ;
// see which portals are valid
for ( p = node - > portals ; p ; p = p - > next [ s ] )
{
s = ( p - > nodes [ 1 ] = = node ) ;
p - > face [ s ] = FaceFromPortal ( p , s ) ;
if ( p - > face [ s ] )
{
c_nodefaces + + ;
p - > face [ s ] - > next = p - > onnode - > faces ;
p - > onnode - > faces = p - > face [ s ] ;
}
}
}
typedef winding_t * pwinding_t ;
static void PrintWinding ( winding_t * w )
{
int i ;
Msg ( " \t --- \n " ) ;
for ( i = 0 ; i < w - > numpoints ; i + + )
{
Msg ( " \t %f %f %f \n " , w - > p [ i ] . x , w - > p [ i ] . y , w - > p [ i ] . z ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds a winding to the current list of primverts
// Input : *w - the winding
// *pIndices - The output indices
// vertStart - the starting vert index
// vertCount - current count
// Output : int - output count including new verts from this winding
//-----------------------------------------------------------------------------
int AddWindingToPrimverts ( const winding_t * w , unsigned short * pIndices , int vertStart , int vertCount )
{
for ( int i = 0 ; i < w - > numpoints ; i + + )
{
int j ;
for ( j = vertStart ; j < vertStart + vertCount ; j + + )
{
Vector tmp = g_primverts [ j ] . pos - w - > p [ i ] ;
if ( tmp . LengthSqr ( ) < POINT_EPSILON * POINT_EPSILON )
{
pIndices [ i ] = j ;
break ;
}
}
if ( j > = vertStart + vertCount )
{
pIndices [ i ] = j ;
g_primverts [ j ] . pos = w - > p [ i ] ;
vertCount + + ;
g_numprimverts + + ;
if ( g_numprimverts > MAX_MAP_PRIMVERTS )
{
Error ( " Exceeded max water verts. \n Increase surface subdivision size or lower your subdivision size in vmt files! (%d>%d) \n " ,
( int ) g_numprimverts , ( int ) MAX_MAP_PRIMVERTS ) ;
}
}
}
return vertCount ;
}
# pragma optimize( "g", off )
# define USE_TRISTRIPS
// UNDONE: Should split this function into subdivide and primitive building parts
// UNDONE: We should try building strips of shared verts for all water faces in a leaf
// since those will be drawn concurrently anyway. It should be more efficient.
static void SubdivideFaceBySubdivSize ( face_t * f , float subdivsize )
{
// garymcthack - REFACTOR ME!!!
vec_t dummy ;
Vector hackNormal ;
WindingPlane ( f - > w , hackNormal , & dummy ) ;
// HACK - only subdivide stuff that is facing up or down (for water)
if ( fabs ( hackNormal [ 2 ] ) < .9f )
{
return ;
}
// Get the extents of the surface.
// garymcthack - this assumes a surface of constant z for now (for water). . can generalize later.
subdivsize = ( int ) subdivsize ;
winding_t * w ;
w = CopyWinding ( f - > w ) ;
Vector min , max ;
WindingBounds ( w , min , max ) ;
#if 0
Msg ( " START WINDING: \n " ) ;
PrintWinding ( w ) ;
# endif
int xStart , yStart , xEnd , yEnd , xSteps , ySteps ;
xStart = ( int ) subdivsize * ( int ) ( ( min [ 0 ] - subdivsize ) / subdivsize ) ;
xEnd = ( int ) subdivsize * ( int ) ( ( max [ 0 ] + subdivsize ) / subdivsize ) ;
yStart = ( int ) subdivsize * ( int ) ( ( min [ 1 ] - subdivsize ) / subdivsize ) ;
yEnd = ( int ) subdivsize * ( int ) ( ( max [ 1 ] + subdivsize ) / subdivsize ) ;
xSteps = ( xEnd - xStart ) / subdivsize ;
ySteps = ( yEnd - yStart ) / subdivsize ;
int x , y ;
int xi , yi ;
winding_t * * windings = ( winding_t * * ) new pwinding_t [ xSteps * ySteps ] ;
memset ( windings , 0 , sizeof ( winding_t * ) * xSteps * ySteps ) ;
for ( yi = 0 , y = yStart ; y < yEnd ; y + = ( int ) subdivsize , yi + + )
{
for ( xi = 0 , x = xStart ; x < xEnd ; x + = ( int ) subdivsize , xi + + )
{
winding_t * tempWinding , * frontWinding , * backWinding ;
float planeDist ;
Vector normal ;
normal . Init ( 1.0f , 0.0f , 0.0f ) ;
planeDist = ( float ) x ;
tempWinding = CopyWinding ( w ) ;
ClipWindingEpsilon ( tempWinding , normal , planeDist , ON_EPSILON ,
& frontWinding , & backWinding ) ;
if ( tempWinding )
{
FreeWinding ( tempWinding ) ;
}
if ( backWinding )
{
FreeWinding ( backWinding ) ;
}
if ( ! frontWinding )
{
continue ;
}
tempWinding = frontWinding ;
normal . Init ( - 1.0f , 0.0f , 0.0f ) ;
planeDist = - ( float ) ( x + subdivsize ) ;
ClipWindingEpsilon ( tempWinding , normal , planeDist , ON_EPSILON ,
& frontWinding , & backWinding ) ;
if ( tempWinding )
{
FreeWinding ( tempWinding ) ;
}
if ( backWinding )
{
FreeWinding ( backWinding ) ;
}
if ( ! frontWinding )
{
continue ;
}
tempWinding = frontWinding ;
normal . Init ( 0.0f , 1.0f , 0.0f ) ;
planeDist = ( float ) y ;
ClipWindingEpsilon ( tempWinding , normal , planeDist , ON_EPSILON ,
& frontWinding , & backWinding ) ;
if ( tempWinding )
{
FreeWinding ( tempWinding ) ;
}
if ( backWinding )
{
FreeWinding ( backWinding ) ;
}
if ( ! frontWinding )
{
continue ;
}
tempWinding = frontWinding ;
normal . Init ( 0.0f , - 1.0f , 0.0f ) ;
planeDist = - ( float ) ( y + subdivsize ) ;
ClipWindingEpsilon ( tempWinding , normal , planeDist , ON_EPSILON ,
& frontWinding , & backWinding ) ;
if ( tempWinding )
{
FreeWinding ( tempWinding ) ;
}
if ( backWinding )
{
FreeWinding ( backWinding ) ;
}
if ( ! frontWinding )
{
continue ;
}
#if 0
Msg ( " output winding: \n " ) ;
PrintWinding ( frontWinding ) ;
# endif
if ( frontWinding )
{
windings [ xi + yi * xSteps ] = frontWinding ;
}
}
}
FreeWinding ( w ) ;
dprimitive_t & newPrim = g_primitives [ g_numprimitives ] ;
f - > firstPrimID = g_numprimitives ;
f - > numPrims = 1 ;
newPrim . firstIndex = g_numprimindices ;
newPrim . firstVert = g_numprimverts ;
newPrim . indexCount = 0 ;
newPrim . vertCount = 0 ;
# ifdef USE_TRISTRIPS
newPrim . type = PRIM_TRISTRIP ;
# else
newPrim . type = PRIM_TRILIST ;
# endif
CUtlVector < WORD > triListIndices ;
int i ;
for ( i = 0 ; i < xSteps * ySteps ; i + + )
{
if ( ! windings [ i ] )
{
continue ;
}
unsigned short * pIndices =
( unsigned short * ) _alloca ( windings [ i ] - > numpoints * sizeof ( unsigned short ) ) ;
// find indices for the verts.
newPrim . vertCount = AddWindingToPrimverts ( windings [ i ] , pIndices , newPrim . firstVert , newPrim . vertCount ) ;
// Now that we have indices for the verts, fan-tesselate the polygon and spit out tris.
for ( int j = 0 ; j < windings [ i ] - > numpoints - 2 ; j + + )
{
triListIndices . AddToTail ( pIndices [ 0 ] ) ;
triListIndices . AddToTail ( pIndices [ j + 1 ] ) ;
triListIndices . AddToTail ( pIndices [ j + 2 ] ) ;
}
}
delete [ ] windings ;
// We've already updated the verts and have a trilist. . let's strip it!
if ( ! triListIndices . Size ( ) )
{
return ;
}
# ifdef USE_TRISTRIPS
int numTristripIndices ;
WORD * pStripIndices = NULL ;
Stripify ( triListIndices . Size ( ) / 3 , triListIndices . Base ( ) , & numTristripIndices ,
& pStripIndices ) ;
Assert ( pStripIndices ) ;
// FIXME: Should also call ComputeVertexPermutation and reorder the verts.
for ( i = 0 ; i < numTristripIndices ; i + + )
{
Assert ( pStripIndices [ i ] > = newPrim . firstVert & &
pStripIndices [ i ] < newPrim . firstVert + newPrim . vertCount ) ;
g_primindices [ newPrim . firstIndex + newPrim . indexCount ] = pStripIndices [ i ] ;
newPrim . indexCount + + ;
g_numprimindices + + ;
if ( g_numprimindices > MAX_MAP_PRIMINDICES )
{
Error ( " Exceeded max water indicies. \n Increase surface subdivision size! (%d>%d) \n " , g_numprimindices , MAX_MAP_PRIMINDICES ) ;
}
}
delete [ ] pStripIndices ;
# else
for ( i = 0 ; i < triListIndices . Size ( ) ; i + + )
{
g_primindices [ newPrim . firstIndex + newPrim . indexCount ] = triListIndices [ i ] ;
newPrim . indexCount + + ;
g_numprimindices + + ;
if ( g_numprimindices > MAX_MAP_PRIMINDICES )
{
Error ( " Exceeded max water indicies. \n Increase surface subdivision size! (%d>%d) \n " , g_numprimindices , MAX_MAP_PRIMINDICES ) ;
}
}
# endif
g_numprimitives + + ; // don't increment until we get here and are sure that we have a primitive.
if ( g_numprimitives > MAX_MAP_PRIMITIVES )
{
Error ( " Exceeded max water primitives. \n Increase surface subdivision size! (%d>%d) \n " , ( int ) g_numprimitives , ( int ) MAX_MAP_PRIMITIVES ) ;
}
}
void SubdivideFaceBySubdivSize ( face_t * f )
{
if ( f - > numpoints = = 0 | | f - > split [ 0 ] | | f - > split [ 1 ] | | f - > merged | | ! f - > w )
{
return ;
}
// see if the face needs to be subdivided.
texinfo_t * pTexInfo = & texinfo [ f - > texinfo ] ;
dtexdata_t * pTexData = GetTexData ( pTexInfo - > texdata ) ;
bool bFound ;
const char * pMaterialName = TexDataStringTable_GetString ( pTexData - > nameStringTableID ) ;
MaterialSystemMaterial_t matID =
FindOriginalMaterial ( pMaterialName , & bFound , false ) ;
if ( ! bFound )
{
return ;
}
const char * subdivsizeString = GetMaterialVar ( matID , " $subdivsize " ) ;
if ( subdivsizeString )
{
float subdivSize = atof ( subdivsizeString ) ;
if ( subdivSize > 0.0f )
{
// NOTE: Subdivision is unsupported and should be phased out
Warning ( " Using subdivision on %s \n " , pMaterialName ) ;
SubdivideFaceBySubdivSize ( f , subdivSize ) ;
}
}
}
void SplitSubdividedFaces_Node_r ( node_t * node )
{
if ( node - > planenum = = PLANENUM_LEAF )
{
return ;
}
face_t * f ;
for ( f = node - > faces ; f ; f = f - > next )
{
SubdivideFaceBySubdivSize ( f ) ;
}
//
// recursively output the other nodes
//
SplitSubdividedFaces_Node_r ( node - > children [ 0 ] ) ;
SplitSubdividedFaces_Node_r ( node - > children [ 1 ] ) ;
}
void SplitSubdividedFaces ( face_t * pLeafFaceList , node_t * headnode )
{
// deal with leaf faces.
face_t * f = pLeafFaceList ;
while ( f )
{
SubdivideFaceBySubdivSize ( f ) ;
f = f - > next ;
}
// deal with node faces.
SplitSubdividedFaces_Node_r ( headnode ) ;
}
# pragma optimize( "", on )
/*
= = = = = = = = = = = =
MakeFaces
= = = = = = = = = = = =
*/
void MakeFaces ( node_t * node )
{
qprintf ( " --- MakeFaces --- \n " ) ;
c_merge = 0 ;
c_subdivide = 0 ;
c_nodefaces = 0 ;
MakeFaces_r ( node ) ;
qprintf ( " %5i makefaces \n " , c_nodefaces ) ;
qprintf ( " %5i merged \n " , c_merge ) ;
qprintf ( " %5i subdivided \n " , c_subdivide ) ;
}