///////////////////////////////////////////////////////////////////////////////
/// @file dePolyABT.cpp
///
/// @brief Poly ABT
///
/// @author Assassin 
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Mar 2003
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#if 1
//#	include <windows.h>
#	include "deConsole.hpp"
#else
static inline void thunk_ConsoleOut(char *, ...) {}
#	define deConsoleOut thunk_ConsoleOut
#endif

#include "dePolyABT.hpp"
#include "deScene_priv.hpp"
#include "deDriver.hpp"
#include "deCamera.hpp"
#include "deRender.hpp"
#include "deFunctors.hpp"
#include "deHash.hpp"
#include "deFunctors.hpp"
#include "deMesh.hpp"
//#include "deBrush.hpp"


extern IdeSceneLight* s_TestLight;
extern IdeSceneLight* s_TestLight2;
extern IdeSceneLight* s_TestLightBright;

const deBoolean AllowOverlap = deTRUE;


//********************************
// public methods
//********************************

dePolyABT::dePolyABT()
: m_Surfaces(1, NULL), m_WorldOffset(deZeroVec)
{
	m_RootPartition = NULL;
	m_MaxLeafTris = 50;
	m_MaxVBTris = 5000;
	m_CurrSurfaceIndex = 0;
	m_OriginalVertNum = 0;
	// default to equal weights
	m_SplitWeights[0] = 0.25f;
	m_SplitWeights[1] = 0.25f;
	m_SplitWeights[2] = 0.25f;
	m_SplitWeights[3] = 0.25f;
	m_TotalBBox.Min.x = 1000000;
	m_TotalBBox.Min.y = 1000000;
	m_TotalBBox.Min.z = 1000000;
	m_TotalBBox.Max.x = -1000000;
	m_TotalBBox.Max.y = -1000000;
	m_TotalBBox.Max.z = -1000000;
}

dePolyABT::~dePolyABT()
{
	DestroyABT();
}

void dePolyABT::DestroyABT()
{
	// recursive destruction of root partition
	DestroyABTPartition(m_RootPartition);
	{
		deReleasePtrsFunctor <IdeSceneObject> functor;
		deIter_App(m_SceneObjects.begin(), m_SceneObjects.end(), functor);
		m_SceneObjects.Resize(0);
	}
	{
		deReleasePtrsFunctor <IdeSurface> functor;
		deIter_App(m_Surfaces.begin(), m_Surfaces.end(), functor);
		m_Surfaces.Resize(0);
	}
	m_Vertices.Resize(0);
	m_AllFaces.Resize(0);
}

deBoolean dePolyABT::SetMaxLeafTris(long MaxTris)
{
	// record the maximum number of triangles an ABT Partition can contain before it needs splitting
	if (MaxTris <= 0)
		DEBUGRETURN(deFALSE, "SetMaxLeafTris: Invalid number for MaxTris");
	m_MaxLeafTris = MaxTris;
	return deTRUE;
}

deBoolean dePolyABT::SetMaxVBufferTris(long MaxTris)
{
	// record the maximum number of triangles a vertex buffer created from an ABT can contain
	if (MaxTris <= 0)
		DEBUGRETURN(deFALSE, "SetMaxVBufferTris: Invalid number for MaxTris");
	m_MaxVBTris = MaxTris;
	return deTRUE;
}

deBoolean dePolyABT::SetSplitWeights(deFloat Axis, deFloat Volume, deFloat FaceBalance, deFloat NumSplit)
{
	// record the weights applied in our split-plane picking heuristic

	// verify that all the weights are within valid range
	if (Axis < 0 || Axis > 1)
		DEBUGRETURN(deFALSE, "SetSplitWeights: Invalid Axis weight (valid range: 0-1)");
	if (Volume < 0 || Volume > 1)
		DEBUGRETURN(deFALSE, "SetSplitWeights: Invalid Volume weight (valid range: 0-1)");
	if (FaceBalance < 0 || FaceBalance > 1)
		DEBUGRETURN(deFALSE, "SetSplitWeights: Invalid FaceBalance weight (valid range: 0-1)");
	if (NumSplit < 0 || NumSplit > 1)
		DEBUGRETURN(deFALSE, "SetSplitWeights: Invalid NumSplit weight (valid range: 0-1)");

	// sum is used to normalize the weights
	deFloat sum = Axis + Volume + FaceBalance + NumSplit;
	sum = 1.0f/sum;
	// record the weights into our internal members
	m_SplitWeights[0] = sum*Axis;
	m_SplitWeights[1] = sum*Volume;
	m_SplitWeights[2] = sum*FaceBalance;
	m_SplitWeights[3] = sum*NumSplit;

	return deTRUE;
}

deBoolean dePolyABT::SetCurrentSurface(IdeSurface* CurrSurface)
{
	size_t index;
	// see if we already have this surface added
	for (index = 0; index < m_Surfaces.size(); ++index)
	{
		if (m_Surfaces[index] == CurrSurface)
		{
			// we already have it, record its index
			m_CurrSurfaceIndex = index;
			return deTRUE;
		}
	}
	// we didn't have it already, add it and record its index
	if (!m_Surfaces.PushBack(CurrSurface))
		return deFALSE;
	m_CurrSurfaceIndex = m_Surfaces.size() - 1;
	CurrSurface->Claim();
	
	return deTRUE;
}

void dePolyABT::GetVisibleAABB(deAABB& bbox)
{
	bbox = m_TotalBBox;
}

deBoolean dePolyABT::AddVertices(ABTVertex* VertexArray, long NumVerts, long & BaseVertexIndex, const deTransformInfo & worldpos)
{
	// store the offset to the end of the current array
	BaseVertexIndex = m_Vertices.size();
	if (NumVerts < 0 || NumVerts > 0x0fffffff)
		DEBUGRETURN(deFALSE, "AddVertices: Invalid NumVerts value");
	if (BaseVertexIndex + NumVerts > 0x0fffffff)
		DEBUGRETURN(deFALSE, "AddVertices: Too many vertices");

	ABTVertex* InternalArray;
	int k;
	// resize to accomodate the new vertices
	if (!m_Vertices.ResizeQuick(BaseVertexIndex + NumVerts, deFALSE))
		return deFALSE;
	// get a raw pointer to the resized vertex list
	InternalArray = m_Vertices.GetCArray();
	if (!InternalArray)
		return deFALSE;
	// offset to the newly added area
	InternalArray += BaseVertexIndex;
	// copy in all the new vertices
	memcpy(InternalArray, VertexArray, NumVerts*sizeof(ABTVertex));
	if (BaseVertexIndex == 0)
	{
		// only if this is the first vertex ever
		((deTransform&)worldpos).Transform(VertexArray[0].Position, InternalArray[0].Position);
		m_TotalBBox.Min = InternalArray[0].Position;
		m_TotalBBox.Max = InternalArray[0].Position;
	}
	// expand the top-level BBox to include all points
	for (k = 0; k < NumVerts; ++k)
	{
		((deTransform&)worldpos).Transform(VertexArray[k].Position, InternalArray[k].Position);
		deGeometry::AABBAddPoint(m_TotalBBox, InternalArray[k].Position);
	}

	m_OriginalVertNum += NumVerts;

	return deTRUE;
}

deBoolean dePolyABT::AddTriangleIndices(long* IndexArray, long BaseVertexIndex, long NumIndices)
{
	if (NumIndices < 0 || NumIndices > 0x0fffffff)
		DEBUGRETURN(deFALSE, "AddTriangleIndices: Invalid NumVerts value");
	
	ABTFace* FaceArray;
	int ArrayIndex;
	long len;
	long NumFaces;
	
	// assert but don't fail if not a multiple of 3
	DE_ASSERT(NumIndices%3 == 0);
	NumFaces = NumIndices/3;
	// if no faces to add, dont do anything
	if (NumFaces == 0)
		return deTRUE;

	// get the number of faces already stored
	len = m_AllFaces.size();
	if (len + NumFaces > 0x0fffffff)
		DEBUGRETURN(deFALSE, "AddTriangleIndices: Too many faces");
	
	// resize the object's face list to accomodate the added triangles
	if (!m_AllFaces.ResizeQuick(len + NumFaces, deFALSE))
		return deFALSE;
	// Get a raw pointer to the resized face list
	FaceArray = m_AllFaces.GetCArray();
	if (!FaceArray)
		return deFALSE;
	// offset to the newly added area
	FaceArray += len;
	// loop through all the indices, adding BaseVertexIndex to each one
	for (ArrayIndex = 0; ArrayIndex < NumFaces; ++ArrayIndex)
	{
		ABTFace* pFace = &(FaceArray[ArrayIndex]);
		pFace->Indices[0] = IndexArray[3*ArrayIndex  ] + BaseVertexIndex;
		pFace->Indices[1] = IndexArray[3*ArrayIndex+1] + BaseVertexIndex;
		pFace->Indices[2] = IndexArray[3*ArrayIndex+2] + BaseVertexIndex;
		pFace->SurfaceID = m_CurrSurfaceIndex;
	}
	
	return deTRUE;
}

deBoolean dePolyABT::AddIndexedVBuffer(IdeVertexBuffer* VBuffer, long & BaseVertIndex, const deTransformInfo & worldpos)
{
	if (!VBuffer)
		return deFALSE;

	long NumVerts, NumIndices, k;
	deVertex* PositionBuffer;
	deVertex* NormalBuffer;
	deTexCoord* TexCoordBuffers[4];
	deARGB* DiffuseBuffer;
	ABTVertex* ABTVertBuffer;
	long * ABTIndexBuffer;

	VBuffer->Lock();

	ABTVertBuffer = NULL;
	ABTIndexBuffer = NULL;
	NumVerts = VBuffer->GetVertexCount();
	NumIndices = VBuffer->GetIndexCount();

	deConsoleOut("dePolyABT::AddIndexedVBuffer - Verts = %d, Indices = %d\n", NumVerts, NumIndices);
	
	if (NumVerts != 0)
	{
		// grab all data streams from vertex buffer
		PositionBuffer = (deVertex*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
		NormalBuffer = (deVertex*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_NORMAL);
		DiffuseBuffer = (deARGB*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
		TexCoordBuffers[0] = (deTexCoord*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);
		TexCoordBuffers[1] = (deTexCoord*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS1);
		TexCoordBuffers[2] = (deTexCoord*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS2);
		TexCoordBuffers[3] = (deTexCoord*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS3);

		if (!PositionBuffer)
			goto add_fail;

		ABTVertBuffer = new ABTVertex[NumVerts];
		if (!ABTVertBuffer)
			goto add_fail;
		for (k = 0; k < NumVerts; ++k)
		{
			// set position
			ABTVertBuffer[k].Position = deMake_Vec3d(PositionBuffer[k]);

			// add normals
			if (NormalBuffer)
				ABTVertBuffer[k].Normal = NormalBuffer[k];
			else
				ABTVertBuffer[k].Normal = deMake_Vertex(1,0,0);

			// add diffuse color
			if (DiffuseBuffer)
				ABTVertBuffer[k].Diffuse = DiffuseBuffer[k];
			else
				ABTVertBuffer[k].Diffuse = 0xffffffff;

			// only add texture coords that exist
			if (TexCoordBuffers[0])
				ABTVertBuffer[k].TexCoords[0] = TexCoordBuffers[0][k];
			else
				ABTVertBuffer[k].TexCoords[0] = deMake_TexCoord(0,0);
			if (TexCoordBuffers[1])
				ABTVertBuffer[k].TexCoords[1] = TexCoordBuffers[1][k];
			else
				ABTVertBuffer[k].TexCoords[1] = deMake_TexCoord(0,0);
			if (TexCoordBuffers[2])
				ABTVertBuffer[k].TexCoords[2] = TexCoordBuffers[2][k];
			else
				ABTVertBuffer[k].TexCoords[2] = deMake_TexCoord(0,0);
			if (TexCoordBuffers[3])
				ABTVertBuffer[k].TexCoords[3] = TexCoordBuffers[3][k];
			else
				ABTVertBuffer[k].TexCoords[3] = deMake_TexCoord(0,0);
		}
		// add all the vertices to the ABT
		if (!AddVertices(ABTVertBuffer, NumVerts, BaseVertIndex, worldpos))
			goto add_fail;
	}
	else
	{
		//BaseVertIndex = 0; // now it's a parameter
	}

	if (NumIndices != 0)
	{
		ABTIndexBuffer = new long[NumIndices];
		if (!ABTIndexBuffer)
			goto add_fail;

		// grab the appropriate index buffer from the
		if (VBuffer->BufferExists(IdeVertexBuffer::BUFFER_INDICES16))
		{
			u16* Indices = (u16*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_INDICES16);
			if (!Indices)
				goto add_fail;
			for (k = 0; k < NumIndices; ++k)
			{
				ABTIndexBuffer[k] = Indices[k];
			}
		}
		else if (VBuffer->BufferExists(IdeVertexBuffer::BUFFER_INDICES32))
		{
			long* Indices = (long*)VBuffer->GetBuffer(IdeVertexBuffer::BUFFER_INDICES32);
			if (!Indices)
				goto add_fail;
			for (k = 0; k < NumIndices; ++k)
			{
				ABTIndexBuffer[k] = Indices[k];
			}
		}
		else
		{
			VBuffer->Unlock();
			DEBUGRETURN(deFALSE, "AddIndexedVBuffer: VBuffer doesn't have an index stream");
		}

		if (!AddTriangleIndices(ABTIndexBuffer, BaseVertIndex, NumIndices))
			goto add_fail;

	}

	delete[] ABTVertBuffer;
	delete[] ABTIndexBuffer;
	VBuffer->Unlock();
	return deTRUE;

add_fail:
	delete[] ABTVertBuffer;
	delete[] ABTIndexBuffer;
	VBuffer->Unlock();
	return deFALSE;
}

deBoolean dePolyABT::AddMeshGeometry(IdeMesh* pMesh, const deTransformInfo & worldpos)
{
//#pragma todo (AddMeshGeometry - finish me)

	IdeVertexBuffer* pVB;
	IdeSurface* pSurface;
	long k, NumVBs;
	long BaseVert;

	NumVBs = pMesh->GetNumSubMeshes();
	if (NumVBs == 0)
		return deTRUE;

	pVB = pMesh->GetMainVertexBuffer();
	if (!AddIndexedVBuffer(pVB, BaseVert, worldpos)) // grabs BaseVert
		return deFALSE;

	deConsoleOut("dePolyABT::AddMeshGeometry - BaseVert = %d\n", BaseVert);

	for (k = 0; k < NumVBs; ++k)
	{
		pSurface = NULL;
		pSurface = pMesh->GetSubMeshSurface(k);
		SetCurrentSurface(pSurface);
		pVB = pMesh->GetSubMeshIndexBuffer(k);
		if (!AddIndexedVBuffer(pVB, BaseVert, worldpos)) // uses BaseVert
			return deFALSE;
	}
	
	return deTRUE;
}

deBoolean dePolyABT::AddSceneObject(IdeSceneObject* pObject)
{
	if (pObject == NULL)
		DEBUGRETURN(deFALSE, "AddSceneObject: pObject was NULL");
	
	// add an element to the global object list
	deTArray<IdeSceneObject*>::iterator it = m_SceneObjects.IncrementSizeQuick(deFALSE);
	if (it == m_SceneObjects.end())
		return deFALSE;
	// store the object
	*it = pObject;
	// add a reference to the object
	pObject->Claim();
	
	return deTRUE;
}

deBoolean dePolyABT::CompileABT()
{
	// destroy the tree and recreate it from the whole dataset
	DestroyABTPartition(m_RootPartition);
	m_RootPartition = CreateABTPartition(NULL, 0);
	m_RootPartition->BBox = m_TotalBBox;
	m_Vertices.ResizeQuick(m_OriginalVertNum, deFALSE);
	m_RootPartition->Faces = m_AllFaces; // might take a while to copy this

	return ProcessABTPartition(m_RootPartition);
}

deBoolean dePolyABT::RenderVisibleGeometry(IdeSceneGraph::deSceneTraversal* Params)
{
	return RenderVisibleABTPartitions(Params, m_RootPartition, deFALSE);
}

//********************************
// internal (private) methods
//********************************

dePolyABT::ABTPartition* dePolyABT::CreateABTPartition(ABTPartition * Parent, int ChildNum)
{
	ABTPartition* NewPartition;
	NewPartition = new ABTPartition;
	if (!NewPartition)
		DEBUGRETURN(NULL, "CreateABTPartition: error creating Partition");

	NewPartition->Children[0] = NULL; 
	NewPartition->Children[1] = NULL; 
	NewPartition->Split.Axis = -1; 
	NewPartition->Split.Percent = 0;
	NewPartition->Parent = Parent;
	NewPartition->BBox.Min = deZeroVec;
	NewPartition->BBox.Max = deZeroVec;
	NewPartition->RenderableBuilt = deFALSE;
	if (Parent && (ChildNum == 0 || ChildNum == 1))
	{
		Parent->Children[ChildNum] = NewPartition;
		if (Parent->RenderableBuilt)
			NewPartition->RenderableBuilt = deTRUE;
	}

	return NewPartition;
}

deBoolean dePolyABT::DestroyABTPartition(ABTPartition* &pPart)
{
	if (pPart == NULL)
		return deTRUE; // nothing to destroy, so it can be considered destroyed
	
	// destroy both children first
	DestroyABTPartition(pPart->Children[0]);
	DestroyABTPartition(pPart->Children[1]);
	
	// release the geometry
	size_t k;
	for (k = 0; k < pPart->Renderables.size(); ++k)
	{
		if (pPart->Renderables[k].VBuffer)
			pPart->Renderables[k].VBuffer->Release();
	}
	// deallocate the memory used by the Partition
	delete pPart;
	pPart = NULL;
	
	return deTRUE;
}

deBoolean dePolyABT::ShrinkPartitionBBoxes(ABTPartition * pPart)
{
	ABTFace* pFace;
	long k, len = pPart->Faces.size();
	// if we have no faces, the bbox really doesn't mean much
	if (len == 0)
		return deTRUE;
	// initialize with the first point in the first triangle
	pPart->BBox.Min = m_Vertices[pPart->Faces[0].Indices[0]].Position;
	pPart->BBox.Max = pPart->BBox.Min;
	// loop through all the faces and expand the BBox to include them
	for (k = 0; k < len; ++k)
	{
		pFace = &(pPart->Faces[k]);
		deGeometry::AABBAddPoint(pPart->BBox, m_Vertices[pFace->Indices[0]].Position);
		deGeometry::AABBAddPoint(pPart->BBox, m_Vertices[pFace->Indices[1]].Position);
		deGeometry::AABBAddPoint(pPart->BBox, m_Vertices[pFace->Indices[2]].Position);
	}
	return deTRUE;
}

deBoolean dePolyABT::ProcessABTPartition(ABTPartition * pPart)
{
#pragma todo (ProcessABTPartition - finish me)
	// tweak the partition choosing to use statistical analysis
	// fix T-junctions

	if (pPart->Faces.size() == 0)
	{
		return deTRUE;
	}
	else if (!pPart->RenderableBuilt && pPart->Faces.size() < m_MaxVBTris)
	{
		deConsoleOut("Building ABT Partition VBuffer (%d faces)\n", pPart->Faces.size());
		BuildPartitionVBuffer(pPart);
	}
	// this else stops it from going down to leaf-level
	else if (pPart->Faces.size() > m_MaxLeafTris)
	{
		deConsoleOut("Splitting ABT Partition 0x%x (%d faces)\n", pPart, pPart->Faces.size());
		deTArray <long> MinTris, MaxTris;
		deTList <long> SplitTris;
		long FaceIndex;
		ABTSplitStat BestSplit;
		deDouble a,b,c;
		size_t k, NumFaces;
		long * Indices;
		void * ptr;
		NumFaces = pPart->Faces.size();
		
		// find the best splitting plane
		PickBestSplit(pPart, BestSplit, Split_Statistical);
		pPart->Split = BestSplit.Split;

		// create new child partitions if they don't exist
		if (pPart->Children[0] == NULL)
		{
			if (!CreateABTPartition(pPart, 0))
				return deFALSE;
		}
		if (pPart->Children[1] == NULL)
		{
			if (!CreateABTPartition(pPart, 1))
			{
				DestroyABTPartition(pPart->Children[0]);
				return deFALSE;
			}
		}

		// split the faces into 3 groups - minside, maxside, and split
		if (!MinTris.Reserve(NumFaces, deTRUE))
			return deFALSE;
		if (!MaxTris.Reserve(NumFaces, deTRUE))
			return deFALSE;
		for (k = 0; k < NumFaces; ++k)
		{
			Indices = pPart->Faces[k].Indices;
			a = deGeometry::PlaneDist(m_Vertices[Indices[0]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
			b = deGeometry::PlaneDist(m_Vertices[Indices[1]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
			c = deGeometry::PlaneDist(m_Vertices[Indices[2]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
			// classify the polygons depending on where they lie
			if (a > 0.0001 && b > 0.0001 && c > 0.0001)
				MaxTris.PushBack(k);
			else if (a < -0.0001 && b < -0.0001 && c < -0.0001)
				MinTris.PushBack(k);
			else if (a > -0.0001 && b > -0.0001 && c > -0.0001 && 
				a <  0.0001 && b <  0.0001 && c <  0.0001)
			{
				// coplanar polygons get put onto the side that naturally has the least
				if (BestSplit.FrontBackDiff >= 0)
					MinTris.PushBack(k);
				else
					MaxTris.PushBack(k);
			}
			else
				SplitTris.AddElement(k);
		}

		// if allowing interpenetration, re-classify the split faces
		// (perhaps re-classify other faces if face balance was imperfect)
		if (AllowOverlap)
		{
			deDouble PosOverlap, NegOverlap;
			long tempFaceIndex;
			void * tempPtr;
			int PosTest, NegTest;
			PosOverlap = Vec3d_GetElement(pPart->BBox.Max, pPart->Split.Axis);
			PosOverlap-= Vec3d_GetElement(pPart->BBox.Min, pPart->Split.Axis);
			PosOverlap *= 0.03125;
			NegOverlap = -PosOverlap;
			ptr = SplitTris.GetNext(NULL, FaceIndex);
			while (ptr)
			{
				tempPtr = SplitTris.GetNext(ptr, tempFaceIndex);
				Indices = pPart->Faces[FaceIndex].Indices;
				a = deGeometry::PlaneDist(m_Vertices[Indices[0]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
				b = deGeometry::PlaneDist(m_Vertices[Indices[1]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
				c = deGeometry::PlaneDist(m_Vertices[Indices[2]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
				PosTest = (a < PosOverlap && b < PosOverlap && c < PosOverlap);
				NegTest = (a > NegOverlap && b > NegOverlap && c > NegOverlap);
				if (PosTest && NegTest)
				{
					// if a polygon is entirely in the overlap region
					// put it into the side that has most of its area in
					PosTest = 0;
					NegTest = 0;
					a = (a+b+c)/3;
					if (a >= 0)
						NegTest = 1;
					else
						PosTest = 1;
				}
				if (NegTest)
				{
					MaxTris.PushBack(FaceIndex);
					SplitTris.RemoveElement(ptr);
				}
				else if (PosTest)
				{
					MinTris.PushBack(FaceIndex);
					SplitTris.RemoveElement(ptr);
				}

				ptr = tempPtr;
				FaceIndex = tempFaceIndex;
			}
		}

		// cut the split faces
		{
			deTArray <long> FrontSplit(4), BackSplit(4);
			deTHashFunctor <long, HashEdge32> EdgeHasher(SplitTris.size()/2);
			ABTFace* pFace;
			long CutIndex;
			long j;
			long * pCutIndex;
			long SurfaceID;
			deBoolean OnFront, LastFront, LastAdded;
			int NumFront, NumBack;
			LastFront = deFALSE;
			LastAdded = deFALSE;
			ptr = SplitTris.GetNext(NULL, FaceIndex);
			while (ptr)
			{
				pFace = &(pPart->Faces[FaceIndex]);
				SurfaceID = pFace->SurfaceID;
				NumFront = 0;
				NumBack = 0;
				for (k = 0; k <= 3; ++k)
				{
					j = k % 3;
					OnFront = deGeometry::PointOnPlaneFront(m_Vertices[pFace->Indices[j]].Position, BestSplit.Plane.Norm, BestSplit.Plane.Dist);
					if (OnFront && (k == 0 || LastFront))
					{
						if (k > 0 && !LastAdded)
						{
							FrontSplit[NumFront] = pFace->Indices[k-1];
							NumFront += 1;
						}
						if (k == 3)
							break;
						FrontSplit[NumFront] = pFace->Indices[j];
						NumFront += 1;
						LastAdded = deTRUE;
					}
					else if (!OnFront && (k == 0 || !LastFront))
					{
						if (k > 0 && !LastAdded)
						{
							BackSplit[NumBack] = pFace->Indices[k-1];
							NumBack += 1;
						}
						if (k == 3)
							break;
						BackSplit[NumBack] = pFace->Indices[j];
						NumBack += 1;
						LastAdded = deTRUE;
					}
					else
					{
						HashEdge32 cut(pFace->Indices[k-1], pFace->Indices[j]);
						pCutIndex = EdgeHasher.FindElement(cut);
						if (!pCutIndex)
						{
							deTArray<ABTVertex>::iterator it = m_Vertices.IncrementSizeQuick(deTRUE);
							InterpolateCutEdge(&*it, cut.index1(), cut.index2(), BestSplit.Plane);
							CutIndex = m_Vertices.size() - 1;
						}
						else
							CutIndex = *pCutIndex;
						if (!LastAdded)
						{
							if (OnFront)
							{
								BackSplit[NumBack] = pFace->Indices[k-1];
								NumBack += 1;
							}
							else
							{
								FrontSplit[NumFront] = pFace->Indices[k-1];
								NumFront += 1;
							}
						}
						LastAdded = deFALSE;
						FrontSplit[NumFront] = CutIndex;
						BackSplit[NumBack] = CutIndex;
						NumFront += 1;
						NumBack += 1;
					}
					LastFront = OnFront;
				}
				
				deTArray<ABTFace>::iterator pFaceIt;
				pFaceIt = pPart->Faces.IncrementSizeQuick(deFALSE);
				k = pPart->Faces.size() - 1;
				MaxTris.PushBack(k);
				pFaceIt->Indices[0] = FrontSplit[0];
				pFaceIt->Indices[1] = FrontSplit[1];
				pFaceIt->Indices[2] = FrontSplit[2];
				pFaceIt->SurfaceID = SurfaceID;

				pFaceIt = pPart->Faces.IncrementSizeQuick(deFALSE);
				k = pPart->Faces.size() - 1;
				MinTris.PushBack(k);
				pFaceIt->Indices[0] = BackSplit[0];
				pFaceIt->Indices[1] = BackSplit[1];
				pFaceIt->Indices[2] = BackSplit[2];
				pFaceIt->SurfaceID = SurfaceID;
				if (NumFront == 4)
				{
					pFaceIt = pPart->Faces.IncrementSizeQuick(deFALSE);
					k = pPart->Faces.size() - 1;
					MaxTris.PushBack(k);
					pFaceIt->Indices[0] = FrontSplit[0];
					pFaceIt->Indices[1] = FrontSplit[2];
					pFaceIt->Indices[2] = FrontSplit[3];
					pFaceIt->SurfaceID = SurfaceID;
				}
				else if (NumBack == 4)
				{
					pFaceIt = pPart->Faces.IncrementSizeQuick(deFALSE);
					k = pPart->Faces.size() - 1;
					MaxTris.PushBack(k);
					pFaceIt->Indices[0] = BackSplit[0];
					pFaceIt->Indices[1] = BackSplit[2];
					pFaceIt->Indices[2] = BackSplit[3];
					pFaceIt->SurfaceID = SurfaceID;
				}
				else
				{
					DEBUGMSG0("ProcessABTPartition: Splitting triangle and didn't get quadrilateral");
				}

				ptr = SplitTris.GetNext(ptr, FaceIndex);
			}
			SplitTris.EmptyList();
		}

		// insert the minside and maxside faces into child partitions
		{
			deTArray <ABTFace> * FaceArray;
			// reserve space for the added faces
			pPart->Children[0]->Faces.Reserve(pPart->Children[0]->Faces.size() + MinTris.size());
			pPart->Children[1]->Faces.Reserve(pPart->Children[1]->Faces.size() + MaxTris.size());
			// insert the minside faces to child 0
			FaceArray = &(pPart->Children[0]->Faces);
			for (k = 0 ; k < MinTris.size(); ++k)
			{
				FaceArray->PushBack(pPart->Faces[MinTris[k]]);
			}
			// insert the maxside faces to child 1
			FaceArray = &(pPart->Children[1]->Faces);
			for (k = 0 ; k < MaxTris.size(); ++k)
			{
				FaceArray->PushBack(pPart->Faces[MaxTris[k]]);
			}
			// and destroy the arrays of faces that have been relocated to child partitions
			MinTris.Resize(0);
			MaxTris.Resize(0);
			pPart->Faces.Resize(0);
		}

		// get the child BBoxes to fit their new datasets
		ShrinkPartitionBBoxes(pPart->Children[0]);
		ShrinkPartitionBBoxes(pPart->Children[1]);

		// process child Partitions
		ProcessABTPartition(pPart->Children[0]);
		ProcessABTPartition(pPart->Children[1]);
	}

	return deTRUE;
}

deBoolean dePolyABT::InterpolateCutEdge(ABTVertex * NewVert, long Index1, long Index2, dePlane& SplitPlane)
{
	deDouble Percent;
	ABTVertex* pVert1;
	ABTVertex* pVert2;
	pVert1 = &(m_Vertices[Index1]);
	pVert2 = &(m_Vertices[Index2]);

	if (!deGeometry::PlaneLineIntersect(SplitPlane.Norm, SplitPlane.Dist, pVert1->Position, pVert2->Position, NewVert->Position, Percent))
		return deFALSE;
	deGeometry::Vertex_Interpolate(NewVert->Normal, pVert1->Normal, pVert2->Normal, Percent);
	Vertex_Normalize(NewVert->Normal);
	deGeometry::ARGB_Interpolate(NewVert->Diffuse, pVert1->Diffuse, pVert2->Diffuse, (deFloat)Percent);
	deGeometry::TexCoord_Interpolate(NewVert->TexCoords[0], pVert1->TexCoords[0], pVert2->TexCoords[0], Percent);
	deGeometry::TexCoord_Interpolate(NewVert->TexCoords[1], pVert1->TexCoords[1], pVert2->TexCoords[1], Percent);
	deGeometry::TexCoord_Interpolate(NewVert->TexCoords[2], pVert1->TexCoords[2], pVert2->TexCoords[2], Percent);

	return deTRUE;
}

void dePolyABT::PickBestSplit(const ABTPartition* pPart,
							  ABTSplitStat & best, 
							  ABTSplitMethod method) const
{
	int axis, sample;
	deFloat AxisScores[3];
	ABTSplitStat BestSplit[3];
	{
		// calculate the scores for each of the axes
		// based on how close they are to being the largest axis
		deDouble MaxDimension;
		deVec3d Diff;
		Diff = pPart->BBox.Max;
		Diff -= pPart->BBox.Min;
		MaxDimension = max(Diff.x, Diff.y);
		MaxDimension = max(MaxDimension, Diff.z);
		AxisScores[0] = deFloat(1.0 - Diff.x / MaxDimension);
		AxisScores[1] = deFloat(1.0 - Diff.y / MaxDimension);
		AxisScores[2] = deFloat(1.0 - Diff.z / MaxDimension);
	}

	switch (method)
	{
	default:
		DEBUGRETURNNOVAL("Unsupported split method");
		break;
	case Split_SuccessiveApprox:
		{
			// successive approximation
#pragma todo (test the successive approximation algorithm for picking the best split-plane)
			ABTSplitStat Split1, Split2;
			for (axis = 0; axis < 3; ++axis)
			{
				BestSplit[axis].Split.Axis = axis;
				BestSplit[axis].Split.Percent = 0.5;
				ScorePartitionSplit(pPart, &BestSplit[axis], AxisScores);
				for (sample = 0; sample < 9; ++sample)
				{
					Split1.Split.Axis = BestSplit[axis].Split.Axis;
					Split2.Split.Axis = BestSplit[axis].Split.Axis;
					Split1.Split.Percent = BestSplit[axis].Split.Percent;
					Split2.Split.Percent = BestSplit[axis].Split.Percent;
					deFloat PercentChange = (deFloat)pow(2, -(sample+2));
					Split1.Split.Percent -= PercentChange;
					Split2.Split.Percent += PercentChange;
					ScorePartitionSplit(pPart, &Split1, AxisScores);
					ScorePartitionSplit(pPart, &Split2, AxisScores);
					if (Split1.Score > BestSplit[axis].Score && Split2.Score > BestSplit[axis].Score)
						break;
					else if (Split1.Score < Split2.Score)
						BestSplit[axis] = Split1;
					else
						BestSplit[axis] = Split2;
				}
			}
		}
		break;
	case Split_AllVerts:
		{
			// discrete vertex sampling - scores a split at every vertex location (slow but potentially good)
			int NumVerts, k;
			ABTSplitStat CurrSplit;
			NumVerts = m_Vertices.size();
			BestSplit[0].Score = 1;
			BestSplit[1].Score = 1;
			BestSplit[2].Score = 1;
			for (k = 0; k < NumVerts; ++k)
			{
				if (deGeometry::PointInAABB(m_Vertices[k].Position, pPart->BBox))
				{
					for (axis = 0; axis < 3; ++axis)
					{
						CurrSplit.Split.Axis = axis;
						CurrSplit.Split.Percent = Vec3d_GetElement(m_Vertices[k].Position, axis) - Vec3d_GetElement(pPart->BBox.Min, axis);
						CurrSplit.Split.Percent /= Vec3d_GetElement(pPart->BBox.Max, axis) - Vec3d_GetElement(pPart->BBox.Min, axis);
						ScorePartitionSplit(pPart, &CurrSplit, AxisScores);
						if (CurrSplit.Score < BestSplit[axis].Score)
							BestSplit[axis] = CurrSplit;
					}
				}
			}
		}
		break;
	case Split_MultiSample:
		{
			// multi-sample method
			// picks 'x' evenly-spaced splits along each axis and selects the one with the best score (lower is better)
			ABTSplitStat CurrSplit;
			const int NumDivisions = 124;
			for (axis = 0; axis < 3; ++axis)
			{
				BestSplit[axis].Score = 1;
				CurrSplit.Split.Axis = axis;
				for (sample = 0; sample < NumDivisions; ++sample)
				{
					CurrSplit.Split.Percent = (sample+1)/((deDouble)(NumDivisions+1));
					ScorePartitionSplit(pPart, &CurrSplit, AxisScores);
					// remember this one if it's the best score
					if (CurrSplit.Score < BestSplit[axis].Score)
					{
						BestSplit[axis] = CurrSplit;
					}
				}
			}
		}
		break;
	case Split_Statistical:
		{
			// statistical sampling method
			const int NumDivisions = 7;
			int len = pPart->Faces.size();
			if (len > 0)
			{
				mean_variance<deDouble> x(m_Vertices[pPart->Faces[0].Indices[0]].Position.x);
				mean_variance<deDouble> y(m_Vertices[pPart->Faces[0].Indices[0]].Position.y);
				mean_variance<deDouble> z(m_Vertices[pPart->Faces[0].Indices[0]].Position.z);
				for (sample = 0; sample < len; ++sample)
				{
					const long (&Indices)[3] = pPart->Faces[sample].Indices;
					for (axis = 0; axis < 3; ++axis)
					{
						deVec3d pos = m_Vertices[Indices[axis]].Position;
						x(pos.x);
						y(pos.y);
						z(pos.z);
					}
				}
				deVec3d mean, var;
				mean.x = (x.mean(len*3) - pPart->BBox.Min.x)/(pPart->BBox.Max.x - pPart->BBox.Min.x);
				mean.y = (y.mean(len*3) - pPart->BBox.Min.y)/(pPart->BBox.Max.y - pPart->BBox.Min.y);
				mean.z = (z.mean(len*3) - pPart->BBox.Min.z)/(pPart->BBox.Max.z - pPart->BBox.Min.z);
				var.x = x.var(len*3);
				var.y = y.var(len*3);
				var.z = z.var(len*3);
				var.x = sqrt(var.x);
				var.y = sqrt(var.y);
				var.z = sqrt(var.z);
				var.x = (var.x)/(pPart->BBox.Max.x - pPart->BBox.Min.x);
				var.y = (var.y)/(pPart->BBox.Max.y - pPart->BBox.Min.y);
				var.z = (var.z)/(pPart->BBox.Max.z - pPart->BBox.Min.z);
				deAABB Percents;
				Percents.Min.x = mean.x - var.x;
				Percents.Min.y = mean.y - var.y;
				Percents.Min.z = mean.z - var.z;
				Percents.Max.x = 2 * var.x;
				Percents.Max.y = 2 * var.y;
				Percents.Max.z = 2 * var.z;
				
				ABTSplitStat CurrSplit;
				for (axis = 0; axis < 3; ++axis)
				{
					BestSplit[axis].Score = 1;
					CurrSplit.Split.Axis = axis;
					for (sample = 0; sample < NumDivisions; ++sample)
					{
						CurrSplit.Split.Percent = ((float)sample/(NumDivisions-1))*Vec3d_GetElement(Percents.Max, axis);
						CurrSplit.Split.Percent += Vec3d_GetElement(Percents.Min, axis);
						//CurrSplit.Split.Percent = (sample+1)/((deDouble)(NumDivisions+1));
						//CurrSplit.Split.Percent = Vec3d_GetElement(mean, axis);
						ScorePartitionSplit(pPart, &CurrSplit, AxisScores);
						// remember this one if it's the best score
						if (CurrSplit.Score < BestSplit[axis].Score)
						{
							BestSplit[axis] = CurrSplit;
						}
					}
				}
			}
		}
		break;
	}

	if (BestSplit[0].Score < BestSplit[1].Score)
	{
		if (BestSplit[0].Score < BestSplit[2].Score)
			best = BestSplit[0];
		else
			best = BestSplit[2];
	}
	else
	{
		if (BestSplit[1].Score < BestSplit[2].Score)
			best = BestSplit[1];
		else
			best = BestSplit[2];
	}
}

deBoolean dePolyABT::ScorePartitionSplit(const ABTPartition* pPart, ABTSplitStat * pStat, deFloat AxisScores[3]) const
{
	int NumFaces = pPart->Faces.size();
	{
		int k;
		deDouble a,b,c;
		pStat->Plane.Norm = deZeroVec;
		switch (pStat->Split.Axis)
		{
		case 0: 
			pStat->Plane.Norm.x = 1.0; 
			pStat->Plane.Dist = pPart->BBox.Min.x;
			pStat->Plane.Dist+= pStat->Split.Percent*(pPart->BBox.Max.x - pPart->BBox.Min.x); 
			break;
		case 1: 
			pStat->Plane.Norm.y = 1.0; 
			pStat->Plane.Dist = pPart->BBox.Min.y;
			pStat->Plane.Dist+= pStat->Split.Percent*(pPart->BBox.Max.y - pPart->BBox.Min.y); 
			break;
		case 2: 
			pStat->Plane.Norm.z = 1.0; 
			pStat->Plane.Dist = pPart->BBox.Min.z;
			pStat->Plane.Dist+= pStat->Split.Percent*(pPart->BBox.Max.z - pPart->BBox.Min.z); 
			break;
		}

		pStat->NumOn = 0;
		pStat->FrontBackDiff = 0;
		for (k = 0; k < NumFaces; ++k)
		{
			const long (&Indices)[3] = pPart->Faces[k].Indices;
			a = deGeometry::PlaneDist(m_Vertices[Indices[0]].Position, pStat->Plane.Norm, pStat->Plane.Dist);
			b = deGeometry::PlaneDist(m_Vertices[Indices[1]].Position, pStat->Plane.Norm, pStat->Plane.Dist);
			c = deGeometry::PlaneDist(m_Vertices[Indices[2]].Position, pStat->Plane.Norm, pStat->Plane.Dist);
			if (a > 0.0001 && b > 0.0001 && c > 0.0001)
				pStat->FrontBackDiff += 1;
			else if (a < -0.0001 && b < -0.0001 && c < -0.0001)
				pStat->FrontBackDiff -= 1;
			else if (a < -0.0001 || b < -0.0001 || c < -0.0001 || a > 0.0001 || b > 0.0001 || c > 0.0001)
				pStat->NumOn += 1;
		}
	}
	
	{
		deDouble Scores[4];
		Scores[0] = AxisScores[pStat->Split.Axis];
		Scores[1] = 2*fabs(0.5f - pStat->Split.Percent);
		Scores[2] = abs(pStat->FrontBackDiff)/NumFaces;
		Scores[3] = abs(pStat->NumOn)/NumFaces;
		pStat->Score = (deFloat)(m_SplitWeights[0]*Scores[0] + m_SplitWeights[1]*Scores[1] + 
						m_SplitWeights[2]*Scores[2] + m_SplitWeights[3]*Scores[3]);
	}

	return deTRUE;
}

deBoolean dePolyABT::BuildPartitionVBuffer(ABTPartition* pPart)
{
#pragma todo (BuildPartitionVBuffer - finish me and speed up)

	deVertex* PosBuffer;
	deVertex* NormBuffer;
	deARGB* DiffBuffer;
	deTexCoord* TexBuffer[3];
	deBoundsCheckedArray<u16> IdxBuffer;
	deTArray<long> SurfaceIDsFound;
	deTArray<size_t> SurfaceFaceNum;
	deTArray<long> VertexIndicesFound;
	IdeVertexBuffer* pParentVB;
	IdeVertexBuffer* pVB;
	size_t NumSurfaces;
	size_t NumFaces, FaceNum;
	long n, x;
	size_t c, j, k;

	NumSurfaces = 0;
	NumFaces = pPart->Faces.size();
	VertexIndicesFound.Reserve(NumFaces*3);
	SurfaceIDsFound.Reserve(m_Surfaces.size());
	SurfaceFaceNum.Reserve(m_Surfaces.size());
	for (k = 0; k < NumFaces; ++k)
	{
		// for each face, find the surface it has and the vertices it uses
		x = pPart->Faces[k].SurfaceID;
		for (j = 0; j < SurfaceIDsFound.size(); ++j)
		{
			if (x == SurfaceIDsFound[j])
			{
				// found the surface ID, incrememt the number of faces it has
				SurfaceFaceNum[j] += 1;
				break;
			}
		}
		if (j >= SurfaceIDsFound.size())
		{
			// didn't find a matching surface ID, insert one
			NumSurfaces += 1;
			SurfaceIDsFound.PushBack(x);
			deTArray<size_t>::iterator it = SurfaceFaceNum.IncrementSizeQuick(deFALSE);
			*it = 1;
		}
		// loop through indices of this face and keep track of vertices used
		for (n = 0; n < 3; ++n)
		{
			x = pPart->Faces[k].Indices[n];
			for (j = 0; j < VertexIndicesFound.size(); ++j)
			{
				if (x == VertexIndicesFound[j])
				{
					break;
				}
			}
			if (j >= VertexIndicesFound.size())
			{
				// vertex not found, add it
				VertexIndicesFound.PushBack(x);
			}
		}
	}

	pPart->Renderables.Resize(NumSurfaces+2);
	pVB = IdeVertexBuffer_CreateVBuffer();
	if (!pVB)
		return deFALSE;
	pPart->Renderables[0].VBuffer = pVB;
	pPart->Renderables[0].Surface = 0;
	pVB->Lock();
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_POSITION, deFALSE);
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_NORMAL, deFALSE);
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_DIFFUSE, deFALSE);
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0, deFALSE);
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS1, deFALSE);
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS2, deFALSE);
	pVB->CreateBuffer(IdeVertexBuffer::BUFFER_INDICES16, deFALSE);
	pVB->SetBufferSize(VertexIndicesFound.size(), deFALSE);
	
	PosBuffer = (deVertex*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
	NormBuffer = (deVertex*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_NORMAL);
	DiffBuffer = (deARGB*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
	TexBuffer[0] = (deTexCoord*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);
	TexBuffer[1] = (deTexCoord*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS1);
	TexBuffer[2] = (deTexCoord*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS2);

	for (k = 0; k < VertexIndicesFound.size(); ++k)
	{
		PosBuffer[k] = deMake_Vertex(m_Vertices[VertexIndicesFound[k]].Position);
		NormBuffer[k] = m_Vertices[VertexIndicesFound[k]].Normal;
		DiffBuffer[k] = m_Vertices[VertexIndicesFound[k]].Diffuse;
		// uncomment the next 2 lines to have each partition have a distinct coloring
		//j = (((DWORD)pPart) % 15)*17;
		//DiffBuffer[k] = (DiffBuffer[k] & 0xffffff00) | j;
		TexBuffer[0][k] = m_Vertices[VertexIndicesFound[k]].TexCoords[0];
		TexBuffer[1][k] = m_Vertices[VertexIndicesFound[k]].TexCoords[1];
		TexBuffer[2][k] = m_Vertices[VertexIndicesFound[k]].TexCoords[2];
	}
	pVB->Unlock();

	pParentVB = IdeVertexBuffer_CreateVBuffer();
	if (!pParentVB)
		return deFALSE;
	pPart->Renderables[1].VBuffer = pParentVB;
	pPart->Renderables[1].Surface = 0;
	pParentVB->Lock();
	pParentVB->CreateBuffer(IdeVertexBuffer::BUFFER_INDICES16, deFALSE);
	pParentVB->SetBufferSize(NumFaces*3, deTRUE);
	pParentVB->Unlock();

	// loop through all surfaces found to make a separate sub-VBuffer for each one
	for (k = 0; k < NumSurfaces; ++k)
	{
		// create this surface's VBuffer and record the actual surface used
		pVB = IdeVertexBuffer_CreateSubVBuffer(pParentVB);
		if (!pVB)
			return deFALSE;
		pPart->Renderables[k+2].VBuffer = pVB;
		pPart->Renderables[k+2].Surface = SurfaceIDsFound[k];//m_Surfaces[SurfaceIDsFound[k]];

		// lock the VB and set the data we need
		pVB->Lock();
		pVB->SetBufferSize(SurfaceFaceNum[k]*3, deTRUE);
		pVB->SetRenderType(IdeVertexBuffer::RENDER_TRIANGLELIST);
		IdxBuffer = (u16*)pVB->GetBuffer(IdeVertexBuffer::BUFFER_INDICES16);
		IdxBuffer.SetLength(SurfaceFaceNum[k]*3);

		FaceNum = 0;
		// loop through all faces to find the ones that use this surface
		for (j = 0; j < NumFaces; ++j)
		{
			if (SurfaceIDsFound[k] == pPart->Faces[j].SurfaceID)
			{
				// we found a matching face
				// loop through each vertex of the face to translate the indices
				for (n = 0; n < 3; ++n)
				{
					x = pPart->Faces[j].Indices[n];
					for (c = 0; c < VertexIndicesFound.size(); ++c)
					{
						if (x == VertexIndicesFound[c])
						{
							// found the matching vertex index, translate
							IdxBuffer[FaceNum*3+n] = (u16)c;
							break;
						}
					}
				}
				FaceNum++;
			}
			if (FaceNum >= SurfaceFaceNum[k])
				break;
		}

		pVB->Unlock();
	}

	pPart->RenderableBuilt = deTRUE;

	return deTRUE;
}


deBoolean dePolyABT::RenderVisibleABTPartitions(IdeSceneGraph::deSceneTraversal* Params, ABTPartition* CurrentPart, deBoolean AllVisible)
{
	if (!CurrentPart)
		return deTRUE;
	deBoolean Valid = deTRUE;
	deBoolean Visible = AllVisible;
	if (!AllVisible)
	{
		Visible = Params->CurrentFrustum->TestAABBInside(CurrentPart->BBox, m_WorldOffset, AllVisible);
	}
	if (Visible)
	{
		int k;
		if (CurrentPart->RenderableBuilt)
		{
			long DebugFlags = IdeSceneGraph_GetSceneGraphPtr()->GetDebugFlags();
			if (DebugFlags & IdeSceneGraph::deDrawAABB)
			{
				IdeRender::deRenderObject obj;
				deAABB aabb;
				memset(&obj, 0, sizeof(IdeRender::deRenderObject));
				aabb = CurrentPart->BBox;
				aabb.Max -= aabb.Min;
				((deTransform&)obj.Transform).Reset();
				obj.Transform.Translation = aabb.Min;
				obj.Transform.Translation += m_WorldOffset;
				{
					deVec3d X,Y,Z;
					X = deVecX;
					X *= aabb.Max.x;
					Y = deVecY;
					Y *= aabb.Max.y;
					Z = deVecZ;
					Z *= aabb.Max.z;
					((deTransform&)obj.Transform).SetRightUpIn(X,Y,Z);
				}
				obj.VB = s_AABB_VB;
				Params->render->RenderPrepBuffer(obj);
			}
			if (!RenderABTPartition(Params, CurrentPart))
				Valid = deFALSE;
		}
		else
		{
			for (k = 0; k < 2; ++k)
			{
				if (!RenderVisibleABTPartitions(Params, CurrentPart->Children[k], AllVisible))
					Valid = deFALSE;
			}
		}
	}
	
	return Valid;
}

deBoolean dePolyABT::RenderABTPartition(IdeSceneGraph::deSceneTraversal* Params, ABTPartition* pPart)
{
	if (!Params || !pPart)
		DEBUGRETURN(deFALSE, "RenderABTPartition: Pointer argument was NULL");

	ABTRenderChunk* pChunk;
	IdeRender::deRenderObject obj;
	memset(&obj, 0, sizeof(IdeRender::deRenderObject));
	((deTransform&)obj.Transform).Reset();
	obj.VB = pPart->Renderables[0].VBuffer;
	size_t k;
	for (k = 2; k < pPart->Renderables.size(); ++k)
	{
		memset(&obj.Lights, 0, sizeof(obj.Lights));
		pChunk = &(pPart->Renderables[k]);
		obj.IndexOverride = pChunk->VBuffer;
		obj.pSurface = m_Surfaces[pChunk->Surface];
		Params->CurrentSceneGraph->LightExtents(deZeroVec, pPart->BBox, obj.Lights, 4);
//		obj.Lights[0] = s_TestLight;
//		obj.Lights[1] = s_TestLight2;
		if (!obj.Lights[0])
			obj.Lights[0] = NULL;//s_TestLightBright;
		else
			obj.Lights[0] = obj.Lights[0];

		obj.SurfaceDetail.Transparency = deFALSE;
		if (obj.pSurface)
		{
			IdeRenderTexture* pRT = obj.pSurface->GetRenderTexture();
			if (pRT->GetAlphaOperation(0) != IdeRenderTexture::TEXTUREOP_DISABLE)
				obj.SurfaceDetail.Transparency = deTRUE;
		}

		Params->render->RenderPrepBuffer(obj);
	}
	return deTRUE;
}

struct deABT_Header_t
{
	u16 VersionMajor;
	u16 VersionMinor;
};
static const deABT_Header_t deABT_Header = {0,1};
/*
Version 0.1:
December 22, 2003 (Merry Christmas!)

const int deABT_TexCoords = 4;
struct deVec3d {deDouble[3]; }
struct deVec3f {deFloat[3]; }
struct deTexCoord2 {deFloat[2]; }
struct deAABB {deVec3d Mins, Maxs; }
struct deIDPair {u16 worldID; u32 objectID; }
struct ABTVertex {deVec3d pos; 
                  deVec3f norm;
                  u32 diffuse_argb; 
                  deTexCoord2 tex[deABT_TexCoords]; }
struct ABTFace {u32 vertIndices[3]; u32 SurfaceID; }
struct ABTSplit {s32 axis; deDouble Percent; }
struct ABTPartition {deAABB BBox;
                     ABTSplit Split;
                     u32 childPart[2]; u32 parentPart;
                     u32 numFaces, faceIndexStart;
                     u32 numObjects, objectIndexStart; }

**** begin
u16		Version_Major
u16		Version_Minor
u32		Max_LeafTris
u32		Max_VBTris
deVec3d	World_Offset
deAABB	Full_BBox
deFloat	Split_Weights[4]
u32		Num_Verts
u32		Num_Faces
u32		Num_Objects
u32		Num_Surfaces
u32		Num_Partitions
u32		Num_Partition_Faces
u32		Num_Partition_Objects
deIDPair		Surfaces[Num_Surfaces]
deIDPair		Objects[NumObjects]
ABTVertex		Vertices[Num_Verts]
ABTFace			Original_Faces[Num_Faces]
ABTFace			PartitionFaces[Num_Partition_Faces]
u32				objectIndices[Num_Partition_Objects]
ABTPartition	Partitions[Num_Partitions]
**** end

*/

deBoolean dePolyABT::Serialize(IdeFile * File)
{
	deConsoleOut("dePolyABT::Serialize\n");
	if (!File)
		return deFALSE;
	
	long length;
	
	deConsoleOut("dePolyABT::Serialize - writing version header\n");

	// header information
	DE3D_STREAM_PUT(File, deABT_Header.VersionMajor, u16);
	DE3D_STREAM_PUT(File, deABT_Header.VersionMinor, u16);
	
	deConsoleOut("dePolyABT::Serialize - writing member values\n");

	// persistent member values
	DE3D_STREAM_PUT(File, m_MaxLeafTris, u32);
	DE3D_STREAM_PUT(File, m_MaxVBTris, u32);
	DE3D_STREAM_PUT(File, m_WorldOffset, deVec3d);
	DE3D_STREAM_PUT(File, m_TotalBBox.Min, deVec3d);
	DE3D_STREAM_PUT(File, m_TotalBBox.Max, deVec3d);
	DE3D_STREAM_PUTP(File, m_SplitWeights, 4*sizeof(deFloat));
	
	deConsoleOut("dePolyABT::Serialize - writing array sizes\n");

	// number of vertices
	length = m_Vertices.size();
	DE3D_STREAM_PUT(File, length, u32);
	// number of faces
	length = m_AllFaces.size();
	DE3D_STREAM_PUT(File, length, u32);
	// number of objects
	length = m_SceneObjects.size();
	DE3D_STREAM_PUT(File, length, u32);
	// number of surfaces
	length = m_Surfaces.size();
	DE3D_STREAM_PUT(File, length, u32);

	deConsoleOut("dePolyABT::Serialize - compiling tree\n");

	// walk the tree and enter everything...
	deTArray<ABTFilePartition_01> partitions;
	u32 TotalFaces, TotalObjects;
	TotalFaces = 0;
	TotalObjects = 0;
	AppendFilePartition(partitions, m_RootPartition, 0, TotalFaces, TotalObjects);

	// number of partitions
	length = partitions.size();
	deConsoleOut("dePolyABT::Serialize - writing tree size (%d)\n", length);
	DE3D_STREAM_PUT(File, length, u32);
	// number of partitioned faces
	DE3D_STREAM_PUT(File, TotalFaces, u32);
	// number of partitioned objects
	DE3D_STREAM_PUT(File, TotalObjects, u32);

	// all surfaces
	deConsoleOut("dePolyABT::Serialize - writing surfaces (%d)\n", m_Surfaces.size());
	readWriteObject_t writeSurface = { File, deTRUE };
	if (!deIter_All(m_Surfaces.begin(), m_Surfaces.end(), writeSurface))
		return deFALSE;
	// all objects
	deConsoleOut("dePolyABT::Serialize - writing objects (%d)\n", m_SceneObjects.size());
	if (!deIter_All(m_SceneObjects.begin(), m_SceneObjects.end(), writeSurface))
		return deFALSE;
	// all vertex data
	deConsoleOut("dePolyABT::Serialize - writing vertices (%d)\n", m_Vertices.size());
	readWriteVertex_t writeVertex = { File, deTRUE };
	if (!deIter_All(m_Vertices.begin(), m_Vertices.end(), writeVertex))
		return deFALSE;
	// all original faces
	deConsoleOut("dePolyABT::Serialize - writing original faces (%d)\n", m_AllFaces.size());
	readWriteFace_t writeFace = { File, deTRUE };
	if (!deIter_All(m_AllFaces.begin(), m_AllFaces.end(), writeFace))
		return deFALSE;

	deConsoleOut("dePolyABT::Serialize - processing partitions\n");

	// process partitions so they can be written
	grabPartitionData_t PartitionData;
	if (!deIter_All(partitions.begin(), partitions.end(), PartitionData))
		return deFALSE;

	// all partition faces
	deConsoleOut("dePolyABT::Serialize - writing partitioned faces (%d)\n", PartitionData.faces.size());
	if (!deIter_All(PartitionData.faces.begin(), PartitionData.faces.end(), writeFace))
		return deFALSE;
	// all partition object indices
	deConsoleOut("dePolyABT::Serialize - writing partitioned objects (%d)\n", PartitionData.objects.size());
	if (PartitionData.objects.size() > 0)
	{
		DE3D_STREAM_PUTP(File, &*PartitionData.objects.begin(), PartitionData.objects.size()*sizeof(u32));
	}
	// all partitions
	deConsoleOut("dePolyABT::Serialize - writing partitions (%d)\n", partitions.size());
	readWritePartition_t writePart = { File, deTRUE };
	if (!deIter_All(partitions.begin(), partitions.end(), writePart))
		return deFALSE;

	deConsoleOut("dePolyABT::Serialize - finished successfully\n");

	return deTRUE;
}

deBoolean dePolyABT::DeSerialize(IdeFile * File, u32 DataLength, u32 & AmtRead)
{
	DestroyABT();

	if (!File)
		return deFALSE;

	u16 VersionMajor, VersionMinor;
	// header information
	DE3D_STREAM_GET(File, VersionMajor, u16, AmtRead, DataLength);
	DE3D_STREAM_GET(File, VersionMinor, u16, AmtRead, DataLength);

	// check the version
	if (VersionMajor > deABT_Header.VersionMajor ||
		(VersionMajor == deABT_Header.VersionMajor && VersionMinor > deABT_Header.VersionMinor))
		return deFALSE;

	if (VersionMajor == 0 && VersionMinor == 1)
	{
		u32 NumVerts, NumOrigFaces, NumObjects, NumSurfaces;
		u32 NumPartitions, TotalFaces, TotalObjects;
		deTArray<u32> objectIndices;
		deTArray<ABTFace> faces;
		deTArray<ABTFilePartition_01> partitions;
		// persistent member values
		DE3D_STREAM_GET(File, m_MaxLeafTris, u32, AmtRead, DataLength);
		DE3D_STREAM_GET(File, m_MaxVBTris, u32, AmtRead, DataLength);
		DE3D_STREAM_GET(File, m_WorldOffset, deVec3d, AmtRead, DataLength);
		DE3D_STREAM_GET(File, m_TotalBBox.Min, deVec3d, AmtRead, DataLength);
		DE3D_STREAM_GET(File, m_TotalBBox.Max, deVec3d, AmtRead, DataLength);
		DE3D_STREAM_GETP(File, m_SplitWeights, 4*sizeof(deFloat), AmtRead, DataLength);

		// number of vertices
		DE3D_STREAM_GET(File, NumVerts, u32, AmtRead, DataLength);
		// number of faces
		DE3D_STREAM_GET(File, NumOrigFaces, u32, AmtRead, DataLength);
		// number of objects
		DE3D_STREAM_GET(File, NumObjects, u32, AmtRead, DataLength);
		// number of surfaces
		DE3D_STREAM_GET(File, NumSurfaces, u32, AmtRead, DataLength);

		// number of partitions
		DE3D_STREAM_GET(File, NumPartitions, u32, AmtRead, DataLength);
		// number of partitioned faces
		DE3D_STREAM_GET(File, TotalFaces, u32, AmtRead, DataLength);
		// number of partitioned objects
		DE3D_STREAM_GET(File, TotalObjects, u32, AmtRead, DataLength);

		// resize all the storage arrays - some are local, some are members
		if (!m_Vertices.resize(NumVerts))
			return deFALSE;
		if (!m_AllFaces.resize(NumOrigFaces))
			return deFALSE;
		if (!m_SerialObjects.resize(NumObjects))
			return deFALSE;
		if (!m_SerialSurfaces.resize(NumSurfaces))
			return deFALSE;
		if (!objectIndices.resize(TotalObjects))
			return deFALSE;
		if (!faces.resize(TotalFaces))
			return deFALSE;
		if (!partitions.resize(NumPartitions))
			return deFALSE;

		// all surfaces - they need to be resolved later
		if (m_SerialSurfaces.size() > 0)
		{
			DE3D_STREAM_GETP(File, &*m_SerialSurfaces.begin(), m_SerialSurfaces.size()*sizeof(deIDPair), AmtRead, DataLength);
		}
		// all objects - they need to be resolved later
		if (m_SerialObjects.size() > 0 )
		{
			DE3D_STREAM_GETP(File, &*m_SerialObjects.begin(), m_SerialObjects.size()*sizeof(deIDPair), AmtRead, DataLength);
		}
		// all vertex data
		readWriteVertex_t readVertex = { File, deFALSE, DataLength, &AmtRead };
		if (!deIter_All(m_Vertices.begin(), m_Vertices.end(), readVertex))
			return deFALSE;
		// all original faces
		readWriteFace_t readFace = { File, deFALSE, DataLength, &AmtRead };
		if (!deIter_All(m_AllFaces.begin(), m_AllFaces.end(), readFace))
			return deFALSE;

		// all partition faces
		if (!deIter_All(faces.begin(), faces.end(), readFace))
			return deFALSE;
		// all partition object indices
		if (objectIndices.size() > 0)
		{
			DE3D_STREAM_GETP(File, &*objectIndices.begin(), objectIndices.size()*sizeof(u32), AmtRead, DataLength);
		}
		// all partitions
		readWritePartition_t readPart = { File, deFALSE, DataLength, &AmtRead };
		if (!deIter_All(partitions.begin(), partitions.end(), readPart))
			return deFALSE;

		m_RootPartition = DecomposeFilePartition(partitions, 0, NULL, objectIndices, faces);

		return deTRUE;
	}
	return deFALSE;
}

deBoolean dePolyABT::DeSerializeLoad()
{
	m_Surfaces.ResizeQuick(m_SerialSurfaces.size(), deTRUE);
	m_SceneObjects.ResizeQuick(m_SerialObjects.size(), deTRUE);

	size_t i;
	deIDPair pair;
	IdeWorldManager* manager = IdeWorld_GetManager();
	for (i = 0; i < m_Surfaces.size(); ++i)
	{
		pair = m_SerialSurfaces[i];
		IdeWorldObject* pObject;
		IdeSurface * surface = NULL;
		if (pair.wid)
		{
			pObject = manager->GetWorldObject(pair.wid, pair.oid);
			if (pObject)
				surface = (IdeSurface*)pObject->GetWOInterface(IdeSurface_GetWOInterfaceID());
			else
				DEBUGMSG2("dePolyABT::DeSerializeLoad - failure loading object with WID=%d and OID=%d", pair.wid, pair.oid);
		}
		if (surface)
			surface->Claim();
		m_Surfaces[i] = surface;
	}
	for (i = 0; i < m_SceneObjects.size(); ++i)
	{
		pair = m_SerialObjects[i];
		IdeWorldObject* pObject;
		IdeSceneObject * object = NULL;
		if (pair.wid)
		{
			pObject = manager->GetWorldObject(pair.wid, pair.oid);
			if (pObject)
				object = (IdeSceneObject*)pObject->GetWOInterface(IdeSurface_GetWOInterfaceID());
			else
				DEBUGMSG2("dePolyABT::DeSerializeLoad - failure loading object with WID=%d and OID=%d", pair.wid, pair.oid);
		}
		if (object)
			object->Claim();
		m_SceneObjects[i] = object;
	}

	m_SerialSurfaces.resize(0);
	m_SerialObjects.resize(0);

	// need to resolve objects, but especially surfaces
	return deTRUE;
}

u32 dePolyABT::AppendFilePartition(deTArray<ABTFilePartition_01> & fileparts, 
								   const ABTPartition* current, 
								   u32 parentIdx, 
								   u32 & TotalFaces, u32 & TotalObjects)
{
	if (!current)
		return 0;
	u32 retval = fileparts.size();
	deConsoleOut("dePolyABT::AppendFilePartition - adding %d\n", retval);
	fileparts.IncrementSizeQuick(deTRUE);

	u32 child0, child1;
	// grab the children also
	child0 = AppendFilePartition(fileparts, current->Children[0], retval, TotalFaces, TotalObjects);
	child1 = AppendFilePartition(fileparts, current->Children[1], retval, TotalFaces, TotalObjects);

	deTArray<ABTFilePartition_01>::iterator it = fileparts.get_iterator(retval);
	it->BBox = current->BBox;
	it->Split = current->Split;
	it->parentPart = parentIdx;
	it->numObjects = current->SceneObjectList.size();
	it->numFaces = current->Faces.size();
	it->childPart[0] = child0;
	it->childPart[1] = child1;

	// add to the output params
	TotalFaces += it->numFaces;
	TotalObjects += it->numObjects;
	
	// encode the pointer so we can grab the data later
	it->objectIndexStart = (u32)current;

	deConsoleOut("dePolyABT::AppendFilePartition -  added %d, children: %d, %d\n", 
		retval, child0, child1);

	return retval;
}

dePolyABT::ABTPartition* dePolyABT::DecomposeFilePartition(const deTArray<ABTFilePartition_01> & fileparts, 
														   u32 currentIdx, ABTPartition* parent, 
														   const deTArray<u32> objectIndices, 
														   const deTArray<ABTFace> faces)
{
	deTArray<ABTFilePartition_01>::iterator it = fileparts.get_iterator(currentIdx);
	// create a partition
	ABTPartition* newpart = new ABTPartition;
	// grab basic data
	newpart->BBox = it->BBox;
	newpart->Split = it->Split;
	newpart->Parent = parent;
	newpart->RenderableBuilt = deFALSE;

	// recursive calls on existent children
	if (it->childPart[0] > 0)
	{
		newpart->Children[0] = DecomposeFilePartition(fileparts, it->childPart[0], newpart, objectIndices, faces);
	}
	else
		newpart->Children[0] = NULL;
	if (it->childPart[1] > 0)
	{
		newpart->Children[1] = DecomposeFilePartition(fileparts, it->childPart[1], newpart, objectIndices, faces);
	}
	else
		newpart->Children[1] = NULL;
	
	// grab the faces
	if (it->numFaces > 0)
	{
		if (!newpart->Faces.ResizeQuick(it->numFaces))
		{
			DEBUGRETURN(NULL, "DecomposeFilePartition: out of memory for Faces");
		}
		deTArray<ABTFace>::iterator
			dst = newpart->Faces.begin(),
			end = newpart->Faces.end(),
			src = faces.get_iterator(it->faceIndexStart);
		for (; dst != end; ++dst, ++src)
		{
			*dst = *src;
		}

#pragma todo(save the vertex streams into the file also for quicker loading - version 0.2)
		// build the geometry into a renderable
		BuildPartitionVBuffer(newpart);
	}
	
	// grab the object indices
	if (it->numObjects)
	{
		if (!newpart->SceneObjectList.ResizeQuick(it->numObjects))
		{
			DEBUGRETURN(NULL, "DecomposeFilePartition: out of memory for Objects");
		}
		deTArray<long>::iterator dst = newpart->SceneObjectList.begin(),
			end = newpart->SceneObjectList.end();
		deTArray<u32>::iterator src = objectIndices.get_iterator(it->objectIndexStart);
		for (; dst != end; ++dst, ++src)
		{
			*dst = *src;
		}
	}
	return newpart;
}

deBoolean dePolyABT::readWriteObject_t::operator()(IdeWorldObject* obj)
{
	deIDPair pair = {0, 0};
	if (m_Write)
	{
		deConsoleOut("dePolyABT::readWriteObject_t - obj = 0x%.8x", obj);
		if (obj)
		{
			//deConsoleOut(", func = 0x%.8x\n", obj->GetObjectID);
			pair.oid = obj->GetObjectID();
			pair.wid = obj->GetWorldID();
		}
		deConsoleOut("\n");
		DE3D_STREAM_PUT(m_File, pair, deIDPair);
	}
	else
	{
		return deFALSE;
	}
	return deTRUE;
}
deBoolean dePolyABT::readWriteVertex_t::operator()(ABTVertex& vert)
{
	if (m_Write)
	{
		DE3D_STREAM_PUT(m_File, vert.Position,     deVec3d    );
		DE3D_STREAM_PUT(m_File, vert.Normal,       deVec3f    );
		DE3D_STREAM_PUT(m_File, vert.Diffuse,      deARGB     );
		DE3D_STREAM_PUT(m_File, vert.TexCoords[0], deTexCoord2);
		DE3D_STREAM_PUT(m_File, vert.TexCoords[1], deTexCoord2);
		DE3D_STREAM_PUT(m_File, vert.TexCoords[2], deTexCoord2);
		DE3D_STREAM_PUT(m_File, vert.TexCoords[3], deTexCoord2);
	}
	else
	{
		DE3D_STREAM_GET(m_File, vert.Position,     deVec3d,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, vert.Normal,       deVec3f,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, vert.Diffuse,      deARGB,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, vert.TexCoords[0], deTexCoord2,	*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, vert.TexCoords[1], deTexCoord2,	*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, vert.TexCoords[2], deTexCoord2,	*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, vert.TexCoords[3], deTexCoord2,	*m_AmtRead, m_DataLength);
	}
	return deTRUE;
}
deBoolean dePolyABT::readWriteFace_t::operator()(ABTFace& face)
{
	if (m_Write)
	{
		DE3D_STREAM_PUT(m_File, face.Indices[0], u32);
		DE3D_STREAM_PUT(m_File, face.Indices[1], u32);
		DE3D_STREAM_PUT(m_File, face.Indices[2], u32);
		DE3D_STREAM_PUT(m_File, face.SurfaceID,  u32);
	}
	else
	{
		DE3D_STREAM_GET(m_File, face.Indices[0], u32, *m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, face.Indices[1], u32, *m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, face.Indices[2], u32, *m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, face.SurfaceID,  u32, *m_AmtRead, m_DataLength);
	}
	return deTRUE;
}
deBoolean dePolyABT::readWritePartition_t::operator()(ABTFilePartition_01& part)
{
	if (m_Write)
	{
		DE3D_STREAM_PUT(m_File, part.BBox.Min,		deVec3d );
		DE3D_STREAM_PUT(m_File, part.BBox.Max,		deVec3d );
		DE3D_STREAM_PUT(m_File, part.Split.Axis,	s32     );
		DE3D_STREAM_PUT(m_File, part.Split.Percent,	deDouble);
		DE3D_STREAM_PUT(m_File, part.childPart[0],	u32     );
		DE3D_STREAM_PUT(m_File, part.childPart[1],	u32     );
		DE3D_STREAM_PUT(m_File, part.parentPart,	u32     );
		DE3D_STREAM_PUT(m_File, part.numFaces,		u32     );
		DE3D_STREAM_PUT(m_File, part.faceIndexStart,u32     );
		DE3D_STREAM_PUT(m_File, part.numObjects,	u32     );
		DE3D_STREAM_PUT(m_File, part.objectIndexStart, u32  );
	}
	else
	{
		DE3D_STREAM_GET(m_File, part.BBox.Min,		deVec3d,	*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.BBox.Max,		deVec3d,	*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.Split.Axis,	s32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.Split.Percent,	deDouble,	*m_AmtRead, m_DataLength); // really a double
		DE3D_STREAM_GET(m_File, part.childPart[0],	u32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.childPart[1],	u32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.parentPart,	u32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.numFaces,		u32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.faceIndexStart,u32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.numObjects,	u32,		*m_AmtRead, m_DataLength);
		DE3D_STREAM_GET(m_File, part.objectIndexStart, u32,		*m_AmtRead, m_DataLength);
	}
	return deTRUE;
}
deBoolean dePolyABT::grabPartitionData_t::operator()(ABTFilePartition_01& part)
{
	u32 i, k;
	ABTPartition* mem_part = (ABTPartition*)part.objectIndexStart;
	part.faceIndexStart = faces.size();
	if (!faces.ResizeQuick(part.faceIndexStart + part.numFaces))
		return deFALSE;
	for (k = 0, i = faces.size()-part.numFaces; k < part.numFaces; ++k, ++i)
	{
		faces[i] = mem_part->Faces[k];
	}
	
	part.objectIndexStart = objects.size();
	if (!objects.ResizeQuick(part.objectIndexStart + part.numObjects))
		return deFALSE;
	for (k = 0, i = objects.size()-part.numObjects; k < part.numObjects; ++k, ++i)
	{
		objects[i] = (u32)mem_part->SceneObjectList[k];
	}
	
	return deTRUE;
}
