/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program 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.
**
**	This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : WWPhys                                                       *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/wwphys/bpt.cpp                               $*
 *                                                                                             *
 *                       Author:: Greg_h                                                       *
 *                                                                                             *
 *                     $Modtime:: 9/09/99 11:29a                                              $*
 *                                                                                             *
 *                    $Revision:: 15                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   BptClass::BptClass -- constructor                                                         *
 *   BptClass::~BptClass -- destructor                                                         *
 *   BptClass::Free -- releases all assets in use                                              *
 *   BptClass::Build -- build a bpt from the given mesh (generates a new mesh)                 *
 *   BptClass::Load -- Initialize this Bpt object from contents of a W3D file                  *
 *   BptClass::Save -- Save this Bpt object into a W3D file                                    *
 *   BptClass::Render -- submit this object for rendering                                      *
 *   BptClass::Cast_Ray -- Intersect a ray with this Bpt Object                                *
 *   BptClass::Cast_AABox -- Intersect a swept axis-aligned box.                               *
 *   BptClass::Cast_OBBox -- Intersect a swept oriented box with this bpt object               *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#if 0 // OBSOLETE!!!
#ifdef PORT140

#include "bpt.h"
#include "vector.h"
#include "uarray.h"
#include "mesh.h"
#include "material.h"
#include "chunkio.h"
#include "vector3.h"
#include "tri.h"
#include "wwdebug.h"
#include "meshbuild.h"
#include "camera.h"
#include "sr.hpp"


#define BPT_FRONT						0x01
#define BPT_BACK						0x02
#define BPT_ON							0x04
#define BPT_BOTH						0x08
#define BPT_EPSILON					0.0001f
#define BPT_COINCIDENCE_EPSILON	0.000001f

class BptVertexClass;
class BptPolyClass;
class BptNodeClass;
class BptBuilderClass;
class BptImpNodeClass;
class BptImpClass;
class BptImpBuilderClass;



/*
** NormalHasherClass, used by the code which builds the
** table of unique Normal vectors.
*/
class NormalHasherClass : public HashCalculatorClass<Vector3>
{
public:

	virtual bool	Items_Match(const Vector3 & a, const Vector3 & b) 
	{
		// looking for an exact match for normals...
		return ((a.X == b.X) && (a.Y == b.Y) && (a.Z == b.Z));
	}

	virtual void	Compute_Hash(const Vector3 & item)
	{
		HashVal = (int)(item.X*12345.6f + item.Y*1714.38484f + item.Z*27561.3f)&1023; 
	}

	virtual int		Num_Hash_Bits(void) 
	{ 
		return 10;  // 10bit hash value. 
	}
	
	virtual int		Num_Hash_Values(void)
	{
		return 1;	// only one hash value for normals, require *exact* match
	}
	
	virtual int		Get_Hash_Value(int index)
	{
		return HashVal;
	}

private:
	
	int HashVal;

};


/*
** DistanceHasherClass, used by the code which builds the
** table of unique distance values.
*/
class DistanceHasherClass : public HashCalculatorClass<float>
{
	virtual bool	Items_Match(const float & a, const float & b) 
	{
		// looking for an exact match for distances
		return (a == b);
	}

	virtual void	Compute_Hash(const float & item)
	{
		HashVal = (int)(item)&1023; 
	}

	virtual int		Num_Hash_Bits(void) 
	{ 
		return 12;  // 12bit hash value. 
	}
	
	virtual int		Num_Hash_Values(void)
	{
		return 1;	// only one hash value for normals, require *exact* match
	}

	virtual int		Get_Hash_Value(int index) 
	{
		return HashVal;
	}

private:

	int HashVal;

};



 
/*
** BPT Notes
**
** - Probably need to build the initial tree in a big straightforward recursive datastructure
**   with no indirection or anything.  Then, when the whole thing is being built correctly,
**   process the tree down so that everything that can be shared (e.g. plane equations) are
**   shared.
**
** - Hide most of the implementation details even from the bpt header file by using a
**   BptImpClass.  This class will contain the final "optimized" bpt information.
**
** - Maybe need a BptBuilder class that builds the initial "straightforward" tree structure
**   and then compresses it into a BptImp.
**
** - Extend PlaneClass to know about special case planes such as x-y, y-z, x-z.  Optimize
**   the culling and clipping functions to take advantage of them.
** 
** - Separate the normal from the distance in the plane equation?  Many many planes will
**   probably share the same normal but different distances.  This probably should be a
**   very late optimization.
*/


/*
** BptVertexClass
** This is the vertex representation used in building the BPT
*/
class BptVertexClass
{
public:

	BptVertexClass(void);
	BptVertexClass(const BptVertexClass & that);

	BptVertexClass &	operator = (const BptVertexClass & that);
	
	int					Which_Side(const PlaneClass & plane) const;

	Vector3				Position;
	Vector3				Normal;
	Vector3				Color;
	Vector3				TexCoord;
	
	static BptVertexClass Lerp(const BptVertexClass & v0,const BptVertexClass & v1,double lerp);
	static BptVertexClass Intersect_Plane(const BptVertexClass & v0,const BptVertexClass & v1,const PlaneClass & plane);

};


/*
** BptPolyClass
** This is the polygon representation used in building the BPT
*/
class BptPolyClass
{
public:

	enum { BPT_POLY_MAX_VERTS = 16 };

	BptPolyClass(void);
	BptPolyClass(const BptPolyClass & that);
	BptPolyClass(const BptVertexClass * points, int num);

	BptPolyClass &				operator = (const BptPolyClass & that);
	
	const BptVertexClass &	operator[] (int i) const { return Verts[i]; }
	BptVertexClass &			operator[] (int i) { return Verts[i]; }

	void							Compute_Plane(void);
	int							Which_Side(const PlaneClass & plane) const;
	void							Split(const PlaneClass & plane,BptPolyClass & front,BptPolyClass & back) const;
	bool							Is_Degenerate(void);
	bool							Salvage_Degenerate(void);

	int							MaterialId;
	int							NumVerts;
	BptVertexClass				Verts[BPT_POLY_MAX_VERTS];
	PlaneClass					Plane;
};


/*
** BptNodeClass
** This is the node representation used in building the BPT
*/
class BptNodeClass
{

public:

	BptNodeClass(void) : NumPolys(0),Polys(NULL),Front(NULL),Back(NULL),ArrayIndex(-1) { }
	~BptNodeClass(void) { if (Front) delete Front; if (Back) delete Back; if (Polys) delete[] Polys; }
	
	void				Build(int numpolys,BptPolyClass * polys);
	int				Num_Polys(void) const;
	int				Num_Tris(void) const;
	int				Num_Nodes(void) const;
	int				Max_Depth(void) const;
	void				Submit_Polys(MeshBuilderClass & builder) const;

	void				Compute_Bounding_Box(void);
	int				Assign_Array_Indices(int index);
	void				Submit_Nodes(BptImpBuilderClass & impbuilder) const;

private:


	PlaneClass					Plane;		// splitting plane
	uint32						NumPolys;	// num polys on plane
	BptPolyClass *				Polys;		// array of polys
	BptNodeClass *				Front;		// pointer to front tree
	BptNodeClass *				Back;			// pointer to back tree

	int							ArrayIndex;	// used in building the tree into array form.
	
	Vector3						Min;			// bounding box for this tree.
	Vector3						Max;

	/*
	** SplitChoiceStruct.  Whenever a plane is tested to see if it is a suitable splitting
	** plane, we'll cache all of the values it computes so we do not duplicate the effort elsewhere.
	*/
	struct SplitChoiceStruct
	{
		SplitChoiceStruct(void) : Score(0.0f),FrontCount(0),BackCount(0),OnCount(0),SplitCount(0),Plane(0,0,0,0) {}
		
		float			Score;			// 0.0 is worst, 1.0 is "perfect" 
		int			FrontCount;		// number of polys in front of the plane
		int			BackCount;		// number of polys behind the plane
		int			OnCount;			// number of polys on the plane
		int			SplitCount;		// number of polys split by the plane
		PlaneClass	Plane;			// plane equation
	};

	/*
	** SplitArrayStruct.  This struct simply stores the result of a split operation.  The
	** arrays for the front, back and "on" polygons will be allocated and filled in.
	*/
	struct SplitArraysStruct
	{
		SplitArraysStruct(void) : FrontCount(0),BackCount(0),OnCount(0),FrontPolys(NULL),BackPolys(NULL),OnPolys(NULL) {}

		int					FrontCount;
		int					BackCount;
		int					OnCount;
		BptPolyClass *		FrontPolys;
		BptPolyClass *		BackPolys;
		BptPolyClass *		OnPolys;
	};

	SplitChoiceStruct		Compute_Plane_Score(int numpolys,BptPolyClass * polys,const PlaneClass & plane);	
	SplitChoiceStruct		Select_Splitting_Plane(int numpolys,BptPolyClass * polys);

	void						Split_Polys(const int num_polys,
												const BptPolyClass * polys,
												const SplitChoiceStruct	& sc,
												SplitArraysStruct * arrays);

	void						Max_Depth_Internal(int cur_depth, int & max_depth) const;

	friend class BptImpBuilderClass;
};


/*
** BptBuilderClass
** This class builds the initial straightforward binary tree representation of
** the mesh given to it.  It then "optimizes" the tree into a BptImpClass to
** save ram.
*/
class BptBuilderClass
{
public:

	BptBuilderClass(void);
	~BptBuilderClass(void);

	BptImpClass * Build(MeshClass * mesh);
	BptImpClass * Build(int numpolys,BptPolyClass * polyarray,MaterialInfoClass * matinfo);
	
private:

	void						unwind_mesh(MeshClass * mesh,int & set_numpolys,BptPolyClass * & set_polyarray);
	BptImpClass *			build_imp(void);
	
	BptNodeClass *			Root;
	MaterialInfoClass *	MatInfo;
	int						InputPolyCount;
	int						OutputPolyCount;
	int						InputTriCount;
	int						OutputTriCount;
	int						NodeCount;
	int						MaxDepth;
};


/*
** BptImpNodeClass
** This is the node representation in the final BPT implementation.
** It allows for sharing of plane equations, indirection to the polygons, etc
*/
class BptImpNodeClass
{
public:

	float							MinX,MinY,MinZ;
	float							MaxX,MaxY,MaxZ;

	BptImpNodeClass *			Front;
	BptImpNodeClass *			Back;

	uint16						FirstPoly;
	uint16						PolyCount;

	uint16						NormalIndex;
	uint16						DistanceIndex;

	bool							Is_Visible(const CameraClass & camera);
	bool							Cast_AABox_To_Polys(PhysAABoxCollisionTestClass & coltest);
};


/*
** BptImpClass
** This class contains the actual run-time binary partition tree information
** and the code for building a bpt out of an arbitrary list of polygons.
*/
class BptImpClass
{
public:

	~BptImpClass(void);
	void Free(void);

	void Build_Apt(const CameraClass & camera);
	bool Cast_Ray(PhysRayCollisionTestClass & raytest) const;
	bool Cast_AABox(PhysAABoxCollisionTestClass & boxtest) const;
	bool Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest) const;

private:

	// These functions are used by the BptImpBuilder to construct
	// the BptImp...
	BptImpClass(void);
	
	void Set_Mesh(MeshClass * mesh);
	void Set_Node_Array(int numnodes, BptImpNodeClass * nodes);
	void Set_Normal_Array(const UniqueArrayClass<Vector3> & normals);
	void Set_Distance_Array(const UniqueArrayClass<float> & distances);
	void Set_Poly_Index_Array(int numremaps,int * polyremaps);

	void Begin_Apt(void);
	void Add_Polys_To_Apt(BptImpNodeClass * node);
	void End_Apt(void);
	void Build_Apt_Recursive(BptImpNodeClass * node,const CameraClass & cam);

	bool Cast_Ray_Recursive(BptImpNodeClass * node,PhysRayCollisionTestClass & boxtest) const;
	bool Cast_AABox_Recursive(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const;
	bool Cast_OBBox_Recursive(BptImpNodeClass * node,PhysOBBoxCollisionTestClass & boxtest) const;
	
	bool Cast_Ray_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const { assert(0); return false; }
	bool Cast_AABox_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const;
	bool Cast_OBBox_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const { assert(0); return false; }

	void Verify_Node_Bounding_Volume(BptImpNodeClass * node,const Vector3 & min,const Vector3 & max) const;
	
	// Polygon Mesh in w3d-friendly form
	MeshClass *					Mesh;

	// Binary partition nodes
	int							NodeCount;
	BptImpNodeClass *			Nodes;
	
	// Node Polygon remap arrays
	int							PolyIndexCount;
	int *							PolyIndices;

	// Plane equation normals, these are stored separate from the distances
	// because many many of the planes will share the same normal but not dist.
	int							NormalCount;
	Vector3 *					Normals;

	// Plane equation distances, again, stored separately from the normals
	int							DistanceCount;
	float *						Distances;

	// Active Poly Table support.  These variables are used internally by
	// the culling code.  The ActivePolyTable pointer is set to point into
	// the surrender mesh's apt.
	int							ActivePolyCount;
	SRDWORD *					ActivePolyTable;

	friend class BptClass;
	friend class BptImpBuilderClass;

};


/*
** BptImpBuilderClass
** This guy takes a completed bpt tree (given as a pointer to the root node)
** and churns through the thing, generating a BptImpClass.
*/
class BptImpBuilderClass
{
public:

	BptImpBuilderClass(void);
	~BptImpBuilderClass(void);

	void					Free(void);

	BptImpClass *		Build_Bpt_Imp(BptNodeClass * root,MaterialInfoClass * matinfo);
	void					Add_Bpt_Node(const BptNodeClass * node);

private:

	void					Remap_Triangle_Indices(void);
	
	MeshBuilderClass					MeshBuilder;			// accumulate the polygons

	NormalHasherClass					NormalHasher;			// computes hash values for the normals
	DistanceHasherClass				DistanceHasher;		// computes hash values for the distances
	
	UniqueArrayClass<Vector3> *	UniqueNormals;			// accumulates the unique normals
	UniqueArrayClass<float>	*		UniqueDistances;		// accumulates the unique distances

	int									NodeCount;				// number of nodes in the tree
	BptImpNodeClass *					Nodes;					// array of all of the nodes

	int									TriCount;				// number of triangles in the tree
	int									CurTriIndex;			// current remap index
	int *									TriIndexArray;			// array of triangle remap indices

};








/***********************************************************************************************
 * BptClass::BptClass -- constructor                                                           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
BptClass::BptClass(void) :
	BptImp(NULL)
{

}


/***********************************************************************************************
 * BptClass::~BptClass -- destructor                                                           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
BptClass::~BptClass(void) 
{
	Free();
}


/***********************************************************************************************
 * BptClass::Free -- releases all assets in use                                                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
void BptClass::Free(void)
{
	if (BptImp) {
		delete BptImp;
		BptImp = NULL;
	}
}


/***********************************************************************************************
 * BptClass::Build -- build a bpt from the given mesh (generates a new mesh)                   *
 *                                                                                             *
 * This function will process the given mesh into a binary partition tree.  It has to generate *
 * a new mesh due to the fact that some polygons may be split in the partioning process.       *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
void BptClass::Build(MeshClass * mesh)
{
	assert(mesh);

	/*
	** First, release everything that we have!
	*/
	Free();

	/*
	** Create a new BptImp object using the given mesh
	*/
	BptBuilderClass builder;
	BptImp = builder.Build(mesh);
}


/***********************************************************************************************
 * BptClass::Load -- Initialize this Bpt object from contents of a W3D file                    *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
int BptClass::Load(ChunkLoadClass & cload)
{
	assert(0);
	return 0;
}


/***********************************************************************************************
 * BptClass::Save -- Save this Bpt object into a W3D file                                      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
int BptClass::Save(ChunkSaveClass & csave)
{
	assert(0);
	return 0;
}


/***********************************************************************************************
 * BptClass::Render -- submit this object for rendering                                        *
 *                                                                                             *
 * This function sets the active polygon table for the mesh based on the camera frustum.  The  *
 * bpt is used to cull out portions of the level which are not inside the frustum.  Then the   *
 * mesh is added to the given surrender scene.                                                 *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *   5/29/98    GTH : Created.                                                                 *
 *=============================================================================================*/
void BptClass::Render( srScene * scene, const CameraClass &camera )
{
	/*
	** Do our APT stuff
	*/
	BptImp->Build_Apt(camera);
	
	/*
	** Then let Mesh render itself
	*/
	BptImp->Mesh->Render(scene,camera);
}
	

void BptClass::Update_Cached_Bounding_Volumes(void) const
{
	CachedBoundingSphere = BptImp->Mesh->Get_Bounding_Sphere();
	CachedBoundingBox = BptImp->Mesh->Get_Bounding_Box();
	Validate_Cached_Bounding_Volumes();
}

#if 0
MeshClass * BptClass::Get_Mesh(void)
{
	if (BptImp) {
		if (BptImp->Mesh) BptImp->Mesh->Add_Ref();
		return BptImp->Mesh;
	} else {
		return NULL;
	}
}
#endif
/***********************************************************************************************
 * BptClass::Cast_Ray -- Intersect a ray with this Bpt Object                                  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
bool BptClass::Cast_Ray(PhysRayCollisionTestClass & raytest) const
{
	assert(BptImp);
	return BptImp->Cast_Ray(raytest);
}


/***********************************************************************************************
 * BptClass::Cast_AABox -- Intersect a swept axis-aligned box.                                 *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
bool BptClass::Cast_AABox(PhysAABoxCollisionTestClass & aaboxtest) const
{
	assert(BptImp);
	return BptImp->Cast_AABox(aaboxtest);
}


/***********************************************************************************************
 * BptClass::Cast_OBBox -- Intersect a swept oriented box with this bpt object                 *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   5/6/98     GTH : Created.                                                                 *
 *=============================================================================================*/
bool BptClass::Cast_OBBox(PhysOBBoxCollisionTestClass & obboxtest) const
{
	assert(BptImp);
	return BptImp->Cast_OBBox(obboxtest);
}


/**************************************************************

	BptVertexClass Implementation

**************************************************************/

BptVertexClass::BptVertexClass(void) :
	Position(0,0,0),
	Normal(0,0,0),
	Color(0,0,0),
	TexCoord(0,0,0)
{
}

BptVertexClass::BptVertexClass(const BptVertexClass & that)
{
	Position = that.Position;
	Normal = that.Normal;
	Color = that.Color;
	TexCoord = that.TexCoord;
}

BptVertexClass & BptVertexClass::operator = (const BptVertexClass & that)
{
	if (this != &that) {
		Position = that.Position;
		Normal = that.Normal;
		Color = that.Color;
		TexCoord = that.TexCoord;
	}
	return *this;
}

BptVertexClass BptVertexClass::Lerp(const BptVertexClass & v0,const BptVertexClass & v1,double lerp)
{
	assert(lerp >= -BPT_EPSILON);
	assert(lerp <= 1.0 + BPT_EPSILON);
	
	// interpolate position
	BptVertexClass res;
	res.Position = ::Lerp(v0.Position,v1.Position,lerp);

	// interpolate normal, renormalize
	res.Normal = ::Lerp(v0.Normal,v1.Normal,lerp);
	res.Normal.Normalize();

	// interpolate color
	res.Color = ::Lerp(v0.Color,v1.Color,lerp);

	// interpolate texture coordinates
	res.TexCoord = ::Lerp(v0.TexCoord,v1.TexCoord,lerp);

	return res;
}

int BptVertexClass::Which_Side(const PlaneClass & plane) const
{
	float d = Vector3::Dot_Product(plane.N,Position);
	d -= plane.D;

	if (d > BPT_EPSILON) {
		return BPT_FRONT;
	} 

	if (d < -BPT_EPSILON) {
		return BPT_BACK;
	}

	return BPT_ON;
}

BptVertexClass BptVertexClass::Intersect_Plane
(
	const BptVertexClass & p0,
	const BptVertexClass & p1,
	const PlaneClass & plane
)
{
	double alpha = 0.0f;
	plane.Compute_Intersection(p0.Position,p1.Position,&alpha);
	return BptVertexClass::Lerp(p0,p1,alpha);				
}

/**************************************************************

	BptPolyClass Implementation

**************************************************************/
BptPolyClass::BptPolyClass(void) :
	NumVerts(0)
{
}

BptPolyClass::BptPolyClass(const BptPolyClass & that)
{
	NumVerts = that.NumVerts;
	for (int i=0;i<NumVerts;i++) {
		Verts[i] = that.Verts[i];
	}
}

BptPolyClass::BptPolyClass(const BptVertexClass * points, int num)
{
	NumVerts = num;
	for (int i=0; i<NumVerts; i++) {
		Verts[i] = points[i];
	}
}

BptPolyClass & BptPolyClass::operator = (const BptPolyClass & that)
{
	if (this != &that) {
		MaterialId = that.MaterialId;
		NumVerts = that.NumVerts;
		Plane = that.Plane;
		for (int i=0;i<NumVerts;i++) {
			Verts[i] = that.Verts[i];
		}
	}
	return * this;
}

void BptPolyClass::Compute_Plane(void)
{
	double nx = 0;
	double ny = 0;
	double nz = 0;
	double ax = 0;
	double ay = 0;
	double az = 0;

	int i,j;

	for (i=0; i<NumVerts; i++) {
		j = (i+1) % NumVerts;
		
		nx += (double)(Verts[i].Position.Y - Verts[j].Position.Y) * (double)(Verts[i].Position.Z + Verts[j].Position.Z);
		ny += (double)(Verts[i].Position.Z - Verts[j].Position.Z) * (double)(Verts[i].Position.X + Verts[j].Position.X);
		nz += (double)(Verts[i].Position.X - Verts[j].Position.X) * (double)(Verts[i].Position.Y + Verts[j].Position.Y);

		ax += (double)Verts[i].Position.X;
		ay += (double)Verts[i].Position.Y;
		az += (double)Verts[i].Position.Z;
	}

	ax /= (double)NumVerts;
	ay /= (double)NumVerts;
	az /= (double)NumVerts;

	double len = sqrt(nx*nx + ny*ny + nz*nz);
	nx /= len;
	ny /= len;
	nz /= len;
	
	Plane.Set(Vector3(nx,ny,nz),Vector3(ax,ay,az));
}

int BptPolyClass::Which_Side(const PlaneClass & plane) const
{
	int side_mask = 0;
	for (int i=0; i<NumVerts;i++) {
		
		side_mask |= Verts[i].Which_Side(plane);

	}

	// check if all verts are "ON"
	if (side_mask == BPT_ON) {
		return BPT_ON;
	}

	// check if all verts are either "ON" or "FRONT"
	if ((side_mask & ~(BPT_FRONT | BPT_ON)) == 0) {
		return BPT_FRONT;
	}

	// check if all verts are either "ON" or "BACK"
	if ((side_mask & ~(BPT_BACK | BPT_ON)) == 0) {
		return BPT_BACK;
	}

	// otherwise, poly spans the plane.
	return BPT_BOTH;
}

void BptPolyClass::Split(const PlaneClass & plane,BptPolyClass & front,BptPolyClass & back) const
{
	front = *this;
	back = *this;
	front.NumVerts = back.NumVerts = 0;
	assert(Which_Side(plane) == BPT_BOTH);

	BptVertexClass point;
	front.NumVerts = 0; 
	back.NumVerts = 0;

	// find a vertex on one side or the other
	int side = BPT_ON;
	int i;
	for (i = 0; (i < NumVerts) && ((side = Verts[i].Which_Side(plane)) == BPT_ON); i++);

	// perform clipping
	int iprev = i;
	int sideprev = side;
	int sidelastdefinite = 0;
	
	i = (i+1) % NumVerts;

	for (int j=0; j<NumVerts; j++) { 
		
		side = Verts[i].Which_Side(plane);
		
		if (sideprev == BPT_FRONT) {

			if (side == BPT_FRONT) {
			
				// Previous vertex was in front of plane and this vertex is in
				// front of the plane so we emit this vertex in the front poly
				front.Verts[(front.NumVerts)++] = Verts[i];

			} else if (side == BPT_ON) {

				// Previous vert was in front, this vert is "on" so emit
				// the vertex into the front poly.
				sidelastdefinite = BPT_FRONT;
				front.Verts[(front.NumVerts)++] = Verts[i];
			
			} else { // side == BPT_BACK

				// Previous vert was in front, this vert is behind, compute
				// the intersection and emit the point in both the front
				// and back polys.  Then continue the edge into the back halfspace
				point = BptVertexClass::Intersect_Plane(Verts[iprev],Verts[i],plane);
				front.Verts[(front.NumVerts)++] = point;
				back.Verts[(back.NumVerts)++] = point;
				back.Verts[(back.NumVerts)++] = Verts[i];

			}

		} else if (sideprev == BPT_BACK) {

			if (side == BPT_FRONT) {

				// segment is going from the back halfspace to the front halfspace
				// compute the intersection and emit it in both polys, then continue
				// the edge into the front halfspace.
				point = BptVertexClass::Intersect_Plane(Verts[iprev],Verts[i],plane);
				back.Verts[(back.NumVerts)++] = point;
				front.Verts[(front.NumVerts)++] = point;
				front.Verts[(front.NumVerts)++] = Verts[i];
			
			} else if (side == BPT_ON) {
			
				// segment went from back halfspace to "on" the plane.  Emit
				// the vertex into the back poly and remember that we came 
				// from the back halfspace.
				sidelastdefinite = BPT_BACK;
				back.Verts[(back.NumVerts)++] = Verts[i];

			} else {  // side == BPT_BACK

				// segment is completely in the back halfspace, just emit the
				// vertex into the back poly
				back.Verts[(back.NumVerts)++] = Verts[i];

			}
		
		} else if (sideprev == BPT_ON) {

			if (side == BPT_FRONT) {

				// segment is on the plane
				if (sidelastdefinite == BPT_BACK) {
					front.Verts[(front.NumVerts)++] =	Verts[iprev];
				}
				front.Verts[(front.NumVerts)++] = Verts[i];
			
			} else if (side == BPT_ON) {

				if (sidelastdefinite == BPT_FRONT) {
					front.Verts[(front.NumVerts)++] = Verts[i];
				} else {
					back.Verts[(back.NumVerts)++] = Verts[i];
				}
			
			} else { // side == BPT_BACK

				if (sidelastdefinite == BPT_FRONT) {
					back.Verts[(back.NumVerts)++] =	Verts[iprev];
				}
				back.Verts[(back.NumVerts)++] = Verts[i];
			}
		} else {
			WWASSERT_PRINT(0,"BptPolyClass::Split : invalid side\n");
		}

		sideprev = side;
		iprev = i;
		i = (i+1)%NumVerts;

	}

	front.Compute_Plane();
	back.Compute_Plane();

	// check the two polygons
	if (front.Is_Degenerate()) {
		front.Salvage_Degenerate();
	}

	if (back.Is_Degenerate()) {
		back.Salvage_Degenerate();
	}
}



bool BptPolyClass::Is_Degenerate(void)
{
	int i,j;

	if (NumVerts == 2) {
		WWDEBUG_SAY(("Degenerate Poly - fewer than 3 vertices\r\n"));
		return true;
	}

	for (i=0; i<NumVerts; i++) {
		for (j = i+1; j < NumVerts; j++) {
			
			float delta = (Verts[i].Position - Verts[j].Position).Length();
			if (delta < BPT_COINCIDENCE_EPSILON) {
				WWDEBUG_SAY(("Degenerate Poly - coincident vertices!\r\n"));
				return true;
			}
		}
	}

	for (i=0; i<NumVerts; i++) {
		int side = Verts[i].Which_Side(Plane);
		if (side != BPT_ON) {

			// hmmm, try to recalculate the plane, if it is still bad, then give up
			Compute_Plane();

			if (Verts[i].Which_Side(Plane) != BPT_ON) {
				WWDEBUG_SAY(("Degenerate Poly - invalid plane!\r\n"));
				return true;
			}
		}
	}

	return false;
}

bool BptPolyClass::Salvage_Degenerate(void)
{
	/*
	** About all we can do is combine sequential vertices which are co-incident
	*/
	int i = 0;
	while (i < NumVerts) {

		float delta = (Verts[i].Position - Verts[i+1].Position).Length();
		if (delta < BPT_COINCIDENCE_EPSILON) {
			
			for (int j=i+1; j<NumVerts-1; j++) {
				Verts[j] = Verts[j+1];
			}

			NumVerts--;
		
		} else {

			i++;
		}
	}

	return !Is_Degenerate();
}

/**************************************************************

	BptBuilderClass Implementation

**************************************************************/
BptBuilderClass::BptBuilderClass(void) : 
	Root(NULL),
	MatInfo(NULL),
	InputPolyCount(0),
	OutputPolyCount(0),
	NodeCount(0),
	MaxDepth(0)
{
}

BptBuilderClass::~BptBuilderClass(void) 
{ 
	if (Root) delete Root; 
	if (MatInfo) MatInfo->Release_Ref(); 
}

BptImpClass * BptBuilderClass::Build(MeshClass * mesh)
{
	/*
	** Here we go, unwind the mesh into BptVerts and BptPolys
	*/
	int numpolys;
	BptPolyClass * polys;

	unwind_mesh(mesh,numpolys,polys);
	assert(numpolys > 0);
	
	/*
	** Jump into the generalized build method
	*/
	MaterialInfoClass * matinfo = mesh->Get_Material_Info();
	BptImpClass * result = Build(numpolys,polys,matinfo);
	matinfo->Release_Ref();

	/*
	** Build function deletes the poly array...
	*/
	polys = NULL;

	return result;
}

BptImpClass * BptBuilderClass::Build(int numpolys,BptPolyClass * polys,MaterialInfoClass * matinfo)
{
	/*
	** Create a copy of all of the materials
	*/
	MatInfo = (MaterialInfoClass *)matinfo->Clone();


	/*
	** Store the input counts
	*/
	InputPolyCount = numpolys;
	InputTriCount = 0;
	for (int i=0; i<numpolys; i++) {
		InputTriCount += polys[i].NumVerts-2;
	}

	WWDEBUG_SAY(("Building Binary Tree\r\n"));

	/*
	** Build the fat tree
	*/
	assert(Root == NULL);
	Root = new BptNodeClass;
	Root->Build(numpolys,polys);

	/*
	** Compile some statistics about the tree
	*/
	OutputPolyCount = Root->Num_Polys();
	OutputTriCount = Root->Num_Tris();
	NodeCount = Root->Num_Nodes();
	MaxDepth = Root->Max_Depth();

	WWDEBUG_SAY(("Binary Tree Generated!\r\n"));
	WWDEBUG_SAY(("Node Count:            %d\r\n",NodeCount));
	WWDEBUG_SAY(("Max Depth:             %d\r\n",MaxDepth));
	WWDEBUG_SAY(("Input Poly Count:      %d\r\n",InputPolyCount));
	WWDEBUG_SAY(("Output Poly Count:     %d\r\n",OutputPolyCount));
	WWDEBUG_SAY(("Input Tri Count:       %d\r\n",InputTriCount));
	WWDEBUG_SAY(("Output Tri Count:      %d\r\n",OutputTriCount));
	WWDEBUG_SAY(("Out/In Poly Ratio:     %f\r\n",(float)OutputPolyCount / (float)InputPolyCount));
	WWDEBUG_SAY(("Out/In Tri Ratio:      %f\r\n",(float)OutputTriCount / (float)InputTriCount));
	WWDEBUG_SAY(("\r\n"));

	/*
	** Build the optimized BptImp and return it.
	*/
	return build_imp();
}	

void BptBuilderClass::unwind_mesh(MeshClass * mesh,int & set_numpolys,BptPolyClass * & set_polyarray)
{
	srMeshModel * srmesh = mesh->Get_Surrender_Mesh();

	// get pointers to the interesting stuff in the mesh
	srVector3 * loc = srmesh->getVertexLoc();
	srVector3 * norm = srmesh->getVertexNormal();
	srVector3 * uv = srmesh->getVertexUVW(srMaterial::DIFFUSE_COLOR,false);
	srVector3 * dcg = srmesh->getVertexDCG(false);
	srVector3i * polyvert = srmesh->getPolyVertex();
	srMaterial ** polymtl = srmesh->getPolyMaterial(false);

	// allocate a bunch of BptPolygons
	set_numpolys = srmesh->getPolygonCount();
	set_polyarray = new BptPolyClass[set_numpolys];
	
	int src_idx = 0;
	int dst_idx = 0;

	for (src_idx = 0; src_idx<set_numpolys; src_idx++) {
		
		set_polyarray[dst_idx].NumVerts = 3;
		
		for (int vrt_idx=0; vrt_idx<3; vrt_idx++) {
					
			// copy the three positions
			set_polyarray[dst_idx][vrt_idx].Position.X = loc[polyvert[src_idx][vrt_idx]].x;
			set_polyarray[dst_idx][vrt_idx].Position.Y = loc[polyvert[src_idx][vrt_idx]].y;
			set_polyarray[dst_idx][vrt_idx].Position.Z = loc[polyvert[src_idx][vrt_idx]].z;
		
			// copy the three normals
			if (norm) {
				set_polyarray[dst_idx][vrt_idx].Normal.X = norm[polyvert[src_idx][vrt_idx]].x;
				set_polyarray[dst_idx][vrt_idx].Normal.Y = norm[polyvert[src_idx][vrt_idx]].y;
				set_polyarray[dst_idx][vrt_idx].Normal.Z = norm[polyvert[src_idx][vrt_idx]].z;
			}

			// copy the three u-v's
			if (uv) {
				set_polyarray[dst_idx][vrt_idx].TexCoord.X = uv[polyvert[src_idx][vrt_idx]].x;
				set_polyarray[dst_idx][vrt_idx].TexCoord.Y = uv[polyvert[src_idx][vrt_idx]].y;
				set_polyarray[dst_idx][vrt_idx].TexCoord.Z = uv[polyvert[src_idx][vrt_idx]].z;
			}

			// copy the three colors
			if (dcg) {
				set_polyarray[dst_idx][vrt_idx].Color.X = dcg[polyvert[src_idx][vrt_idx]].x;
				set_polyarray[dst_idx][vrt_idx].Color.Y = dcg[polyvert[src_idx][vrt_idx]].y;
				set_polyarray[dst_idx][vrt_idx].Color.Z = dcg[polyvert[src_idx][vrt_idx]].z;
			}
			
			// store the material ID
			if (polymtl) {
				set_polyarray[dst_idx].MaterialId = ((MaterialClass *)(polymtl[src_idx]))->Get_Mat_Info_Index();
			} else {
				set_polyarray[dst_idx].MaterialId = 0;
			}
		}

		set_polyarray[dst_idx].Compute_Plane();

		// if the triangle is not degenerate, accept it; increment the counter
		if (!set_polyarray[dst_idx].Is_Degenerate()) {
			dst_idx++;
		}

	}

	// set the number of polys to the actual number kept
	set_numpolys = dst_idx;
	assert(set_numpolys <= srmesh->getPolygonCount());

	WWDEBUG_SAY(("Unwinding Input Mesh\r\n"));
	WWDEBUG_SAY(("Input poly count:                   %d\r\n",srmesh->getPolygonCount()));
	WWDEBUG_SAY(("Count after removing degenerates:   %d\r\n",set_numpolys));
	WWDEBUG_SAY(("\r\n"));
}

BptImpClass * BptBuilderClass::build_imp(void)
{

	BptImpBuilderClass impbuilder;
	BptImpClass * imp = impbuilder.Build_Bpt_Imp(Root,MatInfo);
	return imp;
}


/**************************************************************

	BptNodeClass Implementation

**************************************************************/



int BptNodeClass::Num_Polys(void) const
{ 
	int count = NumPolys; 
	if (Front) count += Front->Num_Polys();
	if (Back) count += Back->Num_Polys();
	return count;
}

int BptNodeClass::Num_Tris(void) const
{
	int count = 0; 
	for (unsigned int i=0; i<NumPolys; i++) {
		count += Polys[i].NumVerts - 2;
	}
	if (Front) count += Front->Num_Tris();
	if (Back) count += Back->Num_Tris();
	return count;
}

int BptNodeClass::Num_Nodes(void) const
{
	int count = 1;
	if (Front) count += Front->Num_Nodes();
	if (Back) count += Back->Num_Nodes();
	return count;
}

int BptNodeClass::Max_Depth(void) const
{
	int max_depth = 0;
	Max_Depth_Internal(0,max_depth);
	return max_depth;
}

void BptNodeClass::Max_Depth_Internal(int cur_depth,int & max_depth) const
{
	cur_depth++;
	if (cur_depth > max_depth) {
		max_depth = cur_depth;
	}

	if (Front) {
		Front->Max_Depth_Internal(cur_depth,max_depth);
	}

	if (Back) {
		Back->Max_Depth_Internal(cur_depth,max_depth);
	}
}

void BptNodeClass::Build(int numpolys,BptPolyClass * polys)
{
	/*
	** Select and assign the splitting plane
	*/
	SplitChoiceStruct sc = Select_Splitting_Plane(numpolys,polys);
	Plane = sc.Plane;
	
	/*
	** Split the polygons
	*/
	SplitArraysStruct arrays;
	Split_Polys(numpolys,polys,sc,&arrays);

	/*
	** Free the memory in use by polys!
	*/
	delete[] polys;

	/*
	** Store the "on" polys in this node
	*/
	NumPolys = arrays.OnCount;
	Polys = arrays.OnPolys;
	arrays.OnPolys = NULL;

	/*
	** Build a front tree if necessary.  Remember that the Build function
	** deletes the poly array.
	*/
	if (arrays.FrontCount) {
		assert(arrays.FrontPolys != NULL);

		Front = new BptNodeClass;
		Front->Build(arrays.FrontCount,arrays.FrontPolys);
		arrays.FrontPolys = NULL;

	}
	
	/*
	** Build a back tree if necessary. Remember that the build function
	** deletes the poly array.
	*/
	if (arrays.BackCount) {
		assert(arrays.BackPolys != NULL);

		Back = new BptNodeClass;
		Back->Build(arrays.BackCount,arrays.BackPolys);
		arrays.BackPolys = NULL;

	}
}


BptNodeClass::SplitChoiceStruct 
BptNodeClass::Compute_Plane_Score(int numpolys,BptPolyClass * polys,const PlaneClass & plane)
{
	/*
	** Suitability of a plane as a partition plane is based on the following factors:
	** - How many polygons will be split
	** - How many polygons end up on each side of the tree
	** - How many polygons end up on the plane (do NOT want many on the plane)
	** - How evenly the volume of space is divided.
	** The following weights will determine how much influence each factor
	** has on the final score.
	*/
	const float POLY_SPLIT_WEIGHT =					5.0f;
	const float POLY_COUNT_BALANCE_WEIGHT =		1.0f;
	const float POLYS_ON_PLANE_WEIGHT =				1.0f;
	const float TOTAL_WEIGHT = POLY_SPLIT_WEIGHT + POLY_COUNT_BALANCE_WEIGHT + POLYS_ON_PLANE_WEIGHT;

	const int MAX_POLYS_ON_PLANE =					10;
	const int MAX_SPLITS =								10;

	/*
	** Count up the cases
	*/
	SplitChoiceStruct sc;
	sc.Plane = plane;

	for (int i=0; i<numpolys; i++) {
		switch(polys[i].Which_Side(plane)) {

			case BPT_FRONT:	sc.FrontCount++;		break;
			case BPT_BACK:		sc.BackCount++;		break;
			case BPT_ON:		sc.OnCount++;			break;
			case BPT_BOTH:		sc.SplitCount++;		break;

		}
	}
	
	/*
	** Compute the score
	*/
	float max_splits = min(MAX_SPLITS,numpolys);
	float max_on = min(MAX_POLYS_ON_PLANE,numpolys);
	
	float split_score = (float)(max_splits - sc.SplitCount) / max_splits;
	if (split_score < 0.0f) split_score = 0.0f;

	float on_score = 1.0f - (float)sc.OnCount / max_on;
	if (on_score < 0.0f) on_score = 0.0f;

	float balance_score = 1.0f - (fabs(sc.FrontCount - sc.BackCount) / ((float)numpolys / 2.0f));

	sc.Score = 0.0f;
	sc.Score += split_score * POLY_SPLIT_WEIGHT;
	sc.Score += balance_score * POLY_COUNT_BALANCE_WEIGHT;
	sc.Score += on_score * POLYS_ON_PLANE_WEIGHT;
	sc.Score /= TOTAL_WEIGHT;

	/*
	**	Reject the plane if any of the following conditions are true:
	** - plane does not reduce the tree! (all polys in front or behind)
	** - more than MAX_POLYS_ON_PLANE polygons on the splitting plane
	*/
	if ((abs(sc.FrontCount-sc.BackCount) == numpolys) && (sc.OnCount == 0)) {
		sc.Score = 0.0f;
	}
	if (sc.OnCount > MAX_POLYS_ON_PLANE) {
		sc.Score *= 0.4f;
	}

	return sc;
}


BptNodeClass::SplitChoiceStruct
BptNodeClass::Select_Splitting_Plane(int numpolys,BptPolyClass * polys)
{
doitagain:
	const int NUM_VERTEX_TRYS = 30;
	const int NUM_POLY_TRYS = 30;

	SplitChoiceStruct best_plane_stats;
	SplitChoiceStruct considered_plane_stats;
	PlaneClass plane;

	/*
	** Try putting axis-aligned planes through some random vertices
	*/
	for (int vertex_trys = 0; vertex_trys < min(NUM_VERTEX_TRYS,3*numpolys); vertex_trys++) {
	
		int poly_index = rand() % numpolys;
		int vert_index = rand() % polys[poly_index].NumVerts;;
		Vector3 pos = polys[poly_index].Verts[vert_index].Position;

		/*
		** Try the x-axis
		*/
		plane.Set(Vector3(1,0,0),pos);
		considered_plane_stats = Compute_Plane_Score(numpolys,polys,plane);
		if (considered_plane_stats.Score > best_plane_stats.Score) {
			best_plane_stats = considered_plane_stats;
		}

		/*
		** Try the y-axis
		*/
		plane.Set(Vector3(0,1,0),pos);
		considered_plane_stats = Compute_Plane_Score(numpolys,polys,plane);
		if (considered_plane_stats.Score > best_plane_stats.Score) {
			best_plane_stats = considered_plane_stats;
		}
	}

	/*
	** Now try "autopartition" planes which come from the input polygons
	*/
	for (int poly_trys=0; poly_trys<min(NUM_POLY_TRYS,numpolys); poly_trys++) {
		
		int poly_index = rand() % numpolys;

		assert(
			(polys[poly_index][0].Which_Side(polys[poly_index].Plane) == BPT_ON) &&
			(polys[poly_index][1].Which_Side(polys[poly_index].Plane) == BPT_ON) &&
			(polys[poly_index][2].Which_Side(polys[poly_index].Plane) == BPT_ON)
		);
		
		considered_plane_stats = Compute_Plane_Score(numpolys,polys,polys[poly_index].Plane);
		
		if (considered_plane_stats.Score > best_plane_stats.Score) {
			best_plane_stats = considered_plane_stats;
		}
	}

	if (best_plane_stats.Score <= 0.0f) {
		_asm int 0x03;
		goto doitagain;
	}
	//assert(best_plane_stats.Score > 0.0f);

	return best_plane_stats;
}

void BptNodeClass::Split_Polys
(
	const int						num_polys,
	const BptPolyClass *			polys,
	const SplitChoiceStruct	&	sc,
	SplitArraysStruct	*			arrays
)
{
	// Note that this routine allocates three arrays of polygons, an array of polys in
	// front of the plane, an array of polys behind the plane, and an array of polys on the plane.
	// The caller is then responsible for keeping track of the memory this routine allocates.

	if (sc.FrontCount + sc.SplitCount > 0) {
		arrays->FrontPolys = new BptPolyClass[sc.FrontCount + sc.SplitCount];
	}

	if (sc.BackCount + sc.SplitCount > 0) {
		arrays->BackPolys = new BptPolyClass[sc.BackCount + sc.SplitCount];
	}

	if (sc.OnCount > 0) {
		arrays->OnPolys = new BptPolyClass[sc.OnCount];
	}

	arrays->FrontCount = 0;
	arrays->BackCount = 0;
	arrays->OnCount = 0;

	for (int i=0; i<num_polys; i++) {

		switch(polys[i].Which_Side(sc.Plane)) {

			case BPT_FRONT:
				arrays->FrontPolys[arrays->FrontCount++] = polys[i];
				break;

			case BPT_BACK:
				arrays->BackPolys[arrays->BackCount++] = polys[i];
				break;

			case BPT_ON:
				arrays->OnPolys[arrays->OnCount++] = polys[i];
				break;

			case BPT_BOTH:
				polys[i].Split(sc.Plane,arrays->FrontPolys[arrays->FrontCount],arrays->BackPolys[arrays->BackCount]);
				if (!arrays->FrontPolys[arrays->FrontCount].Is_Degenerate()) {
					arrays->FrontCount++;
				}
				if (!arrays->BackPolys[arrays->BackCount].Is_Degenerate()) {
					arrays->BackCount++;
				}
				break;
		}
	}

	// when we are all done, the counts should match. 
	assert(arrays->FrontCount <= sc.FrontCount + sc.SplitCount);
	assert(arrays->BackCount <= sc.BackCount + sc.SplitCount);
	assert(arrays->OnCount == sc.OnCount);

	/*
	** Check if we threw away all of the polys
	** (this can only happen if all of them were degenerate...)
	*/
	if ((arrays->OnCount == 0) && (arrays->OnPolys != NULL)) {
		delete[] arrays->OnPolys;
		arrays->OnPolys = NULL;
	}
	if ((arrays->BackCount == 0) && (arrays->BackPolys != NULL)) {
		delete[] arrays->BackPolys;
		arrays->BackPolys = NULL;
	}
	if ((arrays->FrontCount == 0) && (arrays->FrontPolys != NULL)) {
		delete[] arrays->FrontPolys;
		arrays->FrontPolys = NULL;
	}
}


void BptNodeClass::Submit_Polys(MeshBuilderClass & builder) const
{
	/*
	** Submit the back tree
	*/
	if (Back) {
		Back->Submit_Polys(builder);
	}
	
	/*
	** Triangulate each poly and submit it to the mesh builder
	*/
	MeshBuilderClass::FaceClass face;

	for (unsigned int fi=0; fi<NumPolys; fi++) {

		BptPolyClass * poly = &(Polys[fi]);

		for (int ti = 2;ti < poly->NumVerts; ti++) {
			
			face.MaterialIdx = poly->MaterialId;
			face.SmGroup = 1; //TODO: get the correct smooth group???
			face.Index = 0; //TODO: do we have an actual index?
			face.Attributes = 0;

			face.Verts[0].Position =	poly->Verts[0].Position;
			face.Verts[0].TexCoord =	poly->Verts[0].TexCoord;
			face.Verts[0].Color =		poly->Verts[0].Color;

			face.Verts[1].Position =	poly->Verts[ti-1].Position;
			face.Verts[1].TexCoord =	poly->Verts[ti-1].TexCoord;
			face.Verts[1].Color =		poly->Verts[ti-1].Color;
			
			face.Verts[2].Position =	poly->Verts[ti].Position;
			face.Verts[2].TexCoord =	poly->Verts[ti].TexCoord;
			face.Verts[2].Color =		poly->Verts[ti].Color;

			builder.Add_Face(face);
		}
	}

	/*
	** Submit the front tree
	*/
	if (Front) {
		Front->Submit_Polys(builder);
	}
}

void BptNodeClass::Compute_Bounding_Box(void)
{
	/*
	** make the children update their boxes first
	*/
	if (Front) {
		Front->Compute_Bounding_Box();
	}

	if (Back) { 
		Back->Compute_Bounding_Box();
	}

	Min.Set(10000.0f,10000.0f,10000.0f);
	Max.Set(-10000.0f,-10000.0f,-10000.0f);

	/*
	** Bound the polys in this node
	*/
	for (unsigned int poly_index = 0; poly_index < NumPolys; poly_index++) {
		for (int vert_index = 0; vert_index < Polys[poly_index].NumVerts; vert_index++) {

			Vector3 & point = Polys[poly_index].Verts[vert_index].Position;
			
			if (point.X - BPT_EPSILON < Min.X) Min.X = point.X - BPT_EPSILON;
			if (point.X + BPT_EPSILON > Max.X) Max.X = point.X + BPT_EPSILON;

			if (point.Y - BPT_EPSILON < Min.Y) Min.Y = point.Y - BPT_EPSILON;
			if (point.Y + BPT_EPSILON > Max.Y) Max.Y = point.Y + BPT_EPSILON;

			if (point.Z - BPT_EPSILON < Min.Z) Min.Z = point.Z - BPT_EPSILON;
			if (point.Z + BPT_EPSILON > Max.Z) Max.Z = point.Z + BPT_EPSILON;

		}
	}

	/*
	** bound the polys in the front child node
	*/
	if (Front) {
		if (Front->Min.X < Min.X) Min.X = Front->Min.X;
		if (Front->Max.X > Max.X) Max.X = Front->Max.X;

		if (Front->Min.Y < Min.Y) Min.Y = Front->Min.Y;
		if (Front->Max.Y > Max.Y) Max.Y = Front->Max.Y;
		
		if (Front->Min.Z < Min.Z) Min.Z = Front->Min.Z;
		if (Front->Max.Z > Max.Z) Max.Z = Front->Max.Z;
	}

	/*
	** bound the polys in the back child node
	*/
	if (Back) {
		if (Back->Min.X < Min.X) Min.X = Back->Min.X;
		if (Back->Max.X > Max.X) Max.X = Back->Max.X;

		if (Back->Min.Y < Min.Y) Min.Y = Back->Min.Y;
		if (Back->Max.Y > Max.Y) Max.Y = Back->Max.Y;
		
		if (Back->Min.Z < Min.Z) Min.Z = Back->Min.Z;
		if (Back->Max.Z > Max.Z) Max.Z = Back->Max.Z;
	}

	assert(Min.X != 10000.0f);
	assert(Min.Y != 10000.0f);
	assert(Min.Z != 10000.0f);
	assert(Max.X != -10000.0f);
	assert(Max.Y != -10000.0f);
	assert(Max.Z != -10000.0f);

}

int BptNodeClass::Assign_Array_Indices(int index)
{
	/*
	** This function is used to assign a sequential index to
	** each node in the tree.  The BptImp stores its nodes in 
	** an array so this index is used to determine which slot
	** in the array to put each node into.
	*/
	ArrayIndex = index;
	index++;

	if (Front) {
		index = Front->Assign_Array_Indices(index);
	}

	if (Back) {
		index = Back->Assign_Array_Indices(index);
	}

	return index;
}


void BptNodeClass::Submit_Nodes(BptImpBuilderClass & imp_builder) const
{
	imp_builder.Add_Bpt_Node(this);

	if (Front) {
		Front->Submit_Nodes(imp_builder);
	}

	if (Back) {
		Back->Submit_Nodes(imp_builder);
	}
}


/**************************************************************

	BptImpClass Implementation

**************************************************************/
inline bool BptImpNodeClass::Is_Visible(const CameraClass & camera)
{
	return !camera.Cull_Box(Vector3(MinX,MinY,MinZ),Vector3(MaxX,MaxY,MaxZ));
}

/**************************************************************

	BptImpClass Implementation

**************************************************************/
BptImpClass::BptImpClass(void) : 
	Mesh(NULL),
	NodeCount(0),
	Nodes(NULL),
	PolyIndexCount(0),
	PolyIndices(NULL),
	NormalCount(0),
	Normals(NULL),
	DistanceCount(0),
	Distances(NULL),
	ActivePolyCount(0),
	ActivePolyTable(NULL)

{
} 
	
BptImpClass::~BptImpClass(void)
{
	Free();
}


void BptImpClass::Free(void) 
{
	if (Mesh!=NULL) {
		Mesh->Release_Ref();
		Mesh = NULL;
	}

	NodeCount = 0;
	if (Nodes != NULL) {
		delete[] Nodes;
		Nodes = NULL;
	}

	PolyIndexCount = 0;
	if (PolyIndices != NULL) {
		delete[] PolyIndices;
		PolyIndices = NULL;
	}

	NormalCount = 0;
	if (Normals != NULL) {
		delete[] Normals;
		Normals = NULL;
	}

	DistanceCount = 0;
	if (Distances != NULL) {
		delete[] Distances;
		Distances = NULL;
	}
}

void BptImpClass::Set_Mesh(MeshClass * mesh) 
{ 
	if (Mesh) Mesh->Release_Ref(); 
	Mesh = mesh; 
	if (Mesh) Mesh->Add_Ref(); 
}

void BptImpClass::Set_Node_Array(int numnodes, BptImpNodeClass * nodes)
{ 
	if (Nodes) delete[] Nodes; 
	Nodes = nodes; 
	NodeCount = numnodes; 
}

void BptImpClass::Set_Normal_Array(const UniqueArrayClass<Vector3> & normals)
{
	if (normals.Count() != NormalCount) {
		if (Normals) delete[] Normals;
		Normals = new Vector3[normals.Count()];
	}
	
	NormalCount = normals.Count();

	for (int i=0; i<NormalCount; i++) {
		Normals[i] = normals.Get(i);
	}
}

void BptImpClass::Set_Distance_Array(const UniqueArrayClass<float> & distances)
{
	if (distances.Count() != DistanceCount) {
		if (Distances) delete[] Distances;
		Distances = new float[distances.Count()];
	}
	DistanceCount = distances.Count();

	for (int i=0; i<DistanceCount; i++) {
		Distances[i] = distances.Get(i);
	}
}

void BptImpClass::Set_Poly_Index_Array(int numremaps,int * polyremaps)
{
	if (PolyIndices) delete[] PolyIndices;
	PolyIndices = polyremaps;
	PolyIndexCount = numremaps;
}

void BptImpClass::Build_Apt(const CameraClass & camera)
{
	WWDEBUG_PROFILE_START("BPT::Build_Apt");
	Begin_Apt();	
	Build_Apt_Recursive(&(Nodes[0]),camera);
	End_Apt();
	WWDEBUG_PROFILE_STOP("BPT::Build_Apt");
}

void BptImpClass::Begin_Apt(void)
{
	ActivePolyCount = 0;
	ActivePolyTable = Mesh->Get_Surrender_Mesh()->getActivePolygonTable();
}

void BptImpClass::Add_Polys_To_Apt(BptImpNodeClass * node)
{
	assert(node != NULL);
	
#if 0
	memcpy(&(ActivePolyTable[ActivePolyCount]),&(PolyIndices[node->FirstPoly]),node->PolyCount);
	ActivePolyCount += node->PolyCount;
#else
	for (int poly_index=0; poly_index<node->PolyCount; poly_index++) {
		ActivePolyTable[ActivePolyCount++] = PolyIndices[node->FirstPoly + poly_index];
	}
#endif
}

void BptImpClass::End_Apt(void)
{
	Mesh->Get_Surrender_Mesh()->setActivePolygonCount(ActivePolyCount);
//	WWDEBUG_SAY(("Active Polys: %d\r\n",ActivePolyCount));
}

void BptImpClass::Build_Apt_Recursive(BptImpNodeClass * node,const CameraClass & cam)
{
	/*
	** Check the camera's frustum against this node's
	** bounding volume
	*/
	if (!node->Is_Visible(cam)) {
		Verify_Node_Bounding_Volume(node,Vector3(node->MinX,node->MinY,node->MinZ),Vector3(node->MaxX,node->MaxY,node->MaxZ));
		return;
	}

	/*
	** Ok part of this tree may be visible, compute
	** which side of the splitting plane the camera is on
	*/
	float dist = Vector3::Dot_Product(Normals[node->NormalIndex],cam.Get_Position());
	if (dist > Distances[node->DistanceIndex]) {
		
		/*
		** FRONT: 
		** We are in front of the plane so proceed in the following order:
		** Visit the 'Back' node
		** Add our polys to the APT
		** Visit the 'Front' node
		*/
		if (node->Back) Build_Apt_Recursive(node->Back,cam);
		Add_Polys_To_Apt(node);
		if (node->Front) Build_Apt_Recursive(node->Front,cam);

	} else {
		/*
		** BACK: 
		** We are in back of the plane so proceed in the following order:
		** Visit the 'Front' node
		** Add our polys to the APT
		** Visit the 'Back' node
		*/
		if (node->Front) Build_Apt_Recursive(node->Front,cam);
		Add_Polys_To_Apt(node);
		if (node->Back) Build_Apt_Recursive(node->Back,cam);
	}
}



bool BptImpClass::Cast_Ray(PhysRayCollisionTestClass & raytest) const
{
	return Cast_Ray_Recursive(&(Nodes[0]),raytest);
}

bool BptImpClass::Cast_AABox(PhysAABoxCollisionTestClass & boxtest) const
{
	/*
	** Test the sweeping AABox against the BPT recursively!
	*/
	WWDEBUG_PROFILE_START("BPT::Cast_AABox");
	bool res = Cast_AABox_Recursive(&(Nodes[0]),boxtest);
	WWDEBUG_PROFILE_STOP("BPT::Cast_AABox");
	return res;
}

bool BptImpClass::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest) const
{
	return Cast_OBBox_Recursive(&(Nodes[0]),boxtest);
}

bool BptImpClass::Cast_Ray_Recursive
(
	BptImpNodeClass *				node,
	PhysRayCollisionTestClass	&	raytest
) const
{
	return false;
}

bool BptImpClass::Cast_AABox_Recursive
(
	BptImpNodeClass *				node,
	PhysAABoxCollisionTestClass	&	boxtest
) const
{
	/*
	** First, check the bounding box of the move against the bounding box
	** of this tree, if they don't intersect, bail out
	*/
	if ((boxtest.SweepMin.X > node->MaxX) || (boxtest.SweepMax.X < node->MinX)) {
		return false;
	}

	if ((boxtest.SweepMin.Y > node->MaxY) || (boxtest.SweepMax.Y < node->MinY)) {
		return false;
	}

	if ((boxtest.SweepMin.Z > node->MaxZ) || (boxtest.SweepMax.Z < node->MinZ)) {
		return false;
	}

	if (node->Front) {
		Cast_AABox_Recursive(node->Front,boxtest);
	}
	Cast_AABox_To_Polys(node,boxtest);
	if (node->Back) {
		Cast_AABox_Recursive(node->Back,boxtest);
	}

	return false;
}


bool BptImpClass::Cast_OBBox_Recursive
(
	BptImpNodeClass *					node,
	PhysOBBoxCollisionTestClass &	boxtest
) const
{
	return false;
}

bool BptImpClass::Cast_AABox_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const
{
	/*
	** Simply loop through the polys in this node, checking each for collision
	*/
	TriClass tri;

	srMeshModel * srmesh = Mesh->Get_Surrender_Mesh();
	srVector3 * loc = srmesh->getVertexLoc();
	srVector4 * norms = srmesh->getPolyEq();
	srVector3i * polyverts = srmesh->getPolyVertex();

	int polyhit = -1;
	for (int poly_counter=0; poly_counter<node->PolyCount; poly_counter++) {

		int poly_index = PolyIndices[node->FirstPoly + poly_counter];

		tri.V[0] = As_Vector3(&(loc[ polyverts[poly_index][0] ]));
		tri.V[1] = As_Vector3(&(loc[ polyverts[poly_index][1] ]));
		tri.V[2] = As_Vector3(&(loc[ polyverts[poly_index][2] ]));
		tri.N = (Vector3 *)&(norms[poly_index]);

		if ( boxtest.Box.Cast_To_Tri(boxtest.Move,tri,boxtest.Result) ) {;
			polyhit = poly_index;
		}

		if (boxtest.Result->StartBad) {
			return true;
		}
	}
	if (polyhit != -1) {
		return true;
	}
	return false;
}

void BptImpClass::Verify_Node_Bounding_Volume(BptImpNodeClass * node,const Vector3 & min,const Vector3 & max) const
{
			
	srMeshModel * srmesh = Mesh->Get_Surrender_Mesh();
	srVector3 * loc = srmesh->getVertexLoc();
	srVector3i * polyverts = srmesh->getPolyVertex();

	Vector3 * v;

	for (int remap_index = node->FirstPoly; remap_index < node->FirstPoly + node->PolyCount; remap_index++) {

		int poly_index = PolyIndices[remap_index];

		v = As_Vector3(&loc[ polyverts[poly_index][0] ]);

		assert(v->X >= min.X);
		assert(v->Y >= min.Y);
		assert(v->Z >= min.Z);
		
		assert(v->X <= max.X);
		assert(v->Y <= max.Y);
		assert(v->Z <= max.Z);

		v = As_Vector3(&loc[ polyverts[poly_index][1] ]);

		assert(v->X >= min.X);
		assert(v->Y >= min.Y);
		assert(v->Z >= min.Z);
		
		assert(v->X <= max.X);
		assert(v->Y <= max.Y);
		assert(v->Z <= max.Z);

		v = As_Vector3(&loc[ polyverts[poly_index][2] ]);

		assert(v->X >= min.X);
		assert(v->Y >= min.Y);
		assert(v->Z >= min.Z);
		
		assert(v->X <= max.X);
		assert(v->Y <= max.Y);
		assert(v->Z <= max.Z);
	}

	if (node->Back) {
		Verify_Node_Bounding_Volume(node->Back,min,max);
	}
	if (node->Front) {
		Verify_Node_Bounding_Volume(node->Front,min,max);
	}

}

/**************************************************************

	BptImpBuilderClass Implementation

**************************************************************/

BptImpBuilderClass::BptImpBuilderClass(void) :
	MeshBuilder(true,10000,5000),						// TODO: should move this to Build_Bpt_Imp so it is dynamically initialzed
	UniqueNormals(NULL),
	UniqueDistances(NULL),
	NodeCount(0),
	Nodes(NULL),
	TriCount(0),
	CurTriIndex(0),
	TriIndexArray(NULL)
{
}

BptImpBuilderClass::~BptImpBuilderClass(void)
{
	Free();
}

void BptImpBuilderClass::Free(void)
{
	if (UniqueNormals != NULL) {
		delete UniqueNormals;
		UniqueNormals = NULL;
	}

	if (UniqueDistances != NULL) {
		delete UniqueDistances;
		UniqueDistances = NULL;
	}

	NodeCount = 0;
	if (Nodes != NULL) {
		delete[] Nodes;
		Nodes = NULL;
	}

	TriCount = 0;
	CurTriIndex = 0;
	if (TriIndexArray != NULL) {
		delete[] TriIndexArray;
		TriIndexArray = NULL;
	}
}

BptImpClass * BptImpBuilderClass::Build_Bpt_Imp(BptNodeClass * root,MaterialInfoClass * matinfo)
{
	Free();
	WWDEBUG_SAY(("Building the Run-Time Bpt\r\n"));

	TriCount = root->Num_Tris();
	
	/*
	** Allocate the UniqueNormals and UniqueDistances objects
	*/
	int size = TriCount;
	int growth = TriCount / 3;
	if (growth < 5) growth = 5;

	UniqueNormals = new UniqueArrayClass<Vector3>(size,growth,&NormalHasher);
	UniqueDistances = new UniqueArrayClass<float>(size,growth,&DistanceHasher);

	/*
	** Set up the mesh builder
	*/
	MeshBuilder.Reset(true,TriCount,growth);
	MeshBuilder.Disable_All_Vertex_Channels();
	MeshBuilder.Enable_Vertex_Channel(MeshBuilderClass::VERTEX_CHANNEL_NORMAL);
//	MeshBuilder.Enable_Vertex_Channel(MeshBuilderClass::VERTEX_CHANNEL_COLOR);
	MeshBuilder.Enable_Vertex_Channel(MeshBuilderClass::VERTEX_CHANNEL_TEXCOORD);

	/*
	** Allocate the array of triangle indices, set
	** CurTriIndex to point to the top.
	*/
	TriIndexArray = new int[TriCount];
	CurTriIndex = 0;
	
	/*
	** Allocate the array of nodes
	*/
	NodeCount = root->Num_Nodes();
	Nodes = new BptImpNodeClass[NodeCount];

	/*
	** Compute the hierarchical bounding boxes
	*/
	WWDEBUG_SAY(("Computing Hierarchical Bounding Volumes.\r\n"));
	root->Compute_Bounding_Box();
	WWDEBUG_SAY(("Hierarchical Bounding Volumes Done.\r\n"));

	/*
	** Have the tree "unwind" itself into our arrays
	*/
	root->Assign_Array_Indices(0);
	root->Submit_Nodes(*this);

	/*
	** Process the mesh
	*/
	MeshBuilder.Build_Mesh(true);

	/*
	** Remap all of the polygon indices (account for the 
	** mesh builder re-ording polygons)
	*/
	Remap_Triangle_Indices();

	/*
	** Create the BptImpClass
	*/
	MeshClass * mesh = new MeshClass();
	mesh->Init(MeshBuilder,matinfo,"BspMesh","");

	BptImpClass * bptimp = new BptImpClass();

	bptimp->Set_Mesh(mesh);
	bptimp->Set_Node_Array(NodeCount,Nodes);
	bptimp->Set_Poly_Index_Array(TriCount,TriIndexArray);
	bptimp->Set_Normal_Array(*UniqueNormals);
	bptimp->Set_Distance_Array(*UniqueDistances);

	// the "imp" assumed ownership of this stuff
	mesh->Release_Ref();
	mesh = NULL;
	Nodes = NULL;
	TriIndexArray = NULL;

	Free();

	WWDEBUG_SAY(("Run-Time Bpt Done\r\n"));
	WWDEBUG_SAY(("\r\n"));

	return bptimp;
}


void BptImpBuilderClass::Add_Bpt_Node(const BptNodeClass * node)
{
	BptImpNodeClass newnode;

	/*
	** initialize the front pointer
	*/
	if (node->Front) {
		assert(node->Front->ArrayIndex >= 0);
		assert(node->Front->ArrayIndex < NodeCount);
		newnode.Front = &(Nodes[node->Front->ArrayIndex]);
	} else {
		newnode.Front = NULL; //BptImpNodeClass::NULL_NODE_INDEX;
	}

	/*
	** initialize the back pointer
	*/
	if (node->Back) {
		assert(node->Back->ArrayIndex >= 0);
		assert(node->Back->ArrayIndex < NodeCount);
		newnode.Back = &(Nodes[node->Back->ArrayIndex]);
	} else {
		newnode.Back = NULL; //BptImpNodeClass::NULL_NODE_INDEX;
	}

	/*
	** Triangulate each poly and submit it to the mesh builder
	** install the Triangle Remap Indices as we go
	*/
	newnode.FirstPoly = CurTriIndex;
	newnode.PolyCount = 0;

	MeshBuilderClass::FaceClass face;

	for (unsigned int fi=0; fi<node->NumPolys; fi++) {

		BptPolyClass * poly = &(node->Polys[fi]);

		for (int ti = 2;ti < poly->NumVerts; ti++) {
			
			face.MaterialIdx = poly->MaterialId;
			face.SmGroup = 1; //TODO: get the correct smooth group???
			face.Index = 0; //TODO: do we have an actual index?
			face.Attributes = 0;

			face.Verts[0].Position =	poly->Verts[0].Position;
			face.Verts[0].TexCoord =	poly->Verts[0].TexCoord;
			face.Verts[0].Color =		poly->Verts[0].Color;

			face.Verts[1].Position =	poly->Verts[ti-1].Position;
			face.Verts[1].TexCoord =	poly->Verts[ti-1].TexCoord;
			face.Verts[1].Color =		poly->Verts[ti-1].Color;
			
			face.Verts[2].Position =	poly->Verts[ti].Position;
			face.Verts[2].TexCoord =	poly->Verts[ti].TexCoord;
			face.Verts[2].Color =		poly->Verts[ti].Color;

			TriIndexArray[CurTriIndex] = MeshBuilder.Add_Face(face);
			CurTriIndex++;
			newnode.PolyCount++;

			/*
			** just verifying that all verts are inside the bounding box
			*/
			assert(face.Verts[0].Position.X >= node->Min.X);
			assert(face.Verts[0].Position.Y >= node->Min.Y);
			assert(face.Verts[0].Position.Z >= node->Min.Z);

			assert(face.Verts[1].Position.X >= node->Min.X);
			assert(face.Verts[1].Position.Y >= node->Min.Y);
			assert(face.Verts[1].Position.Z >= node->Min.Z);

			assert(face.Verts[2].Position.X >= node->Min.X);
			assert(face.Verts[2].Position.Y >= node->Min.Y);
			assert(face.Verts[2].Position.Z >= node->Min.Z);

			assert(face.Verts[0].Position.X <= node->Max.X);
			assert(face.Verts[0].Position.Y <= node->Max.Y);
			assert(face.Verts[0].Position.Z <= node->Max.Z);

			assert(face.Verts[1].Position.X <= node->Max.X);
			assert(face.Verts[1].Position.Y <= node->Max.Y);
			assert(face.Verts[1].Position.Z <= node->Max.Z);

			assert(face.Verts[2].Position.X <= node->Max.X);
			assert(face.Verts[2].Position.Y <= node->Max.Y);
			assert(face.Verts[2].Position.Z <= node->Max.Z);
	
		}
	}
	
	/*
	** Bounding box
	*/
	newnode.MinX = node->Min.X;
	newnode.MinY = node->Min.Y;
	newnode.MinZ = node->Min.Z;
	newnode.MaxX = node->Max.X;
	newnode.MaxY = node->Max.Y;
	newnode.MaxZ = node->Max.Z;

	assert(newnode.MinX <= newnode.MaxX);
	assert(newnode.MinY <= newnode.MaxY);
	assert(newnode.MinZ <= newnode.MaxZ);

	/*
	** Plane Equation
	*/
	assert(UniqueNormals);
	assert(UniqueDistances);
	newnode.NormalIndex = UniqueNormals->Add(node->Plane.N);
	newnode.DistanceIndex = UniqueDistances->Add(node->Plane.D);

	/*
	** install the node!
	*/
	Nodes[node->ArrayIndex] = newnode;
}

void BptImpBuilderClass::Remap_Triangle_Indices(void)
{
	int pi;
	int * index_remap = new int[MeshBuilder.Get_Face_Count()];

	assert(MeshBuilder.Get_Face_Count() == TriCount);
	for (pi=0; pi < MeshBuilder.Get_Face_Count(); pi++) {
		const MeshBuilderClass::FaceClass & face = MeshBuilder.Get_Face(pi);
		assert(face.AddIndex >= 0);
		assert(face.AddIndex < TriCount);
		index_remap[ face.AddIndex ] = pi;
	}

	for (pi=0; pi < TriCount; pi++) {
		TriIndexArray[pi] = index_remap[TriIndexArray[pi]];
	}

	delete[] index_remap;

}

#endif // PORT140
#endif // OBSOLETE!
