
#include "stdafx.h"
#include "resource.h"
#include "ProgDlg.h"
#include "FinalBitmapWnd.h"

#include <MLR\MLRHeaders.hpp>

#if !defined(MLR_TERRAIN_HEIGHTFIELD_HPP)
	#include "MLRHeightField.hpp"
	#include "terra\terra.hpp"
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

const Scalar One_Over_Three = 1.0f/3.0f;

#define USE_TRI_MESH 1

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ToDrawTriangle::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 = Point3D::Identity;

	//
	//-----------------------------------------------
	// Generate all the information on the first pass
	//-----------------------------------------------
	//
	position_a = v[0];
	position_b = v[1];
	position_c = v[2];

	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)
	{
		center *= (One_Over_Three/area);
	}
	else
	{
		center *= One_Over_Three;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRHeightField::MLRHeightField()
{
	X = (int)0.0f;
	Z = (int)0.0f;
	
	dX = 2.0f;
	dY = 0.15f;
	dZ = 2.0f;

	visHeight = 0;
	nrOfIZs = 0;
#ifdef _DEBUG
	#undef new 
#endif

	terrainGroup = new GroupElement();
	Register_Object(terrainGroup);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif
	
	fullTerrainGroup = NULL;
	simpleTerrainGroup = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRHeightField::~MLRHeightField()
{
	if(fullTerrainGroup!=NULL)
	{
		Unregister_Object(fullTerrainGroup);
		delete fullTerrainGroup;
		fullTerrainGroup = NULL;
	}

	if(simpleTerrainGroup!=NULL)
	{
		Unregister_Object(simpleTerrainGroup);
		delete simpleTerrainGroup;
		simpleTerrainGroup = NULL;
	}

	Unregister_Object(terrainGroup);
	delete terrainGroup;
	terrainGroup = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Stuff::ExtentBox
	MLRHeightField::GetExtents()
{
	Stuff::ExtentBox ebox;
	ebox.minX = 0.0f;
	ebox.minY = GetOffset();
	ebox.minZ = 0.0f;

	ebox.maxX = dX*(X-1);
	ebox.maxZ = dZ*(Z-1);

	int i;
	ebox.maxY= field[0]*dY+GetOffset() > 16.0f ? field[0]*dY+GetOffset() : 16.0f;

	for (i=1;i<X*Z;i++)
	{
		ebox.maxY = ebox.maxY > (field[i]*dY+GetOffset()) ? ebox.maxY:(field[i]*dY+GetOffset());
	}

	return ebox;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MLRHeightField::MakeTerrainFromField()
{
	terrainGroup->SetNeverCullMode();
	
	terrainGroup->SetName("Terrain");

#ifdef _DEBUG
	#undef new 
#endif

	fullTerrainGroup = new GroupElement();
	Register_Object(fullTerrainGroup);
	fullTerrainGroup->SetName("FullTerrain");

	DisplayFull(true);

	int side = 1 + (int)sqrt( (6*(X-1)*(Z-1)) / Limits::Max_Number_Vertices_Per_Mesh);
	
	meshX = X / side;
	meshZ = Z / side;
	
	UnitVector3D sun;
	sun = Vector3D(-1.0f, -1.0f, 0.0f);
	
	int i, j, mx, mz, k, l, count, test;
		
	test = side-1;
	
	terrain.SetLength(test*test);
	
	i = 0, j = 0, count = 0;
	MLR_I_DT_PMesh *mesh;

	if(field.GetLength() > 0)
	{
		Scalar maxFH = field[0];
		for(i=1;i<X*Z;i++)
		{
			maxFH = field[i] > maxFH ? field[i] : maxFH;
		}

		Offset = -maxFH*dY;
	}

	CProgressDlg pdlg;
	
	pdlg.Create();
	pdlg.ShowWindow(SW_SHOW);
	pdlg.SetWindowText("Create regular terrain");
	pdlg.SetErrorText(0.0f);

	for(i=0;i<test;i++)
	{
		mz = (i+1)*meshZ < Z ? meshZ : Z - i*meshZ;
		mz++;
		
		for(j=0;j<test;j++,count++)
		{
#ifdef _DEBUG
	#undef new 
#endif

			mesh = new MLR_I_DT_PMesh();
			Register_Object(mesh);
			terrain[count] = new MLRShape(1);
			Register_Object(terrain[count]);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif
			
			mx = (j+1)*meshX < X ? meshX : X - j*meshX;
			mx++;
			
			Point3D *points = new Point3D [mx*mz];
			
			Vector2DScalar *texCoords = new Vector2DScalar [2*mx*mz];
			
			Scalar h, minH = 0.0f, maxH = 0.0f;
			
			for(k=0;k<mz;k++)
			{
				for(l=0;l<mx;l++)
				{
					h = GetHeight(j*meshX+l, i*meshZ+k);
					
					if(k+l == 0)
					{
						minH = maxH = h;
					}
					else
					{
						minH = minH < h ? minH : h;
						maxH = maxH > h ? maxH : h;
					}
	

					points[mx*k+l] = 
						Point3D(
						(j*meshX+l) * dX,
						h,
						(i*meshZ+k) * dZ
						);
					
					texCoords[mx*k+l] = 
						Vector2DScalar(
						//							(Scalar)(j*side+l)/(Scalar)X,
						//							(Scalar)(i*side+k)/(Scalar)Z
						//							(Scalar)(l+j*mx)/(16.0f*(mx-1)),
						//							(Scalar)(k+i*mz)/(16.0f*(mz-1))
						//							(Scalar)(l+j*mx)/(16.0f*(mx-0)),
						//							(Scalar)(k+i*mz)/(16.0f*(mz-0))
							(Scalar)(j*meshX+l)/(X-1),
							(Scalar)(i*meshZ+k)/(Z-1)

						);
					
					texCoords[mx*k+l+mx*mz] = 
						Vector2DScalar(
						(Scalar)(l)/(Scalar)(mx-1),
						(Scalar)(k)/(Scalar)(mz-1)
						);
				}
			}
			
			mesh->SetCoordData(points, mx*mz);
			mesh->SetTexCoordData(texCoords, 2*mx*mz);

			delete [] points;
			delete [] texCoords;

			WORD *indices = new WORD [(mx-1)*(mz-1)*6];
			
			for(k=0;k<mz-1;k++)
			{
				for(l=0;l<mx-1;l++)
				{
					indices[6*(k*(mx-1) + l)]   = k*mx + l;
					indices[6*(k*(mx-1) + l)+1] = (k+1)*mx + l;
					indices[6*(k*(mx-1) + l)+2] = k*mx + l + 1;
					indices[6*(k*(mx-1) + l)+3] = (k+1)*mx + l;
					indices[6*(k*(mx-1) + l)+4] = (k+1)*mx + l + 1;
					indices[6*(k*(mx-1) + l)+5] = k*mx + l + 1;
				}
			}
			
			mesh->SetIndexData(indices, (mx-1)*(mz-1)*6);
			
			delete indices;

			unsigned char *lengths = new unsigned char [(mx-1)*(mz-1)*2];
			
			for(k=0;k<(mx-1)*(mz-1)*2;k++)
			{
				lengths[k] = 3;
			}
			
			mesh->SetSubprimitiveLengths(lengths, (mx-1)*(mz-1)*2);
			mesh->FindFacePlanes();
			
			delete lengths;

			
			mesh->SetReferenceState(state0);
			mesh->SetReferenceState(state1, 1);
			

			terrain[count]->Add(mesh);
			mesh->DetachReference();


#ifdef _DEBUG
	#undef new 
#endif
			
			ShapeElement* Shape = new ShapeElement;
			Register_Object(Shape);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif
			
			Shape->SetMLRShape(terrain[count]);
			fullTerrainGroup->AttachChild(Shape);
			
			Shape->localOBB.localToParent.BuildTranslation(Point3D(
				(((Scalar)j + 0.5f)*meshX) * dX,
				(maxH-minH)/2.0f + minH,
				(((Scalar)i + 0.5f)*meshZ) * dZ
				));
			
			Shape->localOBB.sphereRadius = sqrt(3.0f)/2.0f * 
				meshX * dX > meshZ * dZ ?
				(meshX * dX > (maxH-minH) ? meshX * dX : (maxH-minH)) :
			(meshZ * dZ > (maxH-minH) ? meshZ * dZ : (maxH-minH)) ;
			Shape->SetVolumeCullMode();

			pdlg.SetPos(count*100/(test*test));
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRHeightField::Blur2D ()
{

    // 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<3;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;
}

int triangleCounter = 0;

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++;
}

bool
	MLRHeightField::CreateMesh(
		ListElement *parent,
		int listIndex,
		DynamicArrayOf<ToDrawTriangle*>& tdtrilist,
		ExtentBox *uvFrame,
		MLRState *state
	)
{
	int polygon_count = tdtrilist.GetLength();
	if(polygon_count==0 || polygon_count*3 >= Limits::Max_Number_Vertices_Per_Mesh)
	{
		return false;
	}

#ifdef _DEBUG
	#undef new 
#endif


#if USE_TRI_MESH
	MLR_I_DeT_TMesh *erf_mesh = new MLR_I_DeT_TMesh;			
#else
//	MLR_I_C_PMesh *erf_mesh = new MLR_I_C_PMesh;

	MLR_I_DeT_PMesh *erf_mesh = new MLR_I_DeT_PMesh;
#endif

	Register_Object(erf_mesh);
	erf_mesh->SetDetailData(detailInfo.xOff, detailInfo.zOff, detailInfo.xFac, detailInfo.zFac);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

    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]->v[0].x,
		maxx=tdtrilist[0]->v[0].x,
		miny=tdtrilist[0]->v[0].y,
		maxy=tdtrilist[0]->v[0].y,
		minz=tdtrilist[0]->v[0].z,
		maxz=tdtrilist[0]->v[0].z;

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

	for(i=0;i<polygon_count;++i)
	{
		center3 += tdtrilist[i]->center;

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

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

	center0 *= 1.0f/point_count;
	center0.y += Offset;

	center3 *= 1.0f/polygon_count;
	center3.y += Offset;

	Vector3D v3;
	Scalar d[3];

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

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

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

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

	center1.y += Offset;

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

	center2.y += Offset;

	Point3D *coords = new Point3D [point_count];
	Register_Pointer(coords);
	Vector2DScalar *texCoords = new Vector2DScalar[point_count];
	Register_Pointer(texCoords);
	
	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;

				coords[nrOfPoints].x = dX*x;
				coords[nrOfPoints].y = GetHeight(x, z);
				coords[nrOfPoints].z = dZ*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;

				texCoords[nrOfPoints][0] = (coords[nrOfPoints].x - uvFrame->minX)/(uvFrame->maxX-uvFrame->minX);
				texCoords[nrOfPoints][1] = (coords[nrOfPoints].z - uvFrame->minZ)/(uvFrame->maxZ-uvFrame->minZ);

				nrOfPoints++;
			}
			else
				vert_id[z*X+x] = -1;
		}

	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;
			}
		}
	}

	COLORREF col;
	col=(Random::GetLessThan(255))|
		(Random::GetLessThan(255)<<8)|
		(Random::GetLessThan(255)<<16);

	Wnd2D->DrawGrid(
		tdtrilist.GetData(),
		polygon_count,
		col,
		center.x,
		center.z,
		maxRadius
	);

#if USE_TRI_MESH
#else
	unsigned char *lengths = new unsigned char [polygon_count];
	Register_Pointer(lengths);
#endif

	unsigned short	*index = new unsigned short [polygon_count*3];
	Register_Pointer(index);

	int k, l;

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

#if USE_TRI_MESH
#else
		lengths[i] = 3;
#endif

		x = (int)(tdtrilist[i]->v[0].x/dX);
		z = (int)(tdtrilist[i]->v[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]->v[2].x/dX);
		z = (int)(tdtrilist[i]->v[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]->v[1].x/dX);
		z = (int)(tdtrilist[i]->v[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;
		}
	}

#if USE_TRI_MESH
	erf_mesh->SetSubprimitiveLengths(NULL, l/3);
#else
	erf_mesh->SetSubprimitiveLengths(lengths, l/3);
	Unregister_Pointer(lengths);
	delete lengths;
#endif

	erf_mesh->SetCoordData(coords, nrOfPoints);

	erf_mesh->SetTexCoordData(texCoords, nrOfPoints);

	erf_mesh->SetIndexData(index, l);

	erf_mesh->FindFacePlanes();

	Unregister_Pointer(coords);
	delete coords;
	Unregister_Pointer(texCoords);
	delete texCoords;
	Unregister_Pointer(index);
	delete index;


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

	erf_mesh->SetReferenceState(state1, 1);

#ifdef _DEBUG
	#undef new 
#endif

	ShapeElement* Shape = new ShapeElement;
	Register_Object(Shape);

	MLRShape *shape = new MLRShape(1);
	Register_Object(shape);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

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

	Shape->SetMLRShape(shape);

	Shape->localOBB.localToParent.BuildTranslation(center);
	
	Shape->localOBB.sphereRadius = maxRadius;

//	Shape->SetVolumeCullMode();
	Shape->SetRootMode();

	parent->AttachIndexedChild(listIndex, Shape);

	Shape->SetVolumeCullMode();

	return true;
}

int
	primCounter, maxPrims;

bool
	MLRHeightField::BinSort(
		ListElement *parent,
		int index,
		DynamicArrayOf<ToDrawTriangle*>& tdtrilist,
		int binSize,
		CProgressDlg *pdlg,
		ExtentBox *uvFrame,
		MLRState *state
	)
{
	int polygon_count = tdtrilist.GetLength();

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

	unsigned i;
#ifdef _ARMOR
	unsigned j;
	for (i=0; i<polygon_count; ++i)
	{
		for(j=0;j<3;j++)
		{
			Verify(tdtrilist[i]->v[j].x >= 0.0f && tdtrilist[i]->v[j].x <= GetXinM());
			Verify(tdtrilist[i]->v[j].y >= Offset && tdtrilist[i]->v[j].y <= -Offset);
			Verify(tdtrilist[i]->v[j].z >= 0.0f && tdtrilist[i]->v[j].z <= GetZinM());
		}
	}
#endif

	//
	//---------------------------------------------------------
	// If the polygon mesh is already small enough, just return
	//---------------------------------------------------------
	//
	if (polygon_count <= binSize)
	{
		if(pdlg)
			pdlg->SetPos(++primCounter*100/maxPrims);
		return CreateMesh(parent, index, tdtrilist, uvFrame, 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))
	{
		if(pdlg)
			pdlg->SetPos(++primCounter*100/maxPrims);
		return CreateMesh(parent, index, tdtrilist, uvFrame, state);
	}

	//
	//-------------------------------------------------------------------------
	// The mesh is too big, so we have to cut it up.  Make a group proxy to
	// hold the new mesh collection
	//-------------------------------------------------------------------------
	//
#ifdef _DEBUG
	#undef new 
#endif

	Check_Object(parent);
	ListElement *group = new ListElement;
	Register_Object(group);
	group->SetSize(2);

	parent->AttachIndexedChild(index, group);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif
	//
	//----------------------------------
	// 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<ToDrawTriangle*>
		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.DistanceTo(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
	//-------------------------------------------------------------------
	//
	if (BinSort(
		group,
		0,
		group_a,
		binSize,
		pdlg,
		uvFrame,
		state))
	{
	}

	if (BinSort(
		group,
		1,
		group_b,
		binSize,
		pdlg,
		uvFrame,
		state))
	{
	}

	//
	//-----------------------------------------
	// Now set the bounding sphere of the group
	//-----------------------------------------
	//
	Check_Object(group);

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

	Point3D center = Point3D::Identity;
	int x, z, point_count;
	for(i=0;i<polygon_count;++i)
	{
		x = (int)(tdtrilist[i]->v[0].x/dX);
		z = (int)(tdtrilist[i]->v[0].z/dZ);
		vert_id[x + X*z]++;
		if(vert_id[x + X*z]==1)
		{
			center += tdtrilist[i]->v[0];
			point_count++;
		}
		
		x = (int)(tdtrilist[i]->v[2].x/dX);
		z = (int)(tdtrilist[i]->v[2].z/dZ);
		vert_id[x + X*z]++;
		if(vert_id[x + X*z]==1)
		{
			center += tdtrilist[i]->v[1];
			point_count++;
		}
		
		x = (int)(tdtrilist[i]->v[1].x/dX);
		z = (int)(tdtrilist[i]->v[1].z/dZ);
		vert_id[x + X*z]++;
		if(vert_id[x + X*z]==1)
		{
			center += tdtrilist[i]->v[2];
			point_count++;
		}
	}

	center *= 1.0f/point_count;

	center.y += Offset;

    int nrOfPoints = 0;
	Point3D p;
	Vector3D v3;
	Scalar radiusSquared, maxRadius = 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);

				p.x = dX*x;
				p.y = GetHeight(x, z);
				p.z = dZ*z;

				v3.Subtract(p, center);

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

				nrOfPoints++;
			}
			else
				vert_id[z*X+x] = -1;
		}

	maxRadius = static_cast<Scalar>(sqrt(maxRadius));

	group->localOBB.localToParent.BuildTranslation(center);
	
	group->localOBB.sphereRadius = maxRadius;
#endif

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

	return false;
}

void
	MLRHeightField::OptimizeHField(int mode, int depth, int binSize)
{
	if(X*Z == 0)
	{
		return;
	}
#ifdef _DEBUG
	#undef new 
#endif
	simpleTerrainGroup = new ListElement;
	Register_Object(simpleTerrainGroup);
	simpleTerrainGroup->SetSize(1);
	simpleTerrainGroup->SetName("OptimizedTerrain");

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

	DirectMap<real> map(X,Z);
	DynamicArrayOf<ToDrawTriangle>  tdtrilist((X-1)*(Z-1));
	int i, j;

	for(j=0;j<Z;j++)
		for(i=0;i<X;i++)
		{
			map.ref(i,j) = field[j*X + i];
		}

	MASK = new ImportMask;
	MASK->width=X;
	MASK->height=Z;
	
	GreedySubdivision mesh(&map);
	point_limit=1024;

	CProgressDlg pdlg;
	
	pdlg.Create();
	pdlg.ShowWindow(SW_SHOW);
	
	if(depth==0)
		point_limit = GetFullPolyCount()/4;
	else
		point_limit = depth;

	while(mesh.pointCount()<point_limit && mesh.maxError() > error_threshold)
	{
		triangleCounter = 0;
		mesh.greedyInsert();

		if(!(mesh.pointCount()%100))
			pdlg.SetPos(mesh.pointCount()*100/point_limit);

		pdlg.SetErrorText(static_cast<float>(mesh.maxError()));
	}

		mesh.overFaces(GetFacesCB, tdtrilist.GetData());
		for(i=0;i<triangleCounter;i++)
		{
			tdtrilist[i].v[0].y = static_cast<float>(dY*mesh.eval(
				(int)(tdtrilist[i].v[0].x),
				(int)(tdtrilist[i].v[0].z)
			));
			tdtrilist[i].v[1].y = static_cast<float>(dY*mesh.eval(
				(int)(tdtrilist[i].v[1].x),
				(int)(tdtrilist[i].v[1].z)
			));
			tdtrilist[i].v[2].y = 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;

			tdtrilist[i].GetSurfaceAreaAndCentroid();
		}
		Wnd2D->DrawGrid(tdtrilist.GetData(), triangleCounter);
		Sleep(100);

	OptPolyCount = triangleCounter;


	{
		Point3D *points;
		RGBAColor *colors;
		int x, z, point_count, color_count;
		Scalar diff;
		MLRPrimitiveBase *primitive;
		for(i=0;i<terrain.GetLength();++i)
		{
			Check_Object(terrain[i]);
			for(j=0;j<terrain[i]->GetNum();j++)
			{
				primitive = terrain[i]->Find(j);
				Check_Object(primitive);
				if(primitive->IsDerivedFrom(MidLevelRenderer::MLR_I_C_PMesh::DefaultData))
				{
					Cast_Object(MLR_I_C_PMesh*, primitive)->GetColorData(&colors, &color_count);
				} else if(primitive->IsDerivedFrom(MidLevelRenderer::MLR_I_C_DT_PMesh::DefaultData))
				{
					Cast_Object(MLR_I_C_DT_PMesh*, primitive)->GetColorData(&colors, &color_count);
				} else if(primitive->IsDerivedFrom(MidLevelRenderer::MLR_I_C_DeT_PMesh::DefaultData))
				{
					Cast_Object(MLR_I_C_DeT_PMesh*, primitive)->GetColorData(&colors, &color_count);
				} else
				{
					continue;
				}

				primitive->GetCoordData(&points, &point_count);
				Verify(point_count==color_count);

				for(j=0;j<point_count;++j)
				{
					x = static_cast<int>(points[j].x/dX);
					z = static_cast<int>(points[j].z/dZ);

					diff = static_cast<Scalar>(mesh.eval(x, z) - field[z*X + x]);

					if(diff > 0.0f)
					{
						colors[j].red = 1.0f;
						colors[j].green = 1.0f - diff/32.0f;
						colors[j].blue = 1.0f - diff/32.0f;
						colors[j].alpha = 1.0f;
					}
					else
					{
						colors[j].red = 1.0f + diff/32.0f;
						colors[j].green = 1.0f + diff/32.0f;
						colors[j].blue = 1.0f;
						colors[j].alpha = 1.0f;
					}
				}
			}
		}
	}

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

	DynamicArrayOf<ToDrawTriangle*> toSortTriangles(triangleCounter);

	for(i=0,j=0;i<triangleCounter;++i)
	{
		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);

	primCounter = 0;
	maxPrims = (int)(1.5f*triangleCounter/binSize) + 1;

	pdlg.SetWindowText("Create optimized terrain");

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

	BinSort(
		simpleTerrainGroup,
		0,
		toSortTriangles,
		binSize,
		&pdlg,
		&allBox
	);

	simpleTerrainGroup->NeedNewBounds();

	nrOfIZs = 1;
	Sleep(1000);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SuckVerticesFromElement(
		ElementRenderer::Element *group,
		Point3D *points,
		int& numOfPoints,
		LinearMatrix4D& matrix
	)
{
	Check_Object(group);

	LinearMatrix4D localMatrix;
	localMatrix.Multiply(matrix, group->GetLocalToParent());

	if(group->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData))
	{
		Stuff::ChainIteratorOf<ElementRenderer::Element *> *children;
		ElementRenderer::Element *curelt;
		children = Cast_Pointer(ElementRenderer::GroupElement*, group)->MakeIterator();

		int j;
		while ((curelt = children->ReadAndNext()) != NULL)
		{
			if(curelt->IsDerivedFrom(ElementRenderer::ShapeElement::DefaultData) )
			{
				MidLevelRenderer::MLRShape *Shape = Cast_Object(ElementRenderer::ShapeElement *,curelt)->GetMLRShape();

				for(j=0;j<Shape->GetNum();j++)
				{
					MLRPrimitiveBase *primitive = Shape->Find(j);
					Check_Object(primitive);

					Point3D *coords;
					int nrOfPoints;

					if(primitive->IsDerivedFrom(MLRIndexedPrimitiveBase::DefaultData))
					{
						unsigned short *indices;
						int nrOfIndices;
						primitive->GetCoordData(&coords, &nrOfPoints);
						(Cast_Pointer(MLRIndexedPrimitiveBase*, primitive))->GetIndexData(&indices, &nrOfIndices);	

						for(int i=0;i<nrOfIndices;++i)
						{
							(points+numOfPoints++)->Multiply(coords[indices[i]], localMatrix);
						}
					}
					else
					{
						primitive->GetCoordData(&coords, &nrOfPoints);
						for(int i=0;i<nrOfPoints;++i)
						{
							(points+numOfPoints++)->Multiply(coords[i], localMatrix);
						}
					}
				}
			}
			else if(curelt->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData) )
			{
				SuckVerticesFromElement(
					Cast_Object(ElementRenderer::GroupElement *, curelt),
					points,
					numOfPoints,
					localMatrix
				);
			}
		}
	}
	else if(group->IsDerivedFrom(ElementRenderer::ListElement::DefaultData))
	{
		ElementRenderer::Element *curelt;

		int i, j, end = Cast_Pointer(ElementRenderer::ListElement*, group)->GetActiveCount();

		for(i=0;i<end;i++)
		{
			curelt = Cast_Pointer(ElementRenderer::ListElement*, group)->GetIndexedElement(i);

			if(curelt->IsDerivedFrom(ElementRenderer::ShapeElement::DefaultData) )
			{
				MidLevelRenderer::MLRShape *Shape = Cast_Object(ElementRenderer::ShapeElement *,curelt)->GetMLRShape();

				for(j=0;j<Shape->GetNum();j++)
				{
					MLRPrimitiveBase *primitive = Shape->Find(j);
					Check_Object(primitive);

					Point3D *coords;
					int nrOfPoints;

					if(primitive->IsDerivedFrom(MLRIndexedPrimitiveBase::DefaultData))
					{
						unsigned short *indices;
						int nrOfIndices;
						primitive->GetCoordData(&coords, &nrOfPoints);
						(Cast_Pointer(MLRIndexedPrimitiveBase*, primitive))->GetIndexData(&indices, &nrOfIndices);	

						for(int i=0;i<nrOfIndices;++i)
						{
							(points+numOfPoints++)->Multiply(coords[indices[i]], localMatrix);
						}
					}
					else
					{
						primitive->GetCoordData(&coords, &nrOfPoints);
						for(int i=0;i<nrOfPoints;++i)
						{
							(points+numOfPoints++)->Multiply(coords[i], localMatrix);
						}
					}
				}
			}
			else if(curelt->IsDerivedFrom(ElementRenderer::GroupElement::DefaultData) )
			{
				SuckVerticesFromElement(
					Cast_Object(ElementRenderer::GroupElement *, curelt),
					points,
					numOfPoints,
					localMatrix
				);
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLRHeightField::DescentToUnderMountain(
		int depth,
		ElementRenderer::ListElement *group,
		Point3D *points,
		unsigned char *onOffPoints,
		int numOfTriangles,
		int maxNumOfTrianglesPerMesh,
		Scalar x0,
		Scalar z0,
		Scalar x1,
		Scalar z1,
		int tileX,
		int tileZ,
		const char *texRoot
	)
{
	if((depth & 0xff) ==0)
	{
		char textureName[1024];
		DynamicArrayOf<Point3D> tempPoints(3*numOfTriangles);
		DynamicArrayOf<Point3D> collapsedPoints(3*maxNumOfTrianglesPerMesh);
		DynamicArrayOf<unsigned short> tempIndices(3*maxNumOfTrianglesPerMesh);
		DynamicArrayOf<Vector2DScalar> tempTexCoords(3*maxNumOfTrianglesPerMesh);
		DynamicArrayOf<unsigned char> tempLength(3*maxNumOfTrianglesPerMesh);

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

		sprintf(textureName, "%s_%1d_%02x%02x", texRoot, (depth>>8)-depth&0xff, tileZ, tileX);
		MLRState state;

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

		state.SetFilterMode(MLRState::BiLinearFilterMode);

		state.SetFogMode(MLRState::DisableFogMode);
		state.SetFogData(
			0,
			0.0f,
			1.0f,
			100.0f
		);

		MLRTexture *texture = MLRTexturePool::Instance->Add(textureName);
		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;

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

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

				if(orIt == 0)
				{
					onOffPoints[i] = 1;
					tempPoints[newCount++] = points[j];
					tempPoints[newCount++] = points[j+1];
					tempPoints[newCount++] = points[j+2];
				}
				else if(addIt==0)
				{
					Point3D clipPoints[5];

					if(orIt==1 || orIt==2 || orIt==4 || orIt==8)
					{
						l = 0;
						for(k=0;k<3;k++)
						{
							int next = k+1>2?0:k+1;
							if(clipIt[k]==0)
							{
								clipPoints[l++] = points[j+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(
												points[j+k],
												Vector3D(
													points[j+next].x - points[j+k].x,
													points[j+next].y - points[j+k].y,
													points[j+next].z - points[j+k].z
												),
												(x0-points[j+k].x)/(points[j+next].x-points[j+k].x)
											);
										break;
										case 1:
											clipPoints[l++].AddScaled(
												points[j+k],
												Vector3D(
													points[j+next].x - points[j+k].x,
													points[j+next].y - points[j+k].y,
													points[j+next].z - points[j+k].z
												),
												(z1-points[j+k].z)/(points[j+next].z-points[j+k].z)
											);
										break;
										case 2:
											clipPoints[l++].AddScaled(
												points[j+k],
												Vector3D(
													points[j+next].x - points[j+k].x,
													points[j+next].y - points[j+k].y,
													points[j+next].z - points[j+k].z
												),
												(x1-points[j+k].x)/(points[j+next].x-points[j+k].x)
											);
										break;
										case 3:
											clipPoints[l++].AddScaled(
												points[j+k],
												Vector3D(
													points[j+next].x - points[j+k].x,
													points[j+next].y - points[j+k].y,
													points[j+next].z - points[j+k].z
												),
												(z0-points[j+k].z)/(points[j+next].z-points[j+k].z)
											);
										break;
									}
									break;
								}
								mask <<= 1;
							}
						}
						Verify(l>2);
						for(m=1;m<l-1;m++)
						{
							tempPoints[newCount++] = clipPoints[0];
							tempPoints[newCount++] = clipPoints[m];
							tempPoints[newCount++] = clipPoints[m+1];
						}
					}
					else
					{
					}
				}
			}
		}

		if(newCount > 0)
		{
			group->SetSize(1);
#ifdef _DEBUG
	#undef new 
#endif
			DynamicArrayOf<ToDrawTriangle> tdtrilist(newCount/3);
			DynamicArrayOf<ToDrawTriangle*> ptrTdtrilist(newCount/3);
			for(i=0,j=0;i<newCount/3;j+=3,i++)
			{
				tdtrilist[i].v[0].x = tempPoints[j].x;
				tdtrilist[i].v[0].y = tempPoints[j].y;
				tdtrilist[i].v[0].z = tempPoints[j].z;

				tdtrilist[i].v[1].x = tempPoints[j+2].x;
				tdtrilist[i].v[1].y = tempPoints[j+2].y;
				tdtrilist[i].v[1].z = tempPoints[j+2].z;

				tdtrilist[i].v[2].x = tempPoints[j+1].x;
				tdtrilist[i].v[2].y = tempPoints[j+1].y;
				tdtrilist[i].v[2].z = tempPoints[j+1].z;

				tdtrilist[i].GetSurfaceAreaAndCentroid();

				ptrTdtrilist[i] = &tdtrilist[i];
			}

			Stuff::ExtentBox box;
			box.minX = x0;
			box.maxX = x1;
			box.minY = 0.0f;
			box.maxY = 0.0f;
			box.minZ = z0;
			box.maxZ = z1;
			BinSort(
				group,
				0,
				ptrTdtrilist,
				maxNumOfTrianglesPerMesh,
				NULL,
				&box,
				&state
			);
			tdtrilist.SetLength(0);
			ptrTdtrilist.SetLength(0);

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif
		}
		else
		{
		}

		tempPoints.SetLength(0);
		collapsedPoints.SetLength(0);
		tempIndices.SetLength(0);
		tempTexCoords.SetLength(0);
		tempLength.SetLength(0);
	}
	else
	{
		ElementRenderer::ListElement *group00, *group01, *group10, *group11;
		group->SetSize(4);

#ifdef _DEBUG
	#undef new 
#endif

		group00 = new ElementRenderer::ListElement;
		Register_Object(group00);
		group->AttachIndexedChild(0, group00);
		group00->NeedNewBounds();
		group00->SetVolumeCullMode();

		group01 = new ElementRenderer::ListElement;
		Register_Object(group01);
		group->AttachIndexedChild(1, group01);
		group01->NeedNewBounds();
		group01->SetVolumeCullMode();

		group10 = new ElementRenderer::ListElement;
		Register_Object(group10);
		group->AttachIndexedChild(2, group10);
		group10->NeedNewBounds();
		group10->SetVolumeCullMode();

		group11 = new ElementRenderer::ListElement;
		Register_Object(group11);
		group->AttachIndexedChild(3, group11);
		group11->NeedNewBounds();
		group11->SetVolumeCullMode();

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

		DescentToUnderMountain(
			depth-1, group00, points, onOffPoints, numOfTriangles, maxNumOfTrianglesPerMesh,
			x0, z0, x0 + (x1-x0)/2.0f, z0 + (z1-z0)/2.0f, 2*tileX, 2*tileZ, texRoot
		);
		DescentToUnderMountain(
			depth-1, group01, points, onOffPoints, numOfTriangles, maxNumOfTrianglesPerMesh,
			x0 + (x1-x0)/2.0f, z0, x1, z0 + (z1-z0)/2.0f, 2*tileX+1, 2*tileZ, texRoot
		);
		DescentToUnderMountain(
			depth-1, group10, points, onOffPoints, numOfTriangles, maxNumOfTrianglesPerMesh,
			x0, z0 + (z1-z0)/2.0f, x0 + (x1-x0)/2.0f, z1, 2*tileX, 2*tileZ+1, texRoot
		);
		DescentToUnderMountain(
			depth-1, group11, points, onOffPoints, numOfTriangles, maxNumOfTrianglesPerMesh,
			x0 + (x1-x0)/2.0f, z0 + (z1-z0)/2.0f, x1, z1, 2*tileX+1, 2*tileZ+1, texRoot
		);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MLRHeightField::SetMegaTexture(
		int nrOfLevels,
		const char *mega_base_name
	)
{
	if(simpleTerrainGroup==NULL)
	{
		return false;
	}

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

		state1.SetAlphaMode(detailInfo.alphaMode);
		state1.SetPriority(MLRState::DefaultPriority + 1);

		state1.SetFilterMode(MLRState::BiLinearFilterMode);

		state1.SetFogMode(MLRState::DisableFogMode);
		state1.SetFogData(
			0,
			0.0f,
			1.0f,
			100.0f
		);

		MLRTexture *texture = MLRTexturePool::Instance->Add(detailInfo.name);
		state1.SetTextureHandle(texture->GetTextureHandle());
	}

	// analyzing the data
	// textures on the lowest level
	int totll = static_cast<int>(pow(2, nrOfLevels-1));
	
	// lets care about the regular grid
	bool simpleWasAttached = (NULL != (terrainGroup->FindElement(simpleTerrainGroup->GetName())));

	if(simpleWasAttached == true)
			terrainGroup->DetachChild(simpleTerrainGroup);

	DynamicArrayOf<Point3D> allTheTriangles(3*OptPolyCount);

	LinearMatrix4D matrix;
	matrix = LinearMatrix4D::Identity;
	int nrOfTriangles, nrOfPoints = 0;

	SuckVerticesFromElement(
		simpleTerrainGroup,
		allTheTriangles.GetData(),
		nrOfPoints,
		matrix
	);
	Verify(0==nrOfPoints%3);
	allTheTriangles.SetLength(nrOfPoints);
	nrOfTriangles = nrOfPoints/3;

	DynamicArrayOf<unsigned char> onOffTriangles;
	onOffTriangles.AssignValue(0, nrOfTriangles);

	Unregister_Object(simpleTerrainGroup);
	delete simpleTerrainGroup;
	simpleTerrainGroup = NULL;

#ifdef _DEBUG
	#undef new 
#endif
	simpleTerrainGroup = new ListElement;
	Register_Object(simpleTerrainGroup);
	simpleTerrainGroup->SetName("OptimizedTerrain");
#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

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

	DescentToUnderMountain
	(
		(nrOfLevels<<8) + nrOfLevels,
		simpleTerrainGroup,
		allTheTriangles.GetData(),
		onOffTriangles.GetData(),
		nrOfTriangles,
		256,
		allBox.minX,
		allBox.minZ,
		allBox.maxX,
		allBox.maxZ,
		0,
		0,
		mega_base_name
	);

	simpleTerrainGroup->NeedNewBounds();
	MLRTexturePool::Instance->LoadImages();

	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	MLRHeightField::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;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
MLRHeightField::GetHeight(Scalar _x, Scalar _z)
{
	int x, z, x1, z1, key = 0;
	Scalar dy00, dy10, dy20, dy30, dy01, dy11, dy21, dy31;
	Scalar gp0, gp1, gp2, gp3;
	Scalar ret;
	
	x = (int) floor (_x / dX);
	if (x<0) x = 0;
	if (x>GetX()-1)
	{
		x = GetX() - 1;
	}
	z = (int) floor (_z / dZ);
	if (z<0) z = 0;
	if (z>GetZ()-1)
	{
		z = GetZ() - 1;
	}
	
	if (_x - x*dX > 0.5*dX) 
	{
		x1 = x + 1;
		key |= 2;
	} else {
		x1 = x;
		key |= 1;
	}
	
	if (_z - z*dZ > 0.5*dZ) 
	{
		z1 = z + 1;
		key |= 8;
	} else {
		z1 = z;
		key |= 4;
	}
	
	ret = GetHeight (x1, z1);
	
	gp0 = GetHeight (x, z);
	gp1 = GetHeight (x, z+1);
	gp2 = GetHeight (x+1, z+1);
	gp3 = GetHeight (x+1, z);
	
	dy00 = ( gp1 - gp0 ) * (_z - z*dZ) / dZ;
	dy01 = ( gp0 - gp1 ) * ((z+1)*dZ - _z) / dZ;
	
	dy10 = ( gp2 - gp1 ) * (_x - x*dX) / dX;
	dy11 = ( gp1 - gp2 ) * ((x+1)*dX - _x) / dX;
	
	dy20 = ( gp3 - gp2 ) * ((z+1)*dZ - _z) / dZ;
	dy21 = ( gp2 - gp3 ) * (_z - z*dZ) / dZ;
	
	dy30 = ( gp0 - gp3 ) * ((x+1)*dX - _x) / dX;
	dy31 = ( gp3 - gp0 ) * (_x - x*dX) / dX;
	
	switch (key)
	{
	case 5:
		ret += (dy00 + dy31);
		break;
	case 9:
		ret += (dy01 + dy10);
		break;
	case 6:
		ret += (dy21 + dy30);
		break;
	case 10:
		ret += (dy20 + dy11);
		break;
	}
	
	//	ret += visHeight*dY;
	
	return ret;
}

bool
	MLRHeightField::SaveHeightFieldInformations(MString& file_name)
{
	HeightFieldFileHeader hffh;

	hffh.signature = 'HTMP';
	hffh.version = 1;
	hffh.minX = 0.0f;
	hffh.maxX = GetXinM();
	hffh.minZ = 0.0f;
	hffh.maxZ = GetZinM();
	hffh.vertexCountX = X;
	hffh.vertexCountZ = Z;

	hffh.totalSizeOfFile =	sizeof(HeightFieldFileHeader) + 
						X*Z*(sizeof(float) + sizeof(char));


	FILE *hd;

	hd = fopen(file_name, "wb");
	if(!hd)
	{
		return false;
	}

	fwrite(&hffh, sizeof(HeightFieldFileHeader), 1, hd);

	int i, j;
	Scalar height;
	for(i=0;i<Z;i++)
	{
		for(j=0;j<X;j++)
		{
			height = GetHeight(j, i);

			fwrite(&height, sizeof(float), 1, hd);
		}
	}

	unsigned char m = 0;
	for(i=0;i<Z;i++)
	{
		for(j=0;j<X;j++)
		{
			fwrite(&m, 1, 1, hd);
		}
	}

	fclose(hd);

	return true;
}

void MLRHeightField::DisplayFull(bool bl)
{
	ElementRenderer::Element *element;

	if(bl==true)
	{
		element=terrainGroup->FindElement(fullTerrainGroup->GetName());
		if(element==NULL)
			terrainGroup->AttachChild(fullTerrainGroup);
	}
	else
	{
		element=terrainGroup->FindElement(fullTerrainGroup->GetName());
		if(element!=NULL)
			terrainGroup->DetachChild(fullTerrainGroup);
	}
}

void MLRHeightField::DisplayOpt(bool bl) 
{
	ElementRenderer::Element *element;
	
	if(bl==true)
	{
		element=terrainGroup->FindElement(simpleTerrainGroup->GetName());
		if(element==NULL)
			terrainGroup->AttachChild(simpleTerrainGroup);
	}
	else
	{
		element=terrainGroup->FindElement(simpleTerrainGroup->GetName());
		if(element!=NULL)
			terrainGroup->DetachChild(simpleTerrainGroup);
	}
}

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;
}


