#include <MLR\MLRHeaders.hpp>
#include <ElementRenderer\GridElement.hpp>
#include <ElementRenderer\GroupElement.hpp>
#include <ElementRenderer\ShapeElement.hpp>

#if !defined(HFSLIMMZ_HPP)
	#include "HFslimMZ.hpp"
	#include "terra\terra.hpp"
#endif

//#define NON_DEBUG_BUG 1
#define CHANGE_0625

const Scalar One_Over_Three = 1.0f/3.0f;

extern FILE *analyzeFile;

bool doWater = false;
Scalar waterLevel;
bool doBSP = false;
bool doPrecise = false;
bool doInterest = false;
bool doTileCap = false;
bool doTileCapYovin = false;
bool doDensityMap = false;
bool silentMode = false;

const Scalar smaller = SMALL;

FILE *daWaterObj = NULL;
static int vertexCountObj;

DynamicArrayOf<PointStruct> uniquePoints;
int usedUniquePoints;

PointStruct *TriangleStruct::points = NULL;

DynamicArrayOf<TriangleStruct> triangleIndices;
int usedTriangleIndices;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
template <class T> void	Swap(T& t1, T& t2)
{
	T t;

	t = t1;
	t1 = t2;
	t2 = t;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool IsPowerOf2(int X)
{
	return !( X & ( X - 1 ) );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	FindPoint(Point3D& point)
{
	for(int i=0;i<usedUniquePoints;i++)
	{
		if(Close_Enough(uniquePoints[i].p.x, point.x) && Close_Enough(uniquePoints[i].p.z, point.z))
		{
			return i;
		}
	}

	return -1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	FindAddPoint(Point3D& point)
{
	int closest = -1;
	float closestRadius = 10000000000000000000000000000000.0f;

	for(int i=0;i<usedUniquePoints;i++)
	{
		Vector3D v(
			uniquePoints[i].p.x-point.x,
			uniquePoints[i].p.y-point.y,
			uniquePoints[i].p.z-point.z
		);

		float radius = v.GetLengthSquared();

		if(radius<SMALL)
		{
			if(radius<closestRadius)
			{
				closest = i;
				closestRadius = radius;
			}
		}
	}

	if(closest>=0)
	{
		return closest;
	}

	uniquePoints[usedUniquePoints++].p = point;

	return (usedUniquePoints-1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	RemoveTriangle(TriangleStruct *ts)
{
	int i, j;
	for(i=0;i<usedTriangleIndices;i++)
	{
		if(ts == &triangleIndices[i])
		{
			break;
		}
	}

	if(i>=usedTriangleIndices)
	{
		return false;
	}

	PointStruct *point;
	for(i=0;i<3;i++)
	{
		point = &ts->points[ts->v[i]];

		do
		{
			for(j=0;j<point->connectedTo;j++)
			{
				if(point->myTri[j].tri==ts)
				{
					break;
				}
			}
			if(j<point->connectedTo)
			{
				for(;j<point->connectedTo-1;j++)
				{
					point->myTri[j].tri = point->myTri[j+1].tri;
					point->myTri[j].angle = point->myTri[j+1].angle;
					point->myTri[j].pos = point->myTri[j+1].pos;
				}
				point->connectedTo--;
			}
			else
			{
				break;
			}
		} while(point->connectedTo>0);
	}

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	RemoveTriangle(int i)
{
	int j;

	if(i>=usedTriangleIndices)
	{
		return false;
	}

	TriangleStruct *ts = &triangleIndices[i];

	PointStruct *point;
	for(i=0;i<3;i++)
	{
		point = &ts->points[ts->v[i]];

		do
		{
			for(j=0;j<point->connectedTo;j++)
			{
				if(point->myTri[j].tri==ts)
				{
					break;
				}
			}
			if(j<point->connectedTo)
			{
				for(;j<point->connectedTo-1;j++)
				{
					point->myTri[j].tri = point->myTri[j+1].tri;
					point->myTri[j].angle = point->myTri[j+1].angle;
					point->myTri[j].pos = point->myTri[j+1].pos;
				}
				point->connectedTo--;
			}
			else
			{
				break;
			}
		} while(point->connectedTo>0);
	}

	return true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
float
	GetAngle(Point3D& p0, Point3D& p1, Point3D& p2)
{
	Vector3D v1, v2;

	v1.Subtract(p0, p1);
	v2.Subtract(p2, p1);

	v1.y = 0.0f;
	v2.y = 0.0f;

	Scalar l1 = v1.GetLength(), l2 = v2.GetLength();

	if(Small_Enough(l1*l2))
	{
		return 0.0f;
	}

	l1 = (v1*v2)/(l1*l2);

	l1 = l1 > 1.0f ? 1.0f : l1;
	l1 = l1 < -1.0f ? -1.0f : l1;

	return static_cast<float> (acos(l1));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PointStruct::Add (TriangleStruct *ts, int pos)
{
	Verify(Close_Enough(p.x, ts->GetPoint(pos).x) && Close_Enough(p.z, ts->GetPoint(pos).z));
	myTri[connectedTo].tri = ts;

	myTri[connectedTo].pos = pos;
	myTri[connectedTo].angle = GetAngle(ts->GetPoint((pos-1+3)%3), ts->GetPoint(pos), ts->GetPoint((pos+1)%3));

	Verify(myTri[connectedTo].angle<=Pi);

	if(++connectedTo==myTri.GetLength())
	{
		myTri.SetLength(myTri.GetLength()+12);
	}

	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PointStruct::CalculateAngles()
{
	Check_Object(this);

	for(int i=0;i<connectedTo;i++)
	{
		myTri[i].angle = GetAngle(
			myTri[i].tri->GetPoint((myTri[i].pos-1+3)%3),
			myTri[i].tri->GetPoint(myTri[i].pos),
			myTri[i].tri->GetPoint((myTri[i].pos+1)%3)
		);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
float
	PointStruct::GetAngles()
{
	Check_Object(this);

	float angle = 0.0f;

	if(connectedTo==0)
	{
		return angle;
	}

	for(int j=0;j<connectedTo;j++)
	{
		angle += myTri[j].angle;
	}

	return angle;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
float
	PointStruct::GetExpectedAngles(Scalar xMin, Scalar zMin, Scalar xMax, Scalar zMax)
{
	if( (p.x == xMin || p.x==xMax) &&
		(p.z == zMin || p.z==zMax) )
	{
		return Pi*0.5f;
	}
	else
	if( (p.x == xMin || p.x==xMax) ||
		(p.z == zMin || p.z==zMax) )
	{
		return Pi;
	}
	else
	{
		return 2*Pi;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	PointStruct::CheckAngles(Scalar xMin, Scalar zMin, Scalar xMax, Scalar zMax, Scalar dX, Scalar dZ)
{
	Check_Object(this);

	if(connectedTo==0)
	{
		return true;
	}

	float angle = GetAngles();


	if( (p.x == xMin || p.x==xMax) &&
		(p.z == zMin || p.z==zMax) )
	{
		if(!Close_Enough(angle, Pi*0.5f, 0.005f))
		{
			return false;
		}
	}
	else
	if( (p.x == xMin || p.x==xMax) ||
		(p.z == zMin || p.z==zMax) )
	{
		if(!Close_Enough(angle, Pi, 0.005f))
		{
			return false;
		}
	}
	else
	{
		if(!Close_Enough(angle, 2.0f*Pi, 0.005f))
		{
			if(p.z==zMax-dZ)
			{
				if(!Close_Enough(angle, Pi, 0.005f))
				{
					return false;
				}
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	PointStruct::CheckAngles()
{
	for(int j=0;j<connectedTo;j++)
	{
		if(Close_Enough(myTri[j].angle, Pi))
		{
			return false;
		}
	}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PointStruct::TestInstance() const
{
	for(int i=0;i<connectedTo;i++)
	{
		Verify(myTri[i].tri->v[0] < usedUniquePoints);
		Verify(myTri[i].tri->v[1] < usedUniquePoints);
		Verify(myTri[i].tri->v[2] < usedUniquePoints);
	}
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	TriangleAbstract::GetSurfaceAreaAndCentroid()
{
	//
	//---------------------
	// Set up the variables
	//---------------------
	//
	Point3D
		position_a = Point3D::Identity,
		position_b,
		position_c = Point3D::Identity;
	Vector3D
		leg_1,
		leg_2 = Vector3D::Identity;

	//
	//-----------------------------------
	// Spin through, testing the vertices
	//-----------------------------------
	//
	area = 0.0f;
	center = GetPoint(0);
	centerValid = true;

	plane.normal.x = 0.0f;
	plane.normal.y = 0.0f;
	plane.normal.z = 1.0f;
	plane.offset = 0.0f;

	//
	//-----------------------------------------------
	// Generate all the information on the first pass
	//-----------------------------------------------
	//
	position_a = GetPoint(2);
	position_b = GetPoint(0);
	position_c = GetPoint(1);

	leg_1.Subtract(position_b, position_a);
	leg_2.Subtract(position_c, position_a);

	//
	//-----------------------------------------------------------------
	// Compute the cross-product of the two legs to get the area of the
	// triangle
	//-----------------------------------------------------------------
	//
	Vector3D vcp;
	vcp.Cross(leg_1, leg_2);

	//
	//-------------------------------------------------------------------
	// Add the three triangle points together and multiply by the area of
	// the triangle to give a weighted sum for the polygon centroid
	//-------------------------------------------------------------------
	//
	Point3D centroid;
	centroid.Add(position_a, position_b);
	centroid += position_c;

	Scalar wedge_area = vcp.GetLength() * 0.5f;

	if (area <= SMALL)
	{
		if (wedge_area > SMALL)
		{
			area += wedge_area;
			centroid *= wedge_area;
		}
		center = centroid;
	}
	else
	{
		if (wedge_area > SMALL)
		{
			area += wedge_area;
			centroid *= wedge_area;
			center += centroid;
		}
	}

	if(area > SMALL)
	{
		Point3D p0, p1, p2;

		p0 = GetPoint(0);
		p1 = GetPoint(2);
		p2 = GetPoint(1);
		Vector3D v1,v2;
		v1.Subtract(p1, p0);
		v2.Subtract(p2, p1);

		Vector3D axis;
		axis.Cross(v1, v2);
		if(Small_Enough(axis.GetLength()))
		{
			area = 0.0f;
			return false;
		}

		plane.normal = axis;
		plane.offset = plane.normal * p0;

		center *= (One_Over_Three/area);

		if(plane.normal.y<=Stuff::SMALL)
		{
			area = 0.0f;
			return false;
		}
	}
	else
	{
		center *= One_Over_Three;

		area = 0.0f;
		return false;
	}

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	WaterPools::AddEdge(Stuff::Point3D& p0, Stuff::Point3D& p1, const 
Stuff::Normal3D& normal)
{
	int i, j;
	Verify(p0.y==waterLevel);
	Verify(p1.y==waterLevel);

	int oe0 = OnWhatEdge(p0);
	int oe1 = OnWhatEdge(p1);

	if(Close_Enough(p0, p1, smaller))
	{
		return;
	}

	for(i=0;i<waterEdgePointCount;i++)
	{
		if(Close_Enough(waterEdgePoints[i].p, p0, smaller))
		{
			break;
		}
	}

	if(i==waterEdgePointCount)
	{
		Verify(waterEdgePoints[waterEdgePointCount].toEdgeCount == 0);
		waterEdgePoints[waterEdgePointCount].p = p0;
		waterEdgePoints[waterEdgePointCount].toEdge[waterEdgePoints[waterEdgePointCount].toEdgeCount++] 
= waterEdgeCount;
		waterEdgePoints[waterEdgePointCount].onEdge = oe0;

		waterEdgePointCount++;
	}
	else
	{
		Verify(waterEdgePoints[i].toEdgeCount < 8);
		waterEdgePoints[i].toEdge[waterEdgePoints[i].toEdgeCount++] = 
waterEdgeCount;
	}

	waterEdge[waterEdgeCount].p[0] = i;

	for(j=0;j<waterEdgePointCount;j++)
	{
		if(Close_Enough(waterEdgePoints[j].p, p1, smaller))
		{
			break;
		}
	}

	if(j==waterEdgePointCount)
	{
		Verify(waterEdgePoints[waterEdgePointCount].toEdgeCount == 0);
		waterEdgePoints[waterEdgePointCount].p = p1;
		waterEdgePoints[waterEdgePointCount].toEdge[waterEdgePoints[waterEdgePointCount].toEdgeCount++] 
= waterEdgeCount;
		waterEdgePoints[waterEdgePointCount].onEdge = oe1;

		waterEdgePointCount++;
	}
	else
	{
		Verify(waterEdgePoints[j].toEdgeCount < 8);
		waterEdgePoints[j].toEdge[waterEdgePoints[j].toEdgeCount++] = 
waterEdgeCount;
	}

	waterEdge[waterEdgeCount].p[1] = j;

	waterEdge[waterEdgeCount].normal = normal;

	waterEdgeCount++;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MidLevelRenderer::MLRPrimitiveBase*
	WaterPools::LinkEdges()
{
	if(waterEdgePointCount==0)
	{
		return NULL;
	}

	int i, j;
	for(i=0;i<waterEdgePointCount;i++)
	{
		SPEW(("micgaert", "V%2d: %.12f %.12f", i, waterEdgePoints[i].p.x, 
waterEdgePoints[i].p.z));
	}
	for(i=0;i<waterEdgeCount;i++)
	{
		SPEW(("micgaert", "E%2d: %d %d", i, waterEdge[i].p[0], 
waterEdge[i].p[1]));
	}

	for(i=0;i<waterEdgePointCount;i++)
	{
		if(waterEdgePoints[i].toEdge[1] < 0 && waterEdgePoints[i].onEdge==0)
		{
			SPEW(("micgaert", "OH NO %d", i));
			i = OnWhatEdge(waterEdgePoints[i]);
			break;
		}
	}

	for(i=0;i<waterEdgePointCount;i++)
	{
		waterEdgePoints[i].used = false;
	}

	i = 0;
	int pool = 1, edge = -1;
	int usedPoints = 0;
	bool island = false;

	DynamicArrayOf<Point3D> polygon(2*waterEdgePointCount);
	DynamicArrayOf<Normal3D> normals(2*waterEdgePointCount);

	DynamicArrayOf<WaterEdgePoint> edgePoints[4];
	int vertexCount;
	// find the open pools

	int edgePointCount[4] = { 0, 0, 0, 0};
	int mask = 1;
	for(j=0;j<4;j++)
	{
		edgePoints[j].SetLength(waterEdgePointCount);
		for(i=0;i<waterEdgePointCount;i++)
		{
			if(waterEdgePoints[i].onEdge & mask && waterEdgePoints[i].toEdge[1]==-1)
			{
				edgePoints[j][edgePointCount[j]++] = waterEdgePoints[i];
			}
		}
		mask <<= 1;
	}

	if(edgePointCount[0]>1)
	{
		for(i=edgePointCount[0]-1;i>=0;i--)
		{
			for(j=1;j<=i;j++)
			{
				if(edgePoints[0][j-1].p.z > edgePoints[0][j].p.z)
				{
					Swap(edgePoints[0][j-1], edgePoints[0][j]);
				}
			}
		}
	}
	if(edgePointCount[1]>1)
	{
		for(i=edgePointCount[1]-1;i>=0;i--)
		{
			for(j=1;j<=i;j++)
			{
				if(edgePoints[1][j-1].p.x > edgePoints[1][j].p.x)
				{
					Swap(edgePoints[1][j-1], edgePoints[1][j]);
				}
			}
		}
	}
	if(edgePointCount[2]>1)
	{
		for(i=edgePointCount[2]-1;i>=0;i--)
		{
			for(j=1;j<=i;j++)
			{
				if(edgePoints[2][j-1].p.z < edgePoints[2][j].p.z)
				{
					Swap(edgePoints[2][j-1], edgePoints[2][j]);
				}
			}
		}
	}
	if(edgePointCount[3]>1)
	{
		for(i=edgePointCount[3]-1;i>=0;i--)
		{
			for(j=1;j<=i;j++)
			{
				if(edgePoints[3][j-1].p.x < edgePoints[3][j].p.x)
				{
					Swap(edgePoints[3][j-1], edgePoints[3][j]);
				}
			}
		}
	}

	if(edgePointCount[0] + edgePointCount[1] + edgePointCount[2] + 
edgePointCount[3] > 0)

	do
	{
		for(i=0;i<waterEdgePointCount;i++)
		{
			if(waterEdgePoints[i].used == 0 && waterEdgePoints[i].toEdge[1]==-1)
			{
				break;
			}
		}
		if(i==waterEdgePointCount)
		{
			break;
		}

		vertexCount = 0;

		int next;
		do
		{
			while(waterEdgePoints[i].used == 0)
			{
				waterEdgePoints[i].used = pool;
				usedPoints++;
				polygon[vertexCount] = waterEdgePoints[i].p;
				SPEW(("micgaert", "O:	%6.1f	%6.1f", polygon[vertexCount].x, 
polygon[vertexCount].z));

				if(waterEdgePoints[i].toEdge[0]==edge)
				{
					Verify(waterEdgePoints[i].toEdge[1]!=edge);
					edge = waterEdgePoints[i].toEdge[1];
				}
				else
				{
					edge = waterEdgePoints[i].toEdge[0];
				}

				if(edge==-1)
				{
					normals[vertexCount++] = Normal3D::Down;
					break;
				}
				normals[vertexCount++] = waterEdge[edge].normal;

				if(waterEdge[edge].p[0] != i)
				{
					i = waterEdge[edge].p[0];
				}
				else
				{
					Verify(waterEdge[edge].p[1] != i);
					i = waterEdge[edge].p[1];
				}
				if(i<0)
				{
					break;
				}
			}

			//	finding edges along the tile edge

			int onWhatEdgeFirst = OnWhatEdge(polygon[0]);
			int onWhatEdgeLast = OnWhatEdge(polygon[vertexCount-1]);

			if(onWhatEdgeLast==0)
			{
				SPEW(("micgaert", "OH NO %d", edge));
			}

			mask = 1;

	//
	//	i  mask	inD	s	nx	nz	ns
	//
	//	0	1	1	+	1	0	+
	//	1	2	0	+	0	1	-
	//	2	4	1	-	1	0	-
	//	3	8	0	-	0	1	+
	//
	//
			for(i=0;i<4;i++,mask<<=1)
			{
				if(onWhatEdgeLast & mask)
				{
					break;
					Verify(edgePointCount[i] > 0);
				}
			}
//			Verify(i<4);

			bool cloakWise = false;
			int edgePoint = -1;

			if(i<4)
			do
			{
				int sign = (i>1)?-1:1;

				Scalar inDir[2];
				Vector3D vx(1.0f, 0.0f, 0.0f);
				inDir[0] = normals[vertexCount-2]*vx;

				Vector3D vz(0.0f, 0.0f, 1.0f);
				inDir[1] = normals[vertexCount-2]*vz;

				cloakWise = sign*inDir[(i+1)&1]>0.0f;

				if(polygon[vertexCount-1].x == x0 && polygon[vertexCount-1].z == z0)
				{
					edgePoint = 0;
					Verify(((i==0)&&(cloakWise==true)) || ((i==3)&&(cloakWise==false)));
				}
				else
				{
					if(polygon[vertexCount-1].x == x0 && polygon[vertexCount-1].z == z1)
					{
						edgePoint = 1;
						Verify(((i==1)&&(cloakWise==true)) || ((i==0)&&(cloakWise==false)));
					}
					else
					{
						if(polygon[vertexCount-1].x == x1 && polygon[vertexCount-1].z == z1)
						{
							edgePoint = 2;
							Verify(((i==2)&&(cloakWise==true)) || ((i==1)&&(cloakWise==false)));
						}
						else
						{
							if(polygon[vertexCount-1].x == x1 && polygon[vertexCount-1].z == z0)
							{
								edgePoint = 3;
								Verify(((i==3)&&(cloakWise==true)) || ((i==2)&&(cloakWise==false)));
							}
							else
							{
								edgePoint = -1;
							}
						}
					}
				}

				normals[vertexCount-1] = Normal3D(
					(((i+3)&3)>1?1:-1)*((i+1)&1)*1.0f,
					0.0f,
					(((i+3)&3)>1?1:-1)*(i&1)*1.0f
				);
				Verify(Close_Enough(normals[vertexCount-1].GetLengthSquared(), 1.0f));

				Verify(mask & onWhatEdgeLast);

				int same = -1;
				if(edgePointCount[i]>0)
				{
					for(j=0;j<edgePointCount[i];j++)
					{
						if(Close_Enough(polygon[vertexCount-1], edgePoints[i][j].p, smaller))
						{
							same = j;
							break;
						}


					}
					if(j<edgePointCount[i])
					{
					}
					else
					{
						Verify(edgePoint>=0);
						next = cloakWise? 0 : edgePointCount[i]-1;
						break;
					}
				}
				else
				{
				}

				if(edgePointCount[i]==0)
				{
				}

				bool allOurs = j==edgePointCount[i];


	//						SPEW(("micgaert", "%d %f %f: %.1f %.1f %.1f",
	//							onWhatEdgeLast, inDir[0], inDir[1],
	//							normals[vertexCount-1].x, normals[vertexCount-1].y, 
	//							normals[vertexCount-1].z
	//						));

				int whereTo = 0;
				Point3D newPoint;
				if(sign*inDir[(i+1)&1] > 0.0f)
				{
					if(j>=edgePointCount[i]-1)
					{
						newPoint = Point3D(
							(i==0) ? x0 :
								(i==1) ? (inDir[0]>0.0)? x1:x0 :
									(i==2) ? x1 : (inDir[0]>0.0)? x1:x0,

							waterLevel,
							(i==0) ? (inDir[1]>0.0)? z1:z0 :
								(i==1) ? z1 :
									(i==2) ? (inDir[1]>0.0)? z1:z0 : z0
						);
						SPEW(("micgaert", "O:	%6.1f	%6.1f", newPoint.x, newPoint.z));

						int k;
						for(k=0;k<vertexCount;k++)
						{
							if(Close_Enough(polygon[k], newPoint, smaller))
							{
								break;
							}
						}
						Verify(k==vertexCount);
						polygon[vertexCount++] = newPoint;
						onWhatEdgeLast = OnWhatEdge(polygon[vertexCount-1]);
					}
					else
					{
						next = j+1;
						Verify(next<edgePointCount[i]);
						break;
					}
					whereTo = 1;
					mask<<=1;
					if(mask>8)
					{
						mask = 1;
					}
				}
				else
				{
					if(j==0 || j>=edgePointCount[i])
					{
						newPoint = Point3D(
							(i==0) ? x0 :
								(i==1) ? (inDir[0]>0.0)? x1:x0 :
									(i==2) ? x1 : (inDir[0]>0.0)? x1:x0,

							waterLevel,
							(i==0) ? (inDir[1]>0.0)? z1:z0 :
								(i==1) ? z1 :
									(i==2) ? (inDir[1]>0.0)? z1:z0 : z0
						);

						SPEW(("micgaert", "O:	%6.1f	%6.1f", newPoint.x, newPoint.z));

						int k;
						for(k=0;k<vertexCount;k++)
						{
							if(Close_Enough(polygon[k], newPoint, smaller))
							{
								break;
							}
						}
						Verify(k==vertexCount);
						polygon[vertexCount++] = newPoint;
						onWhatEdgeLast = OnWhatEdge(polygon[vertexCount-1]);
					}
					else
					{
						next = j-1;
						Verify(next<edgePointCount[i]);
						break;
					}
					whereTo = -1;
					mask>>=1;
					if(mask<=0)
					{
						mask = 8;
					}
				}
				Verify(mask>0 && mask<16);
				Verify(onWhatEdgeLast!=0);

				i = (i+4+whereTo)&3;
			} while(1);

			int hello;
			for(hello=0;hello<waterEdgePointCount;hello++)
			{
				if(Close_Enough(waterEdgePoints[hello].p, edgePoints[i][next].p, 
smaller))
				{
					break;
				}
			}

			if(waterEdgePoints[hello].used>0)
			{
				Verify(waterEdgePoints[hello].toEdge[1]==-1);
				break;
			}
			else
			{
				Verify(hello<waterEdgePointCount);
				i = hello;
			};
		} while (1);

//		Verify(vertexCount>2);

		pool++;


		int j, up=0, down=0;
		int upVC=0, downVC=0;
		int bigA = -1;
		Scalar axisL = 1000000.0f ;
		for(j=0;j<vertexCount;j++)
		{
			if(daWaterObj!=NULL && vertexCount>2)
			{
				fprintf(daWaterObj, "v %.5f %.5f %.5f\n", polygon[j].x, waterLevel, 
polygon[j].z);
			}

			int next = (j+1)%vertexCount;

			Normal3D n(normals[j]);
			n.y = 0.0f;
			n.Normalize(n);

			Vector3D v;
			v.Cross(normals[j], normals[next]);

			Scalar l = v.GetLengthSquared();

			if(l>0.0f)
			{
				up++;
				if(l<axisL)
				{
					bigA = j;
				}
			}
			else
			{
				down++;
			}

			v.Cross(polygon[j], polygon[next], polygon[(j+2)%vertexCount]);

			if(v.y>0.0f)
			{
				upVC++;
			}
			else
			{
				downVC++;
			}
		}
		if(daWaterObj!=NULL && vertexCount>2)
		{
			fprintf(daWaterObj, "f");
			if(upVC>downVC)
			{
				for(j=0;j<vertexCount;j++)
				{
					fprintf(daWaterObj, " %d", j+vertexCountObj);
				}
			}
			else
			{
				for(j=vertexCount-1;j>=0;j--)
				{
					fprintf(daWaterObj, " %d", j+vertexCountObj);
				}
			}
			fprintf(daWaterObj, "\n\n");
			vertexCountObj += vertexCount;
		}

		SPEW(("micgaert", "==="));
//		SPEW(("micgaert", "%d %d", up, down));

/*		if(down==0)
		{
			for(j=0;j<vertexCount;j++)
			{
				(*coords)[cCount+j] = polygon[(j+bigA)%vertexCount];
				(*index)[cCount+j] = cCount+j;
			}

			(*lengths)[pCount++] = vertexCount;

			cCount += j;
		}
*/
	} while(usedPoints < waterEdgePointCount);

	// find the closed pools
	if(usedPoints < waterEdgePointCount)
	{
		do
		{
			for(i=0;i<waterEdgePointCount;i++)
			{
				if(waterEdgePoints[i].used == 0 && waterEdgePoints[i].toEdge[1]>=0)
				{
					break;
				}
			}
			if(i==waterEdgePointCount)
			{
				break;
			}

			vertexCount = 0;
			while(waterEdgePoints[i].used == 0)
			{
				waterEdgePoints[i].used = pool;
				usedPoints++;
				polygon[vertexCount] = waterEdgePoints[i].p;

				if(waterEdgePoints[i].toEdge[0]==edge)
				{
					Verify(waterEdgePoints[i].toEdge[1]!=edge);
					edge = waterEdgePoints[i].toEdge[1];
				}
				else
				{
					edge = waterEdgePoints[i].toEdge[0];
				}

				if(edge==-1)
				{
					normals[vertexCount++] = Normal3D::Down;
					break;
				}
				normals[vertexCount++] = waterEdge[edge].normal;

				if(waterEdge[edge].p[0] != i)
				{
					i = waterEdge[edge].p[0];
				}
				else
				{
					Verify(waterEdge[edge].p[1] != i);
					i = waterEdge[edge].p[1];
				}
				if(i<0)
				{
					break;
				}
			}
			pool++;

			// island or pool
			Verify(vertexCount>2);

			int j, up=0, down=0;
			int upVC=0, downVC=0;
			int bigA = -1;
			Scalar axisL = 1000000.0f ;

			for(j=0;j<vertexCount;j++)
			{
				if(daWaterObj!=NULL)
				{
					fprintf(daWaterObj, "v %.5f %.5f %.5f\n", polygon[j].x, waterLevel, 
polygon[j].z);
				}

				int next = (j+1)%vertexCount;

				Normal3D n(normals[j]);
				n.y = 0.0f;
				n.Normalize(n);

				SPEW(("micgaert", "C:	%6.1f	%6.1f	 %6.1f	%6.1f", polygon[j].x, 
polygon[j].z, n.x, n.z));

				Vector3D v;
				v.Cross(normals[j], normals[next]);

				Scalar l = v.GetLengthSquared();

				if(l>0.0f)
				{
					up++;
					if(l<axisL)
					{
						bigA = j;
					}
				}
				else
				{
					down++;
				}

				v.Cross(polygon[j], polygon[next], polygon[(j+2)%vertexCount]);

				if(v.y>0.0f)
				{
					upVC++;
				}
				else
				{
					downVC++;
				}
			}
			SPEW(("micgaert", "==="));
//			SPEW(("micgaert", "%d %d", up, down));

/*			if(down==0)
			{

				for(j=0;j<vertexCount;j++)
				{
					(*coords)[cCount+j] = polygon[(j+bigA)%vertexCount];
					(*index)[cCount+j] = cCount+j;
				}

				(*lengths)[pCount++] = vertexCount;

				cCount += j;
			}
*/
			if(daWaterObj!=NULL)
			{
				fprintf(daWaterObj, "f");

				if(upVC>downVC)
				{
					for(j=0;j<vertexCount;j++)
					{
						fprintf(daWaterObj, " %d", j+vertexCountObj);
					}
				}
				else
				{
					for(j=vertexCount-1;j>=0;j--)
					{
						fprintf(daWaterObj, " %d", j+vertexCountObj);
					}
				}
				fprintf(daWaterObj, "\n\n");
				vertexCountObj += vertexCount;
			}

		} while(usedPoints < waterEdgePointCount);
	}
	SPEW(("micgaert", "==="));
	Verify(usedPoints == waterEdgePointCount);
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	WaterPools::OnWhatEdge(WaterEdgePoint& wep)
{
	return OnWhatEdge(wep.p);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	WaterPools::OnWhatEdge(Point3D& p)
{
	int onWhatEdge = 0;
	if(Close_Enough(p.x, x0, 100.0f*smaller))
	{
		Verify(!Close_Enough(p.x, x1, 100.0f*smaller));
		p.x = x0;
		onWhatEdge |= 1;
	}
	else
	{
		if(Close_Enough(p.x, x1, 100.0f*smaller))
		{
			p.x = x1;
			onWhatEdge |= 4;
		}
	}
	if(Close_Enough(p.z, z0, 100.0f*smaller))
	{
		Verify(!Close_Enough(p.z, z1, 100.0f*smaller));
		p.z = z0;
		onWhatEdge |= 8;
	}
	else
	{
		if(Close_Enough(p.z, z1, 100.0f*smaller))
		{
			p.z = z1;
			onWhatEdge |= 2;
		}
	}

	return onWhatEdge;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HFSlimMZ::HFSlimMZ()
{
	X = (int)0.0f;
	Z = (int)0.0f;

	dX = 2.0f;
	dY = 0.15f;
	dZ = 2.0f;

	Offset = 0.0f;
	visHeight = 0;

	simpleTerrainGroup.SetLength(0);
	waterGroup.SetLength(0);

	borderPixelFun = 0.0f;

	waterTextureName = NULL;
	waterTextureRatio = 4.0f;
	waterMovieFrames = 1;
	waterDetailMovieFrames = 1;

	xCenterOffset = 0.0f;
	zCenterOffset = 0.0f;

	culturalFadeIn = 50.0f;
	culturalFadeOut = 80.0f;

	interestData = NULL;

	mesh = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
HFSlimMZ::~HFSlimMZ()
{
	for(int i=0;i<simpleTerrainGroup.GetLength();i++)
	{
		Unregister_Object(simpleTerrainGroup[i]);
		delete simpleTerrainGroup[i];
		simpleTerrainGroup[i] = NULL;

		Unregister_Object(waterGroup[i]);
		delete waterGroup[i];
		waterGroup[i] = NULL;
	}
	simpleTerrainGroup.SetLength(0);
	waterGroup.SetLength(0);

	interestData.SetLength(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::ExtentBox
	HFSlimMZ::GetExtents()
{
	Stuff::ExtentBox ebox;

	ebox.minX = GetX(0);
	ebox.minZ = GetZ(0);

//	ebox.maxX = GetX(X-1);
//	ebox.maxZ = GetZ(Z-1);

	ebox.maxX = GetX(xDim*256-1);
	ebox.maxZ = GetZ(zDim*256-1);
	
	int i, j;
	Scalar h;
	ebox.maxY = ebox.minY = GetHeight(0, 0);

	for (j=1;j<Z;j++)
		for (i=1;i<X;i++)
		{
			h = GetHeight(i, j);
			if(ebox.maxY < h)
			{
				ebox.maxY = h;
			}
			if(ebox.minY > h)
			{
				ebox.minY = h;
			}
		}

	return ebox;
}

//---------------------------------------------------------------------------
void
	HFSlimMZ::Blur2D (int smooth)
{

    // Precondition for stability:  0 < scale < exp(0.25)
    // Good choice for iterative blurring is scale = exp(0.125)
	float scale = static_cast<float>(exp(0.125));
	float** temp = new float*[Z];
	int x, y;
	for (y = 0; y < Z; y++)
		temp[y] = new float[X];

	for(int i=0;i<smooth;i++)
	{
		float logscale = float(log(scale));

		for (y = 0; y < Z; y++)
		{
			float ryp = y+scale, rym = y-scale;
			int yp = (int) floor(ryp), ym = (int) ceil(rym);

			for (x = 0; x < X; x++)
			{
				float rxp = x+scale, rxm = x-scale;
				int xp = (int) floor(rxp), xm = (int) ceil(rxm);

				// x portion of second central difference
				float xsum = -2*field[y*X+x];
				if ( xp >= X-1 )  // use boundary value
					xsum += field[y*X+X-1];
				else  // linearly interpolate
					xsum += field[y*X+xp]+(rxp-xp)*(field[y*X+xp+1]-field[y*X+xp]);
				if ( xm <= 0 )  // use boundary value
					xsum += field[y*X+0];
				else  // linearly interpolate
					xsum += field[y*X+xm]+(rxm-xm)*(field[y*X+xm]-field[y*X+xm-1]);

				// y portion of second central difference
				float ysum = -2*field[y*X+x];
				if ( yp >= Z-1 )  // use boundary value
					ysum += field[(Z-1)*X+x];
				else  // linearly interpolate
					ysum += field[yp*X+x]+(ryp-yp)*(field[(yp+1)*X+x]-field[yp*X+x]);
				if ( ym <= 0 )  // use boundary value
					ysum += field[0*X+x];
				else  // linearly interpolate
					ysum += field[ym*X+x]+(rym-ym)*(field[ym*X+x]-field[(ym-1)*X+x]);

				temp[y][x] = field[y*X+x]+logscale*(xsum+ysum);
			}
		}

		for (y = 0; y < Z; y++)
			for (x = 0; x < X; x++)
				field[y*X+x] = temp[y][x];
	}

    for (y = 0; y < Z; y++)
        delete[] temp[y];
    delete[] temp;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HFSlimMZ::FillFromImage(Image &image, int smooth)
{
	Verify(image.GetBpp()==8);

	int
		imgX = image.GetWidth(),
		imgZ = image.GetHeight();

	if((imgX%256 != 0) || (imgZ%256 != 0))
	{
		if(silentMode==true)
		{
			fprintf(stderr, "Image width and height has to be a multiple of 256. Program exits !\n");
			exit(-1);
		}
		else
		{
			STOP(("Image width and height has to be a multiple of 256"));
		}
	}

	xDim = imgX/256;
	zDim = imgZ/256;

	int xb = 0, zb = 0;
	if(!IsPowerOf2(xDim) && xDim!=1)
	{
		xb = 2*( (xDim/2) + ((xDim%2) ? 1 : 0));
	}
	else
	{
		xb = xDim;
	}

	if(!IsPowerOf2(zDim) && zDim!=1)
	{
		zb = 2*( (zDim/2) + ((zDim%2) ? 1 : 0));
	}
	else
	{
		zb = zDim;
	}

	if(xb>zb)
	{
		X = xb*256 + 1;
		Z = xb*256 + 1;
	}
	else
	{
		X = zb*256 + 1;
		Z = zb*256 + 1;
	}

	Xoffset = (xb>xDim) ? 256 : 0;
	Zoffset = (zb>zDim) ? 256 : 0;

	field.SetLength(X*Z);

	BYTE *ptr;
	ptr = (BYTE *)image.Lock();

	int i, j;
	BYTE c;

#if 1
	for(i=0;i<imgZ;i++)
	{
		for(j=0;j<imgX;j++)
		{
			field[(i+Zoffset)*X+j+Xoffset] = ptr[(imgZ-i-1)*imgX + imgX-j-1];
		}
	}

//============== filling in the corners ====================
	c = ptr[(imgZ-1)*imgX + imgX-1];
	for(i=0;i<Zoffset;i++)
	{
		for(j=0;j<Xoffset;j++)
		{
			field[i*X+j] = c;
		}
	}
	c = ptr[(imgZ-1)*imgX + 0];
	for(i=0;i<Zoffset;i++)
	{
		for(j=Xoffset+imgX;j<X;j++)
		{
			field[i*X+j] = c;
		}
	}
	c = ptr[imgX-1];
	for(i=Zoffset+imgZ;i<Z;i++)
	{
		for(j=0;j<Xoffset;j++)
		{
			field[i*X+j] = c;
		}
	}
	c = ptr[0];
	for(i=Zoffset+imgZ;i<Z;i++)
	{
		for(j=Xoffset+imgX;j<X;j++)
		{
			field[i*X+j] = c;
		}
	}

//============== filling in the sides ====================
	for(i=Zoffset;i<Zoffset+imgZ;i++)
	{
		c = ptr[(imgZ-1-i+Zoffset)*imgX + imgX-1];
		for(j=0;j<Xoffset;j++)
		{
			field[i*X+j] = c;
		}
	}
	for(j=Xoffset;j<Xoffset+imgX;j++)
	{
		c = ptr[(imgZ-1)*imgX + imgX-1-j+Xoffset];
		for(i=0;i<Zoffset;i++)
		{
			field[i*X+j] = c;
		}
	}
	for(i=Zoffset;i<Zoffset+imgZ;i++)
	{
		c = ptr[(imgZ-1-i+Zoffset)*imgX];
		for(j=Xoffset+imgX;j<X;j++)
		{
			field[i*X+j] = c;
		}
	}
	for(j=Xoffset;j<Xoffset+imgX;j++)
	{
		c = ptr[imgX-1-j+Xoffset];
		for(i=Zoffset+imgZ;i<Z;i++)
		{
			field[i*X+j] = c;
		}
	}
#else
	for(i=0;i<Z-1;i++)
	{
		for(j=0;j<X-1;j++)
		{
			field[i*X+j] = ptr[(image.GetHeight()-i-1)*(X-1)+image.GetWidth()-j-1];
		}
		field[i*X+j] = field[i*X+j-1];
	}
	for(j=0;j<X-1;j++)
	{
		field[i*X+j] = field[(i-1)*X+j];
	}
	field[i*X+j] = field[i*X+j-1];
#endif

	image.UnLock();

	Blur2D(smooth);
/*
	Image testImage(X, Z, ITYPE_INDEXED);;
	testImage.MakeGrayscalePalette();
	ptr = testImage.Lock();

	for(i=0;i<X*Z;i++)
	{
		ptr[i] = field[i];
	}
	testImage.UnLock();
	testImage.SaveGif("c:\\test1.gif");
*/
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HFSlimMZ::FillInterestDataFromImage(Image &image)
{
	Verify(image.GetBpp()>8);

	int
		imgX = image.GetWidth(),
		imgZ = image.GetHeight();

	if((imgX%256 != 0) || (imgZ%256 != 0))
	{
		if(silentMode==true)
		{
			fprintf(stderr, "Interest data image width and height has to be a multiple of 256. Program exits !\n");
			exit(-1);
		}
		else
		{
			STOP(("Interest data image width and height has to be a multiple of 256"));
		}
	}

	xDim = imgX/256;
	zDim = imgZ/256;

	int xb = 0, zb = 0;
	if(!IsPowerOf2(xDim) && xDim!=1)
	{
		xb = 2*( (xDim/2) + ((xDim%2) ? 1 : 0));
	}
	else
	{
		xb = xDim;
	}

	if(!IsPowerOf2(zDim) && zDim!=1)
	{
		zb = 2*( (zDim/2) + ((zDim%2) ? 1 : 0));
	}
	else
	{
		zb = zDim;
	}

	if(xb>zb)
	{
		X = xb*256 + 1;
		Z = xb*256 + 1;
	}
	else
	{
		X = zb*256 + 1;
		Z = zb*256 + 1;
	}

	Xoffset = (xb>xDim) ? 256 : 0;
	Zoffset = (zb>zDim) ? 256 : 0;

	interestData.SetLength(X*Z);

	BYTE *ptr;
	ptr = (BYTE *)image.Lock();

	int i, j;
	BYTE c;
	int cd = image.GetBpp()>>3;

#if 1
	for(i=0;i<imgZ;i++)
	{
		for(j=0;j<imgX;j++)
		{
			interestData[(i+Zoffset)*X+j+Xoffset] = ptr[cd*((imgZ-i-1)*imgX + 
imgX-j-1)+1];
		}
	}

//============== filling in the corners ====================
	c = ptr[cd*((imgZ-1)*imgX + imgX-1)+1];
	for(i=0;i<Zoffset;i++)
	{
		for(j=0;j<Xoffset;j++)
		{
			interestData[i*X+j] = c;
		}
	}
	c = ptr[cd*((imgZ-1)*imgX + 0)+1];
	for(i=0;i<Zoffset;i++)
	{
		for(j=Xoffset+imgX;j<X;j++)
		{
			interestData[i*X+j] = c;
		}
	}
	c = ptr[cd*(imgX-1)+1];
	for(i=Zoffset+imgZ;i<Z;i++)
	{
		for(j=0;j<Xoffset;j++)
		{
			interestData[i*X+j] = c;
		}
	}
	c = ptr[cd*(0)+1];
	for(i=Zoffset+imgZ;i<Z;i++)
	{
		for(j=Xoffset+imgX;j<X;j++)
		{
			interestData[i*X+j] = c;
		}
	}

//============== filling in the sides ====================
	for(i=Zoffset;i<Zoffset+imgZ;i++)
	{
		c = ptr[cd*((imgZ-1-i+Zoffset)*imgX + imgX-1)+1];
		for(j=0;j<Xoffset;j++)
		{
			interestData[i*X+j] = c;
		}
	}
	for(j=Xoffset;j<Xoffset+imgX;j++)
	{
		c = ptr[cd*((imgZ-1)*imgX + imgX-1-j+Xoffset)+1];
		for(i=0;i<Zoffset;i++)
		{
			interestData[i*X+j] = c;
		}
	}
	for(i=Zoffset;i<Zoffset+imgZ;i++)
	{
		c = ptr[cd*((imgZ-1-i+Zoffset)*imgX)+1];
		for(j=Xoffset+imgX;j<X;j++)
		{
			interestData[i*X+j] = c;
		}
	}
	for(j=Xoffset;j<Xoffset+imgX;j++)
	{
		c = ptr[cd*(imgX-1-j+Xoffset)+1];
		for(i=Zoffset+imgZ;i<Z;i++)
		{
			interestData[i*X+j] = c;
		}
	}
#else
	for(i=0;i<Z-1;i++)
	{
		for(j=0;j<X-1;j++)
		{
			interestData[i*X+j] = 
ptr[cd*((image.GetHeight()-i-1)*(X-1)+image.GetWidth()-j-1];
		}
		interestData[i*X+j] = interestData[i*X+j-1];
	}
	for(j=0;j<X-1;j++)
	{
		interestData[i*X+j] = interestData[(i-1)*X+j];
	}
	interestData[i*X+j] = interestData[i*X+j-1];
#endif

	image.UnLock();
/*
	Image testImage(X, Z, ITYPE_INDEXED);;
	testImage.MakeGrayscalePalette();
	ptr = testImage.Lock();

	for(i=0;i<X*Z;i++)
	{
		ptr[i] = interestData[i];
	}
	testImage.UnLock();
	testImage.SaveGif("c:\\test_id.gif");
*/
}

int triangleCounter = 0;

Scalar xGridSize, zGridSize;
int tX, tY;

void GetFacesCB(Triangle &tri,void *cbp)
{
	ToDrawTriangle *tdtrilist = (ToDrawTriangle *)cbp;
	tdtrilist[triangleCounter].SetPoint(0,(float)tri.point1()[0],0.0f,(float)tri.point1()[1]);
	tdtrilist[triangleCounter].SetPoint(1,(float)tri.point2()[0],0.0f,(float)tri.point2()[1]);
	tdtrilist[triangleCounter].SetPoint(2,(float)tri.point3()[0],0.0f,(float)tri.point3()[1]);

	triangleCounter++;
}

void GetFacesCBPrecise(Triangle &tri, void*)
{
	Point3D point;

	point.x = (float)tri.point1()[0];
	point.y = 0.0f;
	point.z = (float)tri.point1()[1];

	triangleIndices[usedTriangleIndices].v[0] = FindAddPoint(point);

	point.x = (float)tri.point3()[0];
	point.y = 0.0f;
	point.z = (float)tri.point3()[1];

	triangleIndices[usedTriangleIndices].v[2] = FindAddPoint(point);

	point.x = (float)tri.point2()[0];
	point.y = 0.0f;
	point.z = (float)tri.point2()[1];

	triangleIndices[usedTriangleIndices].v[1] = FindAddPoint(point);

	usedTriangleIndices++;
}

#define TERRAIN2_TEST

bool
	HFSlimMZ::CreateMesh(
		ElementRenderer::ListElement *parent,
		int listIndex,
		int diffLevel,
		DynamicArrayOf<TriangleAbstract*>& tdtrilist,
		frameArray *frame,
		MLRState *state
	)
{
	int polygon_count = tdtrilist.GetLength();
	if(polygon_count==0)
	{
		return false;
	}

	gos_PushCurrentHeap(MLR_Terrain2::PrimitiveHeap);
	MLR_Terrain2 *erf_mesh = new MLR_Terrain2;
	gos_PopCurrentHeap();

	erf_mesh->SetDepthData(maxAllDepth-diffLevel, maxAllDepth);
	erf_mesh->SetTileData(tX, tY);
#ifdef BORDERPIXEL
	erf_mesh->SetBorderPixel(borderPixelFun);
#endif

//	erf_mesh->SetOffset(xOffset, zOffset);
//	erf_mesh->SetGrid(xGridSize, zGridSize);
	for(int ii=0;ii<8;ii++)
	{
		erf_mesh->SetFrame(
			ii,
			(*frame)[ii][0] - xCenterOffset - Xoffset*dX,
			(*frame)[ii][1] - zCenterOffset - Zoffset*dZ,
			(*frame)[ii][2] - xCenterOffset - Xoffset*dX,
			(*frame)[ii][3] - zCenterOffset - Zoffset*dZ
		);
	}

	Register_Object(erf_mesh);

	erf_mesh->SetDetailData(
		detailInfo.xOff,
		detailInfo.zOff,
		detailInfo.xFac*(1<<diffLevel),
		detailInfo.zFac*(1<<diffLevel),
		detailInfo.dStart,
		detailInfo.dEnd
	);

    DynamicArrayOf<int> vert_id(X*Z);
	memset(vert_id.GetData(), 0, vert_id.GetSize());

	int i, j, x, z, point_count = 0;

	int minX[2]={0,0},
		maxX[2]={0,0},
		minY[2]={0,0},
		maxY[2]={0,0},
		minZ[2]={0,0},
		maxZ[2]={0,0};
	Scalar
		minx=tdtrilist[0]->GetPoint(0).x,
		maxx=tdtrilist[0]->GetPoint(0).x,
		miny=tdtrilist[0]->GetPoint(0).y,
		maxy=tdtrilist[0]->GetPoint(0).y,
		minz=tdtrilist[0]->GetPoint(0).z,
		maxz=tdtrilist[0]->GetPoint(0).z;

	Point3D
		center0 = Point3D::Identity,
		center1 = Point3D::Identity,
		center2 = Point3D::Identity,
		center3 = Point3D::Identity;

	for(i=0;i<polygon_count;++i)
	{
		if(tdtrilist[i]->centerValid==false)
		{
			SPEWALWAYS(("micgaert", "Found invalid center!"));
			if(false==tdtrilist[i]->GetSurfaceAreaAndCentroid())
			{
				continue;
			}
		}

		center3 += tdtrilist[i]->center;

		if(tdtrilist[i]->GetPoint(0).x < minx)
		{
			minX[0] = i;
			minX[1] = 0;
			minx = tdtrilist[i]->GetPoint(0).x;
		} else if(tdtrilist[i]->GetPoint(0).x > maxx)
		{
			maxX[0] = i;
			maxX[1] = 0;
			maxx = tdtrilist[i]->GetPoint(0).x;
		}
		if(tdtrilist[i]->GetPoint(0).y < miny)
		{
			minY[0] = i;
			minY[1] = 0;
			miny = tdtrilist[i]->GetPoint(0).y;
		} else if(tdtrilist[i]->GetPoint(0).y > maxy)
		{
			maxY[0] = i;
			maxY[1] = 0;
			maxy = tdtrilist[i]->GetPoint(0).y;
		}
		if(tdtrilist[i]->GetPoint(0).z < minz)
		{
			minZ[0] = i;
			minZ[1] = 0;
			minz = tdtrilist[i]->GetPoint(0).z;
		} else if(tdtrilist[i]->GetPoint(0).z > maxz)
		{
			maxZ[0] = i;
			maxZ[1] = 0;
			maxz = tdtrilist[i]->GetPoint(0).z;
		}

		x = (int)(tdtrilist[i]->GetPoint(0).x/dX);
		z = (int)(tdtrilist[i]->GetPoint(0).z/dZ);

		vert_id[x + X*z]++;
		if(vert_id[x + X*z]==1)
		{
			center0 += tdtrilist[i]->GetPoint(0);
			point_count++;
		}

		if(tdtrilist[i]->GetPoint(2).x < minx)
		{
			minX[0] = i;
			minX[1] = 2;
			minx = tdtrilist[i]->GetPoint(2).x;
		} else if(tdtrilist[i]->GetPoint(2).x > maxx)
		{
			maxX[0] = i;
			maxX[1] = 2;
			maxx = tdtrilist[i]->GetPoint(2).x;
		}
		if(tdtrilist[i]->GetPoint(2).y < miny)
		{
			minY[0] = i;
			minY[1] = 2;
			miny = tdtrilist[i]->GetPoint(2).y;
		} else if(tdtrilist[i]->GetPoint(2).y > maxy)
		{
			maxY[0] = i;
			maxY[1] = 2;
			maxy = tdtrilist[i]->GetPoint(2).y;
		}
		if(tdtrilist[i]->GetPoint(2).z < minz)
		{
			minZ[0] = i;
			minZ[1] = 2;
			minz = tdtrilist[i]->GetPoint(2).z;
		} else if(tdtrilist[i]->GetPoint(2).z > maxz)
		{
			maxZ[0] = i;
			maxZ[1] = 2;
			maxz = tdtrilist[i]->GetPoint(2).z;
		}
		x = (int)(tdtrilist[i]->GetPoint(2).x/dX);
		z = (int)(tdtrilist[i]->GetPoint(2).z/dZ);
		vert_id[x + X*z]++;
		if(vert_id[x + X*z]==1)
		{
			center0 += tdtrilist[i]->GetPoint(1);
			point_count++;
		}

		if(tdtrilist[i]->GetPoint(1).x < minx)
		{
			minX[0] = i;
			minX[1] = 1;
			minx = tdtrilist[i]->GetPoint(1).x;
		} else if(tdtrilist[i]->GetPoint(1).x > maxx)
		{
			maxX[0] = i;
			maxX[1] = 1;
			maxx = tdtrilist[i]->GetPoint(1).x;
		}
		if(tdtrilist[i]->GetPoint(1).y < miny)
		{
			minY[0] = i;
			minY[1] = 1;
			miny = tdtrilist[i]->GetPoint(1).y;
		} else if(tdtrilist[i]->GetPoint(1).y > maxy)
		{
			maxY[0] = i;
			maxY[1] = 1;
			maxy = tdtrilist[i]->GetPoint(1).y;
		}
		if(tdtrilist[i]->GetPoint(1).z < minz)
		{
			minZ[0] = i;
			minZ[1] = 1;
			minz = tdtrilist[i]->GetPoint(1).z;
		} else if(tdtrilist[i]->GetPoint(1).z > maxz)
		{
			maxZ[0] = i;
			maxZ[1] = 1;
			maxz = tdtrilist[i]->GetPoint(1).z;
		}
		x = (int)(tdtrilist[i]->GetPoint(1).x/dX);
		z = (int)(tdtrilist[i]->GetPoint(1).z/dZ);
		vert_id[x + X*z]++;
		if(vert_id[x + X*z]==1)
		{
			center0 += tdtrilist[i]->GetPoint(2);
			point_count++;
		}
	}

	center0 *= 1.0f/point_count;
	center0.x = GetX(center0.x);
	center0.y += Offset;
	center0.z = GetZ(center0.z);

	center3 *= 1.0f/polygon_count;
	center3.x = GetX(center3.x);
	center3.y += Offset;
	center3.z = GetZ(center3.z);

	Vector3D v3;
	Scalar d[3];

	v3.Subtract(tdtrilist[maxX[0]]->GetPoint(maxX[1]), tdtrilist[minX[0]]->GetPoint(minX[1]));
	d[0] = v3.GetLengthSquared();

	v3.Subtract(tdtrilist[maxY[0]]->GetPoint(maxY[1]), tdtrilist[minY[0]]->GetPoint(minY[1]));
	d[1] = v3.GetLengthSquared();

	v3.Subtract(tdtrilist[maxZ[0]]->GetPoint(maxZ[1]), tdtrilist[minZ[0]]->GetPoint(minZ[1]));
	d[2] = v3.GetLengthSquared();

	if(d[0] > d[1])
	{
		if(d[0]>d[2])
		{
			center1 = tdtrilist[maxX[0]]->GetPoint(maxX[1]);
			center1 += tdtrilist[minX[0]]->GetPoint(minX[1]);
			center1 *= 0.5f;
		}
		else
		{
			center1 = tdtrilist[maxZ[0]]->GetPoint(maxZ[1]);
			center1 += tdtrilist[minZ[0]]->GetPoint(minZ[1]);
			center1 *= 0.5f;
		}
	}
	else
	{
		if(d[1]>d[2])
		{
			center1 = tdtrilist[maxY[0]]->GetPoint(maxY[1]);
			center1 += tdtrilist[minY[0]]->GetPoint(minY[1]);
			center1 *= 0.5f;
		}
		else
		{
			center1 = tdtrilist[maxZ[0]]->GetPoint(maxZ[1]);
			center1 += tdtrilist[minZ[0]]->GetPoint(minZ[1]);
			center1 *= 0.5f;
		}
	}

	center1.x = GetX(center1.x);
	center1.y += Offset;
	center1.z = GetZ(center1.z);

	center2.x = GetX((maxx+minx)*0.5f);
	center2.y = (maxy+miny)*0.5f;
	center2.z = GetZ((maxz+minz)*0.5f);

	center2.y += Offset;

	Point3D *coords = new Point3D [point_count];
	Vector2DScalar *texCoords = new Vector2DScalar[point_count];

	UnitVector3D sun;
	sun = Vector3D(-1.0f, -1.0f, 0.0f);

    int nrOfPoints = 0;
	Scalar radiusSquared,
		maxRadius0 = 0.0f,
		maxRadius1 = 0.0f,
		maxRadius2 = 0.0f,
		maxRadius3 = 0.0f;

	for(z=0;z<Z;z++)
		for(x=0;x<X;x++)
		{
			if( vert_id[x + X*z] > 0 )
			{
				Verify(nrOfPoints<point_count);
				vert_id[z*X+x] = nrOfPoints;

				Verify(GetX(x)<=(*frame)[maxAllDepth][2] && 
GetX(x)>=(*frame)[maxAllDepth][0]);
				Verify(GetZ(z)<=(*frame)[maxAllDepth][3] && 
GetZ(z)>=(*frame)[maxAllDepth][1]);

				coords[nrOfPoints].x = GetX(x);
				coords[nrOfPoints].y = GetHeight(x, z);
				coords[nrOfPoints].z = GetZ(z);

				v3.Subtract(coords[nrOfPoints], center0);

				radiusSquared = v3.GetLengthSquared();
				maxRadius0 = radiusSquared>maxRadius0 ? radiusSquared:maxRadius0;

				v3.Subtract(coords[nrOfPoints], center1);

				radiusSquared = v3.GetLengthSquared();
				maxRadius1 = radiusSquared>maxRadius1 ? radiusSquared:maxRadius1;

				v3.Subtract(coords[nrOfPoints], center2);

				radiusSquared = v3.GetLengthSquared();
				maxRadius2 = radiusSquared>maxRadius2 ? radiusSquared:maxRadius2;

				v3.Subtract(coords[nrOfPoints], center3);

				radiusSquared = v3.GetLengthSquared();
				maxRadius3 = radiusSquared>maxRadius3 ? radiusSquared:maxRadius3;

#ifndef TERRAIN2_TEST
				texCoords[nrOfPoints][0] =
					borderPixelFun + (1.0f-2*borderPixelFun)*((uvFrame->maxX - 
coords[nrOfPoints).x)/(uvFrame->maxX-uvFrame->minX));
				texCoords[nrOfPoints][1] =
					borderPixelFun + (1.0f-2*borderPixelFun)*((uvFrame->maxZ - 
coords[nrOfPoints).z)/(uvFrame->maxZ-uvFrame->minZ));
#endif
				nrOfPoints++;
			}
			else
				vert_id[z*X+x] = -1;
		}

	if(nrOfPoints >= Limits::Max_Number_Vertices_Per_Mesh)
	{
		return false;
	}

	maxRadius0 = static_cast<Scalar>(sqrt(maxRadius0));
	maxRadius1 = static_cast<Scalar>(sqrt(maxRadius1));
	maxRadius2 = static_cast<Scalar>(sqrt(maxRadius2));
	maxRadius3 = static_cast<Scalar>(sqrt(maxRadius3));

	Point3D center;
	Scalar maxRadius;

	if(maxRadius0<maxRadius1)
	{
		if(maxRadius0<maxRadius2)
		{
			if(maxRadius0<maxRadius3)
			{
				maxRadius = maxRadius0;
				center = center0;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
		else
		{
			if(maxRadius2<maxRadius3)
			{
				maxRadius = maxRadius2;
				center = center2;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
	}
	else
	{
		if(maxRadius1<maxRadius2)
		{
			if(maxRadius1<maxRadius3)
			{
				maxRadius = maxRadius1;
				center = center1;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
		else
		{
			if(maxRadius2<maxRadius3)
			{
				maxRadius = maxRadius2;
				center = center2;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
	}

	BYTE *index = new BYTE [polygon_count*3];

	int k, l;

	for(i=0,l=0;i<polygon_count;i++)
	{
		k = 0;

		x = (int)(tdtrilist[i]->GetPoint(0).x/dX);
		z = (int)(tdtrilist[i]->GetPoint(0).z/dZ);
		j = x + X*z;
		Verify(vert_id[j]>=0);
		index[l++] = vert_id[j];

		if(z==Z-1)
			k++;

		x = (int)(tdtrilist[i]->GetPoint(2).x/dX);
		z = (int)(tdtrilist[i]->GetPoint(2).z/dZ);
		j = x + X*z;
		Verify(vert_id[j]>=0);
		index[l++] = vert_id[j];
		if(z==Z-1)
			k++;

		x = (int)(tdtrilist[i]->GetPoint(1).x/dX);
		z = (int)(tdtrilist[i]->GetPoint(1).z/dZ);
		j = x + X*z;
		Verify(vert_id[j]>=0);
		index[l++] = vert_id[j];
		if(z==Z-1)
			k++;

		if(k>=2)
		{
			j = 0;
		}

		if(
			index[l-3]==index[l-2] ||
			index[l-2]==index[l-1] ||
			index[l-1]==index[l-3]
		)
		{
			l-=3;
			continue;
		}

		Vector3D vCross;
		vCross.Cross(
			coords[index[l-1]],
			coords[index[l-2]],
			coords[index[l-3]]
		);

		if(Small_Enough(vCross) || vCross.y<=SMALL)
		{
			l-=3;
			continue;
		}
	}

	if(l<3)
	{
		erf_mesh->DetachReference();
		return false;
	}

	gos_PushCurrentHeap(MLR_Terrain2::PrimitiveHeap);
	erf_mesh->SetSubprimitiveLengths(NULL, l/3);

	Scalar xOffset2 = xCenterOffset + Xoffset*dX;
	Scalar zOffset2 = zCenterOffset + Zoffset*dZ;
	for(i=0;i<nrOfPoints;i++)
	{
		coords[i].x -= xOffset2;
		coords[i].z -= zOffset2;
	}

	erf_mesh->SetCoordData(coords, nrOfPoints);

#ifdef TERRAIN2_TEST
	erf_mesh->SetCurrentDepth(maxAllDepth-diffLevel);
#else
	erf_mesh->SetTexCoordData(texCoords, nrOfPoints);
#endif

	erf_mesh->SetIndexData(index, l);
	gos_PopCurrentHeap();

//	delete [] coords;
//	delete [] texCoords;
//	delete [] index;

	erf_mesh->FindFacePlanes();

	if(state)
	{
		erf_mesh->SetReferenceState(*state);
	}
	else
	{
		erf_mesh->SetReferenceState(state0);
	}

	erf_mesh->SetReferenceState(state1, 1);

	gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);
	ElementRenderer::ShapeElement* Shape = new ElementRenderer::ShapeElement;
	Register_Object(Shape);
	gos_PopCurrentHeap();

	gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
	MLRShape *shape = new MLRShape(1);
	Register_Object(shape);
	gos_PopCurrentHeap();

	shape->Add(erf_mesh);
	erf_mesh->DetachReference();

	Shape->SetMLRShape(shape);

	const Point3D *p = NULL;
	int nr_of_points = 0;
	erf_mesh->GetCoordData(&p, &nr_of_points);
	ReadOnlyArrayOf<Point3D> p_array;
	p_array.AssignData(p, nr_of_points);
	Shape->m_localOBB.ComputeBounds(p_array);
	Shape->SetOBBMode();
		
/*		
		.localToParent = LinearMatrix4D::Identity;

	center.x -= xCenterOffset + Xoffset*dX;
	center.z -= zCenterOffset + Zoffset*dZ;

	Shape->m_localOBB.localToParent.BuildTranslation(center);

	Shape->m_localOBB.sphereRadius = maxRadius;
*/
	parent->AttachIndexedChild(listIndex, Shape);
	Shape->SetVolumeCullMode();

	return true;
}

bool
	HFSlimMZ::CreateMesh2(
		ElementRenderer::ListElement *parent,
		int listIndex,
		int diffLevel,
		DynamicArrayOf<TriangleAbstract*>& tdtrilist,
		frameArray *frame,
		MLRState *state,
		bool underWater
	)
{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCreateMesh2 parameters: parent = 0x%08X, listIndex = %d, diffLevel = %d,"
									"\n    &tdtrilist=0x%08X, frame = 0x%08X, MLRState = 0x%08X, underwater = %s",
										(DWORD)(void*)parent, listIndex, diffLevel, (DWORD)(void*)&tdtrilist,
										(DWORD)(void*)frame, (DWORD)(void*)state, underWater ? "TRUE" : "FALSE");
#endif
//#undef NON_DEBUG_BUG
	int polygon_count = tdtrilist.GetLength();
	if(polygon_count==0)
	{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nReturning right away with false");
#endif

		return false;
	}

//	SPEWALWAYS(("micgaert", "#cm0"));

	gos_PushCurrentHeap(MLR_Terrain2::PrimitiveHeap);
	MLR_Terrain2 *erf_mesh = new MLR_Terrain2;
	gos_PopCurrentHeap();

	erf_mesh->SetDepthData(maxAllDepth-diffLevel, maxAllDepth);
	erf_mesh->SetTileData(tX, tY);
#ifdef BORDERPIXEL
	erf_mesh->SetBorderPixel(borderPixelFun);
#endif

//	erf_mesh->SetOffset(xOffset, zOffset);
//	erf_mesh->SetGrid(xGridSize, zGridSize);
	for(int ii=0;ii<8;ii++)
	{
		erf_mesh->SetFrame(
			ii,
			(*frame)[ii][0] - xCenterOffset - Xoffset*dX,
			(*frame)[ii][1] - zCenterOffset - Zoffset*dZ,
			(*frame)[ii][2] - xCenterOffset - Xoffset*dX,
			(*frame)[ii][3] - zCenterOffset - Zoffset*dZ
		);

	}

	Register_Object(erf_mesh);

	erf_mesh->SetDetailData(
		detailInfo.xOff,
		detailInfo.zOff,
		detailInfo.xFac*(1<<diffLevel),
		detailInfo.zFac*(1<<diffLevel),
		detailInfo.dStart,
		detailInfo.dEnd
	);

    DynamicArrayOf<int> vert_id(X*Z);
	DynamicArrayOf<Point3D> l_uniquePoints(Limits::Max_Number_Vertices_Per_Mesh);

	BYTE *index = new BYTE [polygon_count*3];

	int nrOfUniquePoints = 0;

//	SPEWALWAYS(("micgaert", "#cm1"));
	memset(vert_id.GetData(), 0, vert_id.GetSize());

	int i, j, k;

	int minX[2]={0,0},
		maxX[2]={0,0},
		minY[2]={0,0},
		maxY[2]={0,0},
		minZ[2]={0,0},
		maxZ[2]={0,0};
	Scalar
		minx=tdtrilist[0]->GetPoint(0).x,
		maxx=tdtrilist[0]->GetPoint(0).x,
		miny=tdtrilist[0]->GetPoint(0).y,
		maxy=tdtrilist[0]->GetPoint(0).y,
		minz=tdtrilist[0]->GetPoint(0).z,
		maxz=tdtrilist[0]->GetPoint(0).z;

	Point3D
		center0 = Point3D::Identity,
		center1 = Point3D::Identity,
		center2 = Point3D::Identity,
		center3 = Point3D::Identity;

	int indexCount = 0;
	for(i=0;i<polygon_count;++i)
	{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nPolygon count %d", i);
#endif
		if(tdtrilist[i]->centerValid==false)
		{
			SPEWALWAYS(("micgaert", "Found invalid center"));
		}

		if(!tdtrilist[i]->GetSurfaceAreaAndCentroid())
		{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\ncontinue %d", i);
#endif
			continue;
		}

//		SPEWALWAYS(("micgaert", "#cmZ.%d %f %f %f", i , tdtrilist[i]->center.x, 
//tdtrilist[i]->center.y, tdtrilist[i]->center.z));
		center3 += tdtrilist[i]->center;

		for(j=0;j<3;j++)
		{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nBegin loop j %d", j);
#endif
			if(tdtrilist[i]->GetPoint(j).x < minx)
			{
				minX[0] = i;
				minX[1] = 0;
				minx = tdtrilist[i]->GetPoint(j).x;
			} else if(tdtrilist[i]->GetPoint(j).x > maxx)
			{
				maxX[0] = i;
				maxX[1] = 0;
				maxx = tdtrilist[i]->GetPoint(j).x;
			}
			if(tdtrilist[i]->GetPoint(j).y < miny)
			{
				minY[0] = i;
				minY[1] = 0;
				miny = tdtrilist[i]->GetPoint(j).y;
			} else if(tdtrilist[i]->GetPoint(j).y > maxy)
			{
				maxY[0] = i;
				maxY[1] = 0;
				maxy = tdtrilist[i]->GetPoint(j).y;
			}
			if(tdtrilist[i]->GetPoint(j).z < minz)
			{
				minZ[0] = i;
				minZ[1] = 0;
				minz = tdtrilist[i]->GetPoint(j).z;
			} else if(tdtrilist[i]->GetPoint(j).z > maxz)
			{
				maxZ[0] = i;
				maxZ[1] = 0;
				maxz = tdtrilist[i]->GetPoint(j).z;
			}

#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nSearching on k for unique points");
#endif
			for(k=0;k<nrOfUniquePoints;k++)
			{
				if(Close_Enough(l_uniquePoints[k], tdtrilist[i]->GetPoint(j)))
				{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nBreaking %d", k);
#endif
				break;
				}
			}

			if(k==nrOfUniquePoints)
			{
				center0 += tdtrilist[i]->GetPoint(j);
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nAdding unique point before %d", k);
	fprintf(stdout, "\n          {%f, %f, %f}", tdtrilist[i]->GetPoint(j).x, tdtrilist[i]->GetPoint(j).y, tdtrilist[i]->GetPoint(j).z  );
#endif
				l_uniquePoints[nrOfUniquePoints++] = tdtrilist[i]->GetPoint(j);
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nAdding unique point after %d", k);
	fprintf(stdout, "\n          {%f, %f, %f}", l_uniquePoints[k].x, l_uniquePoints[k].y, l_uniquePoints[k].z  );
#endif

				//				SPEWALWAYS(("micgaert", "#cm#.%d %f %f %f", nrOfUniquePoints-1, 
//l_uniquePoints[nrOfUniquePoints-1].x, l_uniquePoints[nrOfUniquePoints-1].y, 
//l_uniquePoints[nrOfUniquePoints-1].z));

			}

			index[indexCount+j] = k;
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nEnd loop j %d", j);
#endif

		}

		if(
			index[indexCount]!=index[indexCount+1] &&
			index[indexCount]!=index[indexCount+2] &&
			index[indexCount+1]!=index[indexCount+2]
		)
		{
			Swap(index[indexCount+1], index[indexCount+2]);

			indexCount+=3;
		}
	}

	if(nrOfUniquePoints==0 || nrOfUniquePoints >= Limits::Max_Number_Vertices_Per_Mesh)
	{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nReturning (no unique points) with false");
#endif
		return false;
	}

//	SPEWALWAYS(("micgaert", "#cm2"));
	center0 *= 1.0f/nrOfUniquePoints;
	center0.x = GetX(center0.x);
	center0.y += Offset;
	center0.z = GetZ(center0.z);

//	SPEWALWAYS(("micgaert", "#cmB.%f %f %f", center3.x, center3.y, 
//center3.z));

	center3 *= 1.0f/polygon_count;
	center3.x = GetX(center3.x);
	center3.y += Offset;
	center3.z = GetZ(center3.z);

	Vector3D v3;
	Scalar d[3];

	v3.Subtract(tdtrilist[maxX[0]]->GetPoint(maxX[1]), 
tdtrilist[minX[0]]->GetPoint(minX[1]));
	d[0] = v3.GetLengthSquared();

	v3.Subtract(tdtrilist[maxY[0]]->GetPoint(maxY[1]), 
tdtrilist[minY[0]]->GetPoint(minY[1]));
	d[1] = v3.GetLengthSquared();

	v3.Subtract(tdtrilist[maxZ[0]]->GetPoint(maxZ[1]), 
tdtrilist[minZ[0]]->GetPoint(minZ[1]));
	d[2] = v3.GetLengthSquared();

	if(d[0] > d[1])
	{
		if(d[0]>d[2])
		{
			center1 = tdtrilist[maxX[0]]->GetPoint(maxX[1]);
			center1 += tdtrilist[minX[0]]->GetPoint(minX[1]);
			center1 *= 0.5f;
		}
		else
		{
			center1 = tdtrilist[maxZ[0]]->GetPoint(maxZ[1]);
			center1 += tdtrilist[minZ[0]]->GetPoint(minZ[1]);
			center1 *= 0.5f;
		}
	}
	else
	{
		if(d[1]>d[2])
		{
			center1 = tdtrilist[maxY[0]]->GetPoint(maxY[1]);
			center1 += tdtrilist[minY[0]]->GetPoint(minY[1]);
			center1 *= 0.5f;
		}
		else
		{
			center1 = tdtrilist[maxZ[0]]->GetPoint(maxZ[1]);
			center1 += tdtrilist[minZ[0]]->GetPoint(minZ[1]);
			center1 *= 0.5f;
		}
	}

	center1.x = GetX(center1.x);
	center1.y += Offset;
	center1.z = GetZ(center1.z);

	center2.x = GetX((maxx+minx)*0.5f);
	center2.y = (maxy+miny)*0.5f;
	center2.z = GetZ((maxz+minz)*0.5f);

	center2.y += Offset;

	Vector2DScalar *texCoords = new Vector2DScalar[nrOfUniquePoints];
	Point3D	*coords = new Point3D [nrOfUniquePoints];

	UnitVector3D sun;
	sun = Vector3D(-1.0f, -1.0f, 0.0f);

	Scalar radiusSquared = 0.0f,
		maxRadius0 = 0.0f,
		maxRadius1 = 0.0f,
		maxRadius2 = 0.0f,
		maxRadius3 = 0.0f;

//	SPEWALWAYS(("micgaert", "#cm4 %d", nrOfUniquePoints));
	for(i=0;i<nrOfUniquePoints;i++)
	{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nnrOfUniquePoints loop %d of %d", i, nrOfUniquePoints);
#endif
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nnrOfUniquePoints, coords[i].x, y, z {%f, %f, %f} before", l_uniquePoints[i].x, l_uniquePoints[i].y, l_uniquePoints[i].z );
#endif
#ifdef NON_DEBUG_BUG
	{
		float testfloat1 = 5.0f;
		float testfloat2 = testfloat1 + coords[i].x;
		fprintf(stdout, "\nTest floating point 1 (%f)", testfloat2);
	}
#endif

		coords[i] = l_uniquePoints[i];
		coords[i].y += Offset;

		v3.Subtract(coords[i], center0);

#ifdef NON_DEBUG_BUG
	{
		float testfloat1 = 5.0f;
		float testfloat2 = testfloat1 + coords[i].x;
		fprintf(stdout, "\nTest floating point 2 (%f)", testfloat2);
	}
#endif
		radiusSquared = v3.GetLengthSquared();
		maxRadius0 = radiusSquared>maxRadius0 ? radiusSquared:maxRadius0;

		v3.Subtract(coords[i], center1);

#ifdef NON_DEBUG_BUG
	{
		float testfloat1 = 5.0f;
		float testfloat2 = testfloat1 + coords[i].x;
		fprintf(stdout, "\nTest floating point 3 (%f)", testfloat2);
	}
#endif
		radiusSquared = v3.GetLengthSquared();
		maxRadius1 = radiusSquared>maxRadius1 ? radiusSquared:maxRadius1;

		v3.Subtract(coords[i], center2);

#ifdef NON_DEBUG_BUG
	{
		float testfloat1 = 5.0f;
		float testfloat2 = testfloat1 + coords[i].x;
		fprintf(stdout, "\nTest floating point 4 (%f)", testfloat2);
	}
#endif
		radiusSquared = v3.GetLengthSquared();
		maxRadius2 = radiusSquared>maxRadius2 ? radiusSquared:maxRadius2;

		v3.Subtract(coords[i], center3);

#ifdef NON_DEBUG_BUG
	{
		float testfloat1 = 5.0f;
		float testfloat2 = testfloat1 + coords[i].x;
		fprintf(stdout, "\nTest floating point 5 (%f)", testfloat2);
	}
#endif
		radiusSquared = v3.GetLengthSquared();
		maxRadius3 = radiusSquared>maxRadius3 ? radiusSquared:maxRadius3;

#ifndef TERRAIN2_TEST
		texCoords[nrOfPoints][0] =
			borderPixelFun + (1.0f-2*borderPixelFun)*((uvFrame->maxX - 
coords[nrOfPoints].x)/(uvFrame->maxX-uvFrame->minX));
		texCoords[nrOfPoints][1] =
			borderPixelFun + (1.0f-2*borderPixelFun)*((uvFrame->maxZ - 
coords[nrOfPoints].z)/(uvFrame->maxZ-uvFrame->minZ));
#endif
#ifdef NON_DEBUG_BUG
	{
		float testfloat1 = 5.0f;
		float testfloat2 = testfloat1 + coords[i].x;
		fprintf(stdout, "\nTest floating point 6 (%f)", testfloat2);
	}
#endif
	}
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nCalculating radius");
#endif

	maxRadius0 = static_cast<Scalar>(sqrt(maxRadius0));
	maxRadius1 = static_cast<Scalar>(sqrt(maxRadius1));
	maxRadius2 = static_cast<Scalar>(sqrt(maxRadius2));
	maxRadius3 = static_cast<Scalar>(sqrt(maxRadius3));

	Point3D center;
	Scalar maxRadius;

//	SPEWALWAYS(("micgaert", "#cm5"));
	if(maxRadius0<maxRadius1)
	{
		if(maxRadius0<maxRadius2)
		{
			if(maxRadius0<maxRadius3)
			{
				maxRadius = maxRadius0;
				center = center0;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
		else
		{
			if(maxRadius2<maxRadius3)
			{
				maxRadius = maxRadius2;
				center = center2;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
	}
	else
	{
		if(maxRadius1<maxRadius2)
		{
			if(maxRadius1<maxRadius3)
			{
				maxRadius = maxRadius1;
				center = center1;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
		else
		{
			if(maxRadius2<maxRadius3)
			{
				maxRadius = maxRadius2;
				center = center2;
			}
			else
			{
				maxRadius = maxRadius3;
				center = center3;
			}
		}
	}
//	SPEWALWAYS(("micgaert", "#cm6"));

	if(indexCount<3)
	{
		erf_mesh->DetachReference();
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nReturning (indexCount < 3) with false");
#endif
		return false;
	}

	gos_PushCurrentHeap(MLR_Terrain2::PrimitiveHeap);
	erf_mesh->SetSubprimitiveLengths(NULL, indexCount/3);

	Scalar xOffset2 = xCenterOffset + Xoffset*dX;
	Scalar zOffset2 = zCenterOffset + Zoffset*dZ;
	for(i=0;i<nrOfUniquePoints;i++)
	{
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nnrOfUniquePoints loop %d or %d, coords[i].x, y, z {%f, %f, %f} before", i, nrOfUniquePoints, coords[i].x , coords[i].y , coords[i].z );
#endif

		coords[i].x -= xOffset2;
		coords[i].z -= zOffset2;
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nnrOfUniquePoints loop %d or %d, coords[i].x, y, z {%f, %f, %f} after", i, nrOfUniquePoints, coords[i].x , coords[i].y , coords[i].z );
#endif
	}

	erf_mesh->SetCoordData(coords, nrOfUniquePoints);

#ifdef TERRAIN2_TEST
	erf_mesh->SetCurrentDepth(maxAllDepth-diffLevel);
#else
	erf_mesh->SetTexCoordData(texCoords, nrOfPoints);
#endif

	erf_mesh->SetIndexData(index, indexCount);
	gos_PopCurrentHeap();

//	delete [] texCoords;
//	delete [] index;

//	SPEWALWAYS(("micgaert", "#cm7"));
	erf_mesh->FindFacePlanes();

	for(i=0;i<indexCount/3;i++)
	{
		Verify(erf_mesh->GetTrianglePlane(i)->normal.y > Stuff::SMALL);
	}

	if(state)
	{
		erf_mesh->SetReferenceState(*state);
	}
	else
	{
		erf_mesh->SetReferenceState(state0);
	}

	if(underWater==true)
	{
		erf_mesh->SetReferenceState(state2, 1);
	}
	else
	{
		erf_mesh->SetReferenceState(state1, 1);
	}

//	SPEWALWAYS(("micgaert", "#cm8"));
	gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);
	ElementRenderer::ShapeElement* Shape = new ElementRenderer::ShapeElement;
	Register_Object(Shape);
	gos_PopCurrentHeap();

	gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
	MLRShape *shape = new MLRShape(1);
	Register_Object(shape);
	gos_PopCurrentHeap();

	shape->Add(erf_mesh);
	erf_mesh->DetachReference();

	Shape->SetMLRShape(shape);

	const Point3D *p = NULL;
	int nr_of_points = 0;
	erf_mesh->GetCoordData(&p, &nr_of_points);
	ReadOnlyArrayOf<Point3D> p_array;
	p_array.AssignData(p, nr_of_points);
	Shape->m_localOBB.ComputeBounds(p_array);
	Shape->SetOBBMode();

/*
		.localToParent = LinearMatrix4D::Identity;

	center.x -= xCenterOffset + Xoffset*dX;
	center.z -= zCenterOffset + Zoffset*dZ;

	Shape->m_localOBB.localToParent.BuildTranslation(center);

	Shape->m_localOBB.sphereRadius = maxRadius;
*/
	parent->AttachIndexedChild(listIndex, Shape);
	Shape->SetVolumeCullMode();
//#define NON_DEBUG_BUG 1
#ifdef NON_DEBUG_BUG
	fprintf(stdout, "\nReturning true");
#endif

	return true;
}


MLRPrimitiveBase*
	HFSlimMZ::CreateWater(
		Scalar x0, Scalar x1,
		Scalar z0, Scalar z1,
		Scalar waterLevel
	)
{
	gos_PushCurrentHeap(MLR_Water::PrimitiveHeap);
	MLR_Water *ret = new MLR_Water;

	DynamicArrayOf<Point3D> *coords = &ret->dataStore->coords;
	Check_Object(coords);
	coords->SetLength(25);

	DynamicArrayOf<Vector2DScalar> *texCoords = &ret->dataStore->texCoords;
	Check_Object(texCoords);
	texCoords->SetLength(25);

	DynamicArrayOf<ColorType> *colors = &ret->dataStore->colors;
	Check_Object(colors);
	colors->SetLength(25);

	DynamicArrayOf<BYTE> *index = &ret->dataStore->index;
	Check_Object(index);
	index->SetLength(16*2*3);

	int i, j;
	for(i=0;i<5;i++)
	{
		for(j=0;j<5;j++)
		{
			int offset = i*5+j;
			(*coords)[offset] = Point3D( x0+(i*(x1-x0))/4.0f, waterLevel, z0+(j*(z1-z0))/4.0f );
#if COLOR_AS_DWORD>0
			(*colors)[offset] = 0xffffffff;
#else
			(*colors)[offset] = RGBAColor::White;
#endif

			(*texCoords)[offset] = Vector2DScalar(
				((*coords)[offset].x-(x1+x0)/2.0f)*(waterTextureRatio/(x1-x0)),
				((*coords)[offset].z-(z1+z0)/2.0f)*(waterTextureRatio/(z1-z0))
			);

			Verify((*texCoords)[offset][0] >= -waterTextureRatio && (*texCoords)[offset][0] <= waterTextureRatio);
			Verify((*texCoords)[offset][1] >= -waterTextureRatio && (*texCoords)[offset][0] <= waterTextureRatio);
		}
	}

	int k = 0;
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
//			if((i+j)&1)
			{
				(*index)[k++] = i*5+j;
				(*index)[k++] = (i+1)*5+j;
				(*index)[k++] = i*5+j+1;

				(*index)[k++] = i*5+j+1;
				(*index)[k++] = (i+1)*5+j;
				(*index)[k++] = (i+1)*5+j+1;
			}
/*
			else
			{
				(*index)[k++] = i*5+j;
				(*index)[k++] = (i+1)*5+j;
				(*index)[k++] = (i+1)*5+j+1;

				(*index)[k++] = i*5+j+1;
				(*index)[k++] = (i+1)*5+j+1;
				(*index)[k++] = i*5+j+1;
			}
*/
		}
	}

	ret->SetSubprimitiveLengths(NULL, 16*2);

	Scalar xOffset2 = xCenterOffset + Xoffset*dX;
	Scalar zOffset2 = zCenterOffset + Zoffset*dZ;
	for(i=0;i<25;i++)
	{
		(*coords)[i].x -= xOffset2;
		(*coords)[i].z -= zOffset2;
	}

	ret->SetCoordData(coords->GetData(), 25);
	ret->SetColorData(colors->GetData(), 25);
	ret->SetIndexData(index->GetData(), 16*2*3);
	ret->SetTexCoordData(texCoords->GetData(), 25);
	ret->FindFacePlanes();

	MLRState state, detState;

	MLRTexture *texture = NULL;
	if(waterMovieFrames>1)
	{
		texture = MLRTexturePool::Instance->AddMovie(waterTextureName, waterMovieFrames, 0.01f);
		state.SetMovieTextureOn();
	}
	else
	{
		texture = MLRTexturePool::Instance->Add(waterTextureName);
	}

	Check_Object(texture);
	state.SetTextureHandle(texture->GetTextureHandle());

	state.SetBackFaceOff();
	state.SetDitherOn();
	state.SetTextureCorrectionOn();
	state.SetZBufferCompareOn();
	state.SetZBufferWriteOff();
	state.SetAlphaMode(MLRState::AlphaInvAlphaMode);
	state.SetPriority(MLRState::AlphaPriority);

	ret->SetReferenceState(state);

	detState = state;
	detState.SetPriority(state.GetPriority()+1);

	if(waterDetailInfo.movieFrames>1)
	{
		texture = MLRTexturePool::Instance->AddMovie(waterDetailInfo.name, waterDetailInfo.movieFrames, 0.01f);
		detState.SetMovieTextureOn();
	}
	else
	{
		texture = MLRTexturePool::Instance->Add(waterDetailInfo.name);
	}

	Check_Object(texture);
	detState.SetTextureHandle(texture->GetTextureHandle());

	ret->SetDetailData(
		waterDetailInfo.xOff,
		waterDetailInfo.zOff,
		waterDetailInfo.xFac,
		waterDetailInfo.zFac,
		waterDetailInfo.dStart,
		waterDetailInfo.dEnd
	);

	ret->SetReferenceState(detState, 1);

	gos_PopCurrentHeap();

	return ret;
}

int
	primCounter, maxPrims;

bool
	HFSlimMZ::BinSort(
		ElementRenderer::ListElement *parent,
		int index,
		int levDiff,
		DynamicArrayOf<TriangleAbstract*>& tdtrilist,
		int binSize,
		frameArray *frame,
		MLRState *state
	)
{
	int polygon_count = tdtrilist.GetLength();

	if (polygon_count==0)
	{
		return false;
	}

	//
	//---------------------------------------------------------
	// If the polygon mesh is already small enough, just return
	//---------------------------------------------------------
	//
	if (polygon_count <= binSize)
	{
		return CreateMesh(parent, index, levDiff, tdtrilist, frame, state);
	}

	DynamicArrayOf<Point3D> centroids(polygon_count);
	unsigned i;
	for (i=0; i<polygon_count; ++i)
	{
		centroids[i] = tdtrilist[i]->center;
	}

	//
	//------------------------------------------------------------------------
	// Calculate the dividing plane, and if none can be found, don't do nothin
	//------------------------------------------------------------------------
	//
	Plane plane;
	if (!plane.ComputeBestDividingPlane(centroids))
	{
		return CreateMesh(parent, index, levDiff, tdtrilist, frame, state);
	}

	//
	//-------------------------------------------------------------------------
	// The mesh is too big, so we have to cut it up.  Make a group proxy to
	// hold the new mesh collection
	//-------------------------------------------------------------------------
	//
	Check_Object(parent);
	gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
	ElementRenderer::ListElement *group = new ElementRenderer::ListElement;
	Register_Object(group);
	group->SetSize(2);
	gos_PopCurrentHeap();
	//
	//----------------------------------
	// Set the position of the group
	//----------------------------------
	//
	LinearMatrix4D m;
	m = parent->GetLocalToParent();

	parent->SetLocalToParent(LinearMatrix4D::Identity);
	group->SetLocalToParent(m);

#if 0
	const char *name;
	if ((name=parent->GetName())!=NULL)
	{
		parent->SetName(NULL);
		group->SetName(name);
	}
#endif

	DynamicArrayOf<TriangleAbstract*>
		group_a(polygon_count),
		group_b(polygon_count);
	unsigned
		count_a = 0,
		count_b = 0;

	//
	//------------------------------------------------------------------
	// Sort each of the centroids against the plane into one of two bins
	//------------------------------------------------------------------
	//
	for (i=0; i<polygon_count; ++i)
	{
		if (plane.GetDistanceTo(centroids[i]) < 0.0f)
			group_b[count_b++] = tdtrilist[i];
		else
			group_a[count_a++] = tdtrilist[i];
	}

	//
	//---------------------------------
	// Now add the polygons to each bin
	//---------------------------------
	//
	Verify(count_a>0);
	group_a.SetLength(count_a);
//	bin_a->AddPolygons(process, group_a);
	Verify(count_b>0);
	group_b.SetLength(count_b);
//	bin_b->AddPolygons(process, group_b);

	//
	//-------------------------------------------------------------------
	// Now that the mesh has been split up, Bin_Sort each smaller mesh and
	// destroy this mesh
	//-------------------------------------------------------------------
	//
	int worked = 0;

	if (BinSort(
		group,
		0,
		levDiff,
		group_a,
		binSize,
		frame,
		state))
	{
		Verify(group->GetIndexedElement(0) != NULL);
		worked |= 1;
	}

	if (BinSort(
		group,
		1,
		levDiff,
		group_b,
		binSize,
		frame,
		state))
	{
		Verify(group->GetIndexedElement(1) != NULL);
		worked |= 2;
	}

	if(worked != 3)
	{
		if(worked == 0)
			return false;

		ElementRenderer::ListElement *smallgroup = new 
ElementRenderer::ListElement;
		Register_Object(smallgroup);
		smallgroup->SetSize(1);

		ElementRenderer::Element *element;
		if(worked==1)
		{
			element = group->GetIndexedElement(0);
			element->DetachFromParent();
		}
		else
		{
			element = group->GetIndexedElement(1);
			element->DetachFromParent();
		}
		delete group;

		smallgroup->AttachIndexedChild(0, element);
		Check_Object(smallgroup);
		parent->AttachIndexedChild(index, smallgroup);

		smallgroup->NeedNewBounds();
		smallgroup->SetVolumeCullMode();
	}
	else
	{
		Check_Object(group);
		parent->AttachIndexedChild(index, group);

		group->NeedNewBounds();
		group->SetVolumeCullMode();
	}

	//
	//-----------------------------------------
	// Now set the bounding sphere of the group
	//-----------------------------------------
	//

	return true;
}

bool
	HFSlimMZ::BinSort2(
		ElementRenderer::ListElement *parent,
		int index,
		int levDiff,
		DynamicArrayOf<TriangleAbstract*>& tdtrilist,
		int binSize,
		frameArray *frame,
		MLRState *state,
		bool underWater
	)
{
	int polygon_count = tdtrilist.GetLength();

	if (polygon_count==0)
	{
		return false;
	}

	//
	//---------------------------------------------------------
	// If the polygon mesh is already small enough, just return
	//---------------------------------------------------------
	//
//	SPEWALWAYS(("micgaert", "#bs0"));
	if (polygon_count <= binSize)
	{
		return CreateMesh2(parent, index, levDiff, tdtrilist, frame, state, 
underWater);
	}

	DynamicArrayOf<Point3D> centroids(polygon_count);
	unsigned i;
	for (i=0; i<polygon_count; ++i)
	{
		if(tdtrilist[i]->centerValid==false)
		{
			SPEWALWAYS(("micgaert", "Found invalid center!"));
		}

		centroids[i] = tdtrilist[i]->center;
	}

	//
	//------------------------------------------------------------------------
	// Calculate the dividing plane, and if none can be found, don't do nothin
	//------------------------------------------------------------------------
	//
//	SPEWALWAYS(("micgaert", "#bs1"));

	Plane plane;
	if (!plane.ComputeBestDividingPlane(centroids))
	{
		return CreateMesh2(parent, index, levDiff, tdtrilist, frame, state, 
underWater);
	}

	//
	//-------------------------------------------------------------------------
	// The mesh is too big, so we have to cut it up.  Make a group proxy to
	// hold the new mesh collection
	//-------------------------------------------------------------------------
	//
//	SPEWALWAYS(("micgaert", "#bs2"));
	Check_Object(parent);
	gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
	ElementRenderer::ListElement *group = new ElementRenderer::ListElement;
	Register_Object(group);
	group->SetSize(2);
	gos_PopCurrentHeap();
	//
	//----------------------------------
	// Set the position of the group
	//----------------------------------
	//
	LinearMatrix4D m;
	m = parent->GetLocalToParent();

	parent->SetLocalToParent(LinearMatrix4D::Identity);
	group->SetLocalToParent(m);

//	SPEWALWAYS(("micgaert", "#bs3"));
#if 0
	const char *name;
	if ((name=parent->GetName())!=NULL)
	{
		parent->SetName(NULL);
		group->SetName(name);
	}
#endif

	DynamicArrayOf<TriangleAbstract*>
		group_a(polygon_count),
		group_b(polygon_count);
	unsigned
		count_a = 0,
		count_b = 0;

	//
	//------------------------------------------------------------------
	// Sort each of the centroids against the plane into one of two bins
	//------------------------------------------------------------------
	//
//	SPEWALWAYS(("micgaert", "#bs4"));
	for (i=0; i<polygon_count; ++i)
	{
		if (plane.GetDistanceTo(centroids[i]) < 0.0f)
			group_b[count_b++] = tdtrilist[i];
		else
			group_a[count_a++] = tdtrilist[i];
	}

	//
	//---------------------------------
	// Now add the polygons to each bin
	//---------------------------------
	//
	Verify(count_a>0);
	group_a.SetLength(count_a);
//	bin_a->AddPolygons(process, group_a);
	Verify(count_b>0);
	group_b.SetLength(count_b);
//	bin_b->AddPolygons(process, group_b);
//	SPEWALWAYS(("micgaert", "#bs5"));

	//
	//-------------------------------------------------------------------
	// Now that the mesh has been split up, Bin_Sort each smaller mesh and
	// destroy this mesh
	//-------------------------------------------------------------------
	//
	int worked = 0;

	if (BinSort2(
		group,
		0,
		levDiff,
		group_a,
		binSize,
		frame,
		state,
		underWater))
	{
		worked |= 1;
	}
//	SPEWALWAYS(("micgaert", "#bs6"));
	if (BinSort2(
		group,
		1,
		levDiff,
		group_b,
		binSize,
		frame,
		state,
		underWater))
	{
		worked |= 2;
	}

//	SPEWALWAYS(("micgaert", "#bs7"));
	if(worked != 3)
	{
		if(worked == 0)
			return false;

		ElementRenderer::ListElement *smallgroup = new ElementRenderer::ListElement;
		Register_Object(smallgroup);
		smallgroup->SetSize(1);

		ElementRenderer::Element *element;
		if(worked==1)
		{
			element = group->GetIndexedElement(0);
			element->DetachFromParent();
		}
		else
		{
			element = group->GetIndexedElement(1);
			element->DetachFromParent();
		}
		delete group;

		smallgroup->AttachIndexedChild(0, element);
		Check_Object(smallgroup);
		parent->AttachIndexedChild(index, smallgroup);

		group = smallgroup;
	}
	else
	{
		Check_Object(group);
		parent->AttachIndexedChild(index, group);
	}
//	SPEWALWAYS(("micgaert", "#bs8"));
	//
	//-----------------------------------------
	// Now set the bounding sphere of the group
	//-----------------------------------------
	//
	group->NeedNewBounds();
	group->SetVolumeCullMode();

	return true;
}

void
	HFSlimMZ::OptimizeHField(int depth, int binSize, const char *imagename)
{
	if(X*Z == 0)
	{
		return;
	}
	fprintf(stdout, "Going to optimize the terrain: MaxNr.Of Points: %d BinSize: %d\n", depth, binSize);

	tdtrilist.SetLength((X-1)*(Z-1));
	map = new DirectMap<real>(X,Z);

	int i, j;

	float maxY = 0;
	for(j=0;j<Z;j++)
		for(i=0;i<X;i++)
		{
			map->ref(i,j) = field[j*X + i];
		}
	Offset = 0.0;

	MASK = new ImportMask;
	MASK->width=X;
	MASK->height=Z;

	Scalar percentage = (Scalar)depth / (X*Z);

	mesh = new GreedySubdivision(map, percentage);

	if(doInterest==true)
	{
		Verify(X*Z == interestData.GetLength());
		mesh->interest_data.init(X, Z, interestData.GetData());
	}
	mesh->SetInterestMethod(doInterest);
	mesh->SetSquareMethod(doTileCap);
	mesh->SetBiggerSquareMethod(doTileCapYovin);

	point_limit = depth;

	triangleCounter = 0;
	int count = 1;
	while(mesh->pointCount()<point_limit && mesh->maxError() > error_threshold)
	{
		mesh->greedyInsert();
	}
	fprintf(stdout, "The mesh was greedy.\n");

	if(doDensityMap==true && imagename!=NULL)
	{
		Image img;

		img.CreateBlank(X, Z);

		BYTE *ptr = img.Lock();
		for(j=0;j<Z;j++)
		{
			for(i=0;i<X;i++)
			{
				ptr[3*(j*X+i)] = mesh->areUsed(Z-j-1, X-i-1);
				ptr[3*(j*X+i)+1] = mesh->is_used(X-i-1, Z-j-1);
				ptr[3*(j*X+i)+2] = mesh->are_used_32((X-i-1)>>5, (Z-j-1)>>5);
			}
		}
		img.UnLock();

		img.SaveTga((LPSTR)imagename);

		exit(-1);
	}

	if(!doPrecise)
	{
		fprintf(stdout, "You don't care about precision !\n");

		mesh->overFaces(GetFacesCB, tdtrilist.GetData());

		for(i=0;i<triangleCounter;i++)
		{
			tdtrilist[i].v[0].y = Offset + static_cast<float>(dY*mesh->eval(
				(int)(tdtrilist[i].v[0].x),
				(int)(tdtrilist[i].v[0].z)
			));

			tdtrilist[i].v[1].y = Offset + static_cast<float>(dY*mesh->eval(
				(int)(tdtrilist[i].v[1].x),
				(int)(tdtrilist[i].v[1].z)
			));

			tdtrilist[i].v[2].y = Offset + static_cast<float>(dY*mesh->eval(
				(int)(tdtrilist[i].v[2].x),
				(int)(tdtrilist[i].v[2].z)
			));

			tdtrilist[i].v[0].x *= dX;
			tdtrilist[i].v[0].z *= dZ;

			tdtrilist[i].v[1].x *= dX;
			tdtrilist[i].v[1].z *= dZ;

			tdtrilist[i].v[2].x *= dX;
			tdtrilist[i].v[2].z *= dZ;

			if(tdtrilist[i].GetSurfaceAreaAndCentroid()!=true)
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Found a triangle with a face normal not pointing up, consider revising map ! Program exits !\n");
					exit(-1);
				}
				else
				{
					STOP(("Found a triangle with a face normal not pointing up, consider revising map !"));
				}
			}
		}

		OptPolyCount = triangleCounter;

		if(triangleCounter == 0 || mesh->pointCount() == 0)
		{
			return;
		}

		toSortTriangles.SetLength(triangleCounter);

		for(i=0,j=0;i<triangleCounter;++i)
		{
			if(! (tdtrilist[i].plane.normal.y > Stuff::SMALL) )
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Found a triangle with a face normal not pointing up, consider revising map ! Program exits !\n");
					exit(-1);
				}
				else
				{
					STOP(("Found a triangle with a face normal not pointing up, consider revising map !"));
				}
			}

	/*
			if(	!(tdtrilist[i].GetV0().x==0.0f && tdtrilist[i].GetV0().z==GetZinM()) 
&&
				!(tdtrilist[i].GetV1().x==0.0f && tdtrilist[i].GetV1().z==GetZinM()) &&
				!(tdtrilist[i].GetV2().x==0.0f && tdtrilist[i].GetV2().z==GetZinM())
			)
	*/
			{
				toSortTriangles[j++] = &tdtrilist[i];
			}
		}
		toSortTriangles.SetLength(j);

		fprintf(stdout, "optimized to %d triangles.\n", j);

	}
	else
	{
		fprintf(stdout, "You wanted it precise !");
		usedUniquePoints = 0;
		usedTriangleIndices = 0;

		uniquePoints.SetLength(4*mesh->pointCount());
		TriangleStruct::points = uniquePoints.GetData();

		triangleIndices.SetLength(8*mesh->pointCount());

		mesh->overFaces(GetFacesCBPrecise, NULL);

		for(i=0;i<usedUniquePoints;i++)
		{
			uniquePoints[i].p.y = Offset + static_cast<float>(dY*mesh->eval(
				(int)(uniquePoints[i].p.x),
				(int)(uniquePoints[i].p.z)
			));

			uniquePoints[i].p.x *= dX;
			uniquePoints[i].p.z *= dZ;
		}

		for(i=0;i<usedTriangleIndices;i++)
		{
			triangleIndices[i].GetSurfaceAreaAndCentroid();
		}

		OptPolyCount = usedTriangleIndices;

		if(usedTriangleIndices == 0 || mesh->pointCount() == 0)
		{
			return;
		}

		for(i=0;i<usedTriangleIndices;++i)
		{
			if(! (triangleIndices[i].plane.normal.y > Stuff::SMALL) )
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Found a triangle with a face normal not pointing up, consider revising map ! Program exits !\n");
					exit(-1);
				}
				else
				{
					STOP(("Found a triangle with a face normal not pointing up, consider revising map !"));
				}
			}
		}

		fprintf(stdout, "optimized to %d triangles.\n", usedTriangleIndices);
	}
}

int brokenTriangles;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	AnalyzeThis(
		DynamicArrayOf<TriangleAbstract*> &tempTrianglesPtr,
		Scalar x0,
		Scalar z0,
		Scalar x1,
		Scalar z1
	)
{
	int i, j, k, l, len = tempTrianglesPtr.GetLength();
	int divide = 2;
	Scalar x0n, z0n, x1n, z1n;

	fprintf(analyzeFile, "%d	", len);

	int in, out;

	for(i=0;i<divide;i++)
	{
		for(j=0;j<divide;j++)
		{
			x0n = x0 + j*(x1-x0)/divide;
			x1n = x0 + (j+1)*(x1-x0)/divide;
			z0n = z0 + i*(z1-z0)/divide;
			z1n = z0 + (i+1)*(z1-z0)/divide;

			in = 0;
			out = 0;
			for(k=0;k<len;k++)
			{
				l = 0;
				if(	tempTrianglesPtr[k]->GetPoint(0).x >= x0n && 
tempTrianglesPtr[k]->GetPoint(0).x <= x1n &&
					tempTrianglesPtr[k]->GetPoint(0).z >= z0n && 
tempTrianglesPtr[k]->GetPoint(0).z < z1n
					)
				{
					l++;
				}
				if(	tempTrianglesPtr[k]->GetPoint(1).x >= x0n && 
tempTrianglesPtr[k]->GetPoint(1).x <= x1n &&
					tempTrianglesPtr[k]->GetPoint(1).z >= z0n && 
tempTrianglesPtr[k]->GetPoint(1).z <= z1n
					)
				{
					l++;
				}
				if(	tempTrianglesPtr[k]->GetPoint(2).x >= x0n && 
tempTrianglesPtr[k]->GetPoint(2).x <= x1n &&
					tempTrianglesPtr[k]->GetPoint(2).z >= z0n && 
tempTrianglesPtr[k]->GetPoint(2).z <= z1n
					)
				{
					l++;
				}

				if(l==3)
				{
					in++;
				}
				else if(l>0)
				{
					if(tempTrianglesPtr[k]->centerValid==false)
					{
						SPEWALWAYS(("micgaert", "Found invalid center!"));
					}

					if(	tempTrianglesPtr[k]->center.x >= x0n && 
tempTrianglesPtr[k]->center.x <= x1n &&
						tempTrianglesPtr[k]->center.z >= z0n && tempTrianglesPtr[k]->center.z 
<= z1n
						)
					{
						out++;
					}
				}
			}

			fprintf(analyzeFile, "%d	%d	", in, out);

		}
	}


	fprintf(analyzeFile, "\n");
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HFSlimMZ::BuildTile(
		DynamicArrayOf<TriangleStruct*>& tempTrianglesPtr,
		int index,
		int depth, int maxdepth,
		int numOfTriangles,
		DynamicArrayOf<unsigned char>& onOffPoints,
		int maxNumOfTrianglesPerMesh,
		Scalar xGridOffset,
		Scalar zGridOffset,
		Scalar xGrid,
		Scalar zGrid,
		int tileX,
		int tileZ
	)
{
	int orgUsedTriangleIndices = usedTriangleIndices;

	int i, j, inCount, newCount = 0, extraTriangles = 0, refused = 0;
	int k, l, m;

	Scalar x0, z0, x1, z1;

	x0 = xGridOffset + tileX*xGrid;
	z0 = zGridOffset + tileZ*zGrid;
	x1 = x0 + xGrid;
	z1 = z0 + zGrid;

	for(i=0,j=0;i<numOfTriangles;j+=3,++i)
	{
		Verify(i<numOfTriangles);
		Verify(j<3*numOfTriangles);

/*		m = 0;
		for(k=0;k<3;k++)
		{
			if(triangleIndices[i].v[k]==2)
			{
				m++;
			}
		}

		if(m==1 && (z0==800.0f || z1==800.0f))
		{
			m--;
		}
*/
		if(onOffPoints[i] == 0)
		{
			inCount = 0;
			int clipIt[3] = {0, 0, 0}, orIt = 0, addIt = 0xf;

			for(k=0;k<3;k++)
			{
				if(triangleIndices[i].GetPoint(k).x < x0)
				{
					clipIt[k] |= 1;
				}
				if(triangleIndices[i].GetPoint(k).x > x1)
				{
					clipIt[k] |= 4;
				}
				if(triangleIndices[i].GetPoint(k).z < z0)
				{
					clipIt[k] |= 8;
				}
				if(triangleIndices[i].GetPoint(k).z > z1)
				{
					clipIt[k] |= 2;
				}

				orIt |= clipIt[k];
				addIt &= clipIt[k];
			}

			if(orIt == 0)
			{
				onOffPoints[i] = 1;

				tempTrianglesPtr[newCount++] = &triangleIndices[i];

				uniquePoints[triangleIndices[i].v[0]].Add(&triangleIndices[i], 0);
				uniquePoints[triangleIndices[i].v[1]].Add(&triangleIndices[i], 1);
				uniquePoints[triangleIndices[i].v[2]].Add(&triangleIndices[i], 2);
			}
			else if(addIt==0)
			{
				if(orIt==1 || orIt==2 || orIt==4 || orIt==8)
				{
					Point3D triangle[3], clipPoints[8];
					int index[8];
					float angles[8];

					for(k=0;k<3;k++)
					{
						triangle[k] = triangleIndices[i].GetPoint(k);
					}

					l = 0;
					for(k=0;k<3;k++)
					{
						int next = k+1>2?0:k+1;
						if(clipIt[k]==0)
						{
							clipPoints[l++] = triangle[k];
							if(clipIt[next]==0)
							{
								continue;
							}
						}
						else
						{
							if(clipIt[next]!=0)
							{
								continue;
							}
						}
						//
						//-----------------------------------------------------
						// Find the boundary conditions that match our clipping
						// plane
						//-----------------------------------------------------
						//
						int mask = 1;
						for (m=0; m<4; m++)
						{
							if((clipIt[k] | clipIt[next]) & mask)
							{
								switch(m)
								{
									case 0:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(x0-triangle[k].x)/(triangle[next].x-triangle[k].x)

										);
										clipPoints[l++].x = x0;
									break;
									case 1:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(z1-triangle[k].z)/(triangle[next].z-triangle[k].z)
										);
										clipPoints[l++].z = z1;
									break;
									case 2:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(x1-triangle[k].x)/(triangle[next].x-triangle[k].x)
										);
										clipPoints[l++].x = x1;
									break;
									case 3:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(z0-triangle[k].z)/(triangle[next].z-triangle[k].z)
										);
										clipPoints[l++].z = z0;
									break;
								}
								break;
							}
							mask <<= 1;
						}
					}
					Verify(l>2);

					for(m=0;m<l;m++)
					{
						index[m] = FindAddPoint(clipPoints[m]);
					}

					int ba = -1;
					float a = -360.0f;
					for(m=0;m<l;m++)
					{
						angles[m] = GetAngle(
							uniquePoints[index[(m-1+l)%l]].p,
							uniquePoints[index[m]].p,
							uniquePoints[index[(m+l)%l]].p
						);

						if(angles[m] > a)
						{
							a = angles[m];
							ba = m;
						}
					}

					for(m=1;m<l-1;m++)
					{
						triangleIndices[usedTriangleIndices].v[0] = index[ba];
						triangleIndices[usedTriangleIndices].v[1] = index[(ba+m)%l];
						triangleIndices[usedTriangleIndices].v[2] = index[(ba+m+1)%l];

						if(	(triangleIndices[usedTriangleIndices].v[0] != 
triangleIndices[usedTriangleIndices].v[1]) &&
							(triangleIndices[usedTriangleIndices].v[0] != 
triangleIndices[usedTriangleIndices].v[2]) &&
							(triangleIndices[usedTriangleIndices].v[1] != 
triangleIndices[usedTriangleIndices].v[2]) )
						{
							uniquePoints[triangleIndices[usedTriangleIndices].v[0]].Add(&triangleIndices[usedTriangleIndices], 
0);
							uniquePoints[triangleIndices[usedTriangleIndices].v[1]].Add(&triangleIndices[usedTriangleIndices], 
1);
							uniquePoints[triangleIndices[usedTriangleIndices].v[2]].Add(&triangleIndices[usedTriangleIndices], 
2);

							triangleIndices[usedTriangleIndices++].GetSurfaceAreaAndCentroid();
						}
						else
						{
							refused++;
						}
					}
				}
				else
				{
					int key=0, len[2] = {0, 0};
					Point3D clipPoints[2][8];
					int index[8];
					float angles[8];
					int clipMeToo[2][8];

					for(k=0;k<3;k++)
					{
						clipPoints[key][k] = triangleIndices[i].GetPoint(k);
					}

					clipMeToo[key][0] = clipIt[0];
					clipMeToo[key][1] = clipIt[1];
					clipMeToo[key][2] = clipIt[2];

					len[key] = 3;

					int next, mask = 1;
					for(m=0;m<4;++m)
					{
						if(orIt & mask)
						{
							for(k=0;k<len[key];k++)
							{
								next = (k+1) < len[key] ? k+1 : 0;

								if(!(clipMeToo[key][k] & mask))
								{
									clipPoints[!key][len[!key]++] = clipPoints[key][k];
									if(!(clipMeToo[key][next] & mask))
									{
										continue;
									}
								}
								else
								{
									if(clipMeToo[key][next] & mask)
									{
										continue;
									}
								}

								switch(m)
								{
									case 0:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(x0-clipPoints[key][k].x)/(clipPoints[key][next].x-clipPoints[key][k].x)
										);
										Verify(len[!key]<7);
										clipPoints[!key][len[!key]++].x = x0;
									break;
									case 1:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(z1-clipPoints[key][k].z)/(clipPoints[key][next].z-clipPoints[key][k].z)
										);
										Verify(len[!key]<7);
										clipPoints[!key][len[!key]++].z = z1;
									break;
									case 2:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(x1-clipPoints[key][k].x)/(clipPoints[key][next].x-clipPoints[key][k].x)
										);
										Verify(len[!key]<7);
										clipPoints[!key][len[!key]++].x = x1;
									break;
									case 3:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(z0-clipPoints[key][k].z)/(clipPoints[key][next].z-clipPoints[key][k].z)
										);
										Verify(len[!key]<7);
										clipPoints[!key][len[!key]++].z = z0;
									break;
								}
							}
							key = !key;
							for(k=0;k<len[key];k++)
							{
								clipMeToo[key][k] = 0;
								if(clipPoints[key][k].x < x0)
								{
									clipMeToo[key][k] |= 1;
								}
								if(clipPoints[key][k].x > x1)
								{
									clipMeToo[key][k] |= 4;
								}
								if(clipPoints[key][k].z < z0)
								{
									clipMeToo[key][k] |= 8;
								}
								if(clipPoints[key][k].z > z1)
								{
									clipMeToo[key][k] |= 2;
								}
							}
							len[!key] = 0;
						}
						mask <<= 1;
					}

					for(m=0;m<len[key];m++)
					{
						index[m] = FindAddPoint(clipPoints[key][m]);
					}

					int ba = -1;
					float a = -360.0f;
					for(m=0;m<len[key];m++)
					{
						angles[m] = GetAngle(
							uniquePoints[index[(m-1+len[key])%len[key]]].p,
							uniquePoints[index[m]].p,
							uniquePoints[index[(m+len[key])%len[key]]].p
						);

						if(angles[m] > a)
						{
							a = angles[m];
							ba = m;
						}
					}

					for(m=1;m<len[key]-1;m++)
					{
						triangleIndices[usedTriangleIndices].v[0] = index[ba];
						triangleIndices[usedTriangleIndices].v[1] = index[(ba+m)%len[key]];
						triangleIndices[usedTriangleIndices].v[2] = index[(ba+m+1)%len[key]];

						if(	(triangleIndices[usedTriangleIndices].v[0] != 
triangleIndices[usedTriangleIndices].v[1]) &&
							(triangleIndices[usedTriangleIndices].v[0] != 
triangleIndices[usedTriangleIndices].v[2]) &&
							(triangleIndices[usedTriangleIndices].v[1] != 
triangleIndices[usedTriangleIndices].v[2]) )
						{
							uniquePoints[triangleIndices[usedTriangleIndices].v[0]].Add(&triangleIndices[usedTriangleIndices], 
0);
							uniquePoints[triangleIndices[usedTriangleIndices].v[1]].Add(&triangleIndices[usedTriangleIndices], 
1);
							uniquePoints[triangleIndices[usedTriangleIndices].v[2]].Add(&triangleIndices[usedTriangleIndices], 
2);

							triangleIndices[usedTriangleIndices++].GetSurfaceAreaAndCentroid();
						}
						else
						{
							refused++;
						}
					}
				}
			}
		}
	}

	for(i=orgUsedTriangleIndices;i<usedTriangleIndices;i++)
	{
		tempTrianglesPtr[newCount++] = &triangleIndices[i];
	}

	tempTrianglesPtr.SetLength(newCount);

#if _ARMOR
	for(i=0,j=0;i<newCount;++i)
	{
		if(onOffPoints[i] == 0)
		{
			Verify(tempTrianglesPtr[i]->GetPoint(0).x >= x0);
			Verify(tempTrianglesPtr[i]->GetPoint(0).x <= x1);
			Verify(tempTrianglesPtr[i]->GetPoint(0).z >= z0);
			Verify(tempTrianglesPtr[i]->GetPoint(0).z <= z1);

			Verify(tempTrianglesPtr[i]->GetPoint(1).x >= x0);
			Verify(tempTrianglesPtr[i]->GetPoint(1).x <= x1);
			Verify(tempTrianglesPtr[i]->GetPoint(1).z >= z0);
			Verify(tempTrianglesPtr[i]->GetPoint(1).z <= z1);

			Verify(tempTrianglesPtr[i]->GetPoint(2).x >= x0);
			Verify(tempTrianglesPtr[i]->GetPoint(2).x <= x1);
			Verify(tempTrianglesPtr[i]->GetPoint(2).z >= z0);
			Verify(tempTrianglesPtr[i]->GetPoint(2).z <= z1);
		}
	}
#endif

	brokenTriangles += newCount;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HFSlimMZ::CreateTile(
		ElementRenderer::ListElement *list,
		ElementRenderer::GroupElement *waterList,
		DynamicArrayOf<TriangleStruct*>& tempTrianglesPtr,
		int index,
		int depth, int maxdepth,
		int maxNumOfTrianglesPerMesh,
		Scalar xGridOffset,
		Scalar zGridOffset,
		Scalar xGrid,
		Scalar zGrid,
		int tileX,
		int tileZ,
		const char *texRoot
	)
{
	char textureName[1024];
	Scalar x0, z0, x1, z1;

	x0 = xGridOffset + tileX*xGrid;
	z0 = zGridOffset + tileZ*zGrid;
	x1 = x0 + xGrid;
	z1 = z0 + zGrid;

	MLRState state;

	gos_PushCurrentHeap(ElementRenderer::GroupElement::s_Heap);
	ElementRenderer::GroupElement *group = new ElementRenderer::GroupElement;
	Register_Object(group);

	list->AttachIndexedChild(index, group);
	group->NeedNewBounds();
	group->SetVolumeCullMode();
	ElementRenderer::ListElement *subList = new ElementRenderer::ListElement;
	Register_Object(subList);
	subList->SetSize(1);
	group->AttachChild(subList);
	subList->NeedNewBounds();
	subList->SetVolumeCullMode();
	gos_PopCurrentHeap();

/*
	state.SetBackFaceOn();
	state.SetDitherOff();
	state.SetTextureCorrectionOn();
	state.SetZBufferCompareOn();
	state.SetZBufferWriteOn();

	state.SetFilterMode(MLRState::BiLinearFilterMode);

	state.SetFogMode(MLRState::OverrideFogMode);
	state.SetFogData(
		0xff9090f0,
		0.0f,
		700.0f,
		1000.0f
	);
*/
	state.SetPriority(MLRState::DefaultPriority);

#if 0
	sprintf(textureName, "%s_%1d_%02x%02x",
		texRoot,
		depth,
		(1<<depth) - tileX/(1<<(maxdepth-depth)) - 1,
		(1<<depth) - tileZ/(1<<(maxdepth-depth)) - 1
	);
#else
	sprintf(textureName, "%s_0_0000", texRoot);
#endif

	MLRTexture *texture = MLRTexturePool::Instance->Add(textureName);
	texture->SetHint(gosHint_AGPMemory | gosHint_DontShrink);
	state.SetTextureHandle(texture->GetTextureHandle());
	state.SetTextureWrapMode(MLRState::TextureClamp);

/*		AnalyzeThis(
		tempTrianglesPtr,
		x0,
		z0,
		x1,
		z1
		);
*/
	int i, j;


	Scalar frame[8][4];

	frame[0][0] = xGridOffset;
	frame[0][1] = zGridOffset;
	frame[0][2] = xGridOffset + 8*xGrid;
	frame[0][3] = zGridOffset + 8*zGrid;

	frame[1][0] = xGridOffset + 4*(tileX/4)*xGrid;
	frame[1][1] = zGridOffset + 4*(tileZ/4)*zGrid;
	frame[1][2] = frame[1][0] + 4*xGrid;
	frame[1][3] = frame[1][1] + 4*zGrid;

	for(i=2;i<8;i++)
	{
		frame[i][0] = xGridOffset + tileX*xGrid;
		frame[i][1] = zGridOffset + tileZ*zGrid;
		frame[i][2] = frame[i][0] + xGrid;
		frame[i][3] = frame[i][1] + zGrid;
	}

	DynamicArrayOf<TriangleAbstract*> group_a;

	int usable = tempTrianglesPtr.GetLength();

	if(doWater==true)
	{
		DynamicArrayOf<TriangleStruct> underWaterTriangles(2*usable);
		DynamicArrayOf<TriangleStruct> aboveWaterTriangles(2*usable);

		WaterPools waterPools(usable, waterLevel);
		waterPools.SetTileEdges(x0, x1, z0, z1);

		int org=0, aw=0, uw=0;

		for(i=0;i<usable;i++)
		{
			int ac=0, uc=0, ec=0;

			for(j=0;j<3;j++)
			{
				if(tempTrianglesPtr[i]->GetPoint(j).y < waterLevel)
				{
					uc++;
				}
				else
				{
					if(tempTrianglesPtr[i]->GetPoint(j).y > waterLevel)
					{
						ac++;
					}
					else
					{
						ec++;
					}
				}
			}

			if(uc==0)
			{
				for(int q=0;q<3;q++)
				{
					Verify(tempTrianglesPtr[i]->v[q]<usedUniquePoints);
					aboveWaterTriangles[aw].v[q] = tempTrianglesPtr[i]->v[q];
				}
				aw++;
			}
			else
			{
				if(ac==0)
				{
					for(int q=0;q<3;q++)
					{
						Verify(tempTrianglesPtr[i]->v[q]<usedUniquePoints);
						underWaterTriangles[uw].v[q] = tempTrianglesPtr[i]->v[q];
					}
					if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
					{
						uw++;
					}
				}
				else
				{
					Verify(ec<2);

					if(ec==0)
					{
						Point3D v[4];
						Vector3D vc;
						Scalar l[4];

						if(ac>uc)
						{
							for(j=0;j<3;j++)
							{
								if(tempTrianglesPtr[i]->GetPoint(j).y < waterLevel)
								{
									break;
								}
							}
							Verify(j<3);

							int next = (j+1)%3;
							int prev = (j+2)%3;
							int now = j;

							v[0].Lerp(
								tempTrianglesPtr[i]->GetPoint(next),
								tempTrianglesPtr[i]->GetPoint(j),
								(waterLevel-tempTrianglesPtr[i]->GetPoint(next).y)/
								(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(next).y)
							);
							Verify(Close_Enough(waterLevel, v[0].y));
							v[0].y = waterLevel;

							v[1] = tempTrianglesPtr[i]->GetPoint(next);

							v[2] = tempTrianglesPtr[i]->GetPoint(prev);

							v[3].Lerp(
								tempTrianglesPtr[i]->GetPoint(prev),
								tempTrianglesPtr[i]->GetPoint(j),
								(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
								(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(prev).y)
							);
							Verify(Close_Enough(waterLevel, v[3].y));
							v[3].y = waterLevel;

							waterPools.AddEdge(v[0], v[3], tempTrianglesPtr[i]->plane.normal);

							vc.Cross(v[1], v[0], v[3]);
							l[0] = vc.GetLengthSquared();

							vc.Cross(v[2], v[1], v[0]);
							l[1] = vc.GetLengthSquared();

							vc.Cross(v[3], v[2], v[1]);
							l[2] = vc.GetLengthSquared();

							vc.Cross(v[0], v[3], v[2]);
							l[3] = vc.GetLengthSquared();

							int biggest = 0;
							for(j=1;j<4;j++)
							{
								if(l[biggest]<l[j])
								{
									biggest = j;
								}
							}

							aboveWaterTriangles[aw].v[0] = FindAddPoint(v[biggest]);
							aboveWaterTriangles[aw].v[1] = FindAddPoint(v[(biggest+1)%4]);
							aboveWaterTriangles[aw].v[2] = FindAddPoint(v[(biggest+2)%4]);
							if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
							{
								aw++;
							}

							aboveWaterTriangles[aw].v[0] = FindAddPoint(v[biggest]);
							aboveWaterTriangles[aw].v[1] = FindAddPoint(v[(biggest+2)%4]);
							aboveWaterTriangles[aw].v[2] = FindAddPoint(v[(biggest+3)%4]);
							if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
							{
								aw++;
							}

							underWaterTriangles[uw].v[0] = tempTrianglesPtr[i]->v[now];
							Verify(underWaterTriangles[uw].v[0]<usedUniquePoints);

							underWaterTriangles[uw].v[1] = FindAddPoint(v[0]);
							Verify(underWaterTriangles[uw].v[1]<usedUniquePoints);

							underWaterTriangles[uw].v[2] = FindAddPoint(v[3]);
							Verify(underWaterTriangles[uw].v[2]<usedUniquePoints);

							if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
							{
								uw++;
							}
						}
						else
						{
							for(j=0;j<3;j++)
							{
								if(tempTrianglesPtr[i]->GetPoint(j).y > waterLevel)
								{
									break;
								}
							}
							Verify(j<3);

							int next = (j+1)%3;
							int prev = (j+2)%3;
							int now = j;

							v[0].Lerp(
								tempTrianglesPtr[i]->GetPoint(next),
								tempTrianglesPtr[i]->GetPoint(j),
								(waterLevel-tempTrianglesPtr[i]->GetPoint(next).y)/
								(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(next).y)
							);
							Verify(Close_Enough(waterLevel, v[0].y));
							v[0].y = waterLevel;

							v[1] = tempTrianglesPtr[i]->GetPoint(next);

							v[2] = tempTrianglesPtr[i]->GetPoint(prev);

							v[3].Lerp(
								tempTrianglesPtr[i]->GetPoint(prev),
								tempTrianglesPtr[i]->GetPoint(j),
								(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
								(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(prev).y)
							);
							Verify(Close_Enough(waterLevel, v[3].y));
							v[3].y = waterLevel;

							waterPools.AddEdge(v[0], v[3], tempTrianglesPtr[i]->plane.normal);

							vc.Cross(v[1], v[0], v[3]);
							l[0] = vc.GetLengthSquared();

							vc.Cross(v[2], v[1], v[0]);
							l[1] = vc.GetLengthSquared();

							vc.Cross(v[3], v[2], v[1]);
							l[2] = vc.GetLengthSquared();

							vc.Cross(v[0], v[3], v[2]);
							l[3] = vc.GetLengthSquared();

							int biggest = 0;
							for(j=1;j<4;j++)
							{
								if(l[biggest]<l[j])
								{
									biggest = j;
								}
							}

							underWaterTriangles[uw].v[0] = FindAddPoint(v[biggest]);
							Verify(underWaterTriangles[uw].v[0]<usedUniquePoints);

							underWaterTriangles[uw].v[1] = FindAddPoint(v[(biggest+1)%4]);
							Verify(underWaterTriangles[uw].v[1]<usedUniquePoints);

							underWaterTriangles[uw].v[2] = FindAddPoint(v[(biggest+2)%4]);
							Verify(underWaterTriangles[uw].v[2]<usedUniquePoints);

							if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
							{
								uw++;
							}

							underWaterTriangles[uw].v[0] = FindAddPoint(v[biggest]);
							Verify(underWaterTriangles[uw].v[0]<usedUniquePoints);

							underWaterTriangles[uw].v[1] = FindAddPoint(v[(biggest+2)%4]);
							Verify(underWaterTriangles[uw].v[1]<usedUniquePoints);

							underWaterTriangles[uw].v[2] = FindAddPoint(v[(biggest+3)%4]);
							Verify(underWaterTriangles[uw].v[2]<usedUniquePoints);

							if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid())
							{
								uw++;
							}

							aboveWaterTriangles[aw].v[0] = tempTrianglesPtr[i]->v[now];
							aboveWaterTriangles[aw].v[1] = FindAddPoint(v[0]);
							aboveWaterTriangles[aw].v[2] = FindAddPoint(v[3]);
							if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
							{
								aw++;
							}
						}
					}
					else
					{
						for(j=0;j<3;j++)
						{
							if(!(tempTrianglesPtr[i]->GetPoint(j).y < waterLevel) && 
!(tempTrianglesPtr[i]->GetPoint(j).y > waterLevel))
							{
								break;
							}
						}
						Verify(j<3);

						int next = (j+1)%3;
						int prev = (j+2)%3;
						Point3D tempPoint;

						if(tempTrianglesPtr[i]->GetPoint(next).y < waterLevel)
						{
							underWaterTriangles[uw].v[0] = tempTrianglesPtr[i]->v[j];
							underWaterTriangles[uw].v[1] = tempTrianglesPtr[i]->v[next];

							tempPoint.Lerp(
								tempTrianglesPtr[i]->GetPoint(prev),
								tempTrianglesPtr[i]->GetPoint(next),
								(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
								(tempTrianglesPtr[i]->GetPoint(next).y-tempTrianglesPtr[i]->GetPoint(prev).y)
							);
							Verify(Close_Enough(waterLevel, tempPoint.y));
							tempPoint.y = waterLevel;

							underWaterTriangles[uw].v[2] = FindAddPoint(tempPoint);

							waterPools.AddEdge(
								underWaterTriangles[uw].GetPoint(0),
								underWaterTriangles[uw].GetPoint(2),
								tempTrianglesPtr[i]->plane.normal
							);

							if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
							{
								uw++;
							}


							aboveWaterTriangles[aw].v[0] = tempTrianglesPtr[i]->v[j];
							aboveWaterTriangles[aw].v[1] = underWaterTriangles[uw-1].v[2];
							aboveWaterTriangles[aw].v[2] = tempTrianglesPtr[i]->v[prev];

							if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
							{
								aw++;
							}
						}
						else
						{
							aboveWaterTriangles[aw].v[0] = tempTrianglesPtr[i]->v[j];
							aboveWaterTriangles[aw].v[1] = tempTrianglesPtr[i]->v[next];

							tempPoint.Lerp(
								tempTrianglesPtr[i]->GetPoint(prev),
								tempTrianglesPtr[i]->GetPoint(next),
								(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
								(tempTrianglesPtr[i]->GetPoint(next).y-tempTrianglesPtr[i]->GetPoint(prev).y)
							);
							Verify(Close_Enough(waterLevel, tempPoint.y));
							tempPoint.y = waterLevel;

							aboveWaterTriangles[aw].v[2] = FindAddPoint(tempPoint);

							waterPools.AddEdge(
								aboveWaterTriangles[aw].GetPoint(0),
								aboveWaterTriangles[aw].GetPoint(2),
								tempTrianglesPtr[i]->plane.normal
							);

							if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
							{
								aw++;
							}

							underWaterTriangles[uw].v[0] = tempTrianglesPtr[i]->v[j];
							underWaterTriangles[uw].v[1] = aboveWaterTriangles[aw-1].v[2];
							underWaterTriangles[uw].v[2] = tempTrianglesPtr[i]->v[prev];

							if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
							{
								uw++;
							}
						}
					}
				}
			}
		}

		Verify(uw+aw >= usable);

		if( (uw==0 && aw==usable) || (aw==0 && uw==usable) )
		{
			if(uw==usable)
			{
				gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
				subList->SetSize(2);
				gos_PopCurrentHeap();
			}

			group_a.SetLength(tempTrianglesPtr.GetLength());
			for (i=0; i<tempTrianglesPtr.GetLength(); ++i)
			{
				group_a[i] = tempTrianglesPtr[i];
			}

			if(!BinSort2(
				subList,
				0,
				maxdepth - depth,
				group_a,
				maxNumOfTrianglesPerMesh,
				&frame,
				&state,
				uw==usable
				)
			)
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Unstable triangle mesh created!");
				}
				else
				{
					PAUSE(("Unstable triangle mesh created!"));
				}
			}
			else
			{
				if(uw==usable)
				{
					gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);

					ElementRenderer::ShapeElement* Shape = new ElementRenderer::ShapeElement;
					Register_Object(Shape);
					gos_PopCurrentHeap();

					gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
					MLRShape *shape = new MLRShape(1);
					Register_Object(shape);
					gos_PopCurrentHeap();

					MLRPrimitiveBase *prim = CreateWater( x0, x1, z0, z1, waterLevel);
					Check_Object(prim);
					shape->Add(prim);

					prim->DetachReference();

					Shape->SetMLRShape(shape);

					const Point3D *p = NULL;
					int nr_of_points = 0;
					prim->GetCoordData(&p, &nr_of_points);
					ReadOnlyArrayOf<Point3D> p_array;
					p_array.AssignData(p, nr_of_points);
					Shape->m_localOBB.ComputeBounds(p_array);
					Shape->SetOBBMode();

/*
					Shape->m_localOBB.localToParent = LinearMatrix4D::Identity;
					Shape->m_localOBB.localToParent.BuildTranslation(
						Point3D(
							(x1+x0)*0.5f - xCenterOffset - Xoffset*dX,
							waterLevel,
							(z1+z0)*0.5f - zCenterOffset - Zoffset*dZ
						)
					);

					Shape->m_localOBB.sphereRadius = 0.5f*xGrid*static_cast<Scalar>(sqrt(2.0f));
					Check_Object(Shape);
*/
					waterList->AttachChild(Shape);
					Shape->SetVolumeCullMode();

					subList->AttachIndexedChild(1, Shape);
				}
				brokenTriangles += usable;
			}

		}
		else
		{
	//				MLRPrimitiveBase *prim = waterPools.LinkEdges();

			gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
			subList->SetSize(3);
			gos_PopCurrentHeap();

			tempTrianglesPtr.SetLength(uw);
			underWaterTriangles.SetLength(uw);

			group_a.SetLength(uw);
			for(i=0;i<uw;i++)
			{
				group_a[i] = &underWaterTriangles[i];

			}

			if(!BinSort2(
				subList,
				0,
				maxdepth - depth,
				group_a,
				maxNumOfTrianglesPerMesh,
				&frame,
				&state,
				true
				)
			)
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Unstable triangle mesh created!");
				}
				else
				{
					PAUSE(("Unstable triangle mesh created!"));
				}
			}
			else
			{
				brokenTriangles += uw;
			}

			tempTrianglesPtr.SetLength(aw);
			aboveWaterTriangles.SetLength(aw);

			group_a.SetLength(aw);
			for(i=0;i<aw;i++)
			{
				group_a[i] = &aboveWaterTriangles[i];

			}

			if(!BinSort2(
				subList,
				1,
				maxdepth - depth,
				group_a,
				maxNumOfTrianglesPerMesh,
				&frame,
				&state,
				false
				)
			)
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Unstable triangle mesh created!");
				}
				else
				{
					PAUSE(("Unstable triangle mesh created!"));
				}
			}
			else
			{
				brokenTriangles += aw;
			}

			gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);

			ElementRenderer::ShapeElement* Shape = new ElementRenderer::ShapeElement;
			Register_Object(Shape);
			gos_PopCurrentHeap();

			gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
			MLRShape *shape = new MLRShape(1);
			Register_Object(shape);
			gos_PopCurrentHeap();

			MLRPrimitiveBase *prim = CreateWater( x0, x1, z0, z1, waterLevel);;
			Check_Object(prim);
			shape->Add(prim);

			prim->DetachReference();

			Shape->SetMLRShape(shape);

			const Point3D *p = NULL;
			int nr_of_points = 0;
			prim->GetCoordData(&p, &nr_of_points);
			ReadOnlyArrayOf<Point3D> p_array;
			p_array.AssignData(p, nr_of_points);
			Shape->m_localOBB.ComputeBounds(p_array);
			Shape->SetOBBMode();

/*
			Shape->m_localOBB.localToParent = LinearMatrix4D::Identity;
			Shape->m_localOBB.localToParent.BuildTranslation(
				Point3D(
					(x1+x0)*0.5f - xCenterOffset - Xoffset*dX,
					waterLevel,
					(z1+z0)*0.5f - zCenterOffset - Zoffset*dZ
				)
			);

			Shape->m_localOBB.sphereRadius = 0.5f*xGrid*static_cast<Scalar>(sqrt(2.0f));
			Check_Object(Shape);
*/
			waterList->AttachChild(Shape);
			Shape->SetVolumeCullMode();

			subList->AttachIndexedChild(2, Shape);
		}

/*
			bool px0z0, px0z1, px1z0, px1z1;

			px0z0 = waterLevel < mesh->eval( (int)(x0/dX), (int)(z0/dZ) );
			px0z1 = waterLevel < mesh->eval( (int)(x0/dX), (int)(z1/dZ) );
			px1z0 = waterLevel < mesh->eval( (int)(x1/dX), (int)(z0/dZ) );
			px1z1 = waterLevel < mesh->eval( (int)(x1/dX), (int)(z1/dZ) );

			if(waterPools.waterEdgePointCount==0)
			{
				if(px0z0 == false)
				{
					Verify(px0z0==px0z1 && px0z0==px1z0 && px0z0==px1z1);
					// found water
				}
			}
*/

	}
	else
	{
		group_a.SetLength(tempTrianglesPtr.GetLength());
		for (i=0; i<tempTrianglesPtr.GetLength(); ++i)
		{
			group_a[i] = tempTrianglesPtr[i];
		}

		if(!BinSort(
			subList,
			0,
			maxdepth - depth,
			group_a,
			maxNumOfTrianglesPerMesh,
			&frame,
			&state
			)
		)
		{
			if(silentMode==true)
			{
				fprintf(stderr, "Unstable triangle mesh created!");
			}
			else
			{
				PAUSE(("Unstable triangle mesh created!"));
			}
		}
	}

	group->Sync();
	group->LockBounds();
	group->SetNeverCullMode();
	subList->LockBounds();
	subList->SetNeverCullMode();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HFSlimMZ::BuildTile(
		ElementRenderer::ListElement *list,
		ElementRenderer::GroupElement *waterList,
		int index,
		int depth, int maxdepth,
		DynamicArrayOf<unsigned char>& onOffPoints,
		int maxNumOfTrianglesPerMesh,
		Scalar xGridOffset,
		Scalar zGridOffset,
		Scalar xGrid,
		Scalar zGrid,
		int tileX,
		int tileZ,
		const char *texRoot
	)
{
	char textureName[1024];
	int numOfTriangles = toSortTriangles.GetLength();

	DynamicArrayOf<TriangleAbstract*> tempTrianglesPtr(numOfTriangles);
	DynamicArrayOf<ToDrawTriangle> tempTriangles(numOfTriangles);

	int i, j, inCount, newCount = 0, extraTriangles = 0;
	int k, l, m;

	Scalar x0, z0, x1, z1;

	x0 = xGridOffset + tileX*xGrid;
	z0 = zGridOffset + tileZ*zGrid;
	x1 = x0 + xGrid;
	z1 = z0 + zGrid;

	MLRState state;

	gos_PushCurrentHeap(ElementRenderer::GroupElement::s_Heap);
	ElementRenderer::GroupElement *group = new ElementRenderer::GroupElement;
	Register_Object(group);
	list->AttachIndexedChild(index, group);
	group->NeedNewBounds();
	group->SetVolumeCullMode();
	gos_PopCurrentHeap();

	gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
	ElementRenderer::ListElement *subList = new ElementRenderer::ListElement;
	Register_Object(subList);
	subList->SetSize(1);
	group->AttachChild(subList);
	subList->NeedNewBounds();
	subList->SetVolumeCullMode();
	gos_PopCurrentHeap();

/*
	state.SetBackFaceOn();
	state.SetDitherOff();
	state.SetTextureCorrectionOn();
	state.SetZBufferCompareOn();
	state.SetZBufferWriteOn();

	state.SetFilterMode(MLRState::BiLinearFilterMode);

	state.SetFogMode(MLRState::OverrideFogMode);
	state.SetFogData(
		0xff9090f0,
		0.0f,
		700.0f,
		1000.0f
	);
*/
	state.SetPriority(MLRState::DefaultPriority);

#if 0
	sprintf(textureName, "%s_%1d_%02x%02x",
		texRoot,
		depth,
		(1<<depth) - tileX/(1<<(maxdepth-depth)) - 1,
		(1<<depth) - tileZ/(1<<(maxdepth-depth)) - 1
	);
#else
	sprintf(textureName, "%s_0_0000", texRoot);
#endif

	MLRTexture *texture = MLRTexturePool::Instance->Add(textureName);
	texture->SetHint(gosHint_AGPMemory | gosHint_DontShrink);
	state.SetTextureHandle(texture->GetTextureHandle());
	state.SetTextureWrapMode(MLRState::TextureClamp);

	for(i=0,j=0;i<numOfTriangles;j+=3,++i)
	{
		Verify(i<numOfTriangles);
		Verify(j<3*numOfTriangles);
		if(onOffPoints[i] == 0)
		{
			inCount = 0;
			int clipIt[3] = {0, 0, 0}, orIt = 0, addIt = 0xf;

			if(toSortTriangles[i]->GetPoint(0).x < x0)
			{
				clipIt[0] |= 1;
			}
			if(toSortTriangles[i]->GetPoint(0).x > x1)
			{
				clipIt[0] |= 4;
			}
			if(toSortTriangles[i]->GetPoint(0).z < z0)
			{
				clipIt[0] |= 8;
			}
			if(toSortTriangles[i]->GetPoint(0).z > z1)
			{
				clipIt[0] |= 2;
			}

			orIt |= clipIt[0];
			addIt &= clipIt[0];

			if(toSortTriangles[i]->GetPoint(1).x < x0)
			{
				clipIt[1] |= 1;
			}
			if(toSortTriangles[i]->GetPoint(1).x > x1)
			{
				clipIt[1] |= 4;
			}
			if(toSortTriangles[i]->GetPoint(1).z < z0)
			{
				clipIt[1] |= 8;
			}
			if(toSortTriangles[i]->GetPoint(1).z > z1)
			{
				clipIt[1] |= 2;
			}

			orIt |= clipIt[1];
			addIt &= clipIt[1];

			if(toSortTriangles[i]->GetPoint(2).x < x0)
			{
				clipIt[2] |= 1;
			}
			if(toSortTriangles[i]->GetPoint(2).x > x1)
			{
				clipIt[2] |= 4;
			}
			if(toSortTriangles[i]->GetPoint(2).z < z0)
			{
				clipIt[2] |= 8;
			}
			if(toSortTriangles[i]->GetPoint(2).z > z1)
			{
				clipIt[2] |= 2;
			}

			orIt |= clipIt[2];
			addIt &= clipIt[2];

			if(orIt == 0)
			{
				onOffPoints[i] = 1;

				if(toSortTriangles[i]->area > SMALL)
				{
					if(! (toSortTriangles[i]->plane.normal.y > Stuff::SMALL) )
					{
						if(silentMode==true)
						{
							fprintf(stderr, "Found a triangle with a face normal not pointing up, consider revising map ! Program exits !\n");
							exit(-1);
						}
						else
						{
							STOP(("Found a triangle with a face normal not pointing up, consider revising map !"));
						}
					}
					tempTrianglesPtr[newCount++] = toSortTriangles[i];
				}
			}
			else if(addIt==0)
			{
				if(orIt==1 || orIt==2 || orIt==4 || orIt==8)
				{
					Point3D triangle[3], clipPoints[6];

					triangle[0].x = toSortTriangles[i]->GetPoint(0).x;
					triangle[0].y = toSortTriangles[i]->GetPoint(0).y;
					triangle[0].z = toSortTriangles[i]->GetPoint(0).z;

					triangle[1].x = toSortTriangles[i]->GetPoint(1).x;
					triangle[1].y = toSortTriangles[i]->GetPoint(1).y;
					triangle[1].z = toSortTriangles[i]->GetPoint(1).z;

					triangle[2].x = toSortTriangles[i]->GetPoint(2).x;
					triangle[2].y = toSortTriangles[i]->GetPoint(2).y;
					triangle[2].z = toSortTriangles[i]->GetPoint(2).z;

					l = 0;
					for(k=0;k<3;k++)
					{
						int next = k+1>2?0:k+1;
						if(clipIt[k]==0)
						{
							clipPoints[l++] = triangle[k];
							if(clipIt[next]==0)
							{
								continue;
							}
						}
						else
						{
							if(clipIt[next]!=0)
							{
								continue;
							}
						}
						//
						//-----------------------------------------------------
						// Find the boundary conditions that match our clipping
						// plane
						//-----------------------------------------------------
						//
						int mask = 1;
						for (m=0; m<4; m++)
						{
							if((clipIt[k] | clipIt[next]) & mask)
							{
								switch(m)
								{
									case 0:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(x0-triangle[k].x)/(triangle[next].x-triangle[k].x)

										);
										clipPoints[l++].x = x0;
									break;
									case 1:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(z1-triangle[k].z)/(triangle[next].z-triangle[k].z)
										);
										clipPoints[l++].z = z1;
									break;
									case 2:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(x1-triangle[k].x)/(triangle[next].x-triangle[k].x)
										);
										clipPoints[l++].x = x1;
									break;
									case 3:
										clipPoints[l].AddScaled(
											triangle[k],
											Vector3D(
												triangle[next].x - triangle[k].x,
												triangle[next].y - triangle[k].y,
												triangle[next].z - triangle[k].z
											),
											(z0-triangle[k].z)/(triangle[next].z-triangle[k].z)
										);
										clipPoints[l++].z = z0;
									break;
								}
								break;
							}
							mask <<= 1;
						}
					}
					Verify(l>2);
					for(m=1;m<l-1;m++)
					{
						tempTriangles[extraTriangles].SetPoint(0, clipPoints[0].x, 
clipPoints[0].y, clipPoints[0].z);
						tempTriangles[extraTriangles].SetPoint(1, clipPoints[m].x, 
clipPoints[m].y, clipPoints[m].z);
						tempTriangles[extraTriangles].SetPoint(2, clipPoints[m+1].x, 
clipPoints[m+1].y, clipPoints[m+1].z);

						if(tempTriangles[extraTriangles].GetSurfaceAreaAndCentroid() == true)
						{
							tempTrianglesPtr[newCount++] = &tempTriangles[extraTriangles];
							extraTriangles++;
						}
					}
				}
				else
				{

					int key=0, len[2] = {0, 0};
					Point3D clipPoints[2][6];
					int clipMeToo[2][6];

					clipPoints[key][0].x = toSortTriangles[i]->GetPoint(0).x;
					clipPoints[key][0].y = toSortTriangles[i]->GetPoint(0).y;
					clipPoints[key][0].z = toSortTriangles[i]->GetPoint(0).z;

					clipPoints[key][1].x = toSortTriangles[i]->GetPoint(1).x;
					clipPoints[key][1].y = toSortTriangles[i]->GetPoint(1).y;
					clipPoints[key][1].z = toSortTriangles[i]->GetPoint(1).z;

					clipPoints[key][2].x = toSortTriangles[i]->GetPoint(2).x;
					clipPoints[key][2].y = toSortTriangles[i]->GetPoint(2).y;
					clipPoints[key][2].z = toSortTriangles[i]->GetPoint(2).z;

					clipMeToo[key][0] = clipIt[0];
					clipMeToo[key][1] = clipIt[1];
					clipMeToo[key][2] = clipIt[2];

					len[key] = 3;

					int next, mask = 1;
					for(m=0;m<4;++m)
					{
						if(orIt & mask)
						{
							for(k=0;k<len[key];k++)
							{
								next = (k+1) < len[key] ? k+1 : 0;

								if(!(clipMeToo[key][k] & mask))
								{
									clipPoints[!key][len[!key]++] = clipPoints[key][k];
									if(!(clipMeToo[key][next] & mask))
									{
										continue;
									}
								}
								else
								{
									if(clipMeToo[key][next] & mask)
									{
										continue;
									}
								}

								switch(m)
								{
									case 0:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(x0-clipPoints[key][k].x)/(clipPoints[key][next].x-clipPoints[key][k].x)
										);
										clipPoints[!key][len[!key]++].x = x0;
									break;
									case 1:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(z1-clipPoints[key][k].z)/(clipPoints[key][next].z-clipPoints[key][k].z)
										);
										clipPoints[!key][len[!key]++].z = z1;
									break;
									case 2:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(x1-clipPoints[key][k].x)/(clipPoints[key][next].x-clipPoints[key][k].x)
										);
										clipPoints[!key][len[!key]++].x = x1;
									break;
									case 3:
										clipPoints[!key][len[!key]].AddScaled(
											clipPoints[key][k],
											Vector3D(
												clipPoints[key][next].x - clipPoints[key][k].x,
												clipPoints[key][next].y - clipPoints[key][k].y,
												clipPoints[key][next].z - clipPoints[key][k].z
											),
											(z0-clipPoints[key][k].z)/(clipPoints[key][next].z-clipPoints[key][k].z)
										);
										clipPoints[!key][len[!key]++].z = z0;
									break;
								}
							}
							key = !key;
							for(k=0;k<len[key];k++)
							{
								clipMeToo[key][k] = 0;
								if(clipPoints[key][k].x < x0)
								{
									clipMeToo[key][k] |= 1;
								}
								if(clipPoints[key][k].x > x1)
								{
									clipMeToo[key][k] |= 4;
								}
								if(clipPoints[key][k].z < z0)
								{
									clipMeToo[key][k] |= 8;
								}
								if(clipPoints[key][k].z > z1)
								{
									clipMeToo[key][k] |= 2;
								}
							}
							len[!key] = 0;
						}
						mask <<= 1;
					}

					for(m=1;m<len[key]-1;m++)
					{
						tempTriangles[extraTriangles].SetPoint(0,
							clipPoints[key][0].x,
							clipPoints[key][0].y,
							clipPoints[key][0].z
						);
						tempTriangles[extraTriangles].SetPoint(1,
							clipPoints[key][m].x,
							clipPoints[key][m].y,
							clipPoints[key][m].z
						);
						tempTriangles[extraTriangles].SetPoint(2,
							clipPoints[key][m+1].x,
							clipPoints[key][m+1].y,
							clipPoints[key][m+1].z
						);

						if(tempTriangles[extraTriangles].GetSurfaceAreaAndCentroid() == true)
						{
							tempTrianglesPtr[newCount++] = &tempTriangles[extraTriangles];
							extraTriangles++;
						}
					}
				}
			}
		}
	}
//	SPEWALWAYS(("micgaert", "#1"));
//	tempTriangles.SetLength(extraTriangles);

	tempTrianglesPtr.SetLength(newCount);

#if _ARMOR
	for(i=0,j=0;i<newCount;++i)
	{
		if(onOffPoints[i] == 0)
		{
			Verify(tempTrianglesPtr[i]->GetPoint(0).x >= x0);
			Verify(tempTrianglesPtr[i]->GetPoint(0).x <= x1);
			Verify(tempTrianglesPtr[i]->GetPoint(0).z >= z0);
			Verify(tempTrianglesPtr[i]->GetPoint(0).z <= z1);

			Verify(tempTrianglesPtr[i]->GetPoint(1).x >= x0);
			Verify(tempTrianglesPtr[i]->GetPoint(1).x <= x1);
			Verify(tempTrianglesPtr[i]->GetPoint(1).z >= z0);
			Verify(tempTrianglesPtr[i]->GetPoint(1).z <= z1);

			Verify(tempTrianglesPtr[i]->GetPoint(2).x >= x0);
			Verify(tempTrianglesPtr[i]->GetPoint(2).x <= x1);
			Verify(tempTrianglesPtr[i]->GetPoint(2).z >= z0);
			Verify(tempTrianglesPtr[i]->GetPoint(2).z <= z1);
		}
	}
#endif


	if(newCount > 0)
	{
		AnalyzeThis(
			tempTrianglesPtr,
			x0,
			z0,
			x1,
			z1
			);

		int i = 1<<(maxdepth - depth);

		frameArray frame;

		frame[0][0] = xGridOffset;
		frame[0][1] = zGridOffset;
		frame[0][2] = xGridOffset + 8*xGrid;
		frame[0][3] = zGridOffset + 8*zGrid;

		frame[1][0] = xGridOffset + 4*(tileX/4)*xGrid;
		frame[1][1] = zGridOffset + 4*(tileZ/4)*zGrid;
		frame[1][2] = frame[1][0] + 4*xGrid;
		frame[1][3] = frame[1][1] + 4*zGrid;

		for(i=2;i<8;i++)
		{
			frame[i][0] = xGridOffset + tileX*xGrid;
			frame[i][1] = zGridOffset + tileZ*zGrid;
			frame[i][2] = frame[i][0] + xGrid;
			frame[i][3] = frame[i][1] + zGrid;
		}

//		SPEWALWAYS(("micgaert", "#2"));

		if(doWater==true)
		{
			DynamicArrayOf<ToDrawTriangle> underWaterTriangles(2*newCount);
			DynamicArrayOf<ToDrawTriangle> aboveWaterTriangles(2*newCount);

			WaterPools waterPools(newCount, waterLevel);
			waterPools.SetTileEdges(x0, x1, z0, z1);

			int org=0, aw=0, uw=0;

			for(i=0;i<newCount;i++)
			{
				int ac=0, uc=0, ec=0;

				if(tempTrianglesPtr[i]->GetPoint(0).y < waterLevel)
				{
					uc++;
				}
				else
				{
					if(tempTrianglesPtr[i]->GetPoint(0).y > waterLevel)
					{
						ac++;
					}
					else
					{
						ec++;
					}
				}

				if(tempTrianglesPtr[i]->GetPoint(1).y < waterLevel)
				{
					uc++;
				}
				else
				{
					if(tempTrianglesPtr[i]->GetPoint(1).y > waterLevel)
					{
						ac++;
					}
					else
					{
						ec++;
					}
				}
				if(tempTrianglesPtr[i]->GetPoint(2).y < waterLevel)
				{
					uc++;
				}
				else
				{
					if(tempTrianglesPtr[i]->GetPoint(2).y > waterLevel)
					{
						ac++;
					}
					else
					{
						ec++;
					}
				}

				if(uc==0)
				{
					for(int q=0;q<3;q++)
					{
						aboveWaterTriangles[aw].v[q] = tempTrianglesPtr[i]->GetPoint(q);
					}
					if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
					{
						aw++;
						for(j=0;j<3;j++)
						{
							Verify(aboveWaterTriangles[aw-1].v[j].x<=frame[maxAllDepth][2] && 
aboveWaterTriangles[aw-1].v[j].x>=frame[maxAllDepth][0]);
							Verify(aboveWaterTriangles[aw-1].v[j].z<=frame[maxAllDepth][3] && 
aboveWaterTriangles[aw-1].v[j].z>=frame[maxAllDepth][1]);
						}
					}

				}
				else
				{
					if(ac==0)
					{
						for(int q=0;q<3;q++)
						{
							underWaterTriangles[uw].v[q] = tempTrianglesPtr[i]->GetPoint(q);
						}
						if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
						{
							uw++;
							for(j=0;j<3;j++)
							{
								Verify(underWaterTriangles[uw-1].v[j].x<=frame[maxAllDepth][2] && 
underWaterTriangles[uw-1].v[j].x>=frame[maxAllDepth][0]);
								Verify(underWaterTriangles[uw-1].v[j].z<=frame[maxAllDepth][3] && 
underWaterTriangles[uw-1].v[j].z>=frame[maxAllDepth][1]);
							}
						}
					}
					else
					{
						Verify(ec<2);

						if(ec==0)
						{
							Point3D v[4];
							Vector3D vc;
							Scalar l[4];

							if(ac>uc)
							{
								for(j=0;j<3;j++)
								{
									if(tempTrianglesPtr[i]->GetPoint(j).y < waterLevel)
									{
										break;
									}
								}
								Verify(j<3);

								int next = (j+1)%3;
								int prev = (j+2)%3;
								int now = j;

								v[0].Lerp(
									tempTrianglesPtr[i]->GetPoint(next),
									tempTrianglesPtr[i]->GetPoint(j),
									(waterLevel-tempTrianglesPtr[i]->GetPoint(next).y)/
									(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(next).y)
								);
								Verify(Close_Enough(waterLevel, v[0].y));
								v[0].y = waterLevel;

								v[1] = tempTrianglesPtr[i]->GetPoint(next);

								v[2] = tempTrianglesPtr[i]->GetPoint(prev);

								v[3].Lerp(
									tempTrianglesPtr[i]->GetPoint(prev),
									tempTrianglesPtr[i]->GetPoint(j),
									(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
									(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(prev).y)
								);
								Verify(Close_Enough(waterLevel, v[3].y));
								v[3].y = waterLevel;

								waterPools.AddEdge(v[0], v[3], tempTrianglesPtr[i]->plane.normal);

								vc.Cross(v[1], v[0], v[3]);
								l[0] = vc.GetLengthSquared();

								vc.Cross(v[2], v[1], v[0]);
								l[1] = vc.GetLengthSquared();

								vc.Cross(v[3], v[2], v[1]);
								l[2] = vc.GetLengthSquared();

								vc.Cross(v[0], v[3], v[2]);
								l[3] = vc.GetLengthSquared();

								int biggest = 0;
								for(j=1;j<4;j++)
								{
									if(l[biggest]<l[j])
									{
										biggest = j;
									}
								}

								aboveWaterTriangles[aw].v[0] = v[biggest];
								aboveWaterTriangles[aw].v[1] = v[(biggest+1)%4];
								aboveWaterTriangles[aw].v[2] = v[(biggest+2)%4];
								if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(aboveWaterTriangles[aw].v[j].x<=frame[maxAllDepth][2] && 
aboveWaterTriangles[aw].v[j].x>=frame[maxAllDepth][0]);
										Verify(aboveWaterTriangles[aw].v[j].z<=frame[maxAllDepth][3] && 
aboveWaterTriangles[aw].v[j].z>=frame[maxAllDepth][1]);
									}
									aw++;
								}

								aboveWaterTriangles[aw].v[0] = v[biggest];
								aboveWaterTriangles[aw].v[1] = v[(biggest+2)%4];
								aboveWaterTriangles[aw].v[2] = v[(biggest+3)%4];
								if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(aboveWaterTriangles[aw].v[j].x<=frame[maxAllDepth][2] && 
aboveWaterTriangles[aw].v[j].x>=frame[maxAllDepth][0]);
										Verify(aboveWaterTriangles[aw].v[j].z<=frame[maxAllDepth][3] && 
aboveWaterTriangles[aw].v[j].z>=frame[maxAllDepth][1]);
									}
									aw++;
								}

								underWaterTriangles[uw].v[0] = tempTrianglesPtr[i]->GetPoint(now);
								underWaterTriangles[uw].v[1] = v[0];
								underWaterTriangles[uw].v[2] = v[3];
								if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(underWaterTriangles[uw].v[j].x<=frame[maxAllDepth][2] && 
underWaterTriangles[uw].v[j].x>=frame[maxAllDepth][0]);
										Verify(underWaterTriangles[uw].v[j].z<=frame[maxAllDepth][3] && 
underWaterTriangles[uw].v[j].z>=frame[maxAllDepth][1]);
									}
									uw++;
								}
							}
							else
							{
								for(j=0;j<3;j++)
								{
									if(tempTrianglesPtr[i]->GetPoint(j).y > waterLevel)
									{
										break;
									}
								}
								Verify(j<3);

								int next = (j+1)%3;
								int prev = (j+2)%3;
								int now = j;

								v[0].Lerp(
									tempTrianglesPtr[i]->GetPoint(next),
									tempTrianglesPtr[i]->GetPoint(j),
									(waterLevel-tempTrianglesPtr[i]->GetPoint(next).y)/
									(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(next).y)
								);
								Verify(Close_Enough(waterLevel, v[0].y));
								v[0].y = waterLevel;

								v[1] = tempTrianglesPtr[i]->GetPoint(next);

								v[2] = tempTrianglesPtr[i]->GetPoint(prev);

								v[3].Lerp(
									tempTrianglesPtr[i]->GetPoint(prev),
									tempTrianglesPtr[i]->GetPoint(j),
									(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
									(tempTrianglesPtr[i]->GetPoint(j).y-tempTrianglesPtr[i]->GetPoint(prev).y)
								);
								Verify(Close_Enough(waterLevel, v[3].y));
								v[3].y = waterLevel;

								waterPools.AddEdge(v[0], v[3], tempTrianglesPtr[i]->plane.normal);

								vc.Cross(v[1], v[0], v[3]);
								l[0] = vc.GetLengthSquared();

								vc.Cross(v[2], v[1], v[0]);
								l[1] = vc.GetLengthSquared();

								vc.Cross(v[3], v[2], v[1]);
								l[2] = vc.GetLengthSquared();

								vc.Cross(v[0], v[3], v[2]);
								l[3] = vc.GetLengthSquared();

								int biggest = 0;
								for(j=1;j<4;j++)
								{
									if(l[biggest]<l[j])
									{
										biggest = j;
									}
								}

								underWaterTriangles[uw].v[0] = v[biggest];
								underWaterTriangles[uw].v[1] = v[(biggest+1)%4];
								underWaterTriangles[uw].v[2] = v[(biggest+2)%4];
								if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(underWaterTriangles[uw].v[j].x<=frame[maxAllDepth][2] && 
underWaterTriangles[uw].v[j].x>=frame[maxAllDepth][0]);
										Verify(underWaterTriangles[uw].v[j].z<=frame[maxAllDepth][3] && 
underWaterTriangles[uw].v[j].z>=frame[maxAllDepth][1]);
									}
									uw++;
								}

								underWaterTriangles[uw].v[0] = v[biggest];
								underWaterTriangles[uw].v[1] = v[(biggest+2)%4];
								underWaterTriangles[uw].v[2] = v[(biggest+3)%4];
								if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid())
								{
									for(j=0;j<3;j++)
									{
										Verify(underWaterTriangles[uw].v[j].x<=frame[maxAllDepth][2] && 
underWaterTriangles[uw].v[j].x>=frame[maxAllDepth][0]);
										Verify(underWaterTriangles[uw].v[j].z<=frame[maxAllDepth][3] && 
underWaterTriangles[uw].v[j].z>=frame[maxAllDepth][1]);
									}
									uw++;
								}

								aboveWaterTriangles[aw].v[0] = tempTrianglesPtr[i]->GetPoint(now);
								aboveWaterTriangles[aw].v[1] = v[0];
								aboveWaterTriangles[aw].v[2] = v[3];
								if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(aboveWaterTriangles[aw].v[j].x<=frame[maxAllDepth][2] && 
aboveWaterTriangles[aw].v[j].x>=frame[maxAllDepth][0]);
										Verify(aboveWaterTriangles[aw].v[j].z<=frame[maxAllDepth][3] && 
aboveWaterTriangles[aw].v[j].z>=frame[maxAllDepth][1]);
									}
									aw++;
								}
							}
						}
						else
						{
							for(j=0;j<3;j++)
							{
								if(!(tempTrianglesPtr[i]->GetPoint(j).y < waterLevel) && 
!(tempTrianglesPtr[i]->GetPoint(j).y > waterLevel))
								{
									break;
								}
							}
							Verify(j<3);

							int next = (j+1)%3;
							int prev = (j+2)%3;

							if(tempTrianglesPtr[i]->GetPoint(next).y < waterLevel)
							{
								underWaterTriangles[uw].v[0] = tempTrianglesPtr[i]->GetPoint(j);
								underWaterTriangles[uw].v[1] = tempTrianglesPtr[i]->GetPoint(next);

								underWaterTriangles[uw].v[2].Lerp(
									tempTrianglesPtr[i]->GetPoint(prev),
									tempTrianglesPtr[i]->GetPoint(next),
									(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
									(tempTrianglesPtr[i]->GetPoint(next).y-tempTrianglesPtr[i]->GetPoint(prev).y)
								);
								Verify(Close_Enough(waterLevel, underWaterTriangles[uw].v[2].y));
								underWaterTriangles[uw].v[2].y = waterLevel;

								waterPools.AddEdge(
									underWaterTriangles[uw].v[0],
									underWaterTriangles[uw].v[2],
									tempTrianglesPtr[i]->plane.normal
								);

								if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(underWaterTriangles[uw].v[j].x<=frame[maxAllDepth][2] && 
underWaterTriangles[uw].v[j].x>=frame[maxAllDepth][0]);
										Verify(underWaterTriangles[uw].v[j].z<=frame[maxAllDepth][3] && 
underWaterTriangles[uw].v[j].z>=frame[maxAllDepth][1]);
									}
									uw++;
								}


								aboveWaterTriangles[aw].v[0] = tempTrianglesPtr[i]->GetPoint(j);
								aboveWaterTriangles[aw].v[1] = underWaterTriangles[uw-1].v[2];
								aboveWaterTriangles[aw].v[2] = tempTrianglesPtr[i]->GetPoint(prev);

								if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(aboveWaterTriangles[aw].v[j].x<=frame[maxAllDepth][2] && 
aboveWaterTriangles[aw].v[j].x>=frame[maxAllDepth][0]);
										Verify(aboveWaterTriangles[aw].v[j].z<=frame[maxAllDepth][3] && 
aboveWaterTriangles[aw].v[j].z>=frame[maxAllDepth][1]);
									}
									aw++;
								}
							}
							else
							{
								aboveWaterTriangles[aw].v[0] = tempTrianglesPtr[i]->GetPoint(j);
								aboveWaterTriangles[aw].v[1] = tempTrianglesPtr[i]->GetPoint(next);

								aboveWaterTriangles[aw].v[2].Lerp(
									tempTrianglesPtr[i]->GetPoint(prev),
									tempTrianglesPtr[i]->GetPoint(next),
									(waterLevel-tempTrianglesPtr[i]->GetPoint(prev).y)/
									(tempTrianglesPtr[i]->GetPoint(next).y-tempTrianglesPtr[i]->GetPoint(prev).y)
								);
								Verify(Close_Enough(waterLevel, aboveWaterTriangles[aw].v[2].y));
								aboveWaterTriangles[aw].v[2].y = waterLevel;

								waterPools.AddEdge(
									aboveWaterTriangles[aw].v[0],
									aboveWaterTriangles[aw].v[2],
									tempTrianglesPtr[i]->plane.normal
								);

								if(aboveWaterTriangles[aw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(aboveWaterTriangles[aw].v[j].x<=frame[maxAllDepth][2] && 
aboveWaterTriangles[aw].v[j].x>=frame[maxAllDepth][0]);
										Verify(aboveWaterTriangles[aw].v[j].z<=frame[maxAllDepth][3] && 
aboveWaterTriangles[aw].v[j].z>=frame[maxAllDepth][1]);
									}
									aw++;
								}

								underWaterTriangles[uw].v[0] = tempTrianglesPtr[i]->GetPoint(j);
								underWaterTriangles[uw].v[1] = aboveWaterTriangles[aw-1].v[2];
								underWaterTriangles[uw].v[2] = tempTrianglesPtr[i]->GetPoint(prev);

								if(underWaterTriangles[uw].GetSurfaceAreaAndCentroid()==true)
								{
									for(j=0;j<3;j++)
									{
										Verify(underWaterTriangles[uw].v[j].x<=frame[maxAllDepth][2] && 
underWaterTriangles[uw].v[j].x>=frame[maxAllDepth][0]);
										Verify(underWaterTriangles[uw].v[j].z<=frame[maxAllDepth][3] && 
underWaterTriangles[uw].v[j].z>=frame[maxAllDepth][1]);
									}
									uw++;
								}
							}
						}
					}
				}
			}

//			SPEWALWAYS(("micgaert", "#3"));

			Verify(uw+aw >= newCount);

			if( (uw==0 && aw==newCount) || (aw==0 && uw==newCount) )
			{
//				SPEWALWAYS(("micgaert", "#3.5"));
				if(uw==newCount)
				{
					gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
					subList->SetSize(2);
					gos_PopCurrentHeap();
				}


				if(!BinSort2(
					subList,
					0,
					maxdepth - depth,
					tempTrianglesPtr,
					maxNumOfTrianglesPerMesh,
					&frame,
					&state,
					uw==newCount
					)
				)
				{
					if(silentMode==true)
					{
						fprintf(stderr, "Unstable triangle mesh created!");
					}
					else
					{
						PAUSE(("Unstable triangle mesh created!"));
					}
				}
				else
				{
//					SPEWALWAYS(("micgaert", "#5"));
					if(uw==newCount)
					{
						gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);

						ElementRenderer::ShapeElement* Shape = new 
ElementRenderer::ShapeElement;
						Register_Object(Shape);
						gos_PopCurrentHeap();

						gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
						MLRShape *shape = new MLRShape(1);
						Register_Object(shape);
						gos_PopCurrentHeap();

						MLRPrimitiveBase *prim = CreateWater( x0, x1, z0, z1, waterLevel);
						Check_Object(prim);
						shape->Add(prim);

						prim->DetachReference();

						Shape->SetMLRShape(shape);

						const Point3D *p = NULL;
						int nr_of_points = 0;
						prim->GetCoordData(&p, &nr_of_points);
						ReadOnlyArrayOf<Point3D> p_array;
						p_array.AssignData(p, nr_of_points);
						Shape->m_localOBB.ComputeBounds(p_array);
						Shape->SetOBBMode();

/*
						Shape->m_localOBB.localToParent = LinearMatrix4D::Identity;
						Shape->m_localOBB.localToParent.BuildTranslation(
							Point3D(
								(x1+x0)*0.5f - xCenterOffset - Xoffset*dX,
								waterLevel,
								(z1+z0)*0.5f - zCenterOffset - Zoffset*dZ
							)
						);

						Shape->m_localOBB.sphereRadius = 0.5f*xGrid*static_cast<Scalar>(sqrt(2.0f));
						Check_Object(Shape);
*/
						waterList->AttachChild(Shape);
						Shape->SetVolumeCullMode();

						subList->AttachIndexedChild(1, Shape);
					}
					brokenTriangles += newCount;
				}

			}
			else
			{
//				MLRPrimitiveBase *prim = waterPools.LinkEdges();

//				SPEWALWAYS(("micgaert", "#6"));

				gos_PushCurrentHeap(ElementRenderer::ListElement::s_Heap);
				subList->SetSize(3);
				gos_PopCurrentHeap();

				tempTrianglesPtr.SetLength(uw);
				underWaterTriangles.SetLength(uw);

				for(i=0;i<uw;i++)
				{
					tempTrianglesPtr[i] = &underWaterTriangles[i];

				}

//				SPEWALWAYS(("micgaert", "#7"));
				if(!BinSort2(
					subList,
					0,
					maxdepth - depth,
					tempTrianglesPtr,
					maxNumOfTrianglesPerMesh,
					&frame,
					&state,
					true
					)
				)
				{
					if(silentMode==true)
					{
						fprintf(stderr, "Unstable triangle mesh created!");
					}
					else
					{
						PAUSE(("Unstable triangle mesh created!"));
					}
				}
				else
				{
					brokenTriangles += uw;
				}

				tempTrianglesPtr.SetLength(aw);
				aboveWaterTriangles.SetLength(aw);

				for(i=0;i<aw;i++)
				{
					tempTrianglesPtr[i] = &aboveWaterTriangles[i];
				}

//				SPEWALWAYS(("micgaert", "#8"));
				if(!BinSort2(
					subList,
					1,
					maxdepth - depth,
					tempTrianglesPtr,
					maxNumOfTrianglesPerMesh,
					&frame,
					&state,
					false
					)
				)
				{
					if(silentMode==true)
					{
						fprintf(stderr, "Unstable triangle mesh created!");
					}
					else
					{
						PAUSE(("Unstable triangle mesh created!"));
					}
				}
				else
				{
					brokenTriangles += aw;
				}

//				SPEWALWAYS(("micgaert", "#8.1"));
				gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);

				ElementRenderer::ShapeElement* Shape = new 
ElementRenderer::ShapeElement;
				Register_Object(Shape);
				gos_PopCurrentHeap();
//				SPEWALWAYS(("micgaert", "#8.2"));

				gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
				MLRShape *shape = new MLRShape(1);
				Register_Object(shape);
				gos_PopCurrentHeap();
//				SPEWALWAYS(("micgaert", "#8.3"));

				MLRPrimitiveBase *prim = CreateWater( x0, x1, z0, z1, waterLevel);;
				Check_Object(prim);
				shape->Add(prim);
//				SPEWALWAYS(("micgaert", "#8.4"));

				prim->DetachReference();

				Shape->SetMLRShape(shape);

				const Point3D *p = NULL;
				int nr_of_points = 0;
				prim->GetCoordData(&p, &nr_of_points);
				ReadOnlyArrayOf<Point3D> p_array;
				p_array.AssignData(p, nr_of_points);
				Shape->m_localOBB.ComputeBounds(p_array);
				Shape->SetOBBMode();
/*
				Shape->m_localOBB.localToParent = LinearMatrix4D::Identity;
				Shape->m_localOBB.localToParent.BuildTranslation(
					Point3D(
						(x1+x0)*0.5f - xCenterOffset - Xoffset*dX,
						waterLevel,
						(z1+z0)*0.5f - zCenterOffset - Zoffset*dZ
					)
				);
//				SPEWALWAYS(("micgaert", "#8.5"));

				Shape->m_localOBB.sphereRadius = 0.5f*xGrid*static_cast<Scalar>(sqrt(2.0f));
				Check_Object(Shape);
*/
				waterList->AttachChild(Shape);
				Shape->SetVolumeCullMode();

				subList->AttachIndexedChild(2, Shape);

//				SPEWALWAYS(("micgaert", "#9"));
			}

/*
			bool px0z0, px0z1, px1z0, px1z1;

			px0z0 = waterLevel < mesh->eval( (int)(x0/dX), (int)(z0/dZ) );
			px0z1 = waterLevel < mesh->eval( (int)(x0/dX), (int)(z1/dZ) );
			px1z0 = waterLevel < mesh->eval( (int)(x1/dX), (int)(z0/dZ) );
			px1z1 = waterLevel < mesh->eval( (int)(x1/dX), (int)(z1/dZ) );

			if(waterPools.waterEdgePointCount==0)
			{
				if(px0z0 == false)
				{
					Verify(px0z0==px0z1 && px0z0==px1z0 && px0z0==px1z1);
					// found water
				}
			}
*/
		}
		else
		{
			if(!BinSort(
				subList,
				0,
				maxdepth - depth,
				tempTrianglesPtr,
				maxNumOfTrianglesPerMesh,
				&frame,
				&state
				)
			)
			{
				if(silentMode==true)
				{
					fprintf(stderr, "Unstable triangle mesh created!");
				}
				else
				{
					PAUSE(("Unstable triangle mesh created!"));
				}
			}
			else
			{
				brokenTriangles += newCount;
			}
		}
	}
	else
	{
		if(silentMode==true)
		{
			fprintf(stderr, "Found an empty mesh ! Program exits !\n");
			exit(-1);
		}
		else
		{
			STOP(("Found an empty mesh !"));
		}
	}
//	SPEWALWAYS(("micgaert", "#10"));

	tempTrianglesPtr.SetLength(0);
	group->Sync();
	group->LockBounds();
	group->SetNeverCullMode();
	subList->LockBounds();
	subList->SetNeverCullMode();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	HFSlimMZ::SetMegaTexture(
		int nrOfLevels,
		int bucketSize,
		const char *theName,
		const char *outputpath
	)
{
	static char megaBaseName[1024];

	if(detailInfo.name)
	{
/*
		state1.SetBackFaceOn();
		state1.SetDitherOff();
		state1.SetTextureCorrectionOn();
		state1.SetZBufferCompareOn();
		state1.SetZBufferWriteOff();


		state1.SetFilterMode(MLRState::BiLinearFilterMode);

		state1.SetFogMode(MLRState::DisableFogMode);
		state1.SetFogData(
			0xff000000,
			0.0f,
			90.0f,
			100.0f
		);
*/
		state1.SetTextureWrapMode(MLRState::TextureWrap);
		state1.SetAlphaMode(detailInfo.alphaMode);
		state1.SetPriority(MLRState::DefaultPriority + 1);
		MLRTexture *texture = MLRTexturePool::Instance->Add(detailInfo.name);
		state1.SetTextureHandle(texture->GetTextureHandle());
	}

	if(detailInfo.altname)
	{
		state2.SetTextureWrapMode(MLRState::TextureWrap);
		state2.SetAlphaMode(detailInfo.alphaMode);
		state2.SetPriority(MLRState::DefaultPriority + 1);
		MLRTexture *texture = MLRTexturePool::Instance->Add(detailInfo.altname);
		state2.SetTextureHandle(texture->GetTextureHandle());
	}

	DynamicArrayOf<unsigned char> onOffTriangles;

	if(!doPrecise)
	{
		onOffTriangles.AssignValue(0, toSortTriangles.GetLength());
	}
	else
	{
		onOffTriangles.AssignValue(0, triangleIndices.GetLength());
	}

	int i, j, k, l, n, level;
	simpleTerrainGroup.SetLength(xDim*zDim);
	waterGroup.SetLength(xDim*zDim);

	Stuff::ExtentBox allBox;
	allBox = GetExtents();

	int len = 1<<nrOfLevels;

	xGridSize = (256/len)*dX;
	zGridSize = (256/len)*dZ;

	if(!doPrecise)
	{
		fprintf(stdout, "You don't care about precision !\n");

		for(l=0;l<zDim;l++)
		{
			for(k=0;k<xDim;k++)
			{
				brokenTriangles = 0;

	//			sprintf(megaBaseName, "Content\\Maps\\%s\\%c%c_water.obj", theName, 'A'+l, 'A'+k );
	//			daWaterObj = fopen(megaBaseName, "wt");
	//			Verify(daWaterObj!=NULL);
				vertexCountObj = 1;

				sprintf(megaBaseName, "Maps\\%s\\%c%c\\%s_%c%c", theName, 'A'+l, 'A'+k, theName, 'A'+l, 'A'+k);

				Scalar xOffset = dX*(k*256+Xoffset);
				Scalar zOffset = dZ*(l*256+Zoffset);
				gos_PushCurrentHeap(ElementRenderer::GridElement::s_Heap);
				simpleTerrainGroup[l*xDim+k] =
					new ElementRenderer::GridElement(
						len, len,
						zOffset - zCenterOffset,
						xOffset - xCenterOffset,
						dZ*256, dX*256
					);
				Register_Object(simpleTerrainGroup[l*xDim+k]);

				waterGroup[l*xDim+k] = new ElementRenderer::GroupElement();
				Register_Object(waterGroup[l*xDim+k]);
				gos_PopCurrentHeap();

				for(j=0;j<len;j++)
				{
					for(i=0;i<len;i++)
					{
						level = nrOfLevels;

						tX = i;
						tY = j;

	//					fprintf(daWaterObj, "\ng G%02d%02d\n", i, j);
						BuildTile(
							simpleTerrainGroup[l*xDim+k],
							waterGroup[l*xDim+k],
							j*len+i,
							level,
							nrOfLevels,
							onOffTriangles,
							bucketSize,
							xOffset,
							zOffset,
							xGridSize,
							zGridSize,
							i,
							j,
							megaBaseName
						);
//						SPEWALWAYS(("micgaert", "%d %d %d %d\n", i, j, k, l));
					}
				}

				simpleTerrainGroup[l*xDim+k]->LockBounds();
				waterGroup[l*xDim+k]->LockBounds();


				fprintf(stdout, "(%d, %d) \"broken\" triangles: %d\n", l, k, brokenTriangles);
	//			fclose(daWaterObj);
			}
		}
	}
	else
	{
		fprintf(stdout, "You wanted it precise !\n");

		int numOfTriangles = usedTriangleIndices;

		DynamicArrayOf<TriangleStruct*> *tempTrianglesPtr;

		tempTrianglesPtr = new DynamicArrayOf<TriangleStruct*> [zDim*xDim*len*len];

		int tJunction=0;
		for(i=0,tJunction=0;i<usedUniquePoints;i++)
		{
			if(uniquePoints[i].CheckAngles(0.0f, 0.0f, GetXinM(), GetZinM(), dX, dZ)==false)
			{
				tJunction++;
				SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
					i,
					uniquePoints[i].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
					uniquePoints[i].connectedTo,
					uniquePoints[i].GetAngles()/Pi
				));

				if(silentMode!=true)
				{
					PAUSE(("Detected hole in terrain mesh!"));
				}
			}
		}

		for(l=0;l<zDim;l++)
		{
			for(k=0;k<xDim;k++)
			{
				brokenTriangles = 0;

				Scalar xOffset = dX*(k*256+Xoffset);
				Scalar zOffset = dZ*(l*256+Zoffset);

				fprintf(stdout, "Working on (%d, %d)", l, k);

				for(j=0;j<len;j++)
				{
					for(i=0;i<len;i++)
					{
						level = nrOfLevels;

						tX = i;
						tY = j;

						tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i].SetLength(numOfTriangles);

//						fprintf(stdout, "\t(%d, %d)\n", i, j);
						BuildTile(
							tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i],
							j*len+i,
							level,
							nrOfLevels,
							numOfTriangles,
							onOffTriangles,
							bucketSize,
							xOffset,
							zOffset,
							xGridSize,
							zGridSize,
							i,
							j
						);
					}
				}

				fprintf(stdout, " \"broken\" triangles: %d\n", brokenTriangles);
			}
		}

		for(i=0,tJunction=0;i<usedUniquePoints;i++)
		{
			if(uniquePoints[i].CheckAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset), dX, dZ)==false)
			{
				SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
					i,
					uniquePoints[i].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
					uniquePoints[i].connectedTo,
					uniquePoints[i].GetAngles()/Pi
				));

//				PAUSE(("Detected hole in terrain mesh!"));
			}
		}

		double one_over_dX = 1.0f/dX;
		double one_over_dZ = 1.0f/dZ;
		bool redo;

		tJunction = 0;
		for(i=0;i<usedUniquePoints;i++)
		{
			if(uniquePoints[i].connectedTo==0)
			{
				continue;
			}

			redo = false;

			Point3D point;

			point = uniquePoints[i].p;

			if(!Small_Enough(static_cast<Scalar>(fmod(uniquePoints[i].p.x, dX))))
			{
				point.x = static_cast<Scalar>(dX*floor(uniquePoints[i].p.x*one_over_dX + 0.5));
				redo = true;
			}
			if(!Small_Enough(static_cast<Scalar>(fmod(uniquePoints[i].p.z, dZ))))
			{
				point.z = static_cast<Scalar>(dZ*floor(uniquePoints[i].p.z*one_over_dZ + 0.5));
				redo = true;
			}

			if(redo==true)
			{
				point.y = GetHeight((int)(point.x/dX), (int)(point.z/dZ));

				int pi = FindPoint(point);

				uniquePoints[i].p = point;

				if(pi<0 || i==pi)
				{
					for(j=0;j<uniquePoints[i].connectedTo;j++)
					{
						uniquePoints[uniquePoints[i].myTri[j].tri->v[0]].CalculateAngles();
						uniquePoints[uniquePoints[i].myTri[j].tri->v[1]].CalculateAngles();
						uniquePoints[uniquePoints[i].myTri[j].tri->v[2]].CalculateAngles();
					}
					uniquePoints[i].CalculateAngles();
				}
				else
				{
					int from, to;

					if(i<pi)
					{
						from = pi;
						to = i;
					}
					else
					{
						from = i;
						to = pi;
					}

					for(j=0;j<uniquePoints[from].connectedTo;j++)
					{
						for(k=0;k<uniquePoints[to].connectedTo;k++)
						{
							if(uniquePoints[from].myTri[j].tri==uniquePoints[to].myTri[k].tri)
							{
								break;
							}
						}
						if(k==uniquePoints[to].connectedTo)
						{
							uniquePoints[to].Add(uniquePoints[from].myTri[j].tri, uniquePoints[from].myTri[j].pos);
						}
					}

					for(j=0;j<uniquePoints[to].connectedTo;j++)
					{
						for(k=0;k<3;k++)
						{
							if(uniquePoints[to].myTri[j].tri->v[k] == from)
							{
								uniquePoints[to].myTri[j].tri->v[k] = to;
							}
						}
					}

					for(k=0;k<uniquePoints[i].connectedTo;k++)
					{
						uniquePoints[uniquePoints[i].myTri[k].tri->v[0]].CalculateAngles();
						uniquePoints[uniquePoints[i].myTri[k].tri->v[1]].CalculateAngles();
						uniquePoints[uniquePoints[i].myTri[k].tri->v[2]].CalculateAngles();
					}

					uniquePoints[to].CalculateAngles();

					uniquePoints[from].connectedTo = 0;
				}
			}
		}

		for(i=0;i<usedTriangleIndices;i++)
		{
			if(
				triangleIndices[i].v[0]==triangleIndices[i].v[1] ||
				triangleIndices[i].v[0]==triangleIndices[i].v[2] ||
				triangleIndices[i].v[1]==triangleIndices[i].v[2]
				)
			{
				RemoveTriangle(i);
				triangleIndices[i].area = -1.0f;
			}
			else
			{
				triangleIndices[i].GetSurfaceAreaAndCentroid();
			}
		}

		for(i=0,tJunction=0;i<usedUniquePoints;i++)
		{
			if(false == uniquePoints[i].CheckAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset), dX, dZ))
			{
				tJunction++;
				SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
					i,
					uniquePoints[i].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
					uniquePoints[i].connectedTo,
					uniquePoints[i].GetAngles()/Pi
				));
//				PAUSE(("Detected hole in terrain mesh!"));
			}
		}

		Verify(tJunction==0);

		int smallOne = 0, tiltedOne = 0;


		for(i=0;i<usedTriangleIndices;i++)
		{
			if(triangleIndices[i].area < 0.0f)
			{
				continue;
			}

			if(triangleIndices[i].area > 0.0f && triangleIndices[i].area <= Stuff::SMALL)
			{
				smallOne++;
			}

			if(triangleIndices[i].plane.normal.y < Stuff::SMALL)
			{
				int pmin, pmid, pmax;
				bool xCase = false, zCase = false;

				if(	//	Triangle forms vertical line
					Close_Enough(triangleIndices[i].GetPoint(0).x, triangleIndices[i].GetPoint(1).x) &&
					Close_Enough(triangleIndices[i].GetPoint(0).x, triangleIndices[i].GetPoint(2).x)
				)
				{
					if(triangleIndices[i].GetPoint(0).z < triangleIndices[i].GetPoint(1).z)
					{
						if(triangleIndices[i].GetPoint(0).z < triangleIndices[i].GetPoint(2).z)
						{
							pmin = triangleIndices[i].v[0];
							if(triangleIndices[i].GetPoint(1).z < triangleIndices[i].GetPoint(2).z)
							{
								pmid = triangleIndices[i].v[1];
								pmax = triangleIndices[i].v[2];
							}
							else
							{
								pmid = triangleIndices[i].v[2];
								pmax = triangleIndices[i].v[1];
							}
						}
						else
						{
							pmin = triangleIndices[i].v[2];
							pmid = triangleIndices[i].v[0];
							pmax = triangleIndices[i].v[1];
						}
					}
					else
					{
						if(triangleIndices[i].GetPoint(1).z < triangleIndices[i].GetPoint(2).z)
						{
							pmin = triangleIndices[i].v[1];
							if(triangleIndices[i].GetPoint(0).z < triangleIndices[i].GetPoint(2).z)
							{
								pmid = triangleIndices[i].v[0];
								pmax = triangleIndices[i].v[2];
							}
							else
							{
								pmid = triangleIndices[i].v[2];
								pmax = triangleIndices[i].v[0];
							}
						}
						else
						{
							pmin = triangleIndices[i].v[2];
							pmid = triangleIndices[i].v[1];
							pmax = triangleIndices[i].v[0];
						}
					}

					xCase = true;
				}
				if(	//	Triangle forms horizontal line
					Close_Enough(triangleIndices[i].GetPoint(0).z, triangleIndices[i].GetPoint(1).z) &&
					Close_Enough(triangleIndices[i].GetPoint(0).z, triangleIndices[i].GetPoint(2).z)
				)
				{
					if(triangleIndices[i].GetPoint(0).x < triangleIndices[i].GetPoint(1).x)
					{
						if(triangleIndices[i].GetPoint(0).x < triangleIndices[i].GetPoint(2).x)
						{
							pmin = triangleIndices[i].v[0];
							if(triangleIndices[i].GetPoint(1).x < triangleIndices[i].GetPoint(2).x)
							{
								pmid = triangleIndices[i].v[1];
								pmax = triangleIndices[i].v[2];
							}
							else
							{
								pmid = triangleIndices[i].v[2];
								pmax = triangleIndices[i].v[1];
							}
						}
						else
						{
							pmin = triangleIndices[i].v[2];
							pmid = triangleIndices[i].v[0];
							pmax = triangleIndices[i].v[1];
						}
					}
					else
					{
						if(triangleIndices[i].GetPoint(1).x < triangleIndices[i].GetPoint(2).x)
						{
							pmin = triangleIndices[i].v[1];
							if(triangleIndices[i].GetPoint(0).x < triangleIndices[i].GetPoint(2).x)
							{
								pmid = triangleIndices[i].v[0];
								pmax = triangleIndices[i].v[2];
							}
							else
							{
								pmid = triangleIndices[i].v[2];
								pmax = triangleIndices[i].v[0];
							}
						}
						else
						{
							pmin = triangleIndices[i].v[2];
							pmid = triangleIndices[i].v[1];
							pmax = triangleIndices[i].v[0];
						}
					}

					zCase = true;
				}

				if(zCase==0 && xCase==0)
				{
					continue;
				}

				Verify(!(zCase && xCase));
				Verify(uniquePoints[pmin].p.x <= uniquePoints[pmid].p.x && uniquePoints[pmid].p.x <= uniquePoints[pmax].p.x);
				Verify(uniquePoints[pmin].p.z <= uniquePoints[pmid].p.z && uniquePoints[pmid].p.z <= uniquePoints[pmax].p.z);

				int pfaraway = -1;
				int pminDI = -1, pminVI = -1;
				for(j=0;j<uniquePoints[pmin].connectedTo;j++)
				{
/*
					if(uniquePoints[pmin].myTri[j].tri->area < Stuff::SMALL)
					{
						continue;
					}
*/
					if(&triangleIndices[i]==uniquePoints[pmin].myTri[j].tri)
					{
						if(pminVI>=0)
						{
							if(silentMode==true)
							{
								fprintf(stderr, "Error while removing triangles without area ! Program exits !\n");
								exit(-1);
							}
							else
							{
								STOP(("Error while removing triangles without area ?"));
							}
						}
						pminVI = j;

						continue;
					}

					for(k=0;k<3;k++)
					{
						if(uniquePoints[pmin].myTri[j].tri->v[k]==pmax)
						{
							break;
						}
					}
					if(k<3)
					{
						for(k=0;k<3;k++)
						{
							if(uniquePoints[pmin].myTri[j].tri->v[k]!=pmax && uniquePoints[pmin].myTri[j].tri->v[k]!=pmin)
							{
								pfaraway = uniquePoints[pmin].myTri[j].tri->v[k];
								break;
							}
						}
						Verify(pfaraway>=0);

						if(pminDI<0)
						{
							pminDI = j;
						}
						else
						{
							if(silentMode==true)
							{
								fprintf(stderr, "Error while removing triangles without area ! Program exits !\n");
								exit(-1);
							}
							else
							{
								STOP(("Error while removing triangles without area !"));
							}
						}
					}
				}

				int pmaxDI = -1, pmaxVI = -1;
				for(j=0;j<uniquePoints[pmax].connectedTo;j++)
				{
/*
					if(uniquePoints[pmax].myTri[j].tri->area < Stuff::SMALL)
					{
						continue;
					}
*/
					if(&triangleIndices[i]==uniquePoints[pmax].myTri[j].tri)
					{
						if(pmaxVI>=0)
						{
							if(silentMode==true)
							{
								fprintf(stderr, "Error while removing triangles without area ! Program exits !\n");
								exit(-1);
							}
							else
							{
								STOP(("Error while removing triangles without area ?"));
							}
						}
						pmaxVI = j;

						continue;
					}

					for(k=0;k<3;k++)
					{
						if(uniquePoints[pmax].myTri[j].tri->v[k]==pmin)
						{
							break;
						}
					}
					if(k<3)
					{
						for(k=0;k<3;k++)
						{
							if(uniquePoints[pmax].myTri[j].tri->v[k]!=pmax && uniquePoints[pmax].myTri[j].tri->v[k]!=pmin)
							{
								Verify(pfaraway == uniquePoints[pmax].myTri[j].tri->v[k]);
								break;
							}
						}

						if(pmaxDI<0)
						{
							pmaxDI = j;
						}
						else
						{
							if(silentMode==true)
							{
								fprintf(stderr, "Error while removing triangles without area ! Program exits !\n");
								exit(-1);
							}
							else
							{
								STOP(("Error while removing triangles without area !"));
							}
						}
					}
				}

				if(pminDI>=0 && pmaxDI>=0)
				{
					for(k=0;k<3;k++)
					{
						if(uniquePoints[pmin].myTri[pminDI].tri->v[k]==pmax)
						{
							uniquePoints[pmin].myTri[pminDI].tri->v[k] = pmid;
							uniquePoints[pmid].Add(uniquePoints[pmin].myTri[pminDI].tri, k);
							uniquePoints[pmin].myTri[pminDI].tri->GetSurfaceAreaAndCentroid();
							break;
						}
					}
					uniquePoints[pmin].connectedTo--;
					for(j=pminVI;j<uniquePoints[pmin].connectedTo;j++)
					{
						uniquePoints[pmin].myTri[j] = uniquePoints[pmin].myTri[j+1];
					}

					for(k=0;k<3;k++)
					{
						if(uniquePoints[pmax].myTri[pmaxVI].tri->v[k]==pmin)
						{
							uniquePoints[pmax].myTri[pmaxVI].tri->v[k] = pfaraway;
							uniquePoints[pfaraway].Add(uniquePoints[pmax].myTri[pmaxVI].tri, k);
							uniquePoints[pmax].myTri[pmaxVI].tri->GetSurfaceAreaAndCentroid();
							break;
						}
					}
					uniquePoints[pmax].connectedTo--;
					for(j=pmaxDI;j<uniquePoints[pmax].connectedTo;j++)
					{
						uniquePoints[pmax].myTri[j] = uniquePoints[pmax].myTri[j+1];
					}

					uniquePoints[pmin].CalculateAngles();
					uniquePoints[pmid].CalculateAngles();
					uniquePoints[pmax].CalculateAngles();
					uniquePoints[pfaraway].CalculateAngles();

					tiltedOne++;
				}
				else
				{
					if(pminDI<0 && pmaxDI<0)
					{
						triangleIndices[i].area = 0.0f;
					}
					else
					{
						if(silentMode==true)
						{
							fprintf(stderr, "Error while removing triangles without area ! Program exits !\n");
							exit(-1);
						}
						else
						{
							STOP(("Error while removing triangles without area !"));
						}
					}
				}

			}
		}

		for(i=0;i<usedTriangleIndices;i++)
		{
			if(triangleIndices[i].area == 0.0f)
			{
				RemoveTriangle(&triangleIndices[i]);
			}
		}

		for(i=0,tJunction=0;i<usedUniquePoints;i++)
		{
			if(
				Small_Enough(static_cast<Scalar>(fmod(uniquePoints[i].p.x, dX))) &&
				Small_Enough(static_cast<Scalar>(fmod(uniquePoints[i].p.z, dZ)))
			)
			{
				uniquePoints[i].p.y = GetHeight(
					Round(uniquePoints[i].p.x/dX),
					Round(uniquePoints[i].p.z/dZ)
				);
			}

			if(false == uniquePoints[i].CheckAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset), dX, dZ))
			{
#ifdef CHANGE_0625
				if(
					Close_Enough(uniquePoints[i].GetAngles(), Pi) && 
					Close_Enough(uniquePoints[i].GetExpectedAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset)), 2*Pi)
				)
				{
					int greaterX = 0, lesserX = 0, greaterZ = 0, lesserZ = 0, all = 0;
					//	check for orientation

					for(j=0;j<uniquePoints[i].connectedTo;j++)
					{
						for(k=0;k<3;k++)
						{
							all++;

							if(uniquePoints[i].p.x > uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.x)
							{
								lesserX++;
							}
							if(uniquePoints[i].p.x < uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.x)
							{
								greaterX++;
							}
							if(uniquePoints[i].p.z > uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.z)
							{
								lesserZ++;
							}
							if(uniquePoints[i].p.z < uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.z)
							{
								greaterZ++;
							}
						}
					}

					int gX = 0, lX = 0, gZ = 0, lZ = 0;
					if(greaterX>0 && lesserX==0)
					{
						gX = 1;
					}
					if(greaterX==0 && lesserX>0)
					{
						lX = 1;
					}
					if(greaterZ>0 && lesserZ==0)
					{
						gZ = 1;
					}
					if(greaterZ==0 && lesserZ>0)
					{
						lZ = 1;
					}

					if(gX + lX + gZ + lZ == 1)
					{
						double dist, distMin = 10000;
						int pmin;
							
						if(gX || lX)
						{
							for(j=0;j<uniquePoints[i].connectedTo;j++)
							{
								for(k=0;k<3;k++)
								{
									if(	i != uniquePoints[i].myTri[j].tri->v[k] &&
										uniquePoints[i].p.x == uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.x)
									{
										dist = fabs(uniquePoints[i].p.z - uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.z);
										if(dist < distMin)
										{
											distMin = dist;
											pmin = uniquePoints[i].myTri[j].tri->v[k];
										}
									}
								}
							}
						}
						else
						{
							for(j=0;j<uniquePoints[i].connectedTo;j++)
							{
								for(k=0;k<3;k++)
								{
									if(	i != uniquePoints[i].myTri[j].tri->v[k] &&
										uniquePoints[i].p.z == uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.z)
									{
										dist = fabs(uniquePoints[i].p.x - uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].p.x);
										if(dist < distMin)
										{
											distMin = dist;
											pmin = uniquePoints[i].myTri[j].tri->v[k];
										}
									}
								}
							}
						}

						for(j=0;j<uniquePoints[i].connectedTo;j++)
						{
							for(k=0;k<3;k++)
							{
								if(	i == uniquePoints[i].myTri[j].tri->v[k] )
								{
									uniquePoints[i].myTri[j].tri->v[k] = pmin;
									break;
								}
							}

							for(l=0;l<uniquePoints[pmin].connectedTo;l++)
							{
								if(uniquePoints[pmin].myTri[l].tri == uniquePoints[i].myTri[j].tri)
								{
									break;
								}
							}

							if(l==uniquePoints[pmin].connectedTo)
							{
								uniquePoints[pmin].Add(uniquePoints[i].myTri[j].tri, k);
							}

							for(k=0;k<3;k++)
							{
								uniquePoints[uniquePoints[i].myTri[j].tri->v[k]].CalculateAngles();
							}
							uniquePoints[i].myTri[j].tri->GetSurfaceAreaAndCentroid();
						}
						
						uniquePoints[i].connectedTo = 0;
						uniquePoints[pmin].CalculateAngles();

						// try a remedy
						tJunction++;
					}
				}
				else
#endif
				{
					tJunction++;
					SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
						i,
						uniquePoints[i].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
						uniquePoints[i].connectedTo,
						uniquePoints[i].GetAngles()/Pi
					));
					if(silentMode!=true)
					{
						PAUSE(("Detected hole in terrain mesh!"));
					}
				}
			}

		}

		DynamicArrayOf<TriangleStruct*> tileTrianglesPtr;

		for(l=0;l<zDim;l++)
		{
			for(k=0;k<xDim;k++)
			{
				sprintf(megaBaseName, "Maps\\%s\\%c%c\\%s_%c%c", theName, 'A'+l, 'A'+k, theName, 'A'+l, 'A'+k);

				Scalar xOffset = dX*(k*256+Xoffset);
				Scalar zOffset = dZ*(l*256+Zoffset);

				gos_PushCurrentHeap(ElementRenderer::GridElement::s_Heap);
				simpleTerrainGroup[l*xDim+k] =
					new ElementRenderer::GridElement(
						len, len,
						zOffset - zCenterOffset,
						xOffset - xCenterOffset,
						dZ*256, dX*256
					);
				Register_Object(simpleTerrainGroup[l*xDim+k]);

				waterGroup[l*xDim+k] = new ElementRenderer::GroupElement();
				Register_Object(waterGroup[l*xDim+k]);
				gos_PopCurrentHeap();

				for(j=0;j<len;j++)
				{
					for(i=0;i<len;i++)
					{
						int m, length = tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i].GetLength();
						int usable = 0;

						tileTrianglesPtr.SetLength(length);
						for(m=0;m<length;m++)
						{
							if(tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i][m]->area < SMALL)
							{
								RemoveTriangle(tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i][m]);

								for(n=0,tJunction=0;n<usedUniquePoints;n++)
								{
									if(false == uniquePoints[n].CheckAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset), dX, dZ))
									{
										tJunction++;
										SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
											i,
											uniquePoints[n].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
											uniquePoints[n].connectedTo,
											uniquePoints[n].GetAngles()/Pi
										));
										if(silentMode!=true)
										{
											PAUSE(("Detected hole in terrain mesh!"));
										}
									}
								}

								continue;
							}
							else if(tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i][m]->plane.normal.y < Stuff::SMALL)
							{
								RemoveTriangle(tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i][m]);

								for(n=0,tJunction=0;n<usedUniquePoints;n++)
								{
									if(false == uniquePoints[n].CheckAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset), dX, dZ))
									{
										tJunction++;
										SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
											i,
											uniquePoints[n].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
											uniquePoints[n].connectedTo,
											uniquePoints[n].GetAngles()/Pi
										));
										if(silentMode!=true)
										{
											PAUSE(("Detected hole in terrain mesh!"));
										}
									}
								}

								continue;
							}

							tileTrianglesPtr[usable++] = tempTrianglesPtr[(l*xDim+k)*len*len + j*len + i][m];
						}
						tileTrianglesPtr.SetLength(usable);

						for(m=0,tJunction=0;m<usedUniquePoints;m++)
						{
							if(false == uniquePoints[m].CheckAngles(Xoffset*dX, Zoffset*dZ, dX*(xDim*256+Xoffset), dZ*(zDim*256+Zoffset), dX, dZ))
							{
								tJunction++;
								SPEWALWAYS((0, "H%4x: %6.2f %6.2f %6.2f - %2d %.2f",
									i,
									uniquePoints[m].p.x, uniquePoints[i].p.y, uniquePoints[i].p.z,
									uniquePoints[m].connectedTo,
									uniquePoints[m].GetAngles()/Pi
								));
								if(silentMode!=true)
								{
									PAUSE(("Detected hole in terrain mesh!"));
								}
							}
						}


						tX = i;
						tY = j;

						CreateTile(
							simpleTerrainGroup[l*xDim+k],
							waterGroup[l*xDim+k],
							tileTrianglesPtr,
							j*len+i,
							level,
							nrOfLevels,
							bucketSize,
							xOffset,
							zOffset,
							xGridSize,
							zGridSize,
							i,
							j,
							megaBaseName
						);

					}
				}

				simpleTerrainGroup[l*xDim+k]->LockBounds();
			}
		}
	}
	fprintf(stdout, "Done	HFSlimMZ::SetMegaTexture()");
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	HFSlimMZ::SetCulturals(CulturalLoc* culLoc, int culLocCount)
{
	int i, j, k, l, n, len = 8;

	Stuff::ExtentBox allBox;
	allBox = GetExtents();

	Scalar xGridSize = (allBox.maxX-allBox.minX)/(len*xDim);
	Scalar zGridSize = (allBox.maxZ-allBox.minZ)/(len*zDim);

	DynamicArrayOf<CulturalLoc> culLocLocal(culLocCount);
	int culLocLocalCount;

	for(l=0;l<zDim;l++)
	{
		for(k=0;k<xDim;k++)
		{
			ElementRenderer::GridElement *zone = simpleTerrainGroup[l*xDim+k];

			Scalar xGridOffset = k*dX*256;
			Scalar zGridOffset = l*dZ*256;

			for(j=0;j<len;j++)
			{
				for(i=0;i<len;i++)
				{
					ElementRenderer::Element *elem = zone->GetIndexedElement(j*len+i);

					ElementRenderer::GroupElement *group = Cast_Object(ElementRenderer::GroupElement*, elem);

					Scalar x0, z0, x1, z1;

					x0 = xGridOffset + i*xGridSize - xCenterOffset;
					z0 = zGridOffset + j*zGridSize - zCenterOffset;
					x1 = x0 + xGridSize;
					z1 = z0 + zGridSize;

					x0 += 4.5f;
					z0 += 4.5f;
					x1 -= 4.5f;
					z1 -= 4.5f;

					for(n=0,culLocLocalCount=0;n<culLocCount;n++)
					{
						if(culLoc[n].loc.x > x0 && culLoc[n].loc.x < x1 && culLoc[n].loc.z > z0 && culLoc[n].loc.z < z1)
						{
							Line3D line(
								Point3D(
									culLoc[n].loc.x,
									Offset-100.0f,
									culLoc[n].loc.z
								),
								UnitVector3D(0.0f, 1.0f, 0.0f),
//								256*dY
								1000
							);
							Normal3D normal;
							ElementRenderer::CollisionQuery cq(&line, &normal);
#ifdef LAB_ONLY
							bool ret = 
#endif
							group->CastRay(&cq);
#ifdef LAB_ONLY
							if (!ret)
							{
								char szTemp[256];
								sprintf(szTemp, "Natural couldn't find soil to plant on at x=%f, z=%f",culLoc[n].loc.x, culLoc[n].loc.z);
						//		PAUSE((szTemp));
							}
#endif

							Point3D end;
							line.FindEnd(&end);

							culLoc[n].loc.y = end.y;

							culLocLocal[culLocLocalCount++] = culLoc[n];
						}
					}

					if(culLocLocalCount>0)
					{
						Stuff::ChainIteratorOf<ElementRenderer::Element*> *children = group->MakeIterator();
						Register_Object(children);

						ElementRenderer::Element *child = NULL;
						int count = 0;
						child = children->ReadAndNext();
						Unregister_Object(children);
						delete children;

						Verify(count<2);

						ElementRenderer::ListElement *list = Cast_Object(ElementRenderer::ListElement*, child);

						gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);
						ElementRenderer::ShapeElement* Shape = new ElementRenderer::ShapeElement;
						Register_Object(Shape);
						gos_PopCurrentHeap();

						gos_PushCurrentHeap(MidLevelRenderer::ShapeHeap);
						MLRCulturShape *shape = new MLRCulturShape();
						Register_Object(shape);
						gos_PopCurrentHeap();

						MLRCultural *data = new MLRCultural[culLocLocalCount];
						
						Mem_Copy(data, culLocLocal.GetData(), culLocLocalCount*sizeof(MLRCultural), culLocLocalCount*sizeof(MLRCultural));

						shape->AssignCulturalData(data, culLocLocalCount);

						shape->FadeValue(culturalFadeIn, culturalFadeOut);

						Shape->SetMLRShape(shape);

						Shape->m_localOBB.localToParent = LinearMatrix4D::Identity;
						Shape->m_localOBB.localToParent.BuildTranslation(
							Point3D(
								xGridOffset + i*xGridSize + xGridSize*0.5f - xCenterOffset/* - Xoffset*dX*/,
								0.0f,
								zGridOffset + j*zGridSize + zGridSize*0.5f - zCenterOffset/* - Zoffset*dZ*/
							)
						);

						Shape->m_localOBB.sphereRadius = static_cast<Scalar>(sqrt(xGridSize*xGridSize + zGridSize*zGridSize));

						shape->SetCenterInWorld(
							xGridOffset + i*xGridSize + xGridSize*0.5f - xCenterOffset/* - Xoffset*dX*/,
							zGridOffset + j*zGridSize + zGridSize*0.5f - zCenterOffset/* - Zoffset*dZ*/,
							Shape->m_localOBB.sphereRadius
						);

						int lCount = list->GetActiveCount();

						gos_PushCurrentHeap(ElementRenderer::g_Heap);
						list->ReSize(lCount+1);
						gos_PopCurrentHeap();

						list->AttachIndexedChild(lCount, Shape);

						Shape->SetNeverCullMode();
					}
				}
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	HFSlimMZ::GetHeight(int x, int z)
{
	if (x<0) x = 0;
	if (x>GetX()-1)
	{
		x = GetX() - 1;
	}
	if (z<0) z = 0;
	if (z>GetZ()-1)
	{
		z = GetZ() - 1;
	}
	return field[z*X + x]*dY + Offset;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ToDrawTriangle::SetPoint(int idx,float x,float y,float z)
{
	Verify(idx>=0 && idx<3);

	v[idx].x=x;
	v[idx].y=y;
	v[idx].z=z;
}



