/*
   BobToolz plugin for GtkRadiant
   Copyright (C) 2001 Gordon Biggans

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// DBrush.cpp: implementation of the DBrush class.
//
//////////////////////////////////////////////////////////////////////

#include "DBrush.h"
#include "globaldefs.h"

#if GDEF_COMPILER_MSVC
#pragma warning(disable : 4786)
#endif

#include <list>
#include "str.h"

#include "DPoint.h"
#include "DPlane.h"
#include "DEPair.h"
#include "DPatch.h"
#include "DEntity.h"
#include "DWinding.h"

#include "dialogs/dialogs-gtk.h"

#include "misc.h"

#include "iundo.h"

#include "generic/referencecounted.h"

#include "scenelib.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

DBrush::DBrush( int ID ){
	m_nBrushID = ID;
	bBoundsBuilt = false;
	QER_entity = NULL;
	QER_brush = NULL;
}

DBrush::~DBrush(){
	ClearFaces();
	ClearPoints();
}

//////////////////////////////////////////////////////////////////////
// Implementation
//////////////////////////////////////////////////////////////////////

DPlane* DBrush::AddFace( const vec3_t va, const vec3_t vb, const vec3_t vc, const _QERFaceData* texData ){
#if GDEF_DEBUG
//	Sys_Printf("(%f %f %f) (%f %f %f) (%f %f %f)\n", va[0], va[1], va[2], vb[0], vb[1], vb[2], vc[0], vc[1], vc[2]);
#endif
	bBoundsBuilt = false;
	DPlane* newFace = new DPlane( va, vb, vc, texData );
	faceList.push_back( newFace );

	return newFace;
}

int DBrush::BuildPoints(){
	ClearPoints();

	if ( faceList.size() <= 3 ) {  // if less than 3 faces, there can be no points
		return 0;                   // with only 3 faces u can't have a bounded soild

	}
	for ( std::list<DPlane *>::const_iterator p1 = faceList.begin(); p1 != faceList.end(); p1++ )
	{
		std::list<DPlane *>::const_iterator p2 = p1;
		for ( p2++; p2 != faceList.end(); p2++ )
		{
			std::list<DPlane *>::const_iterator p3 = p2;
			for ( p3++; p3 != faceList.end(); p3++ )
			{
				vec3_t pnt;
				if ( ( *p1 )->PlaneIntersection( *p2, *p3, pnt ) ) {
					int pos = PointPosition( pnt );

					if ( pos == POINT_IN_BRUSH ) { // ???? shouldn't happen here
						globalErrorStream() << "ERROR:: Build Brush Points: Point IN brush!!!\n";
					}
					else if ( pos == POINT_ON_BRUSH ) { // normal point
						if ( !HasPoint( pnt ) ) {
							AddPoint( pnt );
						}
/*						else
                            Sys_Printf("Duplicate Point Found, pyramids ahoy!!!!!\n");*/
						// point lies on more that 3 planes
					}

					// otherwise point is removed due to another plane..

					// Sys_Printf("(%f, %f, %f)\n", pnt[0], pnt[1], pnt[2]);
				}
			}
		}
	}

#if GDEF_DEBUG
//	Sys_Printf("%i points on brush\n", pointList.size());
#endif

	return static_cast<int>( pointList.size() );
}

void DBrush_addFace( DBrush& brush, const _QERFaceData& faceData ){
	brush.AddFace( vector3_to_array( faceData.m_p0 ), vector3_to_array( faceData.m_p1 ), vector3_to_array( faceData.m_p2 ), 0 );
}
typedef ReferenceCaller<DBrush, void(const _QERFaceData&), DBrush_addFace> DBrushAddFaceCaller;

void DBrush_addFaceTextured( DBrush& brush, const _QERFaceData& faceData ){
	brush.AddFace( vector3_to_array( faceData.m_p0 ), vector3_to_array( faceData.m_p1 ), vector3_to_array( faceData.m_p2 ), &faceData );
}
typedef ReferenceCaller<DBrush, void(const _QERFaceData&), DBrush_addFaceTextured> DBrushAddFaceTexturedCaller;

void DBrush::LoadFromBrush( scene::Instance& brush, bool textured ){
	ClearFaces();
	ClearPoints();

	GlobalBrushCreator().Brush_forEachFace( brush.path().top(), textured ? BrushFaceDataCallback( DBrushAddFaceTexturedCaller( *this ) ) : BrushFaceDataCallback( DBrushAddFaceCaller( *this ) ) );

	QER_entity = brush.path().parent().get_pointer();
	QER_brush = brush.path().top().get_pointer();
}

int DBrush::PointPosition( vec3_t pnt ){
	int state = POINT_IN_BRUSH; // if nothing happens point is inside brush

	for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
	{
		float dist = ( *chkPlane )->DistanceToPoint( pnt );

		if ( dist > MAX_ROUND_ERROR ) {
			return POINT_OUT_BRUSH;     // if point is in front of plane, it CANT be in the brush
		}
		else if ( fabs( dist ) < MAX_ROUND_ERROR ) {
			state = POINT_ON_BRUSH;     // if point is ON plane point is either ON the brush
		}
		// or outside it, it can no longer be in it
	}

	return state;
}

void DBrush::ClearPoints(){
	for ( std::list<DPoint *>::const_iterator deadPoint = pointList.begin(); deadPoint != pointList.end(); deadPoint++ ) {
		delete *deadPoint;
	}
	pointList.clear();
}

void DBrush::ClearFaces(){
	bBoundsBuilt = false;
	for ( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ )
	{
		delete *deadPlane;
	}
	faceList.clear();
}

void DBrush::AddPoint( vec3_t pnt ){
	DPoint* newPoint = new DPoint;
	VectorCopy( pnt, newPoint->_pnt );
	pointList.push_back( newPoint );
}

bool DBrush::HasPoint( vec3_t pnt ){
	for ( std::list<DPoint *>::const_iterator chkPoint = pointList.begin(); chkPoint != pointList.end(); chkPoint++ )
	{
		if ( **chkPoint == pnt ) {
			return true;
		}
	}

	return false;
}

int DBrush::RemoveRedundantPlanes(){
	int cnt = 0;
	std::list<DPlane *>::iterator chkPlane;

	// find duplicate planes
	std::list<DPlane *>::iterator p1 = faceList.begin();

	while ( p1 != faceList.end() )
	{
		std::list<DPlane *>::iterator p2 = p1;

		for ( p2++; p2 != faceList.end(); p2++ )
		{
			if ( **p1 == **p2 ) {
				if ( !strcmp( ( *p1 )->m_shader.c_str(), "textures/common/caulk" ) ) {
					delete *p1;
					p1 = faceList.erase( p1 );    // duplicate plane
				}
				else
				{
					delete *p2;
					p2 = faceList.erase( p2 );    // duplicate plane
				}

				cnt++;
				break;
			}
		}

		if ( p2 == faceList.end() ) {
			p1++;
		}
	}

	//+djbob kill planes with bad normal, they are more of a nuisance than losing a brush
	chkPlane = faceList.begin();
	while ( chkPlane != faceList.end() )
	{
		if ( VectorLength( ( *chkPlane )->normal ) == 0 ) { // plane has bad normal
			delete *chkPlane;
			chkPlane = faceList.erase( chkPlane );
			cnt++;
		}
		else {
			chkPlane++;
		}
	}
	//-djbob

	if ( pointList.size() == 0 ) { // if points may not have been built, build them
/*		if(BuildPoints() == 0)	// just let the planes die if they are all bad
            return cnt;*/
		BuildPoints();
	}

	chkPlane = faceList.begin();
	while ( chkPlane != faceList.end() )
	{
		if ( ( *chkPlane )->IsRedundant( pointList ) ) { // checks that plane "0wnz" :), 3 or more points
			delete *chkPlane;
			chkPlane = faceList.erase( chkPlane );
			cnt++;
		}
		else{
			chkPlane++;
		}
	}

	return cnt;
}

bool DBrush::GetBounds( vec3_t min, vec3_t max ){
	BuildBounds();

	if ( !bBoundsBuilt ) {
		return false;
	}

	VectorCopy( bbox_min, min );
	VectorCopy( bbox_max, max );

	return true;
}

bool DBrush::BBoxCollision( DBrush* chkBrush ){
	vec3_t min1, min2;
	vec3_t max1, max2;

	GetBounds( min1, max1 );
	chkBrush->GetBounds( min2, max2 );

	if ( min1[0] >= max2[0] ) {
		return false;
	}
	if ( min1[1] >= max2[1] ) {
		return false;
	}
	if ( min1[2] >= max2[2] ) {
		return false;
	}

	if ( max1[0] <= min2[0] ) {
		return false;
	}
	if ( max1[1] <= min2[1] ) {
		return false;
	}
	if ( max1[2] <= min2[2] ) {
		return false;
	}

	return true;
}

DPlane* DBrush::HasPlane( DPlane* chkPlane ){
	for ( std::list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
	{
		if ( **brushPlane == *chkPlane ) {
			return *brushPlane;
		}
	}
	return NULL;
}

bool DBrush::IsCutByPlane( DPlane *cuttingPlane ){
	bool isInFront;

	if ( pointList.size() == 0 ) {
		if ( BuildPoints() == 0 ) {
			return false;
		}
	}

	std::list<DPoint *>::const_iterator chkPnt = pointList.begin();

	if ( chkPnt == pointList.end() ) {
		return false;
	}

	float dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );

	if ( dist > MAX_ROUND_ERROR ) {
		isInFront = false;
	}
	else if ( dist < MAX_ROUND_ERROR ) {
		isInFront = true;
	}
	else{
		return true;
	}

	for ( chkPnt++ = pointList.begin(); chkPnt != pointList.end(); chkPnt++ )
	{
		dist = cuttingPlane->DistanceToPoint( ( *chkPnt )->_pnt );

		if ( dist > MAX_ROUND_ERROR ) {
			if ( isInFront ) {
				return true;
			}
		}
		else if ( dist < MAX_ROUND_ERROR ) {
			if ( !isInFront ) {
				return true;
			}
		}
		else{
			return true;
		}
	}

	return false;
}


scene::Node* DBrush::BuildInRadiant( bool allowDestruction, int* changeCnt, scene::Node* entity ){
	if ( allowDestruction ) {
		bool kill = true;

		for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
		{
			if ( ( *chkPlane )->m_bChkOk ) {
				kill = false;
				break;
			}
		}
		if ( kill ) {
			return NULL;
		}
	}

	//+djbob: fixed bug when brush had no faces "phantom brush" in radiant.
	if ( faceList.size() < 4 ) {
		globalErrorStream() << "Possible Phantom Brush Found, will not rebuild\n";
		return NULL;
	}
	//-djbob

	NodeSmartReference node( GlobalBrushCreator().createBrush() );

	for ( std::list<DPlane *>::const_iterator buildPlane = faceList.begin(); buildPlane != faceList.end(); buildPlane++ ) {
		if ( ( *buildPlane )->AddToBrush( node ) && changeCnt ) {
			( *changeCnt )++;
		}
	}

	if ( entity ) {
		Node_getTraversable( *entity )->insert( node );
	}
	else {
		Node_getTraversable( GlobalRadiant().getMapWorldEntity() )->insert( node );
	}

	QER_entity = entity;
	QER_brush = node.get_pointer();

	return node.get_pointer();
}

void DBrush::CutByPlane( DPlane *cutPlane, DBrush **newBrush1, DBrush **newBrush2 ){
	if ( !IsCutByPlane( cutPlane ) ) {
		*newBrush1 = NULL;
		*newBrush2 = NULL;
		return;
	}

	DBrush* b1 = new DBrush;
	DBrush* b2 = new DBrush;

	for ( std::list<DPlane *>::const_iterator parsePlane = faceList.begin(); parsePlane != faceList.end(); parsePlane++ )
	{
		b1->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
		b2->AddFace( ( *parsePlane )->points[0], ( *parsePlane )->points[1], ( *parsePlane )->points[2], NULL );
	}

	b1->AddFace( cutPlane->points[0], cutPlane->points[1], cutPlane->points[2], NULL );
	b2->AddFace( cutPlane->points[2], cutPlane->points[1], cutPlane->points[0], NULL );

	b1->RemoveRedundantPlanes();
	b2->RemoveRedundantPlanes();

	*newBrush1 = b1;
	*newBrush2 = b2;
}

bool DBrush::IntersectsWith( DBrush *chkBrush ){
	if ( pointList.size() == 0 ) {
		if ( BuildPoints() == 0 ) {
			return false;   // invalid brush!!!!

		}
	}
	if ( chkBrush->pointList.size() == 0 ) {
		if ( chkBrush->BuildPoints() == 0 ) {
			return false;   // invalid brush!!!!

		}
	}
	if ( !BBoxCollision( chkBrush ) ) {
		return false;
	}

	std::list<DPlane *>::const_iterator iplPlane;

	for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ )
	{

		bool allInFront = true;
		for ( std::list<DPoint *>::const_iterator iPoint = chkBrush->pointList.begin(); iPoint != chkBrush->pointList.end(); iPoint++ )
		{
			if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
				allInFront = false;
				break;
			}
		}
		if ( allInFront ) {
			return false;
		}
	}

	for ( iplPlane = chkBrush->faceList.begin(); iplPlane != chkBrush->faceList.end(); iplPlane++ )
	{
		bool allInFront = true;
		for ( std::list<DPoint *>::const_iterator iPoint = pointList.begin(); iPoint != pointList.end(); iPoint++ )
		{
			if ( ( *iplPlane )->DistanceToPoint( ( *iPoint )->_pnt ) < -MAX_ROUND_ERROR ) {
				allInFront = false;
				break;
			}
		}
		if ( allInFront ) {
			return false;
		}
	}

	return true;
}

bool DBrush::IntersectsWith( DPlane* p1, DPlane* p2, vec3_t v ) {
	vec3_t vDown = { 0, 0, -1 };

	std::list<DPlane *>::const_iterator iplPlane;
	for ( iplPlane = faceList.begin(); iplPlane != faceList.end(); iplPlane++ ) {
		DPlane* p = ( *iplPlane );

		vec_t d = DotProduct( p->normal, vDown );
		if ( d >= 0 ) {
			continue;
		}
		if ( p->PlaneIntersection( p1, p2, v ) ) {
			if ( PointPosition( v ) != POINT_OUT_BRUSH ) {
				return true;
			}
		}
	}

	return false;
}

void DBrush::BuildBounds(){
	if ( !bBoundsBuilt ) {
		if ( pointList.size() == 0 ) { // if points may not have been built, build them
			if ( BuildPoints() == 0 ) {
				return;
			}
		}

		std::list<DPoint *>::const_iterator first = pointList.begin();
		VectorCopy( ( *first )->_pnt, bbox_min );
		VectorCopy( ( *first )->_pnt, bbox_max );

		std::list<DPoint *>::const_iterator point = pointList.begin();
		for ( point++; point != pointList.end(); point++ )
		{
			if ( ( *point )->_pnt[0] > bbox_max[0] ) {
				bbox_max[0] = ( *point )->_pnt[0];
			}
			if ( ( *point )->_pnt[1] > bbox_max[1] ) {
				bbox_max[1] = ( *point )->_pnt[1];
			}
			if ( ( *point )->_pnt[2] > bbox_max[2] ) {
				bbox_max[2] = ( *point )->_pnt[2];
			}

			if ( ( *point )->_pnt[0] < bbox_min[0] ) {
				bbox_min[0] = ( *point )->_pnt[0];
			}
			if ( ( *point )->_pnt[1] < bbox_min[1] ) {
				bbox_min[1] = ( *point )->_pnt[1];
			}
			if ( ( *point )->_pnt[2] < bbox_min[2] ) {
				bbox_min[2] = ( *point )->_pnt[2];
			}
		}

		bBoundsBuilt = true;
	}
}

bool DBrush::BBoxTouch( DBrush *chkBrush ){
	vec3_t min1, min2;
	vec3_t max1, max2;

	GetBounds( min1, max1 );
	chkBrush->GetBounds( min2, max2 );

	if ( ( min1[0] - max2[0] ) > MAX_ROUND_ERROR ) {
		return false;
	}
	if ( ( min1[1] - max2[1] ) > MAX_ROUND_ERROR ) {
		return false;
	}
	if ( ( min1[2] - max2[2] ) > MAX_ROUND_ERROR ) {
		return false;
	}

	if ( ( min2[0] - max1[0] ) > MAX_ROUND_ERROR ) {
		return false;
	}
	if ( ( min2[1] - max1[1] ) > MAX_ROUND_ERROR ) {
		return false;
	}
	if ( ( min2[2] - max1[2] ) > MAX_ROUND_ERROR ) {
		return false;
	}

	int cnt = 0;

	if ( ( min2[0] - max1[0] ) == 0 ) {
		cnt++;
	}

	if ( ( min2[1] - max1[1] ) == 0 ) {
		cnt++;
	}

	if ( ( min2[2] - max1[2] ) == 0 ) {
		cnt++;
	}

	if ( ( min1[0] - max2[0] ) == 0 ) {
		cnt++;
	}

	if ( ( min1[1] - max2[1] ) == 0 ) {
		cnt++;
	}

	if ( ( min1[2] - max2[2] ) == 0 ) {
		cnt++;
	}

	if ( cnt > 1 ) {
		return false;
	}

	return true;
}

void DBrush::ResetChecks( std::list<Str>* exclusionList ){
	for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
	{
		bool set = false;

		if ( exclusionList ) {
			for ( std::list<Str>::iterator eTexture = exclusionList->begin(); eTexture != exclusionList->end(); eTexture++ )
			{
				if ( strstr( ( *resetPlane )->m_shader.c_str(), eTexture->GetBuffer() ) ) {
					set = true;
					break;
				}
			}
		}

		( *resetPlane )->m_bChkOk = set;
	}
}

DPlane* DBrush::HasPlaneInverted( DPlane *chkPlane ){
	for ( std::list<DPlane *>::const_iterator brushPlane = faceList.begin(); brushPlane != faceList.end(); brushPlane++ )
	{
		if ( **brushPlane != *chkPlane ) {
			if ( fabs( ( *brushPlane )->_d + chkPlane->_d ) < 0.1 ) {
				return ( *brushPlane );
			}
		}
	}
	return NULL;
}

bool DBrush::HasTexture( const char *textureName ){
	for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
	{
		if ( strstr( ( *chkPlane )->m_shader.c_str(), textureName ) ) {
			return true;
		}

	}
	return false;
}

bool DBrush::IsDetail(){
	for ( std::list<DPlane *>::const_iterator chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
	{
		if ( ( *chkPlane )->texInfo.contents & FACE_DETAIL ) {
			return true;
		}

	}
	return false;
}

void DBrush::BuildFromWinding( DWinding *w ){
	if ( w->numpoints < 3 ) {
		globalErrorStream() << "Winding has invalid number of points";
		return;
	}

	DPlane* wPlane = w->WindingPlane();

	DWinding* w2;
	w2 = w->CopyWinding();
	int i;
	for ( i = 0; i < w2->numpoints; i++ )
		VectorAdd( w2->p[i], wPlane->normal, w2->p[i] );

	AddFace( w2->p[0], w2->p[1], w2->p[2], NULL );
	AddFace( w->p[2], w->p[1], w->p[0], NULL );

	for ( i = 0; i < w->numpoints - 1; i++ )
		AddFace( w2->p[i], w->p[i], w->p[i + 1], NULL );
	AddFace( w2->p[w->numpoints - 1], w->p[w->numpoints - 1], w->p[0], NULL );

	delete wPlane;
	delete w2;
}

void DBrush::SaveToFile( FILE *pFile ){
	fprintf( pFile, "{\n" );

	for ( std::list<DPlane *>::const_iterator pp = faceList.begin(); pp != faceList.end(); pp++ )
	{
		char buffer[512];

		sprintf( buffer, "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) %s %.0f %.0f %f %f %.0f 0 0 0\n",
				 ( *pp )->points[0][0], ( *pp )->points[0][1], ( *pp )->points[0][2],
				 ( *pp )->points[1][0], ( *pp )->points[1][1], ( *pp )->points[1][2],
				 ( *pp )->points[2][0], ( *pp )->points[2][1], ( *pp )->points[2][2],
				 ( *pp )->m_shader.c_str(),
				 ( *pp )->texInfo.m_texdef.shift[0], ( *pp )->texInfo.m_texdef.shift[1],
				 ( *pp )->texInfo.m_texdef.scale[0], ( *pp )->texInfo.m_texdef.scale[0],
				 ( *pp )->texInfo.m_texdef.rotate );

		fprintf( pFile, "%s", buffer );
	}

	fprintf( pFile, "}\n" );
}

void DBrush::Rotate( vec3_t vOrigin, vec3_t vRotation ){
	for ( std::list<DPlane *>::const_iterator rotPlane = faceList.begin(); rotPlane != faceList.end(); rotPlane++ )
	{
		for ( int i = 0; i < 3; i++ )
			VectorRotate( ( *rotPlane )->points[i], vRotation, vOrigin );

		( *rotPlane )->Rebuild();
	}
}

void DBrush::RotateAboutCentre( vec3_t vRotation ){
	vec3_t min, max, centre;
	GetBounds( min, max );
	VectorAdd( min, max, centre );
	VectorScale( centre, 0.5f, centre );

	Rotate( centre, vRotation );
}

bool DBrush::ResetTextures( const char* textureName, float fScale[2],    float fShift[2],    int rotation, const char* newTextureName,
							int bResetTextureName,   int bResetScale[2], int bResetShift[2], int bResetRotation ){
	if ( textureName ) {
		bool changed = false;
		for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
		{
			if ( !strcmp( ( *resetPlane )->m_shader.c_str(), textureName ) ) {
				if ( bResetTextureName ) {
					( *resetPlane )->m_shader = newTextureName;
				}

				if ( bResetScale[0] ) {
					( *resetPlane )->texInfo.m_texdef.scale[0] = fScale[0];
				}
				if ( bResetScale[1] ) {
					( *resetPlane )->texInfo.m_texdef.scale[1] = fScale[1];
				}

				if ( bResetShift[0] ) {
					( *resetPlane )->texInfo.m_texdef.shift[0] = fShift[0];
				}
				if ( bResetShift[1] ) {
					( *resetPlane )->texInfo.m_texdef.shift[1] = fShift[1];
				}

				if ( bResetRotation ) {
					( *resetPlane )->texInfo.m_texdef.rotate = (float)rotation;
				}

				changed = true;
			}
		}
		return changed; // no point rebuilding unless we need to, only slows things down
	}
	else
	{
		for ( std::list<DPlane *>::const_iterator resetPlane = faceList.begin(); resetPlane != faceList.end(); resetPlane++ )
		{
			if ( bResetTextureName ) {
				( *resetPlane )->m_shader = newTextureName;
			}

			if ( bResetScale[0] ) {
				( *resetPlane )->texInfo.m_texdef.scale[0] = fScale[0];
			}
			if ( bResetScale[1] ) {
				( *resetPlane )->texInfo.m_texdef.scale[1] = fScale[1];
			}

			if ( bResetShift[0] ) {
				( *resetPlane )->texInfo.m_texdef.shift[0] = fShift[0];
			}
			if ( bResetShift[1] ) {
				( *resetPlane )->texInfo.m_texdef.shift[1] = fShift[1];
			}

			if ( bResetRotation ) {
				( *resetPlane )->texInfo.m_texdef.rotate = (float)rotation;
			}
		}
		return true;
	}
}

bool DBrush::operator ==( DBrush* other ){
	std::list<DPlane *>::const_iterator chkPlane;

	for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
	{
		if ( !other->HasPlane( ( *chkPlane ) ) ) {
			return false;
		}
	}

	for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ )
	{
		if ( !HasPlane( ( *chkPlane ) ) ) {
			return false;
		}
	}

	return true;
}

DPlane* DBrush::AddFace( const vec3_t va, const vec3_t vb, const vec3_t vc, const char *textureName, bool bDetail ){
	bBoundsBuilt = false;
	DPlane* newFace = new DPlane( va, vb, vc, textureName, bDetail );
	faceList.push_back( newFace );

	return newFace;
}

DPlane* DBrush::FindPlaneWithClosestNormal( vec_t* normal ) {
	vec_t bestDot = -2;
	DPlane* bestDotPlane = NULL;
	std::list<DPlane *>::const_iterator chkPlane;
	for ( chkPlane = faceList.begin(); chkPlane != faceList.end(); chkPlane++ ) {
		DPlane* pPlane = ( *chkPlane );

		vec_t dot = DotProduct( pPlane->normal, normal );
		if ( dot > bestDot ) {
			bestDot = dot;
			bestDotPlane = pPlane;
		}
	}

	return bestDotPlane;
}

int DBrush::FindPointsForPlane( DPlane* plane, DPoint** pnts, int maxpnts ) {
	int numpnts = 0;

	if ( !maxpnts ) {
		return 0;
	}

	BuildPoints();

	for ( std::list<DPoint *>::const_iterator points = pointList.begin(); points != pointList.end(); points++ ) {
		DPoint* point = ( *points );

		if ( fabs( plane->DistanceToPoint( point->_pnt ) ) < MAX_ROUND_ERROR ) {
			pnts[numpnts] = point;
			numpnts++;

			if ( numpnts >= maxpnts ) {
				return numpnts;
			}

		}
	}

	return numpnts;
}

void DBrush::RemovePlane( DPlane* plane ) {
	bBoundsBuilt = false;
	for ( std::list<DPlane *>::const_iterator deadPlane = faceList.begin(); deadPlane != faceList.end(); deadPlane++ ) {
		if ( *deadPlane == plane ) {
			delete *deadPlane;
			faceList.remove( plane );
		}
	}
}
