//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#include "vbsp.h"


int		c_nodes;
int		c_nonvis;
int		c_active_brushes;

// if a brush just barely pokes onto the other side,
// let it slide by without chopping
#define	PLANESIDE_EPSILON	0.001
//0.1


void FindBrushInTree (node_t *node, int brushnum)
{
	bspbrush_t	*b;

	if (node->planenum == PLANENUM_LEAF)
	{
		for (b=node->brushlist ; b ; b=b->next)
			if (b->original->brushnum == brushnum)
				Msg("here\n");
		return;
	}
	FindBrushInTree (node->children[0], brushnum);
	FindBrushInTree (node->children[1], brushnum);
}

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

/*
================
DrawBrushList
================
*/
void DrawBrushList (bspbrush_t *brush, node_t *node)
{
	int		i;
	side_t	*s;

	GLS_BeginScene ();
	for ( ; brush ; brush=brush->next)
	{
		for (i=0 ; i<brush->numsides ; i++)
		{
			s = &brush->sides[i];
			if (!s->winding)
				continue;
			if (s->texinfo == TEXINFO_NODE)
				GLS_Winding (s->winding, 1);
			else if (!s->visible)
				GLS_Winding (s->winding, 2);
			else
				GLS_Winding (s->winding, 0);
		}
	}
	GLS_EndScene ();
}

/*
================
WriteBrushList
================
*/
void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
{
	int		i;
	side_t	*s;

	qprintf ("writing %s\n", name);
	FileHandle_t f = g_pFileSystem->Open(name, "w");

	for ( ; brush ; brush=brush->next)
	{
		for (i=0 ; i<brush->numsides ; i++)
		{
			s = &brush->sides[i];
			if (!s->winding)
				continue;
			if (onlyvis && !s->visible)
				continue;
			OutputWinding (brush->sides[i].winding, f);
		}
	}

	g_pFileSystem->Close (f);
}

void PrintBrush (bspbrush_t *brush)
{
	int		i;

	Msg("brush: %p\n", brush);
	for (i=0;i<brush->numsides ; i++)
	{
		pw(brush->sides[i].winding);
		Msg("\n");
	}
}

/*
==================
BoundBrush

Sets the mins/maxs based on the windings
==================
*/
void BoundBrush (bspbrush_t *brush)
{
	int			i, j;
	winding_t	*w;

	ClearBounds (brush->mins, brush->maxs);
	for (i=0 ; i<brush->numsides ; i++)
	{
		w = brush->sides[i].winding;
		if (!w)
			continue;
		for (j=0 ; j<w->numpoints ; j++)
			AddPointToBounds (w->p[j], brush->mins, brush->maxs);
	}
}

Vector PointInsideBrush( bspbrush_t *brush )
{
	Vector insidePoint = vec3_origin;

	for (int i = 0; i < brush->numsides; i++)
	{
		side_t *side = &brush->sides[i];
		plane_t *plane = &mapplanes[side->planenum];
		float d = DotProduct( plane->normal, insidePoint ) - plane->dist;
		if ( d < 0 )
		{
			insidePoint -= d * plane->normal;
		}
	}
	return insidePoint;
}

/*
==================
CreateBrushWindings

==================
*/
void CreateBrushWindings (bspbrush_t *brush)
{
	int			i, j;
	winding_t	*w;
	side_t		*side;
	plane_t		*plane;

	// translate the CSG problem to improve precision
	Vector insidePoint = PointInsideBrush( brush );
	Vector offset = -insidePoint;

	for (i=0 ; i<brush->numsides ; i++)
	{
		side = &brush->sides[i];
		plane = &mapplanes[side->planenum];
		w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal, offset));
		for (j=0 ; j<brush->numsides && w; j++)
		{
			if (i == j)
				continue;
			if (brush->sides[j].bevel)
				continue;
			plane = &mapplanes[brush->sides[j].planenum^1];
			ChopWindingInPlace (&w, plane->normal, plane->dist + DotProduct(plane->normal, offset), 0); //CLIP_EPSILON);
		}

		TranslateWinding( w, -offset );
		side->winding = w;
	}

	BoundBrush (brush);
}

/*
==================
BrushFromBounds

Creates a new axial brush
==================
*/
bspbrush_t	*BrushFromBounds (Vector& mins, Vector& maxs)
{
	bspbrush_t	*b;
	int			i;
	Vector		normal;
	vec_t		dist;

	b = AllocBrush (6);
	b->numsides = 6;
	for (i=0 ; i<3 ; i++)
	{
		VectorClear (normal);
		normal[i] = 1;
		dist = maxs[i];
		b->sides[i].planenum = FindFloatPlane (normal, dist);

		normal[i] = -1;
		dist = -mins[i];
		b->sides[3+i].planenum = FindFloatPlane (normal, dist);
	}

	CreateBrushWindings (b);

	return b;
}

/*
==================
BrushVolume

==================
*/
vec_t BrushVolume (bspbrush_t *brush)
{
	int			i;
	winding_t	*w;
	Vector		corner;
	vec_t		d, area, volume;
	plane_t		*plane;

	if (!brush)
		return 0;

	// grab the first valid point as the corner

	w = NULL;
	for (i=0 ; i<brush->numsides ; i++)
	{
		w = brush->sides[i].winding;
		if (w)
			break;
	}
	if (!w)
		return 0;
	VectorCopy (w->p[0], corner);

	// make tetrahedrons to all other faces

	volume = 0;
	for ( ; i<brush->numsides ; i++)
	{
		w = brush->sides[i].winding;
		if (!w)
			continue;
		plane = &mapplanes[brush->sides[i].planenum];
		d = -(DotProduct (corner, plane->normal) - plane->dist);
		area = WindingArea (w);
		volume += d*area;
	}

	volume /= 3;
	return volume;
}

/*
================
CountBrushList
================
*/
int	CountBrushList (bspbrush_t *brushes)
{
	int	c;

	c = 0;
	for ( ; brushes ; brushes = brushes->next)
		c++;
	return c;
}

/*
================
AllocTree
================
*/
tree_t *AllocTree (void)
{
	tree_t	*tree;

	tree = (tree_t*)malloc(sizeof(*tree));
	memset (tree, 0, sizeof(*tree));
	ClearBounds (tree->mins, tree->maxs);

	return tree;
}

/*
================
AllocNode
================
*/
node_t *AllocNode (void)
{
	static s_NodeCount = 0;

	node_t	*node;

	node = (node_t*)malloc(sizeof(*node));
	memset (node, 0, sizeof(*node));
	node->id = s_NodeCount;
	node->diskId = -1;

	s_NodeCount++;

	return node;
}


/*
================
AllocBrush
================
*/
bspbrush_t *AllocBrush (int numsides)
{
	static int s_BrushId = 0;

	bspbrush_t	*bb;
	int			c;

	c = (int)&(((bspbrush_t *)0)->sides[numsides]);
	bb = (bspbrush_t*)malloc(c);
	memset (bb, 0, c);
	bb->id = s_BrushId++;
	if (numthreads == 1)
		c_active_brushes++;
	return bb;
}

/*
================
FreeBrush
================
*/
void FreeBrush (bspbrush_t *brushes)
{
	int			i;

	for (i=0 ; i<brushes->numsides ; i++)
		if (brushes->sides[i].winding)
			FreeWinding(brushes->sides[i].winding);
	free (brushes);
	if (numthreads == 1)
		c_active_brushes--;
}


/*
================
FreeBrushList
================
*/
void FreeBrushList (bspbrush_t *brushes)
{
	bspbrush_t	*next;

	for ( ; brushes ; brushes = next)
	{
		next = brushes->next;

		FreeBrush (brushes);
	}		
}

/*
==================
CopyBrush

Duplicates the brush, the sides, and the windings
==================
*/
bspbrush_t *CopyBrush (bspbrush_t *brush)
{
	bspbrush_t *newbrush;
	int			size;
	int			i;
	
	size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);

	newbrush = AllocBrush (brush->numsides);
	memcpy (newbrush, brush, size);

	for (i=0 ; i<brush->numsides ; i++)
	{
		if (brush->sides[i].winding)
			newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
	}

	return newbrush;
}


/*
==================
PointInLeaf

==================
*/
node_t	*PointInLeaf (node_t *node, Vector& point)
{
	vec_t		d;
	plane_t		*plane;

	while (node->planenum != PLANENUM_LEAF)
	{
		plane = &mapplanes[node->planenum];
		d = DotProduct (point, plane->normal) - plane->dist;
		if (d > 0)
			node = node->children[0];
		else
			node = node->children[1];
	}

	return node;
}

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

/*
==============
BoxOnPlaneSide

Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
==============
*/
int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane)
{
	int		side;
	int		i;
	Vector	corners[2];
	vec_t	dist1, dist2;

	// axial planes are easy
	if (plane->type < 3)
	{
		side = 0;
		if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
			side |= PSIDE_FRONT;
		if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
			side |= PSIDE_BACK;
		return side;
	}

	// create the proper leading and trailing verts for the box

	for (i=0 ; i<3 ; i++)
	{
		if (plane->normal[i] < 0)
		{
			corners[0][i] = mins[i];
			corners[1][i] = maxs[i];
		}
		else
		{
			corners[1][i] = mins[i];
			corners[0][i] = maxs[i];
		}
	}

	dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
	dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
	side = 0;
	if (dist1 >= PLANESIDE_EPSILON)
		side = PSIDE_FRONT;
	if (dist2 < PLANESIDE_EPSILON)
		side |= PSIDE_BACK;

	return side;
}

/*
============
QuickTestBrushToPlanenum

============
*/
int	QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
{
	int			i, num;
	plane_t		*plane;
	int			s;

	*numsplits = 0;

	// if the brush actually uses the planenum,
	// we can tell the side for sure
	for (i=0 ; i<brush->numsides ; i++)
	{
		num = brush->sides[i].planenum;
		if (num >= 0x10000)
			Error ("bad planenum");
		if (num == planenum)
			return PSIDE_BACK|PSIDE_FACING;
		if (num == (planenum ^ 1) )
			return PSIDE_FRONT|PSIDE_FACING;
	}

	// box on plane side
	plane = &mapplanes[planenum];
	s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);

	// if both sides, count the visible faces split
	if (s == PSIDE_BOTH)
	{
		*numsplits += 3;
	}

	return s;
}

/*
============
TestBrushToPlanenum

============
*/
int	TestBrushToPlanenum (bspbrush_t *brush, int planenum,
						 int *numsplits, qboolean *hintsplit, int *epsilonbrush)
{
	int			i, j, num;
	plane_t		*plane;
	int			s;
	winding_t	*w;
	vec_t		d, d_front, d_back;
	int			front, back;

	*numsplits = 0;
	*hintsplit = false;

	// if the brush actually uses the planenum,
	// we can tell the side for sure
	for (i=0 ; i<brush->numsides ; i++)
	{
		num = brush->sides[i].planenum;
		if (num >= 0x10000)
			Error ("bad planenum");
		if (num == planenum)
			return PSIDE_BACK|PSIDE_FACING;
		if (num == (planenum ^ 1) )
			return PSIDE_FRONT|PSIDE_FACING;
	}

	// box on plane side
	plane = &mapplanes[planenum];
	s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);

	if (s != PSIDE_BOTH)
		return s;

// if both sides, count the visible faces split
	d_front = d_back = 0;

	for (i=0 ; i<brush->numsides ; i++)
	{
		if (brush->sides[i].texinfo == TEXINFO_NODE)
			continue;		// on node, don't worry about splits
		if (!brush->sides[i].visible)
			continue;		// we don't care about non-visible
		w = brush->sides[i].winding;
		if (!w)
			continue;

		front = back = 0;
		for (j=0 ; j<w->numpoints; j++)
		{
			d = DotProduct (w->p[j], plane->normal) - plane->dist;

			if (d > d_front)
				d_front = d;
			if (d < d_back)
				d_back = d;

			if (d > 0.1) // PLANESIDE_EPSILON)
				front = 1;
			if (d < -0.1) // PLANESIDE_EPSILON)
				back = 1;
		}

		if (front && back)
		{
			if ( !(brush->sides[i].surf & SURF_SKIP) )
			{
				(*numsplits)++;
				if (brush->sides[i].surf & SURF_HINT)
					*hintsplit = true;
			}
		}
	}

	if ( (d_front > 0.0 && d_front < 1.0)
		|| (d_back < 0.0 && d_back > -1.0) )
		(*epsilonbrush)++;

#if 0
	if (*numsplits == 0)
	{	//	didn't really need to be split
		if (front)
			s = PSIDE_FRONT;
		else if (back)
			s = PSIDE_BACK;
		else
			s = 0;
	}
#endif

	return s;
}

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

/*
================
WindingIsTiny

Returns true if the winding would be crunched out of
existance by the vertex snapping.
================
*/
#define	EDGE_LENGTH	0.2
qboolean WindingIsTiny (winding_t *w)
{
	int		i, j;
	vec_t	len;
	Vector	delta;
	int		edges;

	edges = 0;
	for (i=0 ; i<w->numpoints ; i++)
	{
		j = i == w->numpoints - 1 ? 0 : i+1;
		VectorSubtract (w->p[j], w->p[i], delta);
		len = VectorLength (delta);
		if (len > EDGE_LENGTH)
		{
			if (++edges == 3)
				return false;
		}
	}
	return true;
}


// UNDONE: JAY: This should be a slightly better heuristic - it builds an OBB 
// around the winding and tests planar dimensions.  NOTE: This can fail when a
// winding normal cannot be constructed (or is degenerate), but that is probably
// desired in this case.
// UNDONE: Test & use this instead.
#if 0
qboolean WindingIsTiny2 (winding_t *w)
{
	int		i, j;
	vec_t	len;
	Vector	delta;
	int		edges;

	vec_t maxLen = 0;
	Vector maxEdge = vec3_origin;

	edges = 0;
	for (i=0 ; i<w->numpoints ; i++)
	{
		j = i == w->numpoints - 1 ? 0 : i+1;
		VectorSubtract (w->p[j], w->p[i], delta);
		len = VectorLength (delta);
		if (len > maxLen)
		{
			maxEdge = delta;
			maxLen = len;
		}
	}
	Vector normal;
	vec_t dist;
	WindingPlane (w, normal, &dist); // normal can come back vec3_origin in some cases
	VectorNormalize(maxEdge);
	Vector cross = CrossProduct(normal, maxEdge);
	VectorNormalize(cross);
	Vector mins, maxs;
	ClearBounds( mins, maxs );
	for (i=0 ; i<w->numpoints ; i++)
	{
		Vector point;
		point.x = DotProduct( w->p[i], maxEdge );
		point.y = DotProduct( w->p[i], cross );
		point.z = DotProduct( w->p[i], normal );
		AddPointToBounds( point, mins, maxs );
	}

	// check to see if the size in the plane is too small in either dimension
	Vector size = maxs - mins;
	for ( i = 0; i < 2; i++ )
	{
		if ( size[i] < EDGE_LENGTH )
			return true;
	}
	return false;
}
#endif


/*
================
WindingIsHuge

Returns true if the winding still has one of the points
from basewinding for plane
================
*/
qboolean WindingIsHuge (winding_t *w)
{
	int		i, j;

	for (i=0 ; i<w->numpoints ; i++)
	{
		for (j=0 ; j<3 ; j++)
			if (w->p[i][j] < MIN_COORD_INTEGER || w->p[i][j] > MAX_COORD_INTEGER)
				return true;
	}
	return false;
}

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

/*
================
Leafnode
================
*/
void LeafNode (node_t *node, bspbrush_t *brushes)
{
	bspbrush_t	*b;
	int			i;

	node->planenum = PLANENUM_LEAF;
	node->contents = 0;

	for (b=brushes ; b ; b=b->next)
	{
		// if the brush is solid and all of its sides are on nodes,
		// it eats everything
		if (b->original->contents & CONTENTS_SOLID)
		{
			for (i=0 ; i<b->numsides ; i++)
				if (b->sides[i].texinfo != TEXINFO_NODE)
					break;
			if (i == b->numsides)
			{
				node->contents = CONTENTS_SOLID;
				break;
			}
		}
		node->contents |= b->original->contents;
	}

	node->brushlist = brushes;
}


void RemoveAreaPortalBrushes_R( node_t *node )
{
	if( node->planenum == PLANENUM_LEAF )
	{
		// Remove any CONTENTS_AREAPORTAL brushes we added. We don't want them in the engine
		// at runtime but we do want their flags in the leaves.
		bspbrush_t **pPrev = &node->brushlist;
		for( bspbrush_t *b=node->brushlist; b; b=b->next )
		{
			if( b->original->contents == CONTENTS_AREAPORTAL )
			{
				*pPrev = b->next;
			}
			else
			{
				pPrev = &b->next;
			}
		}
	}
	else
	{
		RemoveAreaPortalBrushes_R( node->children[0] );
		RemoveAreaPortalBrushes_R( node->children[1] );
	}
}


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

void CheckPlaneAgainstParents (int pnum, node_t *node)
{
	node_t	*p;

	for (p=node->parent ; p ; p=p->parent)
	{
		if (p->planenum == pnum)
			Error ("Tried parent");
	}
}

qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
{
	bspbrush_t	*front, *back;
	qboolean	good;

	SplitBrush (node->volume, pnum, &front, &back);

	good = (front && back);

	if (front)
		FreeBrush (front);
	if (back)
		FreeBrush (back);

	return good;
}

/*
================
SelectSplitSide

Using a hueristic, choses one of the sides out of the brushlist
to partition the brushes with.
Returns NULL if there are no valid planes to split with..
================
*/

side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
{
	int			value, bestvalue;
	bspbrush_t	*brush, *test;
	side_t		*side, *bestside;
	int			i, j, pass, numpasses;
	int			pnum;
	int			s;
	int			front, back, both, facing, splits;
	int			bsplits;
	int			bestsplits;
	int			epsilonbrush;
	qboolean	hintsplit;

	bestside = NULL;
	bestvalue = -99999;
	bestsplits = 0;

	// the search order goes: visible-structural, nonvisible-structural
	// If any valid plane is available in a pass, no further
	// passes will be tried.
	numpasses = 2;
	for (pass = 0 ; pass < numpasses ; pass++)
	{
		for (brush = brushes ; brush ; brush=brush->next)
		{
			for (i=0 ; i<brush->numsides ; i++)
			{
				side = brush->sides + i;

				if (side->bevel)
					continue;	// never use a bevel as a spliter
				if (!side->winding)
					continue;	// nothing visible, so it can't split
				if (side->texinfo == TEXINFO_NODE)
					continue;	// allready a node splitter
				if (side->tested)
					continue;	// we allready have metrics for this plane
				if (side->surf & SURF_SKIP)
					continue;	// skip surfaces are never chosen
				if ( side->visible ^ (pass<1) )
					continue;	// only check visible faces on first pass
				
				pnum = side->planenum;
				pnum &= ~1;	// allways use positive facing plane

				CheckPlaneAgainstParents (pnum, node);

				if (!CheckPlaneAgainstVolume (pnum, node))
					continue;	// would produce a tiny volume

				front = 0;
				back = 0;
				both = 0;
				facing = 0;
				splits = 0;
				epsilonbrush = 0;

				for (test = brushes ; test ; test=test->next)
				{
					s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);

					splits += bsplits;
					if (bsplits && (s&PSIDE_FACING) )
						Error ("PSIDE_FACING with splits");

					test->testside = s;
					// if the brush shares this face, don't bother
					// testing that facenum as a splitter again
					if (s & PSIDE_FACING)
					{
						facing++;
						for (j=0 ; j<test->numsides ; j++)
						{
							if ( (test->sides[j].planenum&~1) == pnum)
								test->sides[j].tested = true;
						}
					}
					if (s & PSIDE_FRONT)
						front++;
					if (s & PSIDE_BACK)
						back++;
					if (s == PSIDE_BOTH)
						both++;
				}

				// give a value estimate for using this plane
				value =  5*facing - 5*splits - abs(front-back);
//					value =  -5*splits;
//					value =  5*facing - 5*splits;
				if (mapplanes[pnum].type < 3)
					value+=5;		// axial is better
				value -= epsilonbrush*1000;	// avoid!

				// trans should split last
				if ( side->surf & SURF_TRANS )
				{
					value -= 500;
				}

				// never split a hint side except with another hint
				if (hintsplit && !(side->surf & SURF_HINT) )
					value = -9999999;

				// water should split first
				if (side->contents & (CONTENTS_WATER | CONTENTS_SLIME))
					value = 9999999;

				// save off the side test so we don't need
				// to recalculate it when we actually seperate
				// the brushes
				if (value > bestvalue)
				{
					bestvalue = value;
					bestside = side;
					bestsplits = splits;
					for (test = brushes ; test ; test=test->next)
						test->side = test->testside;
				}
			}
		}

		// if we found a good plane, don't bother trying any
		// other passes
		if (bestside)
		{
			if (pass > 0)
			{
				if (numthreads == 1)
					c_nonvis++;
			}
			break;
		}
	}

	//
	// clear all the tested flags we set
	//
	for (brush = brushes ; brush ; brush=brush->next)
	{
		for (i=0 ; i<brush->numsides ; i++)
			brush->sides[i].tested = false;
	}

	return bestside;
}


/*
==================
BrushMostlyOnSide

==================
*/
int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
{
	int			i, j;
	winding_t	*w;
	vec_t		d, max;
	int			side;

	max = 0;
	side = PSIDE_FRONT;
	for (i=0 ; i<brush->numsides ; i++)
	{
		w = brush->sides[i].winding;
		if (!w)
			continue;
		for (j=0 ; j<w->numpoints ; j++)
		{
			d = DotProduct (w->p[j], plane->normal) - plane->dist;
			if (d > max)
			{
				max = d;
				side = PSIDE_FRONT;
			}
			if (-d > max)
			{
				max = -d;
				side = PSIDE_BACK;
			}
		}
	}
	return side;
}

/*
================
SplitBrush

Generates two new brushes, leaving the original
unchanged
================
*/


void SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
{
	bspbrush_t	*b[2];
	int			i, j;
	winding_t	*w, *cw[2], *midwinding;
	plane_t		*plane, *plane2;
	side_t		*s, *cs;
	float		d, d_front, d_back;

	*front = *back = NULL;
	plane = &mapplanes[planenum];

	// check all points
	d_front = d_back = 0;
	for (i=0 ; i<brush->numsides ; i++)
	{
		w = brush->sides[i].winding;
		if (!w)
			continue;
		for (j=0 ; j<w->numpoints ; j++)
		{
			d = DotProduct (w->p[j], plane->normal) - plane->dist;
			if (d > 0 && d > d_front)
				d_front = d;
			if (d < 0 && d < d_back)
				d_back = d;
		}
	}

	if (d_front < 0.1) // PLANESIDE_EPSILON)
	{	// only on back
		*back = CopyBrush (brush);
		return;
	}
	if (d_back > -0.1) // PLANESIDE_EPSILON)
	{	// only on front
		*front = CopyBrush (brush);
		return;
	}


	// Move the CSG problem so that offset is at the origin
	// This gives us much better floating point precision in the clipping operations
	Vector offset = -0.5f * (brush->mins + brush->maxs);
	// create a new winding from the split plane

	w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal,offset));
	for (i=0 ; i<brush->numsides && w ; i++)
	{
		plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
		ChopWindingInPlace (&w, plane2->normal, plane2->dist+DotProduct(plane2->normal,offset), 0); // PLANESIDE_EPSILON);
	}

	if (!w || WindingIsTiny (w) )
	{	// the brush isn't really split
		int		side;

		side = BrushMostlyOnSide (brush, plane);
		if (side == PSIDE_FRONT)
			*front = CopyBrush (brush);
		if (side == PSIDE_BACK)
			*back = CopyBrush (brush);
		return;
	}

	if (WindingIsHuge (w))
	{
		qprintf ("WARNING: huge winding\n");
	}

	TranslateWinding( w, -offset );
	midwinding = w;

    //
    //
	// split it for real
    //
    //

    //
    // allocate two new brushes referencing the original
    //
	for( i = 0; i < 2; i++ )
	{
		b[i] = AllocBrush( brush->numsides + 1 );
		b[i]->original = brush->original;
	}

    //
	// split all the current windings
    //
	for( i = 0; i < brush->numsides; i++ )
	{
        // get the current side
		s = &brush->sides[i];

        // get the sides winding
		w = s->winding;
		if( !w )
			continue;

        // clip the winding
		ClipWindingEpsilon_Offset( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1], offset );

		for( j = 0; j < 2; j++ )
		{
            // does winding exist?
			if( !cw[j] )
				continue;
#if 0
			if (WindingIsTiny (cw[j]))
			{
				FreeWinding (cw[j]);
				continue;
			}
#endif

            //
            // create a clipped "side" with the new winding
            //
			cs = &b[j]->sides[b[j]->numsides];
			b[j]->numsides++;
			*cs = *s;
			cs->winding = cw[j];
			cs->tested = false;
            // save the original side information
            //cs->original = s->original;
		}
	}


	// see if we have valid polygons on both sides

	for (i=0 ; i<2 ; i++)
	{
		BoundBrush (b[i]);
		for (j=0 ; j<3 ; j++)
		{
			if (b[i]->mins[j] < MIN_COORD_INTEGER || b[i]->maxs[j] > MAX_COORD_INTEGER)
			{
				qprintf ("bogus brush after clip\n");
				break;
			}
		}

		if (b[i]->numsides < 3 || j < 3)
		{
			FreeBrush (b[i]);
			b[i] = NULL;
		}
	}

	if ( !(b[0] && b[1]) )
	{
		if (!b[0] && !b[1])
			qprintf ("split removed brush\n");
		else
			qprintf ("split not on both sides\n");
		if (b[0])
		{
			FreeBrush (b[0]);
			*front = CopyBrush (brush);
		}
		if (b[1])
		{
			FreeBrush (b[1]);
			*back = CopyBrush (brush);
		}
		return;
	}

	// add the midwinding to both sides
	for (i=0 ; i<2 ; i++)
	{
		cs = &b[i]->sides[b[i]->numsides];
		b[i]->numsides++;

		cs->planenum = planenum^i^1;
		cs->texinfo = TEXINFO_NODE;

        // initialize the displacement map index
		cs->pMapDisp = NULL;

        cs->visible = false;
		cs->tested = false;
		if (i==0)
			cs->winding = CopyWinding (midwinding);
		else
			cs->winding = midwinding;
	}

{
	vec_t	v1;
	int		i;

	for (i=0 ; i<2 ; i++)
	{
		v1 = BrushVolume (b[i]);
		if (v1 < 1.0)
		{
			FreeBrush (b[i]);
			b[i] = NULL;
//			qprintf ("tiny volume after clip\n");
		}
	}
}

	*front = b[0];
	*back = b[1];
}


/*
================
SplitBrushList
================
*/
void SplitBrushList (bspbrush_t *brushes, 
	node_t *node, bspbrush_t **front, bspbrush_t **back)
{
	bspbrush_t	*brush, *newbrush, *newbrush2;
	side_t		*side;
	int			sides;
	int			i;

	*front = *back = NULL;

	for (brush = brushes ; brush ; brush=brush->next)
	{
		sides = brush->side;

		if (sides == PSIDE_BOTH)
		{	// split into two brushes
			SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
			if (newbrush)
			{
				newbrush->next = *front;
				*front = newbrush;
			}
			if (newbrush2)
			{
				newbrush2->next = *back;
				*back = newbrush2;
			}
			continue;
		}

		newbrush = CopyBrush (brush);

		// if the planenum is actualy a part of the brush
		// find the plane and flag it as used so it won't be tried
		// as a splitter again
		if (sides & PSIDE_FACING)
		{
			for (i=0 ; i<newbrush->numsides ; i++)
			{
				side = newbrush->sides + i;
				if ( (side->planenum& ~1) == node->planenum)
					side->texinfo = TEXINFO_NODE;
			}
		}


		if (sides & PSIDE_FRONT)
		{
			newbrush->next = *front;
			*front = newbrush;
			continue;
		}
		if (sides & PSIDE_BACK)
		{
			newbrush->next = *back;
			*back = newbrush;
			continue;
		}
	}
}


/*
================
BuildTree_r
================
*/


node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
{
	node_t		*newnode;
	side_t		*bestside;
	int			i;
	bspbrush_t	*children[2];

	if (numthreads == 1)
		c_nodes++;

	// find the best plane to use as a splitter
	bestside = SelectSplitSide (brushes, node);

	if (!bestside)
	{
		// leaf node
		node->side = NULL;
		node->planenum = -1;
		LeafNode (node, brushes);
		return node;
	}
			 
	// this is a splitplane node
	node->side = bestside;
	node->planenum = bestside->planenum & ~1;	// always use front facing

	SplitBrushList (brushes, node, &children[0], &children[1]);
	FreeBrushList (brushes);

	// allocate children before recursing
	for (i=0 ; i<2 ; i++)
	{
		newnode = AllocNode ();
		newnode->parent = node;
		node->children[i] = newnode;
	}

	SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
		&node->children[1]->volume);

	// recursively process children
	for (i=0 ; i<2 ; i++)
	{
		node->children[i] = BuildTree_r (node->children[i], children[i]);
	}

	return node;
}
	  

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

/*
=================
BrushBSP

The incoming list will be freed before exiting
=================
*/
tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs)
{
	node_t		*node;
	bspbrush_t	*b;
	int			c_faces, c_nonvisfaces;
	int			c_brushes;
	tree_t		*tree;
	int			i;
	vec_t		volume;

	qprintf ("--- BrushBSP ---\n");

	tree = AllocTree ();

	c_faces = 0;
	c_nonvisfaces = 0;
	c_brushes = 0;
	for (b=brushlist ; b ; b=b->next)
	{
		c_brushes++;

		volume = BrushVolume (b);
		if (volume < microvolume)
		{
			Warning("Brush %i: WARNING, microbrush\n", b->original->id);
		}

		for (i=0 ; i<b->numsides ; i++)
		{
			if (b->sides[i].bevel)
				continue;
			if (!b->sides[i].winding)
				continue;
			if (b->sides[i].texinfo == TEXINFO_NODE)
				continue;
			if (b->sides[i].visible)
				c_faces++;
			else
				c_nonvisfaces++;
		}

		AddPointToBounds (b->mins, tree->mins, tree->maxs);
		AddPointToBounds (b->maxs, tree->mins, tree->maxs);
	}

	qprintf ("%5i brushes\n", c_brushes);
	qprintf ("%5i visible faces\n", c_faces);
	qprintf ("%5i nonvisible faces\n", c_nonvisfaces);

	c_nodes = 0;
	c_nonvis = 0;
	node = AllocNode ();

	node->volume = BrushFromBounds (mins, maxs);

	tree->headnode = node;

	node = BuildTree_r (node, brushlist);
	qprintf ("%5i visible nodes\n", c_nodes/2 - c_nonvis);
	qprintf ("%5i nonvis nodes\n", c_nonvis);
	qprintf ("%5i leafs\n", (c_nodes+1)/2);
#if 0
{	// debug code
static node_t	*tnode;
Vector	p;

p[0] = -1469;
p[1] = -118;
p[2] = 119;
tnode = PointInLeaf (tree->headnode, p);
Msg("contents: %i\n", tnode->contents);
p[0] = 0;
}
#endif
	return tree;
}

