//
// ROAM Simplistic Implementation
// All code copyright Bryan Turner (Jan, 2000)
//                    brturn@bellsouth.net
//
// Based on the Tread Marks engine by Longbow Digital Arts
//                               (www.LongbowDigitalArts.com)
// Much help and hints provided by Seumas McNally, LDA.
//
#pragma once

#include <windows.h>
#include <math.h>
#include <gl/gl.h>		// OpenGL

#include "roam_landscape.h"
#include "global.h"

vec3f gViewPosition;


// Desired number of Binary Triangle tessellations per frame.
// This is not the desired number of triangles rendered!
// There are usually twice as many Binary Triangle structures as there are rendered triangles.
int gDesiredTris = 5000;
int gNumTrisRendered;

// Beginning frame varience (should be high, it will adjust automatically)
float gFrameVariance = 10;
float gFovX = 90.0f;

int gDrawMode = DRAW_USE_LIGHTING;

// -------------------------------------------------------------------------------------------------
//	PATCH CLASS
// -------------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------
// Split a single Triangle and link it into the mesh.
// Will correctly force-split diamonds.
//
void Patch::Split(TriTreeNode *tri)
{
	static depth = 0;

	// We are already split, no need to do it again.
	if (tri->LeftChild)
		return;

	depth++;

	static bool l1[5]={true,true,true,true,true};
	if (depth > 50 && l1[0])
		printf("heya\n"),l1[0]=false;

	if (depth > 150 && l1[1])
		printf("uh oh\n",l1[1]=false);

	if (depth > 1000 && l1[2])
		printf("deep!\n",l1[2]=false);

	if (depth > 5000 && l1[3])
		printf("Really deep!\n",l1[3]=false);

	if (depth > 10000 && l1[4])
		printf("Death is imminent!\n",l1[4]=false);

	if (depth > 5000)
	{
		depth--;
		return;
	}

	// If this triangle is not in a proper diamond, force split our base neighbor
	if ( tri->BaseNeighbor && (tri->BaseNeighbor->BaseNeighbor != tri) )
		Split(tri->BaseNeighbor);

	// Create children and link into mesh
	tri->LeftChild  = Landscape::AllocateTri();
	tri->RightChild = Landscape::AllocateTri();

	// If creation failed, just exit.
	if ( !tri->LeftChild )
	{
		depth--;return;
	}

	// Fill in the information we can get from the parent (neighbor pointers)
	tri->LeftChild->BaseNeighbor  = tri->LeftNeighbor;
	tri->LeftChild->LeftNeighbor  = tri->RightChild;

	tri->RightChild->BaseNeighbor  = tri->RightNeighbor;
	tri->RightChild->RightNeighbor = tri->LeftChild;

	// Link our Left Neighbor to the new children
	if (tri->LeftNeighbor != NULL)
	{
		if (tri->LeftNeighbor->BaseNeighbor == tri)
			tri->LeftNeighbor->BaseNeighbor = tri->LeftChild;
		else if (tri->LeftNeighbor->LeftNeighbor == tri)
			tri->LeftNeighbor->LeftNeighbor = tri->LeftChild;
		else if (tri->LeftNeighbor->RightNeighbor == tri)
			tri->LeftNeighbor->RightNeighbor = tri->LeftChild;
		else
			;// Illegal Left Neighbor!
	}

	// Link our Right Neighbor to the new children
	if (tri->RightNeighbor != NULL)
	{
		if (tri->RightNeighbor->BaseNeighbor == tri)
			tri->RightNeighbor->BaseNeighbor = tri->RightChild;
		else if (tri->RightNeighbor->RightNeighbor == tri)
			tri->RightNeighbor->RightNeighbor = tri->RightChild;
		else if (tri->RightNeighbor->LeftNeighbor == tri)
			tri->RightNeighbor->LeftNeighbor = tri->RightChild;
		else
			;// Illegal Right Neighbor!
	}

	// Link our Base Neighbor to the new children
	if (tri->BaseNeighbor != NULL)
	{
		if ( tri->BaseNeighbor->LeftChild )
		{
			tri->BaseNeighbor->LeftChild->RightNeighbor = tri->RightChild;
			tri->BaseNeighbor->RightChild->LeftNeighbor = tri->LeftChild;
			tri->LeftChild->RightNeighbor = tri->BaseNeighbor->RightChild;
			tri->RightChild->LeftNeighbor = tri->BaseNeighbor->LeftChild;
		}
		else
			Split( tri->BaseNeighbor);  // Base Neighbor (in a diamond with us) was not split yet, so do that now.
	}
	else
	{
		// An edge triangle, trivial case.
		tri->LeftChild->RightNeighbor = NULL;
		tri->RightChild->LeftNeighbor = NULL;
	}
	depth--;
}

// ---------------------------------------------------------------------
// Tessellate a Patch.
// Will continue to split until the variance metric is met.
//
void Patch::RecursTessellate( TriTreeNode *tri,
							 int leftX,  int leftY,
							 int rightX, int rightY,
							 int apexX,  int apexY,
							 int node )
{
	float TriVariance;
	int centerX = (leftX + rightX)>>1; // Compute X coordinate of center of Hypotenuse
	int centerY = (leftY + rightY)>>1; // Compute Y coord...

	if ( node < (1<<VARIANCE_DEPTH) )
	{
		// Extremely slow distance metric (sqrt is used).
		// Replace this with a faster one!
		float distance = 1.0f + sqrtf( SQR((float)centerX - gViewPosition[0]) +
									   SQR((float)centerY - gViewPosition[1]) );
		
		// Egads!  A division too?  What's this world coming to!
		// This should also be replaced with a faster operation.
		int mapsize = (m_HeightMap->get_width() + m_HeightMap->get_height())>>1;
		TriVariance = ((float)m_CurrentVariance[node] * mapsize * 2.0)/distance;	// Take both distance and variance into consideration
	}

	if ( (node >= (1<<VARIANCE_DEPTH)) ||	// IF we do not have variance info for this node, then we must have gotten here by splitting, so continue down to the lowest level.
		 (TriVariance > gFrameVariance))	// OR if we are not below the variance tree, test for variance.
	{
		Split(tri);														// Split this triangle.
		
		if (tri->LeftChild &&											// If this triangle was split, try to split it's children as well.
			((abs(leftX - rightX) >= 3) || (abs(leftY - rightY) >= 3)))	// Tessellate all the way down to one vertex per height field entry
		{
			RecursTessellate( tri->LeftChild,   apexX,  apexY, leftX, leftY, centerX, centerY,    node<<1  );
			RecursTessellate( tri->RightChild, rightX, rightY, apexX, apexY, centerX, centerY, 1+(node<<1) );
		}
	}
}

// ---------------------------------------------------------------------
// Render the tree.  Simple no-fan method.
//
void Patch::RecursRender( TriTreeNode *tri, int leftX, int leftY, int rightX, int rightY, int apexX, int apexY )
{
	if ( tri->LeftChild )					// All non-leaf nodes have both children, so just check for one
	{
		int centerX = (leftX + rightX)>>1;	// Compute X coordinate of center of Hypotenuse
		int centerY = (leftY + rightY)>>1;	// Compute Y coord...

		RecursRender( tri->LeftChild,  apexX,   apexY, leftX, leftY, centerX, centerY );
		RecursRender( tri->RightChild, rightX, rightY, apexX, apexY, centerX, centerY );
	}
	else									// A leaf node!  Output a triangle to be rendered.
	{
		// Actual number of rendered triangles...
		gNumTrisRendered++;

		/*
		GLfloat leftZ  = m_HeightMap[(leftY *MAP_SIZE)+leftX ];
		GLfloat rightZ = m_HeightMap[(rightY*MAP_SIZE)+rightX];
		GLfloat apexZ  = m_HeightMap[(apexY *MAP_SIZE)+apexX ];
		*/
		GLfloat leftZ  = (*m_HeightMap)(m_WorldX+leftX,m_WorldY+leftY);
		GLfloat rightZ = (*m_HeightMap)(m_WorldX+rightX,m_WorldY+rightY);
		GLfloat apexZ  = (*m_HeightMap)(m_WorldX+apexX,m_WorldY+apexY);

		// Perform lighting calculations if requested.
		if (gDrawMode == DRAW_USE_LIGHTING)
		{
			/*
			vec3f v[3];
			vec3f out;
			
			v[0] = vec3f( leftX, leftY, leftZ );
			v[1] = vec3f( rightX, rightY, rightZ );
			v[2] = vec3f( apexX, apexY, apexZ );

			calcNormal( v, out );
			glNormal( out );
			*/

			vec3f n;
			n  = (*m_NormalMap)(leftX,leftY) * 1;
			n  += (*m_NormalMap)(rightX,rightY) * 1;
			n  += (*m_NormalMap)(apexX,apexY) * 3;
			n *= (1.0/5.0);
			glNormal( n );

			// Create a vertex normal for this triangle.
			// NOTE: This is an extremely slow operation for illustration purposes only.
			//       You should use a texture map with the lighting pre-applied to the texture.
			/*
			v[0][0] = (GLfloat) leftX;
			v[0][1] = (GLfloat) leftZ;
			v[0][2] = (GLfloat) leftY;
			
			v[1][0] = (GLfloat) rightX;
			v[1][1] = (GLfloat) rightZ ;
			v[1][2] = (GLfloat) rightY;
			
			v[2][0] = (GLfloat) apexX;
			v[2][1] = (GLfloat) apexZ ;
			v[2][2] = (GLfloat) apexY;
			
			calcNormal( v, out );
			glNormal3fv( out );
			*/
		}

		// Perform polygon coloring based on a height sample
		float fColor = (60.0f + rightZ) / 256.0f;
		if ( fColor > 1.0f )  fColor = 1.0f;
		glColor3f( fColor, fColor, fColor );

		// Output the RIGHT VERTEX for the triangle
		glVertex3f(	m_WorldX+	(GLfloat) rightX,
						m_WorldY+(GLfloat) rightY,
						(GLfloat) rightZ );

		if ( gDrawMode == DRAW_USE_TEXTURE ||	// Gaurad shading based on height samples instead of light normal
			 gDrawMode == DRAW_USE_FILL_ONLY )
		{
			float fColor = (60.0f + leftZ) / 256.0f;
			if ( fColor > 1.0f )  fColor = 1.0f;
			glColor3f( fColor, fColor, fColor );
		}

		// Output the LEFT VERTEX for the triangle
		glVertex3f(		m_WorldX+(GLfloat) leftX,
						m_WorldY+(GLfloat) leftY,
						(GLfloat) leftZ );


		if ( gDrawMode == DRAW_USE_TEXTURE ||	// Gaurad shading based on height samples instead of light normal
			 gDrawMode == DRAW_USE_FILL_ONLY )
		{
			float fColor = (60.0f + apexZ) / 256.0f;
			if ( fColor > 1.0f )  fColor = 1.0f;
			glColor3f( fColor, fColor, fColor );
		}

		// Output the APEX VERTEX for the triangle
		glVertex3f(		m_WorldX+(GLfloat) apexX,
						m_WorldY+(GLfloat) apexY,
						(GLfloat) apexZ );
	}
}

// ---------------------------------------------------------------------
// Computes Variance over the entire tree.  Does not examine node relationships.
//
unsigned char Patch::RecursComputeVariance( int leftX,  int leftY,  unsigned char leftZ,
										    int rightX, int rightY, unsigned char rightZ,
											int apexX,  int apexY,  unsigned char apexZ,
											int node)
{
	//        /|\
	//      /  |  \
	//    /    |    \
	//  /      |      \
	//  ~~~~~~~*~~~~~~~  <-- Compute the X and Y coordinates of '*'
	//
	int centerX = (leftX + rightX) >>1;		// Compute X coordinate of center of Hypotenuse
	int centerY = (leftY + rightY) >>1;		// Compute Y coord...
	unsigned char myVariance;

	// Get the height value at the middle of the Hypotenuse
	//unsigned char centerZ  = m_HeightMap[(centerY * MAP_SIZE) + centerX];
	unsigned char centerZ  = (*m_HeightMap)(m_WorldX+centerX,m_WorldY+centerY);

	// Variance of this triangle is the actual height at it's hypotenuse midpoint minus the interpolated height.
	// Use values passed on the stack instead of re-accessing the Height Field.
	myVariance = abs((int)centerZ - (((int)leftZ + (int)rightZ)>>1));

	// Since we're after speed and not perfect representations,
	//    only calculate variance down to an 8x8 block
	if ( (abs(leftX - rightX) >= 8) ||
		 (abs(leftY - rightY) >= 8) )
	{
		// Final Variance for this node is the max of it's own variance and that of it's children.
		myVariance = MAX( myVariance, RecursComputeVariance( apexX,   apexY,  apexZ, leftX, leftY, leftZ, centerX, centerY, centerZ,    node<<1 ) );
		myVariance = MAX( myVariance, RecursComputeVariance( rightX, rightY, rightZ, apexX, apexY, apexZ, centerX, centerY, centerZ, 1+(node<<1)) );
	}

	// Store the final variance for this node.  Note Variance is never zero.
	if (node < (1<<VARIANCE_DEPTH))
		m_CurrentVariance[node] = 1 + myVariance;

	return myVariance;
}

// -------------------------------------------------------------------------------------------------
//	PATCH CLASS
// -------------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------
// Initialize a patch.
//
void Patch::Init( int heightX, int heightY, int worldX, int worldY, gs_image_type* hMap, glh::array2<glh::vec3f>* nMap  )
{
	// Clear all the relationships
	m_BaseLeft.RightNeighbor = m_BaseLeft.LeftNeighbor = m_BaseRight.RightNeighbor = m_BaseRight.LeftNeighbor =
	m_BaseLeft.LeftChild = m_BaseLeft.RightChild = m_BaseRight.LeftChild = m_BaseLeft.LeftChild = NULL;

	// Attach the two m_Base triangles together
	m_BaseLeft.BaseNeighbor = &m_BaseRight;
	m_BaseRight.BaseNeighbor = &m_BaseLeft;

	// Store Patch offsets for the world and heightmap.
	m_WorldX = worldX;
	m_WorldY = worldY;

	// Store pointer to first byte of the height data for this patch.
	//m_HeightMap = &hMap[heightY * MAP_SIZE + heightX];
	// Can't really do that easity with the array class, so just add offsets
	m_HeightMap = hMap;
	m_NormalMap = nMap;

	// Initialize flags
	m_VarianceDirty = true;
	m_isVisible = false;
}

// ---------------------------------------------------------------------
// Reset the patch.
//
void Patch::Reset()
{
	// Assume patch is not visible.
	m_isVisible = 0;

	// Reset the important relationships
	m_BaseLeft.LeftChild = m_BaseLeft.RightChild = m_BaseRight.LeftChild = m_BaseLeft.LeftChild = NULL;

	// Attach the two m_Base triangles together
	m_BaseLeft.BaseNeighbor = &m_BaseRight;
	m_BaseRight.BaseNeighbor = &m_BaseLeft;

	// Clear the other relationships.
	m_BaseLeft.RightNeighbor = m_BaseLeft.LeftNeighbor = m_BaseRight.RightNeighbor = m_BaseRight.LeftNeighbor = NULL;
}

// ---------------------------------------------------------------------
// Compute the variance tree for each of the Binary Triangles in this patch.
//
void Patch::ComputeVariance()
{
	// Compute variance on each of the base triangles...

	m_CurrentVariance = m_VarianceLeft;
	/*
	RecursComputeVariance(	0,          PATCH_SIZE, m_HeightMap[PATCH_SIZE * MAP_SIZE],
							PATCH_SIZE, 0,          m_HeightMap[PATCH_SIZE],
							0,          0,          m_HeightMap[0],
							1);
	*/
	RecursComputeVariance(	0,          PATCH_SIZE, (*m_HeightMap)(m_WorldX+0,m_WorldY+PATCH_SIZE),
							PATCH_SIZE, 0,          (*m_HeightMap)(m_WorldX+PATCH_SIZE,m_WorldY+0),
							0,          0,          (*m_HeightMap)(m_WorldX+0,m_WorldY+0),
							1);

	m_CurrentVariance = m_VarianceRight;
	RecursComputeVariance(	PATCH_SIZE, 0,          (*m_HeightMap)(m_WorldX+PATCH_SIZE,m_WorldY+0),
							0,          PATCH_SIZE, (*m_HeightMap)(m_WorldX+0,m_WorldY+PATCH_SIZE),
							PATCH_SIZE, PATCH_SIZE, (*m_HeightMap)(m_WorldX+PATCH_SIZE,m_WorldY+PATCH_SIZE),
							1);

	// Clear the dirty flag for this patch
	m_VarianceDirty = false;
}

// ---------------------------------------------------------------------
// Discover the orientation of a triangle's points:
//
// Taken from "Programming Principles in Computer Graphics", L. Ammeraal (Wiley)
//
inline int orientation( int pX, int pY, int qX, int qY, int rX, int rY )
{
	int aX, aY, bX, bY;
	float d;

	aX = qX - pX;
	aY = qY - pY;

	bX = rX - pX;
	bY = rY - pY;

	d = (float)aX * (float)bY - (float)aY * (float)bX;
	return (d < 0) ? (-1) : (d > 0);
}

// ---------------------------------------------------------------------
// Set patch's visibility flag.
//
void Patch::SetVisibility( int eyeX, int eyeY, int leftX, int leftY, int rightX, int rightY )
{
	// Get patch's center point
	int patchCenterX = m_WorldX + PATCH_SIZE / 2;
	int patchCenterY = m_WorldY + PATCH_SIZE / 2;
	
	if (SQR(patchCenterX-eyeX)+SQR(patchCenterY-eyeY) < 2*SQR(PATCH_SIZE))
		m_isVisible = true;
	else

	// Set visibility flag (orientation of both triangles must be counter clockwise)
	m_isVisible = (orientation( eyeX,  eyeY,  rightX, rightY, patchCenterX, patchCenterY ) < 0) &&
				  (orientation( leftX, leftY, eyeX,   eyeY,   patchCenterX, patchCenterY ) < 0);
}

// ---------------------------------------------------------------------
// Create an approximate mesh.
//
void Patch::Tessellate()
{
	// Split each of the base triangles
	m_CurrentVariance = m_VarianceLeft;
	RecursTessellate (	&m_BaseLeft,
						m_WorldX,				m_WorldY+PATCH_SIZE,
						m_WorldX+PATCH_SIZE,	m_WorldY,
						m_WorldX,				m_WorldY,
						1 );
					
	m_CurrentVariance = m_VarianceRight;
	RecursTessellate(	&m_BaseRight,
						m_WorldX+PATCH_SIZE,	m_WorldY,
						m_WorldX,				m_WorldY+PATCH_SIZE,
						m_WorldX+PATCH_SIZE,	m_WorldY+PATCH_SIZE,
						1 );
}

// ---------------------------------------------------------------------
// Render the mesh.
//
void Patch::Render()
{
	// Store old matrix
	glPushMatrix();
	
	// Translate the patch to the proper world coordinates
	//glTranslatef( (GLfloat)m_WorldX, (GLfloat)m_WorldY, 0  );
	glBegin(GL_TRIANGLES);
		
		RecursRender (	&m_BaseLeft,
			0,				PATCH_SIZE,
			PATCH_SIZE,		0,
			0,				0);
		
		RecursRender(	&m_BaseRight,
			PATCH_SIZE,		0,
			0,				PATCH_SIZE,
			PATCH_SIZE,		PATCH_SIZE);
	
	glEnd();
	
	// Restore the matrix
	glPopMatrix();
}

// -------------------------------------------------------------------------------------------------
//	LANDSCAPE CLASS
// -------------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------
// Definition of the static member variables
//
int         Landscape::m_NextTriNode;
TriTreeNode Landscape::m_TriPool[POOL_SIZE];

// ---------------------------------------------------------------------
// Allocate a TriTreeNode from the pool.
//
TriTreeNode *Landscape::AllocateTri()
{
	TriTreeNode *pTri;

	// IF we've run out of TriTreeNodes, just return NULL (this is handled gracefully)
	if ( m_NextTriNode >= POOL_SIZE )
		return NULL;

	pTri = &(m_TriPool[m_NextTriNode++]);
	pTri->LeftChild = pTri->RightChild = NULL;

	return pTri;
}

// ---------------------------------------------------------------------
// Initialize all patches
//
void Landscape::Init(gs_image_type* hMap, glh::array2<glh::vec3f>* nMap)
{
	Patch *patch;
	int x, y;

	// Store the Height Field array
	m_HeightMap = hMap;
	m_NormalMap = nMap;
	/*
	m_HeightMap.set_size(hMap->get_width()+1,hMap->get_height()+1);
	for (y=0; y < hMap->get_height(); y++)
	{
		for (x=0; x < hMap->get_width(); x++)
			m_HeightMap(x,y) = (*hMap)(x,y);
		m_HeightMap(x,y) = (*hMap)(x-1,y);
	}
	for (x=0; x < hMap->get_width(); x++)
		m_HeightMap(x,y) = (*hMap)(x,y-1);
	m_HeightMap(x,y) = (*hMap)(x-1,y-1);
	*/


	nPatchesWidth = hMap->get_width() / PATCH_SIZE + 0.99;
	nPatchesHeight = hMap->get_height() / PATCH_SIZE + 0.99;

	// Initialize all terrain patches
	for ( y=0; y < nPatchesHeight; y++)
		for ( x=0; x < nPatchesWidth; x++ )
		{
			patch = &(m_Patches[y][x]);
			patch->Init( x*PATCH_SIZE, y*PATCH_SIZE, x*PATCH_SIZE, y*PATCH_SIZE, m_HeightMap, m_NormalMap );
			patch->ComputeVariance();
		}
}

// ---------------------------------------------------------------------
// Reset all patches, recompute variance if needed
//
void Landscape::Reset()
{
	gViewPosition = game.world.eyepoint.pos * (1.0/xyscale);

	// Visibility culling removed for now - AROMAN

	//
	// Perform simple visibility culling on entire patches.
	//   - Define a triangle set back from the camera by one patch size, following
	//     the angle of the frustum.
	//   - A patch is visible if it's center point is included in the angle: Left,Eye,Right
	//   - This visibility test is only accurate if the camera cannot look up or down significantly.
	//

	//const float& gClipAngle = game.world.tank[0].model.dir;
	vec3f viewDirection = game.world.eyepoint.pos - game.world.tank[0].model.pos;
	float gClipAngle = atan2(viewDirection[Y],viewDirection[X]) - M_PI/2.0;

	const float PI_DIV_180 = M_PI / 180.0f;
	const float FOV_DIV_2 = gFovX/2;

	int eyeX = (int)(gViewPosition[0] - PATCH_SIZE * sinf( gClipAngle ));//* PI_DIV_180 ));
	int eyeY = (int)(gViewPosition[1] + PATCH_SIZE * cosf( gClipAngle ));//* PI_DIV_180 ));

	/*
	int leftX  = (int)(eyeX + 100.0f * sinf( (gClipAngle-FOV_DIV_2) * PI_DIV_180 ));
	int leftY  = (int)(eyeY - 100.0f * cosf( (gClipAngle-FOV_DIV_2) * PI_DIV_180 ));

	int rightX = (int)(eyeX + 100.0f * sinf( (gClipAngle+FOV_DIV_2) * PI_DIV_180 ));
	int rightY = (int)(eyeY - 100.0f * cosf( (gClipAngle+FOV_DIV_2) * PI_DIV_180 ));
	*/
	int leftX  = (int)(eyeX + 100.0f * sinf( gClipAngle-(FOV_DIV_2) * PI_DIV_180 ));
	int leftY  = (int)(eyeY - 100.0f * cosf( gClipAngle-(FOV_DIV_2) * PI_DIV_180 ));

	int rightX = (int)(eyeX + 100.0f * sinf( gClipAngle+(FOV_DIV_2) * PI_DIV_180 ));
	int rightY = (int)(eyeY - 100.0f * cosf( gClipAngle+(FOV_DIV_2) * PI_DIV_180 ));

	int x, y;
	Patch *patch;

	// Set the next free triangle pointer back to the beginning
	SetNextTriNode(0);

	// Reset rendered triangle count.
	gNumTrisRendered = 0;

	// Go through the patches performing resets, compute variances, and linking.
	for ( y=0; y < nPatchesHeight; y++ )
		for ( x=0; x < nPatchesWidth; x++)
		{
			patch = &(m_Patches[y][x]);
			
			// Reset the patch
			patch->Reset();
			//patch->m_isVisible = true;
			patch->SetVisibility( eyeX, eyeY, leftX, leftY, rightX, rightY );
			
			// Check to see if this patch has been deformed since last frame.
			// If so, recompute the varience tree for it.
			if ( patch->isDirty() )
				patch->ComputeVariance();

			if ( patch->isVisibile() )
			{
				// Link all the patches together.
				if ( x > 0 )
					patch->GetBaseLeft()->LeftNeighbor = m_Patches[y][x-1].GetBaseRight();
				else
					patch->GetBaseLeft()->LeftNeighbor = NULL;		// Link to bordering Landscape here..

				if ( x < (MAX_PATCHES_PER_SIDE-1) )
					patch->GetBaseRight()->LeftNeighbor = m_Patches[y][x+1].GetBaseLeft();
				else
					patch->GetBaseRight()->LeftNeighbor = NULL;		// Link to bordering Landscape here..

				if ( y > 0 )
					patch->GetBaseLeft()->RightNeighbor = m_Patches[y-1][x].GetBaseRight();
				else
					patch->GetBaseLeft()->RightNeighbor = NULL;		// Link to bordering Landscape here..

				if ( y < (MAX_PATCHES_PER_SIDE-1) )
					patch->GetBaseRight()->RightNeighbor = m_Patches[y+1][x].GetBaseLeft();
				else
					patch->GetBaseRight()->RightNeighbor = NULL;	// Link to bordering Landscape here..
			}
		}

}

// ---------------------------------------------------------------------
// Create an approximate mesh of the landscape.
//
void Landscape::Tessellate()
{
	gViewPosition = game.world.eyepoint.pos * (1.0/xyscale);
	// Perform Tessellation
	/*
	int nCount;
	Patch *patch = &(m_Patches[0][0]);
	for (nCount=0; nCount < NUM_PATCHES_PER_SIDE*NUM_PATCHES_PER_SIDE; nCount++, patch++ )
	{
		if (patch->isVisibile())
			patch->Tessellate( );
	}
	*/
	int x,y;
	for ( y=0; y < nPatchesHeight; y++)
		for ( x=0; x < nPatchesWidth; x++ )
			if (m_Patches[y][x].isVisibile())
				m_Patches[y][x].Tessellate();
}

// ---------------------------------------------------------------------
// Render each patch of the landscape & adjust the frame variance.
//
void Landscape::Render()
{
	gViewPosition = game.world.eyepoint.pos * (1.0/xyscale);
	//int nCount;
	//Patch *patch = &(m_Patches[0][0]);

	// Scale the terrain by the terrain scale specified at compile time.
	//glScalef( 1.0f, MULT_SCALE, 1.0f );
	glScalef( xyscale, xyscale, zscale );

	int x,y;
	for ( y=0; y < nPatchesHeight; y++)
		for ( x=0; x < nPatchesWidth; x++ )
			if (m_Patches[y][x].isVisibile())
				m_Patches[y][x].Render();

			/*
	for (nCount=0; nCount < NUM_PATCHES_PER_SIDE*NUM_PATCHES_PER_SIDE; nCount++, patch++ )
	{
		if (patch->isVisibile())
			patch->Render();
	}*/

	// Check to see if we got close to the desired number of triangles.
	// Adjust the frame variance to a better value.
	if ( GetNextTriNode() != gDesiredTris )
		gFrameVariance += ((float)GetNextTriNode() - (float)gDesiredTris) / (float)gDesiredTris;
	printf(" gFV: %7.2f  nTris: %d ",gFrameVariance,GetNextTriNode());

	// Bounds checking.
	if ( gFrameVariance < 0 )
		gFrameVariance = 0;
}


















// ---------------------------------------------------------------------
// Reduces a normal vector specified as a set of three coordinates,
// to a unit normal vector of length one.
//
void ReduceToUnit(vec3f& v)
{
	v.normalize();
}

// ---------------------------------------------------------------------
// Points p1, p2, & p3 specified in counter clock-wise order
//
void calcNormal(vec3f v[3], vec3f& out)
{
	float v1[3],v2[3];
	static const int x = 0;
	static const int y = 1;
	static const int z = 2;

	// Calculate two vectors from the three points
	v1[x] = v[0][x] - v[1][x];
	v1[y] = v[0][y] - v[1][y];
	v1[z] = v[0][z] - v[1][z];

	v2[x] = v[1][x] - v[2][x];
	v2[y] = v[1][y] - v[2][y];
	v2[z] = v[1][z] - v[2][z];

	// Take the cross product of the two vectors to get
	// the normal vector which will be stored in out
	out[x] = v1[y]*v2[z] - v1[z]*v2[y];
	out[y] = v1[z]*v2[x] - v1[x]*v2[z];
	out[z] = v1[x]*v2[y] - v1[y]*v2[x];

	// Normalize the vector (shorten length to one)
	ReduceToUnit(out);
}




// ---------------------------------------------------------------------
// Switch GL Contexts when moving between draw modes to improve performance.
//
void SetDrawModeContext()
{
	switch (gDrawMode)
	{
	case DRAW_USE_TEXTURE:
		glDisable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glPolygonMode(GL_FRONT, GL_FILL);
		break;

	case DRAW_USE_LIGHTING:
		glEnable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glPolygonMode(GL_FRONT, GL_FILL);
		break;

	case DRAW_USE_FILL_ONLY:
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glPolygonMode(GL_FRONT, GL_FILL);
		break;

	default:
	case DRAW_USE_WIREFRAME:
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);
		glPolygonMode(GL_FRONT, GL_LINE);
		break;
	}
}
