/// @file deMapCompile.cpp
/// 
/// @brief compiles brush data into polygonal data. based on information at http://folk.uio.no/stefanha
///
/// @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 Dec 2003
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#define LEAK_HUNTING  // comment out to disable leak-hunts


#include "deMapCompile.hpp"
#include "deFunctors.hpp"
#include "deMath.hpp"

namespace deMapConverter
{

class VertexGrid
{
public:
	struct GridVert
	{
		deVec3d Pos;
		long Index;
	};
	struct GridBox
	{
		deAABB BBox;
		deTArray<GridVert> Verts;
	};
	struct GridEdge
	{
		long i1, i2;
		deVec3d dir;
		inline GridEdge(long i1_, long i2_)
		{
			i1 = min(i1_, i2_);
			i2 = max(i1_, i2_);
		}
		inline bool HasVert(long v)
		{
			if (v == i1 || v == i2)
				return true;
			return false;
		}
	};
private:
	deTArray<deVec3d> mVertices;
	deTArray< deTList<GridEdge> > mEdges;
	deTArray<GridBox> mGrid;
	deVec3d mOffset;
	deVec3d mSize;
	deDouble mDimension;
	long mX, mY, mZ;
public:
	VertexGrid(const deAABB& BBox, int NumVerts)
		: mOffset(BBox.Min), mEdges(127)
	{
		NumVerts /= 2;
		mVertices.Reserve(NumVerts);
		long NumBoxes = deTArray<deVec3d>::SmallestLargerPow2(NumVerts/32);
		mSize = BBox.Max;
		mSize -= BBox.Min;
		mX = deTArray<deVec3d>::SmallestLargerPow2(u32(mSize.x));
		mY = deTArray<deVec3d>::SmallestLargerPow2(u32(mSize.y));
		mZ = deTArray<deVec3d>::SmallestLargerPow2(u32(mSize.z));
		long a,b;
		a = min(mX, mY);
		a = min(a,mZ);
		b = a;
		while (a > 1 && mX * mY * mZ > NumBoxes)
		{
			mX /= 2;
			mY /= 2;
			mZ /= 2;
			a = min(mX, mY);
			a = min(a,mZ);
		}
		mDimension = max(mSize.x / mX, mSize.y / mY);
		mDimension = max(mDimension, mSize.z / mZ);
		mDimension = ceil(mDimension + Plane_Epsilon);
		NumBoxes = mX * mY * mZ;
		mGrid.Resize(NumBoxes);
	}
	long GetVertIndex(const deVec3d& v)
	{
		long x,y,z;
		x = long((v.x - mOffset.x) / mDimension);
		y = long((v.y - mOffset.y) / mDimension);
		z = long((v.z - mOffset.z) / mDimension);
		GridBox* box = &mGrid[z*(mY*mX) + y*mX + x];
		int k;
		for (k = 0; k < box->Verts.size(); ++k)
		{
			deVec3d vert = box->Verts[k].Pos;
			if (Vec3d_Compare(vert, v, Plane_Epsilon))
				return box->Verts[k].Index;
		}
		k = mVertices.size();
		mVertices.PushBack(v);
		VertexGrid::GridVert newVert = {{v.x, v.y, v.z}, k};
		box->Verts.PushBack(newVert);
		return k;
	}
	GridEdge* MakeEdge(const deVec3d& vert1, const deVec3d& vert2)
	{
		return new GridEdge(GetVertIndex(vert1), GetVertIndex(vert2));
	}
	void AddEdge(const GridEdge* edge)
	{
		int e1, e2, size;
		size = mEdges.size();
		e1 = edge->i1 % size;
		e2 = edge->i2 % size;
		mEdges[e1].AddElement(*edge);
		if (e1 != e2)
			mEdges[e2].AddElement(*edge);
	}
	void GetUnequalLoEdges(const GridEdge* sharededge, deTList<GridEdge*>& edges)
	{
		long v0, v1, index;
		v0 = sharededge->i1;
		v1 = sharededge->i2;
		index = v0 % mEdges.size();
		deTList<GridEdge>::iterator it;
		deTList<GridEdge>::iterator stop;
		stop = mEdges[index].end();
		for (it = mEdges[index].begin(); it != stop; ++it)
		{
			if (it->HasVert(v0) && !it->HasVert(v1))
				edges.AddElement(&*it);
		}
	}
	void GetUnequalHiEdges(const GridEdge* sharededge, deTList<GridEdge*>& edges)
	{
		long v0, v1, index;
		v0 = sharededge->i1;
		v1 = sharededge->i2;
		index = v1 % mEdges.size();
		deTList<GridEdge>::iterator it;
		deTList<GridEdge>::iterator stop;
		stop = mEdges[index].end();
		for (it = mEdges[index].begin(); it != stop; ++it)
		{
			if (it->HasVert(v1) && !it->HasVert(v0))
				edges.AddElement(&*it);
		}
	}
	void WeldVertex(deVec3d & v)
	{
		long index = GetVertIndex(v);
		v = mVertices[index];
	}
	void GetVertex(long index, deVec3d & v)
	{
		v = mVertices[index];
	}
};
enum Plane_class_t
{
	PLANE_FRONT,
	PLANE_BACK,
	PLANE_SPAN,
	PLANE_COINCIDE
};
typedef deTArray<BrushPlane_t>::iterator	Plane_Iterator;

deBoolean VertOnEdge(deVec3d vert, const Face_t::Vert& end0, const Face_t::Vert& end1, Face_t::Vert & vOut, deDouble& pVal)
{
	deDouble len, dist;
	deVec3d dir = end1.Pos;
	dir -= end0.Pos;
	len = Vec3d_Normalize(dir);
	{
		deVec3d vert2 = vert;
		vert2 -= end0.Pos;
		Vec3d_Normalize(vert2);
		dist = Vec3d_Dot(vert2, dir) - 1;
		if (dist < -Edge_Epsilon || dist > Edge_Epsilon)
			return deFALSE; // not collinear
	}
	dist = -Vec3d_Dot(dir, end0.Pos);
	pVal = (Vec3d_Dot(dir, vert) + dist);

	pVal = pVal / len;
	if (pVal <= 0 || pVal >= 1)
		return deFALSE; // outside line segment range
	deDouble du = end1.TexCoord.u - end0.TexCoord.u;
	deDouble dv = end1.TexCoord.v - end0.TexCoord.v;
	vOut.TexCoord.u = (deFloat)(end0.TexCoord.u + pVal*du);
	vOut.TexCoord.v = (deFloat)(end0.TexCoord.v + pVal*dv);
	vOut.Pos = vert;

	return deTRUE;
}

void WeldVertices(deTArray<ClippedBrush_t>& clippedBrushes)
{
	LogExportMsg("Welding Vertices\n");

	long numTris = 0;
	long numVerts = 0;
	bool boxinit = false;
	deAABB BBox;
	deTArray<ClippedBrush_t>::iterator clipIt;
	deTArray<ClippedBrush_t>::iterator stopClip = clippedBrushes.end();
	// first build up the size of the grid
	for (clipIt = clippedBrushes.begin(); clipIt != stopClip; ++clipIt)
	{
		int i;
		for (i = 0; i < clipIt->mFaces.size(); ++i)
		{
			Face_t* face = clipIt->mFaces[i];
			if (!face)
			{
				clipIt->mFaces.RemoveElementAt(i);
				--i;
				continue;
			}
			do
			{
				int k, vnum;
				vnum = face->mVertices.size();
				numVerts += vnum;
				numTris += vnum - 2;
				if (!boxinit)
				{
					boxinit = true;
					BBox.Min = face->mVertices[0].Pos;
					BBox.Max = BBox.Min;
				}
				for (k = 0; k < vnum; ++k)
					deGeometry::AABBAddPoint(BBox, face->mVertices[k].Pos);
				face = face->pNext;
			}
			while (face);
		}
	}

	VertexGrid grid(BBox, numVerts);
#pragma fixme(weld vertices right on epsilon border)

	// now add and weld all the vertices
	for (clipIt = clippedBrushes.begin(); clipIt != stopClip; ++clipIt)
	{
		int i, size = clipIt->mFaces.size();
		for (i = 0; i < size; ++i)
		{
			Face_t* face = clipIt->mFaces[i];
			while (face)
			{
				int n, vnum;
				vnum = face->mVertices.size();
				for (n = 0; n < vnum; ++n)
				{
					grid.WeldVertex(face->mVertices[n].Pos);
				}
				face = face->pNext;
			}
		}
	}

	// add all edges
	for (clipIt = clippedBrushes.begin(); clipIt != stopClip; ++clipIt)
	{
		int i, size = clipIt->mFaces.size();
		for (i = 0; i < size; ++i)
		{
			Face_t* face = clipIt->mFaces[i];
			while (face)
			{
				int n, vnum;
				vnum = face->mVertices.size();
				for (n = 0; n < vnum; ++n)
				{
					int k = (n+1)%vnum;
					VertexGrid::GridEdge* edge = grid.MakeEdge(face->mVertices[n].Pos, face->mVertices[k].Pos);
					grid.AddEdge(edge);
					delete edge;
				}
				face = face->pNext;
			}
		}
	}

	LogExportMsg("Fixing T-junctions\n");
	// now weld all the edges
	for (clipIt = clippedBrushes.begin(); clipIt != stopClip; ++clipIt)
	{
		int i, size = clipIt->mFaces.size();
		for (i = 0; i < size; ++i)
		{
			Face_t* face = clipIt->mFaces[i];
			while (face)
			{
				int n, vnum;
				deDouble PVal;
				deTArray<int> newIndices;
				deTArray<Face_t::Vert> newVertices;
				deTArray<deDouble> newVertVals;
				deTList<VertexGrid::GridEdge*> edgeList;
				vnum = face->mVertices.size();
				for (n = 0; n < vnum; ++n)
				{
					int k = (n+1)%vnum;
					Face_t::Vert end0, end1;
					end0 = face->mVertices[n];
					end1 = face->mVertices[k];
					VertexGrid::GridEdge* edge = grid.MakeEdge(end0.Pos, end1.Pos);
					grid.GetUnequalLoEdges(edge, edgeList);
					deTList<VertexGrid::GridEdge*>::iterator edgeIt;
					deTList<VertexGrid::GridEdge*>::iterator stopEdge;
					deVec3d tempvert;
					Face_t::Vert newVert;
					stopEdge = edgeList.end();
					for (edgeIt = edgeList.begin(); edgeIt != stopEdge; ++edgeIt)
					{
						VertexGrid::GridEdge* pEdge = *edgeIt;
						if (pEdge->i1 != edge->i1)
							grid.GetVertex(pEdge->i1, tempvert);
						else
							grid.GetVertex(pEdge->i2, tempvert);
						if (VertOnEdge(tempvert, end0, end1, newVert, PVal))
						{
							newIndices.PushBack(n+1);
							newVertices.PushBack(newVert);
							newVertVals.PushBack(PVal);
						}
					}
					grid.GetUnequalHiEdges(edge, edgeList);
					stopEdge = edgeList.end();
					for (edgeIt = edgeList.begin(); edgeIt != stopEdge; ++edgeIt)
					{
						VertexGrid::GridEdge* pEdge = *edgeIt;
						if (pEdge->i1 != edge->i2)
							grid.GetVertex(pEdge->i1, tempvert);
						else
							grid.GetVertex(pEdge->i2, tempvert);
						if (VertOnEdge(tempvert, end0, end1, newVert, PVal))
						{
							newIndices.PushBack(n+1);
							newVertices.PushBack(newVert);
							newVertVals.PushBack(PVal);
						}
					}
					delete edge;
				}
				int newSize;
				newSize = newVertices.size();
				if (newSize > 0)
				{
					// insert the newly-created vertices into the face
					int k, vertnum, oldSize;
					deTArray<Face_t::Vert> oldVertices = face->mVertices;
					oldSize = oldVertices.size();
					face->mVertices.Resize(oldSize + newSize);
					// sort the new vertices into the correct winding order
					for (n = newSize-1; n > 0; --n)
					{
						for (k = 0; k < n; ++k)
						{
							if (newIndices[k] == newIndices[k+1] && newVertVals[k] > newVertVals[k+1])
							{
								deSwap(newVertVals[k], newVertVals[k+1]);
								deSwap(newVertices[k], newVertices[k+1]);
								deSwap(newIndices[k], newIndices[k+1]);
							}
						}
					}
					n = 0;
					k = 0;
					vertnum = 0;
					for(; n < oldSize && k < newSize; ++vertnum)
					{
						int newIndex = newIndices[k];
						if (newIndex <= n)
						{
							face->mVertices[vertnum] = newVertices[k];
							++k;
						}
						else
						{
							face->mVertices[vertnum] = oldVertices[n];
							++n;
						}
					}
					for(; n < oldSize; ++vertnum)
					{
						face->mVertices[vertnum] = oldVertices[n];
						++n;
					}
					for(; k < newSize; ++vertnum)
					{
						face->mVertices[vertnum] = newVertices[k];
						++k;
					}
				}
				face = face->pNext;
			}
		}
	}
}

Plane_class_t ClassifyPoint(const deVec3d& point, const BrushPlane_t& Plane)
{
	deDouble result = Vec3d_Dot(point, Plane.Normal) + Plane.Dist;
	if (result > Plane_Epsilon)
		return PLANE_FRONT;
	else if (result < -Plane_Epsilon)
		return PLANE_BACK;
	else
		return PLANE_COINCIDE;
}

Plane_class_t ClassifyFace(const Face_t* pFace, const BrushPlane_t& Plane)
{
//#pragma todo (test ClassifyFace) // appears to work
	int front=0, back=0, on=0;
	int k, size;
	deDouble result;
	size = pFace->mVertices.size();
	for (k = 0; k < size; ++k)
	{
		result = Vec3d_Dot(pFace->mVertices[k].Pos, Plane.Normal) + Plane.Dist;
		if (result > Plane_Epsilon)
			++front;
		else if (result < -Plane_Epsilon)
			++back;
		else
			++on;
	}
	if (on == size)
	{
		result = Vec3d_Dot(pFace->Normal, Plane.Normal);
		if (Compare_Epsilon(result, 1, Plane_Epsilon))
			return PLANE_COINCIDE; // co-planar, same normals
		return PLANE_BACK; // co-planar, opposite normals
	}
#if 1
	if (front + on == size)
		return PLANE_FRONT;
	if (back + on == size)
		return PLANE_BACK;
#else
	if (front == size)
		return PLANE_FRONT;
	if (back == size)
		return PLANE_BACK;
#endif
	return PLANE_SPAN;
}

// splits all edges that are on the current plane
deBoolean SplitEdge(Face_t::Vert &vOut, const Face_t::Vert &v0, const Face_t::Vert &v1, const BrushPlane_t& Plane);
void SplitCoincidentalEdges(Face_t *pFace,
							Plane_Iterator planeIt, 
							const Plane_Iterator& stop, 
							const BrushPlane_t& CoincidePlane)
{
	deTArray<int> newIndices;
	deTArray<Face_t::Vert> newVertices;
	for (; planeIt != stop; ++planeIt)
	{
		const BrushPlane_t& currplane = *planeIt;
		// skip coplanar planes
		deDouble dot1 = Vec3d_Dot(currplane.Normal, CoincidePlane.Normal);
		deDouble dot2 = dot1 - 1.0;
		dot1 += 1.0;
		if ((dot1 >= -Plane_Epsilon && dot1 <= Plane_Epsilon) ||
			(dot2 >= -Plane_Epsilon && dot2 <= Plane_Epsilon))
			continue;
		int i, size;
		size = pFace->mVertices.size();
		for (i = 0; i < size; ++i)
		{
			int k = i+1;
			if (k >= size)
				k = 0;
			Plane_class_t class0, class1;
			const Face_t::Vert& v0 = pFace->mVertices[i];
			const Face_t::Vert& v1 = pFace->mVertices[k];
			class0 = ClassifyPoint(v0.Pos, CoincidePlane);
			class1 = ClassifyPoint(v1.Pos, CoincidePlane);
			if (class0 != PLANE_COINCIDE || class1 != PLANE_COINCIDE)
				continue;
			class0 = ClassifyPoint(v0.Pos, currplane);
			class1 = ClassifyPoint(v1.Pos, currplane);
			if (class0 == PLANE_COINCIDE || class1 == PLANE_COINCIDE)
				continue;
			Face_t::Vert newVert;
			if (SplitEdge(newVert, v0, v1, currplane))
			{
				// record the index of the new vert
				newIndices.PushBack(i+1);
				newVertices.PushBack(newVert);
			}
		}
	}
	int newSize;
	newSize = newVertices.size();
	if (newSize > 0)
	{
		// insert the newly-created vertices into the face
		int i,k, vertnum, oldSize;
		deTArray<Face_t::Vert> oldVertices = pFace->mVertices;
		oldSize = oldVertices.size();
		pFace->mVertices.Resize(oldSize + newSize);
//#pragma fixme(add the new vertices in where they need to go)
		i = 0;
		k = 0;
		vertnum = 0;
		for(; i < oldSize && k < newSize; ++vertnum)
		{
			int newIndex = newIndices[k];
			if (newIndex <= i)
			{
				pFace->mVertices[vertnum] = newVertices[k];
				++k;
			}
			else
			{
				pFace->mVertices[vertnum] = oldVertices[i];
				++i;
			}
		}
		for(; i < oldSize; ++vertnum)
		{
			pFace->mVertices[vertnum] = oldVertices[i];
			++i;
		}
		for(; k < newSize; ++vertnum)
		{
			pFace->mVertices[vertnum] = newVertices[k];
			++k;
		}
	}
}

deBoolean SplitEdge(Face_t::Vert &vOut, const Face_t::Vert &v0, const Face_t::Vert &v1, const BrushPlane_t& Plane)
{
	// create a new vertex
	// p = (-dot(n,s) + D)/dot(n,d)
	// d = dir, s = first vert, n = normal, D = dist
	deDouble len;
	deVec3d d = v1.Pos;
	d -= v0.Pos;
	len = Vec3d_Normalize(d);
	deDouble denom = Vec3d_Dot(Plane.Normal, d);
	if (denom >= -Plane_Epsilon && denom <= Plane_Epsilon)
		return deFALSE;
	deDouble p = -(Vec3d_Dot(Plane.Normal, v0.Pos) + Plane.Dist) / denom;
	vOut.Pos = d;
	vOut.Pos *= p;
	vOut.Pos += v0.Pos;
	
	p = p / len;
	if (p < 0 || p > 1)
		return deFALSE;
	deDouble du = v1.TexCoord.u - v0.TexCoord.u;
	deDouble dv = v1.TexCoord.v - v0.TexCoord.v;
	vOut.TexCoord.u = (deFloat)(v0.TexCoord.u + p*du);
	vOut.TexCoord.v = (deFloat)(v0.TexCoord.v + p*dv);
	
	return deTRUE;
}

// splits a face into front and back portions
void SplitFace(const Face_t* pFace, Face_t* &pFront, Face_t* &pBack, const BrushPlane_t& Plane, long & FrontVertSplit)
{
//#pragma todo (test SplitFace further) // appears to work properly

	int i, size = pFace->mVertices.size();
	deTArray<Plane_class_t> classifications(size);
	for (i = 0; i < size; ++i)
	{
		classifications[i] = ClassifyPoint(pFace->mVertices[i].Pos, Plane);
	}
	FrontVertSplit = -1;

	pFront = new Face_t;
	pBack = new Face_t;
	pFront->Normal = pFace->Normal;
	pFront->mTexture = pFace->mTexture;
	pBack->Normal = pFace->Normal;
	pBack->mTexture = pFace->mTexture;
	for (i = 0; i < size; ++i)
	{
		bool bIgnore = false;
		int k = i+1;
		if (k == size)
			k = 0;
		switch (classifications[i])
		{
		case PLANE_FRONT:
			pFront->mVertices.push_back(pFace->mVertices[i]);
			break;
		case PLANE_BACK:
			pBack->mVertices.push_back(pFace->mVertices[i]);
			break;
		case PLANE_COINCIDE:
			pFront->mVertices.push_back(pFace->mVertices[i]);
			pBack->mVertices.push_back(pFace->mVertices[i]);
			break;
		default:
			throw mapException("invalid plane classification in SplitFace");
			break;
		}
		if (classifications[i] == PLANE_COINCIDE && classifications[k] != PLANE_COINCIDE)
		{
			bIgnore = true;
		}
		else if (classifications[k] == PLANE_COINCIDE && classifications[i] != PLANE_COINCIDE)
		{
			bIgnore = true;
		}
		if (!bIgnore && (classifications[i] != classifications[k]))
		{
			// create a new vertex
			Face_t::Vert newVert;
			if (!SplitEdge(newVert, pFace->mVertices[i], pFace->mVertices[k], Plane))
				continue;
			
			if (FrontVertSplit < 0)
				FrontVertSplit = pFront->mVertices.size();

			pFront->mVertices.push_back(newVert);
			pBack->mVertices.push_back(newVert);
		}
	}
}

// clips a face, returns front portion
Face_t* ClipFaceToPlanes(Face_t* pFace, 
						 Plane_Iterator planeIt, 
						 const Plane_Iterator& stop, 
						 bool bClipOnPlane)
{
	if (!pFace)
		return NULL;
	Plane_class_t classify = ClassifyFace(pFace, *planeIt);
	switch (classify)
	{
	case PLANE_FRONT:
		return pFace;
	case PLANE_BACK:
		++planeIt;
		if (planeIt == stop)
		{
			delete pFace;
			return NULL;
		}
		return ClipFaceToPlanes(pFace, planeIt, stop, bClipOnPlane);
	case PLANE_COINCIDE:
		{
			deDouble result = Vec3d_Dot(pFace->Normal, planeIt->Normal) - 1;
			if (!bClipOnPlane && result >= -Plane_Epsilon && result <= Plane_Epsilon)
				return pFace;
			++planeIt;
			if (planeIt == stop)
			{
				delete pFace;
				return NULL;
			}
			return ClipFaceToPlanes(pFace, planeIt, stop, bClipOnPlane);
		}
	case PLANE_SPAN:
		{
			Face_t* front;
			Face_t* back;
			long FrontVert;
			const BrushPlane_t& splitplane = *planeIt;
			// split this face into front and back portions
			SplitFace(pFace, front, back, splitplane, FrontVert);
			++planeIt;
			if (planeIt == stop)
			{
				delete pFace;
				delete back;
				return front; // drop back, return front
			}
			Face_t * BackFrags = ClipFaceToPlanes(back, planeIt, stop, bClipOnPlane);
			if (BackFrags == NULL)
			{
				delete pFace;
				return front; // back was dropped, just return front
			}
			if (BackFrags == back)
			{
				delete front;
				delete back;
				return pFace; // no split was made
			}
			front->pNext = BackFrags;
			delete pFace;
			return front;
		}
	default:
		throw mapException("invalid plane classification in SplitFace");
		break;
	}

	delete pFace;
	return NULL;
}

void ClipBrush(ClippedBrush_t& clipBrush, const Brush_t& planeBrush, bool bClipOnPlane)
{
	if (planeBrush.Invisible)
		return;
	if (!deGeometry::AABBIntersect(clipBrush.BBox, planeBrush.BBox))
		return;
	deTArray<Face_t*>::iterator faceIt = clipBrush.mFaces.begin();
	deTArray<Face_t*>::iterator stopFace = clipBrush.mFaces.end();
	for (; faceIt != stopFace; ++faceIt)
	{
		Plane_Iterator planeIt = planeBrush.mPlanes.begin();
		Plane_Iterator stopPlane = planeBrush.mPlanes.end();
		*faceIt = ClipFaceToPlanes(*faceIt, planeIt, stopPlane, bClipOnPlane);
	}
	int i;
	for (i = 0; i < clipBrush.mFaces.size(); ++i)
	{
		Face_t* face = clipBrush.mFaces[i];
		if (face)
		{
			while (face->pNext)
			{
				clipBrush.mFaces.push_back(face->pNext);
				Face_t* t = face;
				face = face->pNext;
				t->pNext = NULL;
			}
		}
	}
}

void CopyBrush(ClippedBrush_t& target, const Brush_t& source)
{
	if (source.Invisible)
		return;
	int i, size = source.mFaces.size();
	target.mFaces.resize(size);
	for (i = 0; i < size; ++i)
	{
		target.mFaces[i] = new Face_t(source.mFaces[i]);
	}
	target.BBox = source.BBox;
	target.BBox.Min.x -= Plane_Epsilon*2;
	target.BBox.Min.y -= Plane_Epsilon*2;
	target.BBox.Min.z -= Plane_Epsilon*2;
	target.BBox.Max.x += Plane_Epsilon*2;
	target.BBox.Max.y += Plane_Epsilon*2;
	target.BBox.Max.z += Plane_Epsilon*2;
}


} // namespace deMapConverter

