dhewm3/neo/tools/compilers/aas/BrushBSP.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

2148 lines
47 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 "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: 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( 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 );
}