/*
===========================================================================

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").

Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "sys/platform.h"
#include "framework/FileSystem.h"

#include "tools/compilers/aas/BrushBSP.h"

#define BSP_GRID_SIZE					512.0f
#define SPLITTER_EPSILON				0.1f
#define VERTEX_MELT_EPSILON				0.1f
#define VERTEX_MELT_HASH_SIZE			32

#define PORTAL_PLANE_NORMAL_EPSILON		0.00001f
#define PORTAL_PLANE_DIST_EPSILON		0.01f

//#define OUPUT_BSP_STATS_PER_GRID_CELL

//===============================================================
//
//	idBrushBSPPortal
//
//===============================================================

/*
============
idBrushBSPPortal::idBrushBSPPortal
============
*/
idBrushBSPPortal::idBrushBSPPortal( void ) {
	planeNum = -1;
	winding = NULL;
	nodes[0] = nodes[1] = NULL;
	next[0] = next[1] = NULL;
	faceNum = 0;
	flags = 0;
}

/*
============
idBrushBSPPortal::~idBrushBSPPortal
============
*/
idBrushBSPPortal::~idBrushBSPPortal( void ) {
	if ( winding ) {
		delete winding;
	}
}

/*
============
idBrushBSPPortal::AddToNodes
============
*/
void idBrushBSPPortal::AddToNodes( idBrushBSPNode *front, idBrushBSPNode *back ) {
	if ( nodes[0] || nodes[1] ) {
		common->Error( "AddToNode: already included" );
	}

	assert( front && back );

	nodes[0] = front;
	next[0] = front->portals;
	front->portals = this;

	nodes[1] = back;
	next[1] = back->portals;
	back->portals = this;
}

/*
============
idBrushBSPPortal::RemoveFromNode
============
*/
void idBrushBSPPortal::RemoveFromNode( idBrushBSPNode *l ) {
	idBrushBSPPortal **pp, *t;

	// remove reference to the current portal
	pp = &l->portals;
	while (1)
	{
		t = *pp;
		if ( !t ) {
			common->Error( "idBrushBSPPortal::RemoveFromNode: portal not in node" );
		}

		if ( t == this ) {
			break;
		}

		if ( t->nodes[0] == l ) {
			pp = &t->next[0];
		}
		else if ( t->nodes[1] == l ) {
			pp = &t->next[1];
		}
		else {
			common->Error( "idBrushBSPPortal::RemoveFromNode: portal not bounding node" );
		}
	}

	if ( nodes[0] == l ) {
		*pp = next[0];
		nodes[0] = NULL;
	}
	else if ( nodes[1] == l ) {
		*pp = next[1];
		nodes[1] = NULL;
	}
	else {
		common->Error( "idBrushBSPPortal::RemoveFromNode: mislinked portal" );
	}
}

/*
============
idBrushBSPPortal::Flip
============
*/
void idBrushBSPPortal::Flip( void ) {
	idBrushBSPNode *frontNode, *backNode;

	frontNode = nodes[0];
	backNode = nodes[1];

	if ( frontNode ) {
		RemoveFromNode( frontNode );
	}
	if ( backNode ) {
		RemoveFromNode( backNode );
	}
	AddToNodes( frontNode, backNode );

	plane = -plane;
	planeNum ^= 1;
	winding->ReverseSelf();
}

/*
============
idBrushBSPPortal::Split
============
*/
int idBrushBSPPortal::Split( const idPlane &splitPlane, idBrushBSPPortal **front, idBrushBSPPortal **back ) {
	idWinding *frontWinding, *backWinding;

	(*front) = (*back) = NULL;
	winding->Split( splitPlane, 0.1f, &frontWinding, &backWinding );
	if ( frontWinding ) {
		(*front) = new idBrushBSPPortal();
		(*front)->plane = plane;
		(*front)->planeNum = planeNum;
		(*front)->flags = flags;
		(*front)->winding = frontWinding;
	}
	if ( backWinding ) {
		(*back) = new idBrushBSPPortal();
		(*back)->plane = plane;
		(*back)->planeNum = planeNum;
		(*back)->flags = flags;
		(*back)->winding = backWinding;
	}

	if ( frontWinding && backWinding ) {
		return PLANESIDE_CROSS;
	}
	else if ( frontWinding ) {
		return PLANESIDE_FRONT;
	}
	else {
		return PLANESIDE_BACK;
	}
}


//===============================================================
//
//	idBrushBSPNode
//
//===============================================================

/*
============
idBrushBSPNode::idBrushBSPNode
============
*/
idBrushBSPNode::idBrushBSPNode( void ) {
	brushList.Clear();
	contents = 0;
	flags = 0;
	volume = NULL;
	portals = NULL;
	children[0] = children[1] = NULL;
	areaNum = 0;
	occupied = 0;
}

/*
============
idBrushBSPNode::~idBrushBSPNode
============
*/
idBrushBSPNode::~idBrushBSPNode( void ) {
	idBrushBSPPortal *p;

	// delete brushes
	brushList.Free();

	// delete volume brush
	if ( volume ) {
		delete volume;
	}

	// delete portals
	for ( p = portals; p; p = portals ) {
		p->RemoveFromNode( this );
		if ( !p->nodes[0] && !p->nodes[1] ) {
			delete p;
		}
	}
}

/*
============
idBrushBSPNode::SetContentsFromBrushes
============
*/
void idBrushBSPNode::SetContentsFromBrushes( void ) {
	idBrush *brush;

	contents = 0;
	for ( brush = brushList.Head(); brush; brush = brush->Next() ) {
		contents |= brush->GetContents();
	}
}

/*
============
idBrushBSPNode::GetPortalBounds
============
*/
idBounds idBrushBSPNode::GetPortalBounds( void ) {
	int s, i;
	idBrushBSPPortal *p;
	idBounds bounds;

	bounds.Clear();
	for ( p = portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == this);

		for ( i = 0; i < p->winding->GetNumPoints(); i++ ) {
			bounds.AddPoint( (*p->winding)[i].ToVec3() );
		}
	}
	return bounds;
}

/*
============
idBrushBSPNode::TestLeafNode
============
*/
bool idBrushBSPNode::TestLeafNode( void ) {
	int s, n;
	float d;
	idBrushBSPPortal *p;
	idVec3 center;
	idPlane plane;

	n = 0;
	center = vec3_origin;
	for ( p = portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == this);
		center += p->winding->GetCenter();
		n++;
	}

	center /= n;

	for ( p = portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == this);
		if ( s ) {
			plane = -p->GetPlane();
		}
		else {
			plane = p->GetPlane();
		}
		d = plane.Distance( center );
		if ( d < 0.0f ) {
			return false;
		}
	}
	return true;
}

/*
============
idBrushBSPNode::Split
============
*/
bool idBrushBSPNode::Split( const idPlane &splitPlane, int splitPlaneNum ) {
	int s, i;
	idWinding *mid;
	idBrushBSPPortal *p, *midPortal, *newPortals[2];
	idBrushBSPNode *newNodes[2];

	mid = new idWinding( splitPlane.Normal(), splitPlane.Dist() );

	for ( p = portals; p && mid; p = p->next[s] ) {
		s = (p->nodes[1] == this);
		if ( s ) {
			mid = mid->Clip( -p->plane, 0.1f, false );
		}
		else {
			mid = mid->Clip( p->plane, 0.1f, false );
		}
	}

	if ( !mid ) {
		return false;
	}

	// allocate two new nodes
	for ( i = 0; i < 2; i++ ) {
		newNodes[i] = new idBrushBSPNode();
		newNodes[i]->flags = flags;
		newNodes[i]->contents = contents;
		newNodes[i]->parent = this;
	}

	// split all portals of the node
	for ( p = portals; p; p = portals ) {
		s = (p->nodes[1] == this);
		p->Split( splitPlane, &newPortals[0], &newPortals[1] );
		for ( i = 0; i < 2; i++ ) {
			if ( newPortals[i] ) {
				if ( s ) {
					newPortals[i]->AddToNodes( p->nodes[0], newNodes[i] );
				}
				else {
					newPortals[i]->AddToNodes( newNodes[i], p->nodes[1] );
				}
			}
		}
		p->RemoveFromNode( p->nodes[0] );
		p->RemoveFromNode( p->nodes[1] );
		delete p;
	}

	// add seperating portal
	midPortal = new idBrushBSPPortal();
	midPortal->plane = splitPlane;
	midPortal->planeNum = splitPlaneNum;
	midPortal->winding = mid;
	midPortal->AddToNodes( newNodes[0], newNodes[1] );

	// set new child nodes
	children[0] = newNodes[0];
	children[1] = newNodes[1];
	plane = splitPlane;

	return true;
}

/*
============
idBrushBSPNode::PlaneSide
============
*/
int idBrushBSPNode::PlaneSide( const idPlane &plane, float epsilon ) const {
	int s, side;
	idBrushBSPPortal *p;
	bool front, back;

	front = back = false;
	for ( p = portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == this);

		side = p->winding->PlaneSide( plane, epsilon );
		if ( side == SIDE_CROSS || side == SIDE_ON) {
			return side;
		}
		if ( side == SIDE_FRONT ) {
			if ( back ) {
				return SIDE_CROSS;
			}
			front = true;
		}
		if ( side == SIDE_BACK ) {
			if ( front ) {
				return SIDE_CROSS;
			}
			back = true;
		}
	}

	if ( front ) {
		return SIDE_FRONT;
	}
	return SIDE_BACK;
}

/*
============
idBrushBSPNode::RemoveFlagFlood
============
*/
void idBrushBSPNode::RemoveFlagFlood( int flag ) {
	int s;
	idBrushBSPPortal *p;

	RemoveFlag( flag );

	for ( p = GetPortals(); p; p = p->Next(s) ) {
		s = (p->GetNode(1) == this);

		if ( !(p->GetNode( !s )->GetFlags() & flag ) ) {
			continue;
		}

		p->GetNode( !s )->RemoveFlagFlood( flag );
	}
}

/*
============
idBrushBSPNode::RemoveFlagRecurse
============
*/
void idBrushBSPNode::RemoveFlagRecurse( int flag ) {
	RemoveFlag( flag );
	if ( children[0] ) {
		children[0]->RemoveFlagRecurse( flag );
	}
	if ( children[1] ) {
		children[1]->RemoveFlagRecurse( flag );
	}
}

/*
============
idBrushBSPNode::RemoveFlagRecurseFlood
============
*/
void idBrushBSPNode::RemoveFlagRecurseFlood( int flag ) {
	RemoveFlag( flag );
	if ( !children[0] && !children[1] ) {
		RemoveFlagFlood( flag );
	}
	else {
		if ( children[0] ) {
			children[0]->RemoveFlagRecurseFlood( flag );
		}
		if ( children[1] ) {
			children[1]->RemoveFlagRecurseFlood( flag );
		}
	}
}


//===============================================================
//
//	idBrushBSP
//
//===============================================================

/*
============
idBrushBSP::idBrushBSP
============
*/
idBrushBSP::idBrushBSP( void ) {
	root = outside = NULL;
	numSplits = numPrunedSplits = 0;
	brushMapContents = 0;
	brushMap = NULL;
}

/*
============
idBrushBSP::~idBrushBSP
============
*/
idBrushBSP::~idBrushBSP( void ) {

	RemoveMultipleLeafNodeReferences_r( root );
	Free_r( root );

	if ( outside ) {
		delete outside;
	}
}

/*
============
idBrushBSP::RemoveMultipleLeafNodeReferences_r
============
*/
void idBrushBSP::RemoveMultipleLeafNodeReferences_r( idBrushBSPNode *node ) {
	if ( !node ) {
		return;
	}

	if ( node->children[0] ) {
		if ( node->children[0]->parent != node ) {
			node->children[0] = NULL;
		}
		else {
			RemoveMultipleLeafNodeReferences_r( node->children[0] );
		}
	}
	if ( node->children[1] ) {
		if ( node->children[1]->parent != node ) {
			node->children[1] = NULL;
		}
		else {
			RemoveMultipleLeafNodeReferences_r( node->children[1] );
		}
	}
}

/*
============
idBrushBSP::Free_r
============
*/
void idBrushBSP::Free_r( idBrushBSPNode *node ) {
	if ( !node ) {
		return;
	}

	Free_r( node->children[0] );
	Free_r( node->children[1] );

	delete node;
}

/*
============
idBrushBSP::IsValidSplitter
============
*/
ID_INLINE bool idBrushBSP::IsValidSplitter( const idBrushSide *side ) {
	return !( side->GetFlags() & ( SFL_SPLIT | SFL_USED_SPLITTER ) );
}

/*
============
idBrushBSP::BrushSplitterStats
============
*/
typedef struct splitterStats_s {
	int numFront;			// number of brushes at the front of the splitter
	int numBack;			// number of brushes at the back of the splitter
	int numSplits;			// number of brush sides split by the splitter
	int numFacing;			// number of brushes facing this splitter
	int epsilonBrushes;		// number of tiny brushes this splitter would create
} splitterStats_t;

int idBrushBSP::BrushSplitterStats( const idBrush *brush, int planeNum, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &stats ) {
	int i, j, num, s, lastNumSplits;
	const idPlane *plane;
	const idWinding *w;
	float d, d_front, d_back, brush_front, brush_back;

	plane = &planeList[planeNum];

	// get the plane side for the brush bounds
	s = brush->GetBounds().PlaneSide( *plane, SPLITTER_EPSILON );
	if ( s == PLANESIDE_FRONT ) {
		stats.numFront++;
		return BRUSH_PLANESIDE_FRONT;
	}
	if ( s == PLANESIDE_BACK ) {
		stats.numBack++;
		return BRUSH_PLANESIDE_BACK;
	}

	// if the brush actually uses the planenum, we can tell the side for sure
	for ( i = 0; i < brush->GetNumSides(); i++ ) {
		num = brush->GetSide( i )->GetPlaneNum();

		if ( !(( num ^ planeNum ) >> 1) ) {
			if ( num == planeNum ) {
				stats.numBack++;
				stats.numFacing++;
				return ( BRUSH_PLANESIDE_BACK | BRUSH_PLANESIDE_FACING );
			}
			if ( num == ( planeNum ^ 1 ) ) {
				stats.numFront++;
				stats.numFacing++;
				return ( BRUSH_PLANESIDE_FRONT | BRUSH_PLANESIDE_FACING );
			}
		}
	}

	lastNumSplits = stats.numSplits;
	brush_front = brush_back = 0.0f;
	for ( i = 0; i < brush->GetNumSides(); i++ ) {

		if ( !IsValidSplitter( brush->GetSide( i ) ) ) {
			continue;
		}

		j = brush->GetSide( i )->GetPlaneNum();
		if ( testedPlanes[j] || testedPlanes[j^1] ) {
			continue;
		}

		w = brush->GetSide(i)->GetWinding();
		if ( !w ) {
			continue;
		}
		d_front = d_back = 0.0f;
		for ( j = 0; j < w->GetNumPoints(); j++ ) {
			d = plane->Distance( (*w)[j].ToVec3() );
			if ( d > d_front ) {
				d_front = d;
			}
			else if ( d < d_back ) {
				d_back = d;
			}
		}
		if ( d_front > SPLITTER_EPSILON && d_back < -SPLITTER_EPSILON ) {
			stats.numSplits++;
		}
		if ( d_front > brush_front ) {
			brush_front = d_front;
		}
		else if ( d_back < brush_back ) {
			brush_back = d_back;
		}
	}

	// if brush sides are split and the brush only pokes one unit through the plane
	if ( stats.numSplits > lastNumSplits && (brush_front < 1.0f || brush_back > -1.0f) ) {
		stats.epsilonBrushes++;
	}

	return BRUSH_PLANESIDE_BOTH;
}

/*
============
idBrushBSP::FindSplitter
============
*/
int idBrushBSP::FindSplitter( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, struct splitterStats_s &bestStats ) {
	int i, planeNum, bestSplitter, value, bestValue, f, numBrushSides;
	idBrush *brush, *b;
	splitterStats_t stats;

	memset( testedPlanes, 0, planeList.Num() * sizeof( bool ) );

	bestSplitter = -1;
	bestValue = -99999999;
	for ( brush = node->brushList.Head(); brush; brush = brush->Next() ) {

		if ( brush->GetFlags() & BFL_NO_VALID_SPLITTERS ) {
			continue;
		}

		for ( i = 0; i < brush->GetNumSides(); i++ ) {

			if ( !IsValidSplitter( brush->GetSide(i) ) ) {
				continue;
			}

			planeNum = brush->GetSide(i)->GetPlaneNum();

			if ( testedPlanes[planeNum] || testedPlanes[planeNum^1] ) {
				continue;
			}

			testedPlanes[planeNum] = testedPlanes[planeNum^1] = true;

			if ( node->volume->Split( planeList[planeNum], planeNum, NULL, NULL ) != PLANESIDE_CROSS ) {
				continue;
			}

			memset( &stats, 0, sizeof( stats ) );

			f = 15 + 5 * (brush->GetSide(i)->GetPlane().Type() < PLANETYPE_TRUEAXIAL);
			numBrushSides = node->brushList.NumSides();

			for ( b = node->brushList.Head(); b; b = b->Next() ) {

				// if the brush has no valid splitters left
				if ( b->GetFlags() & BFL_NO_VALID_SPLITTERS ) {
					b->SetPlaneSide( BRUSH_PLANESIDE_BOTH );
				}
				else {
					b->SetPlaneSide( BrushSplitterStats( b, planeNum, planeList, testedPlanes, stats ) );
				}

				numBrushSides -= b->GetNumSides();
				// best value we can get using this plane as a splitter
				value = f * (stats.numFacing + numBrushSides) - 10 * stats.numSplits - stats.epsilonBrushes * 1000;
				// if the best value for this plane can't get any better than the best value we have
				if ( value < bestValue ) {
					break;
				}
			}

			if ( b ) {
				continue;
			}

			value = f * stats.numFacing - 10 * stats.numSplits - abs(stats.numFront - stats.numBack) - stats.epsilonBrushes * 1000;

			if ( value > bestValue ) {
				bestValue = value;
				bestSplitter = planeNum;
				bestStats = stats;

				for ( b = node->brushList.Head(); b; b = b->Next() ) {
					b->SavePlaneSide();
				}
			}
		}
	}

	return bestSplitter;
}

/*
============
idBrushBSP::SetSplitterUsed
============
*/
void idBrushBSP::SetSplitterUsed( idBrushBSPNode *node, int planeNum ) {
	int i, numValidBrushSplitters;
	idBrush *brush;

	for ( brush = node->brushList.Head(); brush; brush = brush->Next() ) {
		if ( !( brush->GetSavedPlaneSide() & BRUSH_PLANESIDE_FACING ) ) {
			continue;
		}
		numValidBrushSplitters = 0;
		for ( i = 0; i < brush->GetNumSides(); i++ ) {

			if ( !(( brush->GetSide(i)->GetPlaneNum() ^ planeNum ) >> 1) ) {
				brush->GetSide(i)->SetFlag( SFL_USED_SPLITTER );
			}
			else if ( IsValidSplitter( brush->GetSide(i) ) ) {
				numValidBrushSplitters++;
			}
		}
		if ( numValidBrushSplitters == 0 ) {
			brush->SetFlag( BFL_NO_VALID_SPLITTERS );
		}
	}
}

/*
============
idBrushBSP::BuildBrushBSP_r
============
*/
idBrushBSPNode *idBrushBSP::BuildBrushBSP_r( idBrushBSPNode *node, const idPlaneSet &planeList, bool *testedPlanes, int skipContents ) {
	int planeNum;
	splitterStats_t bestStats;

	planeNum = FindSplitter( node, planeList, testedPlanes, bestStats );

	// if no split plane found this is a leaf node
	if ( planeNum == -1 ) {

		node->SetContentsFromBrushes();

		if ( brushMap && ( node->contents & brushMapContents ) ) {
			brushMap->WriteBrush( node->volume );
		}

		// free node memory
		node->brushList.Free();
		delete node->volume;
		node->volume = NULL;

		node->children[0] = node->children[1] = NULL;
		return node;
	}

	numSplits++;
	numGridCellSplits++;

	// mark all brush sides on the split plane as used
	SetSplitterUsed( node, planeNum );

	// set node split plane
	node->plane = planeList[planeNum];

	// allocate children
	node->children[0] = new idBrushBSPNode();
	node->children[1] = new idBrushBSPNode();

	// split node volume and brush list for children
	node->volume->Split( node->plane, -1, &node->children[0]->volume, &node->children[1]->volume );
	node->brushList.Split( node->plane, -1, node->children[0]->brushList, node->children[1]->brushList, true );
	node->children[0]->parent = node->children[1]->parent = node;

	// free node memory
	node->brushList.Free();
	delete node->volume;
	node->volume = NULL;

	// process children
	node->children[0] = BuildBrushBSP_r( node->children[0], planeList, testedPlanes, skipContents );
	node->children[1] = BuildBrushBSP_r( node->children[1], planeList, testedPlanes, skipContents );

	// if both children contain the skip contents
	if ( node->children[0]->contents & node->children[1]->contents & skipContents ) {
		node->contents = node->children[0]->contents | node->children[1]->contents;
		delete node->children[0];
		delete node->children[1];
		node->children[0] = node->children[1] = NULL;
		numSplits--;
		numGridCellSplits--;
	}

	return node;
}

/*
============
idBrushBSP::ProcessGridCell
============
*/
idBrushBSPNode *idBrushBSP::ProcessGridCell( idBrushBSPNode *node, int skipContents ) {
	idPlaneSet planeList;
	bool *testedPlanes;

#ifdef OUPUT_BSP_STATS_PER_GRID_CELL
	common->Printf( "[Grid Cell %d]\n", ++numGridCells );
	common->Printf( "%6d brushes\n", node->brushList.Num() );
#endif

	numGridCellSplits = 0;

	// chop away all brush overlap
	node->brushList.Chop( BrushChopAllowed );

	// merge brushes if possible
	//node->brushList.Merge( BrushMergeAllowed );

	// create a list with planes for this grid cell
	node->brushList.CreatePlaneList( planeList );

#ifdef OUPUT_BSP_STATS_PER_GRID_CELL
	common->Printf( "[Grid Cell BSP]\n" );
#endif

	testedPlanes = new bool[planeList.Num()];

	BuildBrushBSP_r( node, planeList, testedPlanes, skipContents );

	delete testedPlanes;

#ifdef OUPUT_BSP_STATS_PER_GRID_CELL
	common->Printf( "\r%6d splits\n", numGridCellSplits );
#endif

	return node;
}

/*
============
idBrushBSP::BuildGrid_r
============
*/
void idBrushBSP::BuildGrid_r( idList<idBrushBSPNode *> &gridCells, idBrushBSPNode *node ) {
	int axis;
	float dist;
	idBounds bounds;
	idVec3 normal, halfSize;

	if ( !node->brushList.Num() ) {
		delete node->volume;
		node->volume = NULL;
		node->children[0] = node->children[1] = NULL;
		return;
	}

	bounds = node->volume->GetBounds();
	halfSize = (bounds[1] - bounds[0]) * 0.5f;
	for ( axis = 0; axis < 3; axis++ ) {
		if ( halfSize[axis] > BSP_GRID_SIZE ) {
			dist = BSP_GRID_SIZE * ( floor( (bounds[0][axis] + halfSize[axis]) / BSP_GRID_SIZE ) + 1 );
		}
		else {
			dist = BSP_GRID_SIZE * ( floor( bounds[0][axis] / BSP_GRID_SIZE ) + 1 );
		}
		if ( dist > bounds[0][axis] + 1.0f && dist < bounds[1][axis] - 1.0f ) {
			break;
		}
	}
	if ( axis >= 3 ) {
		gridCells.Append( node );
		return;
	}

	numSplits++;

	normal = vec3_origin;
	normal[axis] = 1.0f;
	node->plane.SetNormal( normal );
	node->plane.SetDist( (int) dist );

	// allocate children
	node->children[0] = new idBrushBSPNode();
	node->children[1] = new idBrushBSPNode();

	// split volume and brush list for children
	node->volume->Split( node->plane, -1, &node->children[0]->volume, &node->children[1]->volume );
	node->brushList.Split( node->plane, -1, node->children[0]->brushList, node->children[1]->brushList );
	node->children[0]->brushList.SetFlagOnFacingBrushSides( node->plane, SFL_USED_SPLITTER );
	node->children[1]->brushList.SetFlagOnFacingBrushSides( node->plane, SFL_USED_SPLITTER );
	node->children[0]->parent = node->children[1]->parent = node;

	// free node memory
	node->brushList.Free();
	delete node->volume;
	node->volume = NULL;

	// process children
	BuildGrid_r( gridCells, node->children[0] );
	BuildGrid_r( gridCells, node->children[1] );
}

/*
============
idBrushBSP::Build
============
*/
void idBrushBSP::Build( idBrushList brushList, int skipContents,
								bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ),
								bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) ) {

	int i;
	idList<idBrushBSPNode *> gridCells;

	common->Printf( "[Brush BSP]\n" );
	common->Printf( "%6d brushes\n", brushList.Num() );

	BrushChopAllowed = ChopAllowed;
	BrushMergeAllowed = MergeAllowed;

	numGridCells = 0;
	treeBounds = brushList.GetBounds();
	root = new idBrushBSPNode();
	root->brushList = brushList;
	root->volume = new idBrush();
	root->volume->FromBounds( treeBounds );
	root->parent = NULL;

	BuildGrid_r( gridCells, root );

	common->Printf( "\r%6d grid cells\n", gridCells.Num() );

#ifdef OUPUT_BSP_STATS_PER_GRID_CELL
	for ( i = 0; i < gridCells.Num(); i++ ) {
		ProcessGridCell( gridCells[i], skipContents );
	}
#else
	common->Printf( "\r%6d %%", 0 );
	for ( i = 0; i < gridCells.Num(); i++ ) {
		DisplayRealTimeString( "\r%6d", i * 100 / gridCells.Num() );
		ProcessGridCell( gridCells[i], skipContents );
	}
	common->Printf( "\r%6d %%\n", 100 );
#endif

	common->Printf( "\r%6d splits\n", numSplits );

	if ( brushMap ) {
		delete brushMap;
	}
}

/*
============
idBrushBSP::WriteBrushMap
============
*/
void idBrushBSP::WriteBrushMap( const idStr &fileName, const idStr &ext, int contents ) {
	brushMap = new idBrushMap( fileName, ext );
	brushMapContents = contents;
}

/*
============
idBrushBSP::PruneTree_r
============
*/
void idBrushBSP::PruneTree_r( idBrushBSPNode *node, int contents ) {
	int i, s;
	idBrushBSPNode *nodes[2];
	idBrushBSPPortal *p, *nextp;

	if ( !node->children[0] || !node->children[1] ) {
		return;
	}

	PruneTree_r( node->children[0], contents );
	PruneTree_r( node->children[1], contents );

	if ( ( node->children[0]->contents & node->children[1]->contents & contents ) ) {

		node->contents = node->children[0]->contents | node->children[1]->contents;
		// move all child portals to parent
		for ( i = 0; i < 2; i++ ) {
			for ( p = node->children[i]->portals; p; p = nextp ) {
				s = ( p->nodes[1] == node->children[i] );
				nextp = p->next[s];
				nodes[s] = node;
				nodes[!s] = p->nodes[!s];
				p->RemoveFromNode( p->nodes[0] );
				p->RemoveFromNode( p->nodes[1] );
				if ( nodes[!s] == node->children[!i] ) {
					delete p;	// portal seperates both children
				}
				else {
					p->AddToNodes( nodes[0], nodes[1] );
				}
			}
		}

		delete node->children[0];
		delete node->children[1];
		node->children[0] = NULL;
		node->children[1] = NULL;

		numPrunedSplits++;
	}
}

/*
============
idBrushBSP::PruneTree
============
*/
void idBrushBSP::PruneTree( int contents ) {
	numPrunedSplits = 0;
	common->Printf( "[Prune BSP]\n" );
	PruneTree_r( root, contents );
	common->Printf( "%6d splits pruned\n", numPrunedSplits );
}

/*
============
idBrushBSP::BaseWindingForNode
============
*/
#define	BASE_WINDING_EPSILON		0.001f

idWinding *idBrushBSP::BaseWindingForNode( idBrushBSPNode *node ) {
	idWinding *w;
	idBrushBSPNode *n;

	w = new idWinding( node->plane.Normal(), node->plane.Dist() );

	// clip by all the parents
	for ( n = node->parent; n && w; n = n->parent ) {

		if ( n->children[0] == node ) {
			// take front
			w = w->Clip( n->plane, BASE_WINDING_EPSILON );
		}
		else {
			// take back
			w = w->Clip( -n->plane, BASE_WINDING_EPSILON );
		}
		node = n;
	}

	return w;
}

/*
============
idBrushBSP::MakeNodePortal

  create the new portal by taking the full plane winding for the cutting
  plane and clipping it by all of parents of this node
============
*/
void idBrushBSP::MakeNodePortal( idBrushBSPNode *node ) {
	idBrushBSPPortal *newPortal, *p;
	idWinding *w;
	int side = 0;

	w = BaseWindingForNode( node );

	// clip the portal by all the other portals in the node
	for ( p = node->portals; p && w; p = p->next[side] ) {
		if ( p->nodes[0] == node ) {
			side = 0;
			w = w->Clip( p->plane, 0.1f );
		}
		else if ( p->nodes[1] == node ) {
			side = 1;
			w = w->Clip( -p->plane, 0.1f );
		}
		else {
			common->Error( "MakeNodePortal: mislinked portal" );
		}
	}

	if ( !w ) {
		return;
	}

	if ( w->IsTiny() ) {
		delete w;
		return;
	}

	newPortal = new idBrushBSPPortal();
	newPortal->plane = node->plane;
	newPortal->winding = w;
	newPortal->AddToNodes( node->children[0], node->children[1] );
}

/*
============
idBrushBSP::SplitNodePortals

  Move or split the portals that bound the node so that the node's children have portals instead of node.
============
*/
#define	SPLIT_WINDING_EPSILON		0.001f

void idBrushBSP::SplitNodePortals( idBrushBSPNode *node ) {
	int side;
	idBrushBSPPortal *p, *nextPortal, *newPortal;
	idBrushBSPNode *f, *b, *otherNode;
	idPlane *plane;
	idWinding *frontWinding, *backWinding;

	plane = &node->plane;
	f = node->children[0];
	b = node->children[1];

	for ( p = node->portals; p; p = nextPortal ) {
		if (p->nodes[0] == node) {
			side = 0;
		}
		else if (p->nodes[1] == node) {
			side = 1;
		}
		else {
			common->Error( "idBrushBSP::SplitNodePortals: mislinked portal" );
			return;
		}
		nextPortal = p->next[side];

		otherNode = p->nodes[!side];
		p->RemoveFromNode( p->nodes[0] );
		p->RemoveFromNode( p->nodes[1] );

		// cut the portal into two portals, one on each side of the cut plane
		p->winding->Split( *plane, SPLIT_WINDING_EPSILON, &frontWinding, &backWinding );

		if ( frontWinding && frontWinding->IsTiny() ) {
			delete frontWinding;
			frontWinding = NULL;
			//tinyportals++;
		}

		if ( backWinding && backWinding->IsTiny() ) {
			delete backWinding;
			backWinding = NULL;
			//tinyportals++;
		}

		if ( !frontWinding && !backWinding ) {
			// tiny windings on both sides
			continue;
		}

		if ( !frontWinding ) {
			delete backWinding;
			if ( side == 0 ) {
				p->AddToNodes( b, otherNode );
			}
			else {
				p->AddToNodes( otherNode, b );
			}
			continue;
		}
		if ( !backWinding ) {
			delete frontWinding;
			if ( side == 0 ) {
				p->AddToNodes( f, otherNode );
			}
			else {
				p->AddToNodes( otherNode, f );
			}
			continue;
		}

		// the winding is split
		newPortal = new idBrushBSPPortal();
		*newPortal = *p;
		newPortal->winding = backWinding;
		delete p->winding;
		p->winding = frontWinding;

		if ( side == 0 ) {
			p->AddToNodes( f, otherNode );
			newPortal->AddToNodes( b, otherNode );
		}
		else {
			p->AddToNodes( otherNode, f );
			newPortal->AddToNodes( otherNode, b );
		}
	}

	node->portals = NULL;
}

/*
============
idBrushBSP::MakeTreePortals_r
============
*/
void idBrushBSP::MakeTreePortals_r( idBrushBSPNode *node ) {
	int i;
	idBounds bounds;

	numPortals++;
	DisplayRealTimeString( "\r%6d", numPortals );

	bounds = node->GetPortalBounds();

	if ( bounds[0][0] >= bounds[1][0] ) {
		//common->Warning( "node without volume" );
	}

	for ( i = 0; i < 3; i++ ) {
		if ( bounds[0][i] < MIN_WORLD_COORD || bounds[1][i] > MAX_WORLD_COORD ) {
			common->Warning( "node with unbounded volume" );
			break;
		}
	}

	if ( !node->children[0] || !node->children[1] ) {
		return;
	}

	MakeNodePortal( node );
	SplitNodePortals( node );

	MakeTreePortals_r( node->children[0] );
	MakeTreePortals_r( node->children[1] );
}

/*
============
idBrushBSP::MakeOutsidePortals
============
*/
void idBrushBSP::MakeOutsidePortals( void ) {
	int i, j, n;
	idBounds bounds;
	idBrushBSPPortal *p, *portals[6];
	idVec3 normal;

	// pad with some space so there will never be null volume leaves
	bounds = treeBounds.Expand( 32 );

	for ( i = 0; i < 3; i++ ) {
		if ( bounds[0][i] > bounds[1][i] ) {
			common->Error( "empty BSP tree" );
		}
	}

	outside = new idBrushBSPNode();
	outside->parent = NULL;
	outside->children[0] = outside->children[1] = NULL;
	outside->brushList.Clear();
	outside->portals = NULL;
	outside->contents = 0;

	for ( i = 0; i < 3; i++ ) {
		for ( j = 0; j < 2; j++ ) {

			p = new idBrushBSPPortal();
			normal = vec3_origin;
			normal[i] = j ? -1 : 1;
			p->plane.SetNormal( normal );
			p->plane.SetDist( j ? -bounds[j][i] : bounds[j][i] );
			p->winding = new idWinding( p->plane.Normal(), p->plane.Dist() );
			p->AddToNodes( root, outside );

			n = j * 3 + i;
			portals[n] = p;
		}
	}

	// clip the base windings with all the other planes
	for ( i = 0; i < 6; i++ ) {
		for ( j = 0; j < 6; j++ ) {
			if (j == i) {
				continue;
			}
			portals[i]->winding = portals[i]->winding->Clip( portals[j]->plane, ON_EPSILON );
		}
	}
}

/*
============
idBrushBSP::Portalize
============
*/
void idBrushBSP::Portalize( void ) {
	common->Printf( "[Portalize BSP]\n" );
	common->Printf( "%6d nodes\n", (numSplits - numPrunedSplits) * 2 + 1 );
	numPortals = 0;
	MakeOutsidePortals();
	MakeTreePortals_r( root );
	common->Printf( "\r%6d nodes portalized\n", numPortals );
}

/*
=============
LeakFile

Finds the shortest possible chain of portals that
leads from the outside leaf to a specific occupied leaf.
=============
*/
void idBrushBSP::LeakFile( const idStr &fileName ) {
	int count, next, s;
	idVec3 mid;
	idFile *lineFile;
	idBrushBSPNode *node, *nextNode = NULL;
	idBrushBSPPortal *p, *nextPortal = NULL;
	idStr qpath, name;

	if ( !outside->occupied ) {
		return;
	}

	qpath = fileName;
	qpath.SetFileExtension( "lin" );

	common->Printf( "writing %s...\n", qpath.c_str() );

	lineFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" );
	if ( !lineFile ) {
		common->Error( "Couldn't open %s\n", qpath.c_str() );
		return;
	}

	count = 0;
	node = outside;
	while( node->occupied > 1 ) {

		// find the best portal exit
		next = node->occupied;
		for (p = node->portals; p; p = p->next[!s] ) {
			s = (p->nodes[0] == node);
			if ( p->nodes[s]->occupied && p->nodes[s]->occupied < next ) {
				nextPortal = p;
				nextNode = p->nodes[s];
				next = nextNode->occupied;
			}
		}
		node = nextNode;
		mid = nextPortal->winding->GetCenter();
		lineFile->Printf( "%f %f %f\n", mid[0], mid[1], mid[2] );
		count++;
	}

	// add the origin of the entity from which the leak was found
	lineFile->Printf( "%f %f %f\n", leakOrigin[0], leakOrigin[1], leakOrigin[2] );

	fileSystem->CloseFile( lineFile );
}

/*
============
idBrushBSP::FloodThroughPortals_r
============
*/
void idBrushBSP::FloodThroughPortals_r( idBrushBSPNode *node, int contents, int depth ) {
	idBrushBSPPortal *p;
	int s;

	if ( !node ) {
		common->Error( "FloodThroughPortals_r: NULL node\n" );
	}

	if ( node->occupied ) {
		common->Error( "FloodThroughPortals_r: node already occupied\n" );
	}
	node->occupied = depth;

	for ( p = node->portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == node);

		// if the node at the other side of the portal is removed
		if ( !p->nodes[!s] ) {
			continue;
		}

		// if the node at the other side of the portal is occupied already
		if ( p->nodes[!s]->occupied ) {
			continue;
		}

		// can't flood through the portal if it has the seperating contents at the other side
		if ( p->nodes[!s]->contents & contents ) {
			continue;
		}

		// flood recursively through the current portal
		FloodThroughPortals_r( p->nodes[!s], contents, depth+1 );
	}
}

/*
============
idBrushBSP::FloodFromOrigin
============
*/
bool idBrushBSP::FloodFromOrigin( const idVec3 &origin, int contents ) {
	idBrushBSPNode *node;

	//find the leaf to start in
	node = root;
	while( node->children[0] && node->children[1] ) {

		if ( node->plane.Side( origin ) == PLANESIDE_BACK ) {
			node = node->children[1];
		}
		else {
			node = node->children[0];
		}
	}

	if ( !node ) {
		return false;
	}

	// if inside the inside/outside seperating contents
	if ( node->contents & contents ) {
		return false;
	}

	// if the node is already occupied
	if ( node->occupied ) {
		return false;
	}

	FloodThroughPortals_r( node, contents, 1 );

	return true;
}

/*
============
idBrushBSP::FloodFromEntities

  Marks all nodes that can be reached by entites.
============
*/
bool idBrushBSP::FloodFromEntities( const idMapFile *mapFile, int contents, const idStrList &classNames ) {
	int i, j;
	bool inside;
	idVec3 origin;
	idMapEntity *mapEnt;
	idStr classname;

	inside = false;
	outside->occupied = 0;

	// skip the first entity which is assumed to be the worldspawn
	for ( i = 1; i < mapFile->GetNumEntities(); i++ ) {

		mapEnt = mapFile->GetEntity( i );

		if ( !mapEnt->epairs.GetVector( "origin", "", origin ) ) {
			continue;
		}

		if ( !mapEnt->epairs.GetString( "classname", "", classname ) ) {
			continue;
		}

		for ( j = 0; j < classNames.Num(); j++ ) {
			if ( classname.Icmp( classNames[j] ) == 0 ) {
				break;
			}
		}

		if ( j >= classNames.Num() ) {
			continue;
		}

		origin[2] += 1;

		// nudge around a little
		if ( FloodFromOrigin( origin, contents ) ) {
			inside = true;
		}

		if ( outside->occupied ) {
			leakOrigin = origin;
			break;
		}
	}

	if ( !inside ) {
		common->Warning( "no entities inside" );
	}
	else if ( outside->occupied ) {
		common->Warning( "reached outside from entity %d (%s)", i, classname.c_str() );
	}

	return ( inside && !outside->occupied );
}

/*
============
idBrushBSP::RemoveOutside_r
============
*/
void idBrushBSP::RemoveOutside_r( idBrushBSPNode *node, int contents ) {

	if ( !node ) {
		return;
	}

	if ( node->children[0] || node->children[1] ) {
		RemoveOutside_r( node->children[0], contents );
		RemoveOutside_r( node->children[1], contents );
		return;
	}

	if ( !node->occupied ) {
		if ( !( node->contents & contents ) ) {
			outsideLeafNodes++;
			node->contents |= contents;
		}
		else {
			solidLeafNodes++;
		}
	}
	else {
		insideLeafNodes++;
	}
}

/*
============
idBrushBSP::RemoveOutside
============
*/
bool idBrushBSP::RemoveOutside( const idMapFile *mapFile, int contents, const idStrList &classNames ) {
	common->Printf( "[Remove Outside]\n" );

	solidLeafNodes = outsideLeafNodes = insideLeafNodes = 0;

	if ( !FloodFromEntities( mapFile, contents, classNames ) ) {
		return false;
	}

	RemoveOutside_r( root, contents );

	common->Printf( "%6d solid leaf nodes\n", solidLeafNodes );
	common->Printf( "%6d outside leaf nodes\n", outsideLeafNodes );
	common->Printf( "%6d inside leaf nodes\n", insideLeafNodes );

	//PruneTree( contents );

	return true;
}

/*
============
idBrushBSP::SetPortalPlanes_r
============
*/
void idBrushBSP::SetPortalPlanes_r( idBrushBSPNode *node, idPlaneSet &planeList ) {
	int s;
	idBrushBSPPortal *p;

	if ( !node ) {
		return;
	}

	for ( p = node->portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == node);
		if ( p->planeNum == -1 ) {
			p->planeNum = planeList.FindPlane( p->plane, PORTAL_PLANE_NORMAL_EPSILON, PORTAL_PLANE_DIST_EPSILON );
		}
	}
	SetPortalPlanes_r( node->children[0], planeList );
	SetPortalPlanes_r( node->children[1], planeList );
}

/*
============
idBrushBSP::SetPortalPlanes

  give all portals a plane number
============
*/
void idBrushBSP::SetPortalPlanes( void ) {
	SetPortalPlanes_r( root, portalPlanes );
}

/*
============
idBrushBSP::MergeLeafNodePortals
============
*/
void idBrushBSP::MergeLeafNodePortals( idBrushBSPNode *node, int skipContents ) {
	int s1, s2;
	bool foundPortal;
	idBrushBSPPortal *p1, *p2, *nextp1, *nextp2;
	idWinding *newWinding, *reverse;

	// pass 1: merge all portals that seperate the same leaf nodes
	for ( p1 = node->GetPortals(); p1; p1 = nextp1 ) {
		s1 = (p1->GetNode(1) == node);
		nextp1 = p1->Next(s1);

		for ( p2 = nextp1; p2; p2 = nextp2 ) {
			s2 = (p2->GetNode(1) == node);
			nextp2 = p2->Next(s2);

			// if both portals seperate the same leaf nodes
			if ( p1->nodes[!s1] == p2->nodes[!s2] ) {

				// add the winding of p2 to the winding of p1
				p1->winding->AddToConvexHull( p2->winding, p1->plane.Normal() );

				// delete p2
				p2->RemoveFromNode( p2->nodes[0] );
				p2->RemoveFromNode( p2->nodes[1] );
				delete p2;

				numMergedPortals++;

				nextp1 = node->GetPortals();
				break;
			}
		}
	}

	// pass 2: merge all portals in the same plane if they all have the skip contents at the other side
	for ( p1 = node->GetPortals(); p1; p1 = nextp1 ) {
		s1 = (p1->GetNode(1) == node);
		nextp1 = p1->Next(s1);

		if ( !(p1->nodes[!s1]->contents & skipContents) ) {
			continue;
		}

		// test if all portals in this plane have the skip contents at the other side
		foundPortal = false;
		for ( p2 = node->GetPortals(); p2; p2 = nextp2 ) {
			s2 = (p2->GetNode(1) == node);
			nextp2 = p2->Next(s2);

			if ( p2 == p1 || (p2->planeNum & ~1) != (p1->planeNum & ~1) ) {
				continue;
			}
			foundPortal = true;
			if ( !(p2->nodes[!s2]->contents & skipContents) ) {
				break;
			}
		}

		// if all portals in this plane have the skip contents at the other side
		if ( !p2 && foundPortal ) {
			for ( p2 = node->GetPortals(); p2; p2 = nextp2 ) {
				s2 = (p2->GetNode(1) == node);
				nextp2 = p2->Next(s2);

				if ( p2 == p1 || (p2->planeNum & ~1) != (p1->planeNum & ~1) ) {
					continue;
				}

				// add the winding of p2 to the winding of p1
				p1->winding->AddToConvexHull( p2->winding, p1->plane.Normal() );

				// delete p2
				p2->RemoveFromNode( p2->nodes[0] );
				p2->RemoveFromNode( p2->nodes[1] );
				delete p2;

				numMergedPortals++;
			}
			nextp1 = node->GetPortals();
		}
	}

	// pass 3: try to merge portals in the same plane that have the skip contents at the other side
	for ( p1 = node->GetPortals(); p1; p1 = nextp1 ) {
		s1 = (p1->GetNode(1) == node);
		nextp1 = p1->Next(s1);

		if ( !(p1->nodes[!s1]->contents & skipContents) ) {
			continue;
		}

		for ( p2 = nextp1; p2; p2 = nextp2 ) {
			s2 = (p2->GetNode(1) == node);
			nextp2 = p2->Next(s2);

			if ( !(p2->nodes[!s2]->contents & skipContents) ) {
				continue;
			}

			if ( (p2->planeNum & ~1) != (p1->planeNum & ~1) ) {
				continue;
			}

			// try to merge the two portal windings
			if ( p2->planeNum == p1->planeNum ) {
				newWinding = p1->winding->TryMerge( *p2->winding, p1->plane.Normal() );
			}
			else {
				reverse = p2->winding->Reverse();
				newWinding = p1->winding->TryMerge( *reverse, p1->plane.Normal() );
				delete reverse;
			}

			// if successfully merged
			if ( newWinding ) {

				// replace the winding of the first portal
				delete p1->winding;
				p1->winding = newWinding;

				// delete p2
				p2->RemoveFromNode( p2->nodes[0] );
				p2->RemoveFromNode( p2->nodes[1] );
				delete p2;

				numMergedPortals++;

				nextp1 = node->GetPortals();
				break;
			}
		}
	}
}

/*
============
idBrushBSP::MergePortals_r
============
*/
void idBrushBSP::MergePortals_r( idBrushBSPNode *node, int skipContents ) {

	if ( !node ) {
		return;
	}

	if ( node->contents & skipContents ) {
		return;
	}

	if ( !node->children[0] && !node->children[1] ) {
		MergeLeafNodePortals( node, skipContents );
		return;
	}

	MergePortals_r( node->children[0], skipContents );
	MergePortals_r( node->children[1], skipContents );
}

/*
============
idBrushBSP::MergePortals
============
*/
void idBrushBSP::MergePortals( int skipContents ) {
	numMergedPortals = 0;
	common->Printf( "[Merge Portals]\n" );
	SetPortalPlanes();
	MergePortals_r( root, skipContents );
	common->Printf( "%6d portals merged\n", numMergedPortals );
}

/*
============
idBrushBSP::PruneMergedTree_r
============
*/
void idBrushBSP::PruneMergedTree_r( idBrushBSPNode *node ) {
	int i;
	idBrushBSPNode *leafNode;

	if ( !node ) {
		return;
	}

	PruneMergedTree_r( node->children[0] );
	PruneMergedTree_r( node->children[1] );

	for ( i = 0; i < 2; i++ ) {
		if ( node->children[i] ) {
			leafNode = node->children[i]->children[0];
			if ( leafNode && leafNode == node->children[i]->children[1] ) {
				if ( leafNode->parent == node->children[i] ) {
					leafNode->parent = node;
				}
				delete node->children[i];
				node->children[i] = leafNode;
			}
		}
	}
}

/*
============
idBrushBSP::UpdateTreeAfterMerge_r
============
*/
void idBrushBSP::UpdateTreeAfterMerge_r( idBrushBSPNode *node, const idBounds &bounds, idBrushBSPNode *oldNode, idBrushBSPNode *newNode ) {

	if ( !node ) {
		return;
	}

	if ( !node->children[0] && !node->children[1] ) {
		return;
	}

	if ( node->children[0] == oldNode ) {
		node->children[0] = newNode;
	}
	if ( node->children[1] == oldNode ) {
		node->children[1] = newNode;
	}

	switch( bounds.PlaneSide( node->plane, 2.0f ) ) {
		case PLANESIDE_FRONT:
			UpdateTreeAfterMerge_r( node->children[0], bounds, oldNode, newNode );
			break;
		case PLANESIDE_BACK:
			UpdateTreeAfterMerge_r( node->children[1], bounds, oldNode, newNode );
			break;
		default:
			UpdateTreeAfterMerge_r( node->children[0], bounds, oldNode, newNode );
			UpdateTreeAfterMerge_r( node->children[1], bounds, oldNode, newNode );
			break;
	}
}

/*
============
idBrushBSP::TryMergeLeafNodes

  NOTE: multiple brances of the BSP tree might point to the same leaf node after merging
============
*/
bool idBrushBSP::TryMergeLeafNodes( idBrushBSPPortal *portal, int side ) {
	int i, j, k, s1, s2, s;
	idBrushBSPNode *nodes[2], *node1, *node2;
	idBrushBSPPortal *p1, *p2, *p, *nextp;
	idPlane plane;
	idWinding *w;
	idBounds bounds, b;

	nodes[0] = node1 = portal->nodes[side];
	nodes[1] = node2 = portal->nodes[!side];

	// check if the merged node would still be convex
	for ( i = 0; i < 2; i++ ) {

		j = !i;

		for ( p1 = nodes[i]->portals; p1; p1 = p1->next[s1] ) {
			s1 = (p1->nodes[1] == nodes[i]);

			if ( p1->nodes[!s1] == nodes[j] ) {
				continue;
			}

			if ( s1 ) {
				plane = -p1->plane;
			}
			else {
				plane = p1->plane;
			}

			// all the non seperating portals of the other node should be at the front or on the plane
			for ( p2 = nodes[j]->portals; p2; p2 = p2->next[s2] ) {
				s2 = (p2->nodes[1] == nodes[j]);

				if ( p2->nodes[!s2] == nodes[i] ) {
					continue;
				}

				w = p2->winding;
				for ( k = 0; k < w->GetNumPoints(); k++ ) {
					if ( plane.Distance( (*w)[k].ToVec3() ) < -0.1f ) {
						return false;
					}
				}
			}
		}
	}

	// remove all portals that seperate the two nodes
	for ( p = node1->portals; p; p = nextp ) {
		s = (p->nodes[1] == node1);
		nextp = p->next[s];

		if ( p->nodes[!s] == node2 ) {
			p->RemoveFromNode( p->nodes[0] );
			p->RemoveFromNode( p->nodes[1] );
			delete p;
		}
	}

	// move all portals of node2 to node1
	for ( p = node2->portals; p; p = node2->portals ) {
		s = (p->nodes[1] == node2);

		nodes[s] = node1;
		nodes[!s] = p->nodes[!s];
		p->RemoveFromNode( p->nodes[0] );
		p->RemoveFromNode( p->nodes[1] );
		p->AddToNodes( nodes[0], nodes[1] );
	}

	// get bounds for the new node
	bounds.Clear();
	for ( p = node1->portals; p; p = p->next[s] ) {
		s = (p->nodes[1] == node1);
		p->GetWinding()->GetBounds( b );
		bounds += b;
	}

	// replace every reference to node2 by a reference to node1
	UpdateTreeAfterMerge_r( root, bounds, node2, node1 );

	delete node2;

	return true;
}

/*
============
idBrushBSP::MeltFloor_r

  flood through portals touching the bounds to find all vertices that might be inside the bounds
============
*/
void idBrushBSP::MeltFlood_r( idBrushBSPNode *node, int skipContents, idBounds &bounds, idVectorSet<idVec3,3> &vertexList ) {
	int s1, i;
	idBrushBSPPortal *p1;
	idBounds b;
	const idWinding *w;

	node->SetFlag( NODE_VISITED );

	for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
		s1 = (p1->GetNode(1) == node);

		if ( p1->GetNode( !s1 )->GetFlags() & NODE_VISITED ) {
			continue;
		}

		w = p1->GetWinding();

		for ( i = 0; i < w->GetNumPoints(); i++ ) {
			if ( bounds.ContainsPoint( (*w)[i].ToVec3() ) ) {
				vertexList.FindVector( (*w)[i].ToVec3(), VERTEX_MELT_EPSILON );
			}
		}
	}

	for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
		s1 = (p1->GetNode(1) == node);

		if ( p1->GetNode( !s1 )->GetFlags() & NODE_VISITED ) {
			continue;
		}

		if ( p1->GetNode( !s1 )->GetContents() & skipContents ) {
			continue;
		}

		w = p1->GetWinding();
		w->GetBounds( b );

		if ( !bounds.IntersectsBounds( b ) ) {
			continue;
		}

		MeltFlood_r( p1->GetNode( !s1 ), skipContents, bounds, vertexList );
	}
}

/*
============
idBrushBSP::MeltLeafNodePortals
============
*/
void idBrushBSP::MeltLeafNodePortals( idBrushBSPNode *node, int skipContents, idVectorSet<idVec3,3> &vertexList ) {
	int s1, i;
	idBrushBSPPortal *p1;
	idBounds bounds;

	if ( node->GetFlags() & NODE_DONE ) {
		return;
	}

	node->SetFlag( NODE_DONE );

	// melt things together
	for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
		s1 = (p1->GetNode(1) == node);

		if ( p1->GetNode( !s1 )->GetFlags() & NODE_DONE ) {
			continue;
		}

		p1->winding->GetBounds( bounds );
		bounds.ExpandSelf( 2 * VERTEX_MELT_HASH_SIZE * VERTEX_MELT_EPSILON );
		vertexList.Init( bounds[0], bounds[1], VERTEX_MELT_HASH_SIZE, 128 );

		// get all vertices to be considered
		MeltFlood_r( node, skipContents, bounds, vertexList );
		node->RemoveFlagFlood( NODE_VISITED );

		for ( i = 0; i < vertexList.Num(); i++ ) {
			if ( p1->winding->InsertPointIfOnEdge( vertexList[i], p1->plane, 0.1f ) ) {
				numInsertedPoints++;
			}
		}
	}
	DisplayRealTimeString( "\r%6d", numInsertedPoints );
}

/*
============
idBrushBSP::MeltPortals_r
============
*/
void idBrushBSP::MeltPortals_r( idBrushBSPNode *node, int skipContents, idVectorSet<idVec3,3> &vertexList ) {
	if ( !node ) {
		return;
	}

	if ( node->contents & skipContents ) {
		return;
	}

	if ( !node->children[0] && !node->children[1] ) {
		MeltLeafNodePortals( node, skipContents, vertexList );
		return;
	}

	MeltPortals_r( node->children[0], skipContents, vertexList );
	MeltPortals_r( node->children[1], skipContents, vertexList );
}

/*
============
idBrushBSP::RemoveLeafNodeColinearPoints
============
*/
void idBrushBSP::RemoveLeafNodeColinearPoints( idBrushBSPNode *node ) {
	int s1;
	idBrushBSPPortal *p1;

	// remove colinear points
	for ( p1 = node->GetPortals(); p1; p1 = p1->Next(s1) ) {
		s1 = (p1->GetNode(1) == node);
		p1->winding->RemoveColinearPoints( p1->plane.Normal(), 0.1f );
	}
}

/*
============
idBrushBSP::RemoveColinearPoints_r
============
*/
void idBrushBSP::RemoveColinearPoints_r( idBrushBSPNode *node, int skipContents ) {
	if ( !node ) {
		return;
	}

	if ( node->contents & skipContents ) {
		return;
	}

	if ( !node->children[0] && !node->children[1] ) {
		RemoveLeafNodeColinearPoints( node );
		return;
	}

	RemoveColinearPoints_r( node->children[0], skipContents );
	RemoveColinearPoints_r( node->children[1], skipContents );
}

/*
============
idBrushBSP::MeltPortals
============
*/
void idBrushBSP::MeltPortals( int skipContents ) {
	idVectorSet<idVec3,3> vertexList;

	numInsertedPoints = 0;
	common->Printf( "[Melt Portals]\n" );
	RemoveColinearPoints_r( root, skipContents );
	MeltPortals_r( root, skipContents, vertexList );
	root->RemoveFlagRecurse( NODE_DONE );
	common->Printf( "\r%6d points inserted\n", numInsertedPoints );
}
