// faces.c

#include "vbsp.h"
#include "utlvector.h"
#include "utilmatlib.h"
#include <float.h>
#include "mstristrip.h"
#include "vstdlib/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.25
#define	OFF_EPSILON			0.5

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 ("numvertexes == 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");
	}

	// 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 ("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 ("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 ("MAX_SUPERVERTS");
	superverts[numsuperverts] = p1;
	numsuperverts++;
}

/*
==================
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;

	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);
}

/*
==================
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 (!notjunc)
	{
		FixEdges_r (headnode);

		// UNDONE: What we really want to do here is not add any world-only tjuncs to the details
		// But you'd have to know which verts were in each set in the hash in order to build the
		// proper edges for the leaf faces.
		EmitLeafFaceVertexes( &pLeafFaceList );
		FixLeafFaceEdges( &pLeafFaceList );
	}
	else
	{
		EmitLeafFaceVertexes( &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 ("numedges == 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;
}

/*
=============
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;
    
	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);
	}
}

//===========================================================================

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;

    // save portal info
	f->portal = p;

	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 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 0
		FreeFace( f );
		return NULL;
#endif
		
		// garymcthack - stick in another function
		texinfo_t *pTexInfo = &texinfo[f->texinfo];
		dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
		MaterialSystemMaterial_t matID = 
			FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), NULL, true );
		const char *bottomMatName = GetMaterialVar( matID, "$bottommaterial" );	
		if( !bottomMatName )
		{
			const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
			if( !Q_stristr( pMaterialName, "nodraw" ) )
			{
				Warning("error: material %s doesn't have a $bottommaterial\n", 
					 pMaterialName );
			}
			FreeFace( f );
			return NULL;
		}
		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 = FindTexData( bottomMatName );
		f->texinfo = FindOrCreateTexInfo( newTexInfo );
	}

    //
    // 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.\nIncrease 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 );
	int numWindings = 0;
	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.\nIncrease 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.\nIncrease 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.\nIncrease surface subdivision size! (%d>%d)\n", ( int )g_numprimitives, ( int )MAX_MAP_PRIMITIVES );
	}
}

void SubdivideFaceBySubdivSize( face_t *f )
{
	if( f->numpoints == 0 )
	{
		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 )
		{
			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);
}