dhewm3/neo/tools/compilers/aas/Brush.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

1584 lines
32 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 "idlib/MapFile.h"
#include "framework/Common.h"
#include "framework/FileSystem.h"
#include "tools/compilers/aas/Brush.h"
#define BRUSH_EPSILON 0.1f
#define BRUSH_PLANE_NORMAL_EPSILON 0.00001f
#define BRUSH_PLANE_DIST_EPSILON 0.01f
#define OUTPUT_UPDATE_TIME 500 // update every 500 msec
//#define OUTPUT_CHOP_STATS
/*
============
DisplayRealTimeString
============
*/
void DisplayRealTimeString( const char *string, ... ) {
va_list argPtr;
char buf[MAX_STRING_CHARS];
static int lastUpdateTime;
int time;
time = Sys_Milliseconds();
if ( time > lastUpdateTime + OUTPUT_UPDATE_TIME ) {
va_start( argPtr, string );
vsprintf( buf, string, argPtr );
va_end( argPtr );
common->Printf( buf );
lastUpdateTime = time;
}
}
//===============================================================
//
// idBrushSide
//
//===============================================================
/*
============
idBrushSide::idBrushSide
============
*/
idBrushSide::idBrushSide( void ) {
flags = 0;
planeNum = -1;
winding = NULL;
}
/*
============
idBrushSide::idBrushSide
============
*/
idBrushSide::idBrushSide( const idPlane &plane, int planeNum ) {
this->flags = 0;
this->plane = plane;
this->planeNum = planeNum;
this->winding = NULL;
}
/*
============
idBrushSide::~idBrushSide
============
*/
idBrushSide::~idBrushSide( void ) {
if ( winding ) {
delete winding;
}
}
/*
============
idBrushSide::Copy
============
*/
idBrushSide *idBrushSide::Copy( void ) const {
idBrushSide *side;
side = new idBrushSide( plane, planeNum );
side->flags = flags;
if ( winding ) {
side->winding = winding->Copy();
}
else {
side->winding = NULL;
}
return side;
}
/*
============
idBrushSide::Split
============
*/
int idBrushSide::Split( const idPlane &splitPlane, idBrushSide **front, idBrushSide **back ) const {
idWinding *frontWinding, *backWinding;
assert( winding );
*front = *back = NULL;
winding->Split( splitPlane, 0.0f, &frontWinding, &backWinding );
if ( frontWinding ) {
(*front) = new idBrushSide( plane, planeNum );
(*front)->winding = frontWinding;
(*front)->flags = flags;
}
if ( backWinding ) {
(*back) = new idBrushSide( plane, planeNum );
(*back)->winding = backWinding;
(*back)->flags = flags;
}
if ( frontWinding && backWinding ) {
return PLANESIDE_CROSS;
}
else if ( frontWinding ) {
return PLANESIDE_FRONT;
}
else {
return PLANESIDE_BACK;
}
}
//===============================================================
//
// idBrushSide
//
//===============================================================
/*
============
idBrush::idBrush
============
*/
idBrush::idBrush( void ) {
contents = flags = 0;
bounds.Clear();
sides.Clear();
windingsValid = false;
}
/*
============
idBrush::~idBrush
============
*/
idBrush::~idBrush( void ) {
for ( int i = 0; i < sides.Num(); i++ ) {
delete sides[i];
}
}
/*
============
idBrush::RemoveSidesWithoutWinding
============
*/
bool idBrush::RemoveSidesWithoutWinding( void ) {
int i;
for ( i = 0; i < sides.Num(); i++ ) {
if ( sides[i]->winding ) {
continue;
}
sides.RemoveIndex( i );
i--;
}
return ( sides.Num() >= 4 );
}
/*
============
idBrush::CreateWindings
============
*/
bool idBrush::CreateWindings( void ) {
int i, j;
idBrushSide *side;
bounds.Clear();
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
if ( side->winding ) {
delete side->winding;
}
side->winding = new idWinding( side->plane.Normal(), side->plane.Dist() );
for ( j = 0; j < sides.Num() && side->winding; j++ ) {
if ( i == j ) {
continue;
}
// keep the winding if on the clip plane
side->winding = side->winding->Clip( -sides[j]->plane, BRUSH_EPSILON, true );
}
if ( side->winding ) {
for ( j = 0; j < side->winding->GetNumPoints(); j++ ) {
bounds.AddPoint( (*side->winding)[j].ToVec3() );
}
}
}
if ( bounds[0][0] > bounds[1][0] ) {
return false;
}
for ( i = 0; i < 3; i++ ) {
if ( bounds[0][i] < MIN_WORLD_COORD || bounds[1][i] > MAX_WORLD_COORD ) {
return false;
}
}
windingsValid = true;
return true;
}
/*
============
idBrush::BoundBrush
============
*/
void idBrush::BoundBrush( const idBrush *original ) {
int i, j;
idBrushSide *side;
idWinding *w;
assert( windingsValid );
bounds.Clear();
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
w = side->winding;
if ( !w ) {
continue;
}
for ( j = 0; j < w->GetNumPoints(); j++ ) {
bounds.AddPoint( (*w)[j].ToVec3() );
}
}
if ( bounds[0][0] > bounds[1][0] ) {
if ( original ) {
idBrushMap *bm = new idBrushMap( "error_brush", "_original" );
bm->WriteBrush( original );
delete bm;
}
common->Error( "idBrush::BoundBrush: brush %d on entity %d without windings", primitiveNum, entityNum );
}
for ( i = 0; i < 3; i++ ) {
if ( bounds[0][i] < MIN_WORLD_COORD || bounds[1][i] > MAX_WORLD_COORD ) {
if ( original ) {
idBrushMap *bm = new idBrushMap( "error_brush", "_original" );
bm->WriteBrush( original );
delete bm;
}
common->Error( "idBrush::BoundBrush: brush %d on entity %d is unbounded", primitiveNum, entityNum );
}
}
}
/*
============
idBrush::FromSides
============
*/
bool idBrush::FromSides( idList<idBrushSide *> &sideList ) {
int i;
for ( i = 0; i < sideList.Num(); i++ ) {
sides.Append( sideList[i] );
}
sideList.Clear();
return CreateWindings();
}
/*
============
idBrush::FromWinding
============
*/
bool idBrush::FromWinding( const idWinding &w, const idPlane &windingPlane ) {
int i, j, bestAxis;
idPlane plane;
idVec3 normal, axialNormal;
sides.Append( new idBrushSide( windingPlane, -1 ) );
sides.Append( new idBrushSide( -windingPlane, -1 ) );
bestAxis = 0;
for ( i = 1; i < 3; i++ ) {
if ( idMath::Fabs( windingPlane.Normal()[i] ) > idMath::Fabs( windingPlane.Normal()[bestAxis] ) ) {
bestAxis = i;
}
}
axialNormal = vec3_origin;
if ( windingPlane.Normal()[bestAxis] > 0.0f ) {
axialNormal[bestAxis] = 1.0f;
}
else {
axialNormal[bestAxis] = -1.0f;
}
for ( i = 0; i < w.GetNumPoints(); i++ ) {
j = (i+1) % w.GetNumPoints();
normal = ( w[j].ToVec3() - w[i].ToVec3() ).Cross( axialNormal );
if ( normal.Normalize() < 0.5f ) {
continue;
}
plane.SetNormal( normal );
plane.FitThroughPoint( w[j].ToVec3() );
sides.Append( new idBrushSide( plane, -1 ) );
}
if ( sides.Num() < 4 ) {
for ( i = 0; i < sides.Num(); i++ ) {
delete sides[i];
}
sides.Clear();
return false;
}
sides[0]->winding = w.Copy();
windingsValid = true;
BoundBrush();
return true;
}
/*
============
idBrush::FromBounds
============
*/
bool idBrush::FromBounds( const idBounds &bounds ) {
int axis, dir;
idVec3 normal;
idPlane plane;
for ( axis = 0; axis < 3; axis++ ) {
for ( dir = -1; dir <= 1; dir += 2 ) {
normal = vec3_origin;
normal[axis] = dir;
plane.SetNormal( normal );
plane.SetDist( dir * bounds[(dir == 1)][axis] );
sides.Append( new idBrushSide( plane, -1 ) );
}
}
return CreateWindings();
}
/*
============
idBrush::Transform
============
*/
void idBrush::Transform( const idVec3 &origin, const idMat3 &axis ) {
int i;
bool transformed = false;
if ( axis.IsRotated() ) {
for ( i = 0; i < sides.Num(); i++ ) {
sides[i]->plane.RotateSelf( vec3_origin, axis );
}
transformed = true;
}
if ( origin != vec3_origin ) {
for ( i = 0; i < sides.Num(); i++ ) {
sides[i]->plane.TranslateSelf( origin );
}
transformed = true;
}
if ( transformed ) {
CreateWindings();
}
}
/*
============
idBrush::GetVolume
============
*/
float idBrush::GetVolume( void ) const {
int i;
idWinding *w;
idVec3 corner;
float d, area, volume;
// grab the first valid point as a corner
w = NULL;
for ( i = 0; i < sides.Num(); i++ ) {
w = sides[i]->winding;
if ( w ) {
break;
}
}
if ( !w ) {
return 0.0f;
}
corner = (*w)[0].ToVec3();
// create tetrahedrons to all other sides
volume = 0.0f;
for ( ; i < sides.Num(); i++) {
w = sides[i]->winding;
if ( !w ) {
continue;
}
d = -( corner * sides[i]->plane.Normal() - sides[i]->plane.Dist() );
area = w->GetArea();
volume += d * area;
}
return ( volume * ( 1.0f / 3.0f ) );
}
/*
============
idBrush::Subtract
============
*/
bool idBrush::Subtract( const idBrush *b, idBrushList &list ) const {
int i;
idBrush *front, *back;
const idBrush *in;
list.Clear();
in = this;
for ( i = 0; i < b->sides.Num() && in; i++ ) {
in->Split( b->sides[i]->plane, b->sides[i]->planeNum, &front, &back );
if ( in != this ) {
delete in;
}
if ( front ) {
list.AddToTail( front );
}
in = back;
}
// if didn't really intersect
if ( !in ) {
list.Free();
return false;
}
delete in;
return true;
}
/*
============
idBrush::TryMerge
============
*/
bool idBrush::TryMerge( const idBrush *brush, const idPlaneSet &planeList ) {
int i, j, k, l, m, seperatingPlane;
const idBrush *brushes[2];
const idWinding *w;
const idPlane *plane;
// brush bounds should overlap
for ( i = 0; i < 3; i++ ) {
if ( bounds[0][i] > brush->bounds[1][i] + 0.1f ) {
return false;
}
if ( bounds[1][i] < brush->bounds[0][i] - 0.1f ) {
return false;
}
}
// the brushes should share an opposite plane
seperatingPlane = -1;
for ( i = 0; i < GetNumSides(); i++ ) {
for ( j = 0; j < brush->GetNumSides(); j++ ) {
if ( GetSide(i)->GetPlaneNum() == (brush->GetSide(j)->GetPlaneNum() ^ 1) ) {
// may only have one seperating plane
if ( seperatingPlane != -1 ) {
return false;
}
seperatingPlane = GetSide(i)->GetPlaneNum();
break;
}
}
}
if ( seperatingPlane == -1 ) {
return false;
}
brushes[0] = this;
brushes[1] = brush;
for ( i = 0; i < 2; i++ ) {
j = !i;
for ( k = 0; k < brushes[i]->GetNumSides(); k++ ) {
// if the brush side plane is the seprating plane
if ( !( ( brushes[i]->GetSide(k)->GetPlaneNum() ^ seperatingPlane ) >> 1 ) ) {
continue;
}
plane = &brushes[i]->GetSide(k)->GetPlane();
// all the non seperating brush sides of the other brush should be at the back or on the plane
for ( l = 0; l < brushes[j]->GetNumSides(); l++ ) {
w = brushes[j]->GetSide(l)->GetWinding();
if ( !w ) {
continue;
}
if ( !( ( brushes[j]->GetSide(l)->GetPlaneNum() ^ seperatingPlane ) >> 1 ) ) {
continue;
}
for ( m = 0; m < w->GetNumPoints(); m++ ) {
if ( plane->Distance( (*w)[m].ToVec3() ) > 0.1f ) {
return false;
}
}
}
}
}
// add any sides from the other brush to this brush
for ( i = 0; i < brush->GetNumSides(); i++ ) {
for ( j = 0; j < GetNumSides(); j++ ) {
if ( !( ( brush->GetSide(i)->GetPlaneNum() ^ GetSide(j)->GetPlaneNum() ) >> 1 ) ) {
break;
}
}
if ( j < GetNumSides() ) {
sides[j]->flags &= brush->GetSide(i)->GetFlags();
continue;
}
sides.Append( brush->GetSide(i)->Copy() );
}
// remove any side from this brush that is the opposite of a side of the other brush
for ( i = 0; i < GetNumSides(); i++ ) {
for ( j = 0; j < brush->GetNumSides(); j++ ) {
if ( GetSide(i)->GetPlaneNum() == ( brush->GetSide(j)->GetPlaneNum() ^ 1 ) ) {
break;
}
}
if ( j < brush->GetNumSides() ) {
delete sides[i];
sides.RemoveIndex(i);
i--;
continue;
}
}
contents |= brush->contents;
CreateWindings();
BoundBrush();
return true;
}
/*
============
idBrush::Split
============
*/
int idBrush::Split( const idPlane &plane, int planeNum, idBrush **front, idBrush **back ) const {
int res, i, j;
idBrushSide *side, *frontSide, *backSide;
float dist, maxBack, maxFront, *maxBackWinding, *maxFrontWinding;
idWinding *w, *mid;
assert( windingsValid );
if ( front ) {
*front = NULL;
}
if ( back ) {
*back = NULL;
}
res = bounds.PlaneSide( plane, -BRUSH_EPSILON );
if ( res == PLANESIDE_FRONT ) {
if ( front ) {
*front = Copy();
}
return res;
}
if ( res == PLANESIDE_BACK ) {
if ( back ) {
*back = Copy();
}
return res;
}
maxBackWinding = (float *) _alloca16( sides.Num() * sizeof(float) );
maxFrontWinding = (float *) _alloca16( sides.Num() * sizeof(float) );
maxFront = maxBack = 0.0f;
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
w = side->winding;
if ( !w ) {
continue;
}
maxBackWinding[i] = 10.0f;
maxFrontWinding[i] = -10.0f;
for ( j = 0; j < w->GetNumPoints(); j++ ) {
dist = plane.Distance( (*w)[j].ToVec3() );
if ( dist > maxFrontWinding[i] ) {
maxFrontWinding[i] = dist;
}
if ( dist < maxBackWinding[i] ) {
maxBackWinding[i] = dist;
}
}
if ( maxFrontWinding[i] > maxFront ) {
maxFront = maxFrontWinding[i];
}
if ( maxBackWinding[i] < maxBack ) {
maxBack = maxBackWinding[i];
}
}
if ( maxFront < BRUSH_EPSILON ) {
if ( back ) {
*back = Copy();
}
return PLANESIDE_BACK;
}
if ( maxBack > -BRUSH_EPSILON ) {
if ( front ) {
*front = Copy();
}
return PLANESIDE_FRONT;
}
mid = new idWinding( plane.Normal(), plane.Dist() );
for ( i = 0; i < sides.Num() && mid; i++ ) {
mid = mid->Clip( -sides[i]->plane, BRUSH_EPSILON, false );
}
if ( mid ) {
if ( mid->IsTiny() ) {
delete mid;
mid = NULL;
}
else if ( mid->IsHuge() ) {
// if the winding is huge then the brush is unbounded
common->Warning( "brush %d on entity %d is unbounded"
"( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )-( %1.2f %1.2f %1.2f )", primitiveNum, entityNum,
bounds[0][0], bounds[0][1], bounds[0][2], bounds[1][0], bounds[1][1], bounds[1][2],
bounds[1][0]-bounds[0][0], bounds[1][1]-bounds[0][1], bounds[1][2]-bounds[0][2] );
delete mid;
mid = NULL;
}
}
if ( !mid ) {
if ( maxFront > - maxBack ) {
if ( front ) {
*front = Copy();
}
return PLANESIDE_FRONT;
}
else {
if ( back ) {
*back = Copy();
}
return PLANESIDE_BACK;
}
}
if ( !front && !back ) {
delete mid;
return PLANESIDE_CROSS;
}
*front = new idBrush();
(*front)->SetContents( contents );
(*front)->SetEntityNum( entityNum );
(*front)->SetPrimitiveNum( primitiveNum );
*back = new idBrush();
(*back)->SetContents( contents );
(*back)->SetEntityNum( entityNum );
(*back)->SetPrimitiveNum( primitiveNum );
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
if ( !side->winding ) {
continue;
}
// if completely at the front
if ( maxBackWinding[i] >= BRUSH_EPSILON ) {
(*front)->sides.Append( side->Copy() );
}
// if completely at the back
else if ( maxFrontWinding[i] <= -BRUSH_EPSILON ) {
(*back)->sides.Append( side->Copy() );
}
else {
// split the side
side->Split( plane, &frontSide, &backSide );
if ( frontSide ) {
(*front)->sides.Append( frontSide );
}
else if ( maxFrontWinding[i] > -BRUSH_EPSILON ) {
// favor an overconstrained brush
side = side->Copy();
side->winding = side->winding->Clip( idPlane( plane.Normal(), (plane.Dist() - (BRUSH_EPSILON+0.02f)) ), 0.01f, true );
assert( side->winding );
(*front)->sides.Append( side );
}
if ( backSide ) {
(*back)->sides.Append( backSide );
}
else if ( maxBackWinding[i] < BRUSH_EPSILON ) {
// favor an overconstrained brush
side = side->Copy();
side->winding = side->winding->Clip( idPlane( -plane.Normal(), -(plane.Dist() + (BRUSH_EPSILON+0.02f)) ), 0.01f, true );
assert( side->winding );
(*back)->sides.Append( side );
}
}
}
side = new idBrushSide( -plane, planeNum^1 );
side->winding = mid->Reverse();
side->flags |= SFL_SPLIT;
(*front)->sides.Append( side );
(*front)->windingsValid = true;
(*front)->BoundBrush( this );
side = new idBrushSide( plane, planeNum );
side->winding = mid;
side->flags |= SFL_SPLIT;
(*back)->sides.Append( side );
(*back)->windingsValid = true;
(*back)->BoundBrush( this );
return PLANESIDE_CROSS;
}
/*
============
idBrush::AddBevelsForAxialBox
============
*/
#define BRUSH_BEVEL_EPSILON 0.1f
void idBrush::AddBevelsForAxialBox( void ) {
int axis, dir, i, j, k, l, order;
idBrushSide *side, *newSide;
idPlane plane;
idVec3 normal, vec;
idWinding *w, *w2;
float d, minBack;
assert( windingsValid );
// add the axial planes
order = 0;
for ( axis = 0; axis < 3; axis++ ) {
for ( dir = -1; dir <= 1; dir += 2, order++ ) {
// see if the plane is already present
for ( i = 0; i < sides.Num(); i++ ) {
if ( dir > 0 ) {
if ( sides[i]->plane.Normal()[axis] >= 0.9999f ) {
break;
}
}
else {
if ( sides[i]->plane.Normal()[axis] <= -0.9999f ) {
break;
}
}
}
if ( i >= sides.Num() ) {
normal = vec3_origin;
normal[axis] = dir;
plane.SetNormal( normal );
plane.SetDist( dir * bounds[(dir == 1)][axis] );
newSide = new idBrushSide( plane, -1 );
newSide->SetFlag( SFL_BEVEL );
sides.Append( newSide );
}
}
}
// if the brush is pure axial we're done
if ( sides.Num() == 6 ) {
return;
}
// test the non-axial plane edges
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
w = side->winding;
if ( !w ) {
continue;
}
for ( j = 0; j < w->GetNumPoints(); j++) {
k = (j+1) % w->GetNumPoints();
vec = (*w)[j].ToVec3() - (*w)[k].ToVec3();
if ( vec.Normalize() < 0.5f ) {
continue;
}
for ( k = 0; k < 3; k++ ) {
if ( vec[k] == 1.0f || vec[k] == -1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
break; // axial
}
}
if ( k < 3 ) {
continue; // only test non-axial edges
}
// try the six possible slanted axials from this edge
for ( axis = 0; axis < 3; axis++ ) {
for ( dir = -1; dir <= 1; dir += 2 ) {
// construct a plane
normal = vec3_origin;
normal[axis] = dir;
normal = vec.Cross( normal );
if ( normal.Normalize() < 0.5f ) {
continue;
}
plane.SetNormal( normal );
plane.FitThroughPoint( (*w)[j].ToVec3() );
// if all the points on all the sides are
// behind this plane, it is a proper edge bevel
for ( k = 0; k < sides.Num(); k++ ) {
// if this plane has allready been used, skip it
if ( plane.Compare( sides[k]->plane, 0.001f, 0.1f ) ) {
break;
}
w2 = sides[k]->winding;
if ( !w2 ) {
continue;
}
minBack = 0.0f;
for ( l = 0; l < w2->GetNumPoints(); l++ ) {
d = plane.Distance( (*w2)[l].ToVec3() );
if ( d > BRUSH_BEVEL_EPSILON ) {
break; // point at the front
}
if ( d < minBack ) {
minBack = d;
}
}
// if some point was at the front
if ( l < w2->GetNumPoints() ) {
break;
}
// if no points at the back then the winding is on the bevel plane
if ( minBack > -BRUSH_BEVEL_EPSILON ) {
break;
}
}
if ( k < sides.Num() ) {
continue; // wasn't part of the outer hull
}
// add this plane
newSide = new idBrushSide( plane, -1 );
newSide->SetFlag( SFL_BEVEL );
sides.Append( newSide );
}
}
}
}
}
/*
============
idBrush::ExpandForAxialBox
============
*/
void idBrush::ExpandForAxialBox( const idBounds &bounds ) {
int i, j;
idBrushSide *side;
idVec3 v;
AddBevelsForAxialBox();
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
for ( j = 0; j < 3; j++ ) {
if ( side->plane.Normal()[j] > 0.0f ) {
v[j] = bounds[0][j];
}
else {
v[j] = bounds[1][j];
}
}
side->plane.SetDist( side->plane.Dist() + v * -side->plane.Normal() );
}
if ( !CreateWindings() ) {
common->Error( "idBrush::ExpandForAxialBox: brush %d on entity %d imploded", primitiveNum, entityNum );
}
/*
// after expansion at least all non bevel sides should have a winding
for ( i = 0; i < sides.Num(); i++ ) {
side = sides[i];
if ( !side->winding ) {
if ( !( side->flags & SFL_BEVEL ) ) {
int shit = 1;
}
}
}
*/
}
/*
============
idBrush::Copy
============
*/
idBrush *idBrush::Copy( void ) const {
int i;
idBrush *b;
b = new idBrush();
b->entityNum = entityNum;
b->primitiveNum = primitiveNum;
b->contents = contents;
b->windingsValid = windingsValid;
b->bounds = bounds;
for ( i = 0; i < sides.Num(); i++ ) {
b->sides.Append( sides[i]->Copy() );
}
return b;
}
//===============================================================
//
// idBrushList
//
//===============================================================
/*
============
idBrushList::idBrushList
============
*/
idBrushList::idBrushList( void ) {
numBrushes = numBrushSides = 0;
head = tail = NULL;
}
/*
============
idBrushList::~idBrushList
============
*/
idBrushList::~idBrushList( void ) {
}
/*
============
idBrushList::GetBounds
============
*/
idBounds idBrushList::GetBounds( void ) const {
idBounds bounds;
idBrush *b;
bounds.Clear();
for ( b = Head(); b; b = b->Next() ) {
bounds += b->GetBounds();
}
return bounds;
}
/*
============
idBrushList::AddToTail
============
*/
void idBrushList::AddToTail( idBrush *brush ) {
brush->next = NULL;
if ( tail ) {
tail->next = brush;
}
tail = brush;
if ( !head ) {
head = brush;
}
numBrushes++;
numBrushSides += brush->sides.Num();
}
/*
============
idBrushList::AddToTail
============
*/
void idBrushList::AddToTail( idBrushList &list ) {
idBrush *brush, *next;
for ( brush = list.head; brush; brush = next ) {
next = brush->next;
brush->next = NULL;
if ( tail ) {
tail->next = brush;
}
tail = brush;
if ( !head ) {
head = brush;
}
numBrushes++;
numBrushSides += brush->sides.Num();
}
list.head = list.tail = NULL;
list.numBrushes = 0;
}
/*
============
idBrushList::AddToFront
============
*/
void idBrushList::AddToFront( idBrush *brush ) {
brush->next = head;
head = brush;
if ( !tail ) {
tail = brush;
}
numBrushes++;
numBrushSides += brush->sides.Num();
}
/*
============
idBrushList::AddToFront
============
*/
void idBrushList::AddToFront( idBrushList &list ) {
idBrush *brush, *next;
for ( brush = list.head; brush; brush = next ) {
next = brush->next;
brush->next = head;
head = brush;
if ( !tail ) {
tail = brush;
}
numBrushes++;
numBrushSides += brush->sides.Num();
}
list.head = list.tail = NULL;
list.numBrushes = 0;
}
/*
============
idBrushList::Remove
============
*/
void idBrushList::Remove( idBrush *brush ) {
idBrush *b, *last;
last = NULL;
for ( b = head; b; b = b->next ) {
if ( b == brush ) {
if ( last ) {
last->next = b->next;
}
else {
head = b->next;
}
if ( b == tail ) {
tail = last;
}
numBrushes--;
numBrushSides -= brush->sides.Num();
return;
}
last = b;
}
}
/*
============
idBrushList::Delete
============
*/
void idBrushList::Delete( idBrush *brush ) {
idBrush *b, *last;
last = NULL;
for ( b = head; b; b = b->next ) {
if ( b == brush ) {
if ( last ) {
last->next = b->next;
}
else {
head = b->next;
}
if ( b == tail ) {
tail = last;
}
numBrushes--;
numBrushSides -= b->sides.Num();
delete b;
return;
}
last = b;
}
}
/*
============
idBrushList::Copy
============
*/
idBrushList *idBrushList::Copy( void ) const {
idBrush *brush;
idBrushList *list;
list = new idBrushList;
for ( brush = head; brush; brush = brush->next ) {
list->AddToTail( brush->Copy() );
}
return list;
}
/*
============
idBrushList::Free
============
*/
void idBrushList::Free( void ) {
idBrush *brush, *next;
for ( brush = head; brush; brush = next ) {
next = brush->next;
delete brush;
}
head = tail = NULL;
numBrushes = numBrushSides = 0;
}
/*
============
idBrushList::Split
============
*/
void idBrushList::Split( const idPlane &plane, int planeNum, idBrushList &frontList, idBrushList &backList, bool useBrushSavedPlaneSide ) {
idBrush *b, *front, *back;
frontList.Clear();
backList.Clear();
if ( !useBrushSavedPlaneSide ) {
for ( b = head; b; b = b->next ) {
b->Split( plane, planeNum, &front, &back );
if ( front ) {
frontList.AddToTail( front );
}
if ( back ) {
backList.AddToTail( back );
}
}
return;
}
for ( b = head; b; b = b->next ) {
if ( b->savedPlaneSide & BRUSH_PLANESIDE_BOTH ) {
b->Split( plane, planeNum, &front, &back );
if ( front ) {
frontList.AddToTail( front );
}
if ( back ) {
backList.AddToTail( back );
}
}
else if ( b->savedPlaneSide & BRUSH_PLANESIDE_FRONT ) {
frontList.AddToTail( b->Copy() );
}
else {
backList.AddToTail( b->Copy() );
}
}
}
/*
============
idBrushList::Chop
============
*/
void idBrushList::Chop( bool (*ChopAllowed)( idBrush *b1, idBrush *b2 ) ) {
idBrush *b1, *b2, *next;
idBrushList sub1, sub2, keep;
int i, j, c1, c2;
idPlaneSet planeList;
#ifdef OUTPUT_CHOP_STATS
common->Printf( "[Brush CSG]\n");
common->Printf( "%6d original brushes\n", this->Num() );
#endif
CreatePlaneList( planeList );
for ( b1 = this->Head(); b1; b1 = this->Head() ) {
for ( b2 = b1->next; b2; b2 = next ) {
next = b2->next;
for ( i = 0; i < 3; i++ ) {
if ( b1->bounds[0][i] >= b2->bounds[1][i] ) {
break;
}
if ( b1->bounds[1][i] <= b2->bounds[0][i] ) {
break;
}
}
if ( i < 3 ) {
continue;
}
for ( i = 0; i < b1->GetNumSides(); i++ ) {
for ( j = 0; j < b2->GetNumSides(); j++ ) {
if ( b1->GetSide(i)->GetPlaneNum() == ( b2->GetSide(j)->GetPlaneNum() ^ 1 ) ) {
// opposite planes, so not touching
break;
}
}
if ( j < b2->GetNumSides() ) {
break;
}
}
if ( i < b1->GetNumSides() ) {
continue;
}
sub1.Clear();
sub2.Clear();
c1 = 999999;
c2 = 999999;
// if b2 may chop up b1
if ( !ChopAllowed || ChopAllowed( b2, b1 ) ) {
if ( !b1->Subtract( b2, sub1 ) ) {
// didn't really intersect
continue;
}
if ( sub1.IsEmpty() ) {
// b1 is swallowed by b2
this->Delete( b1 );
break;
}
c1 = sub1.Num();
}
// if b1 may chop up b2
if ( !ChopAllowed || ChopAllowed( b1, b2 ) ) {
if ( !b2->Subtract( b1, sub2 ) ) {
// didn't really intersect
continue;
}
if ( sub2.IsEmpty() ) {
// b2 is swallowed by b1
sub1.Free();
this->Delete( b2 );
continue;
}
c2 = sub2.Num();
}
if ( sub1.IsEmpty() && sub2.IsEmpty() ) {
continue;
}
// don't allow too much fragmentation
if ( c1 > 2 && c2 > 2 ) {
sub1.Free();
sub2.Free();
continue;
}
if ( c1 < c2 ) {
sub2.Free();
this->AddToTail( sub1 );
this->Delete( b1 );
break;
}
else {
sub1.Free();
this->AddToTail( sub2 );
this->Delete( b2 );
continue;
}
}
if ( !b2 ) {
// b1 is no longer intersecting anything, so keep it
this->Remove( b1 );
keep.AddToTail( b1 );
#ifdef OUTPUT_CHOP_STATS
DisplayRealTimeString( "\r%6d", keep.numBrushes );
#endif
}
}
*this = keep;
#ifdef OUTPUT_CHOP_STATS
common->Printf( "\r%6d output brushes\n", Num() );
#endif
}
/*
============
idBrushList::Merge
============
*/
void idBrushList::Merge( bool (*MergeAllowed)( idBrush *b1, idBrush *b2 ) ) {
idPlaneSet planeList;
idBrush *b1, *b2, *nextb2;
int numMerges;
common->Printf( "[Brush Merge]\n");
common->Printf( "%6d original brushes\n", Num() );
CreatePlaneList( planeList );
numMerges = 0;
for ( b1 = Head(); b1; b1 = b1->next ) {
for ( b2 = Head(); b2; b2 = nextb2 ) {
nextb2 = b2->Next();
if ( b2 == b1 ) {
continue;
}
if ( MergeAllowed && !MergeAllowed( b1, b2 ) ) {
continue;
}
if ( b1->TryMerge( b2, planeList ) ) {
Delete( b2 );
DisplayRealTimeString( "\r%6d", ++numMerges );
nextb2 = Head();
}
}
}
common->Printf( "\r%6d brushes merged\n", numMerges );
}
/*
============
idBrushList::SetFlagOnFacingBrushSides
============
*/
void idBrushList::SetFlagOnFacingBrushSides( const idPlane &plane, int flag ) {
int i;
idBrush *b;
const idWinding *w;
for ( b = head; b; b = b->next ) {
if ( idMath::Fabs( b->GetBounds().PlaneDistance( plane ) ) > 0.1f ) {
continue;
}
for ( i = 0; i < b->GetNumSides(); i++ ) {
w = b->GetSide(i)->GetWinding();
if ( !w ) {
if ( b->GetSide(i)->GetPlane().Compare( plane, BRUSH_PLANE_NORMAL_EPSILON, BRUSH_PLANE_DIST_EPSILON ) ) {
b->GetSide(i)->SetFlag( flag );
}
continue;
}
if ( w->PlaneSide( plane ) == SIDE_ON ) {
b->GetSide(i)->SetFlag( flag );
}
}
}
}
/*
============
idBrushList::CreatePlaneList
============
*/
void idBrushList::CreatePlaneList( idPlaneSet &planeList ) const {
int i;
idBrush *b;
idBrushSide *side;
planeList.Resize( 512, 128 );
for ( b = Head(); b; b = b->Next() ) {
for ( i = 0; i < b->GetNumSides(); i++ ) {
side = b->GetSide( i );
side->SetPlaneNum( planeList.FindPlane( side->GetPlane(), BRUSH_PLANE_NORMAL_EPSILON, BRUSH_PLANE_DIST_EPSILON ) );
}
}
}
/*
============
idBrushList::CreatePlaneList
============
*/
void idBrushList::WriteBrushMap( const idStr &fileName, const idStr &ext ) const {
idBrushMap *map;
map = new idBrushMap( fileName, ext );
map->WriteBrushList( *this );
delete map;
}
//===============================================================
//
// idBrushMap
//
//===============================================================
/*
============
idBrushMap::idBrushMap
============
*/
idBrushMap::idBrushMap( const idStr &fileName, const idStr &ext ) {
idStr qpath;
qpath = fileName;
qpath.StripFileExtension();
qpath += ext;
qpath.SetFileExtension( "map" );
common->Printf( "writing %s...\n", qpath.c_str() );
fp = fileSystem->OpenFileWrite( qpath, "fs_devpath" );
if ( !fp ) {
common->Error( "Couldn't open %s\n", qpath.c_str() );
return;
}
texture = "textures/washroom/btile01";
fp->WriteFloatString( "Version %1.2f\n", (float) CURRENT_MAP_VERSION );
fp->WriteFloatString( "{\n" );
fp->WriteFloatString( "\"classname\" \"worldspawn\"\n" );
brushCount = 0;
}
/*
============
idBrushMap::~idBrushMap
============
*/
idBrushMap::~idBrushMap( void ) {
if ( !fp ) {
return;
}
fp->WriteFloatString( "}\n" );
fileSystem->CloseFile( fp );
}
/*
============
idBrushMap::WriteBrush
============
*/
void idBrushMap::WriteBrush( const idBrush *brush ) {
int i;
idBrushSide *side;
if ( !fp ) {
return;
}
fp->WriteFloatString( "// primitive %d\n{\nbrushDef3\n{\n", brushCount++ );
for ( i = 0; i < brush->GetNumSides(); i++ ) {
side = brush->GetSide( i );
fp->WriteFloatString( " ( %f %f %f %f ) ", side->GetPlane()[0], side->GetPlane()[1], side->GetPlane()[2], -side->GetPlane().Dist() );
fp->WriteFloatString( "( ( 0.031250 0 0 ) ( 0 0.031250 0 ) ) %s 0 0 0\n", texture.c_str() );
}
fp->WriteFloatString( "}\n}\n" );
}
/*
============
idBrushMap::WriteBrushList
============
*/
void idBrushMap::WriteBrushList( const idBrushList &brushList ) {
idBrush *b;
if ( !fp ) {
return;
}
for ( b = brushList.Head(); b; b = b->Next() ) {
WriteBrush( b );
}
}