doom3-bfg/neo/tools/compilers/aas/BrushBSP.cpp
2014-08-02 14:48:04 +02:00

2462 lines
48 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "Brush.h"
#include "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()
{
planeNum = -1;
winding = NULL;
nodes[0] = nodes[1] = NULL;
next[0] = next[1] = NULL;
faceNum = 0;
flags = 0;
}
/*
============
idBrushBSPPortal::~idBrushBSPPortal
============
*/
idBrushBSPPortal::~idBrushBSPPortal()
{
if( winding )
{
delete winding;
}
}
/*
============
idBrushBSPPortal::AddToNodes
============
*/
void idBrushBSPPortal::AddToNodes( idBrushBSPNode* front, idBrushBSPNode* back )
{
if( nodes[0] || nodes[1] )
{
common->Error( "AddToNode: allready 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()
{
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()
{
brushList.Clear();
contents = 0;
flags = 0;
volume = NULL;
portals = NULL;
children[0] = children[1] = NULL;
areaNum = 0;
occupied = 0;
}
/*
============
idBrushBSPNode::~idBrushBSPNode
============
*/
idBrushBSPNode::~idBrushBSPNode()
{
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()
{
idBrush* brush;
contents = 0;
for( brush = brushList.Head(); brush; brush = brush->Next() )
{
contents |= brush->GetContents();
}
}
/*
============
idBrushBSPNode::GetPortalBounds
============
*/
idBounds idBrushBSPNode::GetPortalBounds()
{
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()
{
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()
{
root = outside = NULL;
numSplits = numPrunedSplits = 0;
brushMapContents = 0;
brushMap = NULL;
}
/*
============
idBrushBSP::~idBrushBSP
============
*/
idBrushBSP::~idBrushBSP()
{
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 = 0;
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" );
}
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()
{
int i, j, n;
idBounds bounds;
idBrushBSPPortal* p, *portals[6];
idVec3 normal;
idPlane planes[6];
// 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()
{
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->occupied )
{
common->Error( "FloodThroughPortals_r: node already occupied\n" );
}
if( !node )
{
common->Error( "FloodThroughPortals_r: NULL node\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()
{
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 );
}