/*******************************************************************************
 *	ATI 3D RAGE SDK sample code												   *	
 *																			   *
 *  Knight Demo																   *
 *																			   *
 *  Copyright (c) 1996-1997 ATI Technologies, Inc.  All rights reserved.	   *	
 *  																		   *
 * Written by Aaron Orenstein												   *
 *																			   *
 *	Landscape construction functionality. The landscape is constructed from	   *
 *	landsquares. Each landsquare is made up of two faces and two sides. The	   *
 *	sides may or may not be hidden, depending on the specific landscape		   *
 *	feature being built. 													   *
 *******************************************************************************/
#include "stdwin.h"
#include <math.h>
#include "Util.h"
#include "DirectDraw.h"

#include "Ati3dCIFx.h"
#include "Matrix.h"
#include "Vertex.h"
#include "Clipper.h"
#include "XForm.h"
#include "Intersect.h"

#include "Landscape.h"

// -----------------------------------------------------------------------------

#define NEAR_ZERO 0.00001

#define SQUARES_PER_SIDE 17

#define SQUARE_WIDTH (200.0 * 3.0 * 12.0 / (float)SQUARES_PER_SIDE)
#define SQUARE_HEIGHT (200.0 * 3.0 * 12.0 / (float)SQUARES_PER_SIDE)

RGBA g_outline(255, 255, 255);
RGBA g_selected(255, 0, 0);

UV LandSquare::m_alternateUV[4] = {
	UV(0, 0),
	UV(1, 0),
	UV(1, 1),
	UV(0, 1)
};

// -----------------------------------------------------------------------------

Landscape::Landscape(void)
{
	for(int i=0; i<SQUARES_PER_SIDE; i++)
		for(int j=0; j<SQUARES_PER_SIDE; j++)
			Square(i, j).Setup(this, i, j);

	m_pTexture = NULL;
	m_pAlternate = NULL;
}

// -----------------------------------------------------------------------------

void Landscape::Initialize(void)
{
	for(int i=0; i<17; i++)
		for(int j=0; j<17; j++)
		{
			LandSquare *square = &Square(i, j);
			square->SetHeight(0, 0, 0, 0, FALSE);
			square->SurfaceColor() = RGBA(0, 255, 0);
			square->SurfaceNWSE(TRUE);
			square->NorthFaceColor() = RGBA(0, 0, 0);
			square->EastFaceColor() = RGBA(0, 0, 0);
			square->AttachNW(TRUE);
			square->AttachNE(TRUE);
			square->AttachSW(TRUE);
			square->AttachSE(TRUE);
		}
}

// -----------------------------------------------------------------------------

void Landscape::Draw(Clipper &clipper, BOOL outline)
{
	for(int i=0; i<SQUARES_PER_SIDE; i++)
		for(int j=0; j<SQUARES_PER_SIDE; j++)
		{
			LandSquare* pSquare = &Square(i, j);
			pSquare->Draw(clipper);
			if(outline)
				pSquare->DrawOutline(clipper, g_outline, FALSE);
		}
}

// -----------------------------------------------------------------------------

void Landscape::DrawWater(Clipper &clipper)
{
	clipper.Context()->SetAlphaSrc(C3D_EASRC_SRCALPHA);
	clipper.Context()->SetAlphaDst(C3D_EADST_INVSRCALPHA);

	for(int i=0; i<SQUARES_PER_SIDE; i++)
		for(int j=0; j<SQUARES_PER_SIDE; j++)
		{
			LandSquare* pSquare = &Square(i, j);
			pSquare->DrawWater(clipper);
		}

	clipper.Context()->SetAlphaSrc(C3D_EASRC_ONE);
	clipper.Context()->SetAlphaDst(C3D_EADST_ZERO);
}

// -----------------------------------------------------------------------------

LandSquare* Landscape::IntersectingSquare(int* pFace, const Vector& a, const Vector& b, BOOL hidden)
{
	LandSquare* closest = NULL;
	float		closestT;
	int         closestFace;

	for(int i=0; i<SQUARES_PER_SIDE; i++)
		for(int j=0; j<SQUARES_PER_SIDE; j++)
		{
			float t;
			int face;
			if(Square(i, j).Intersects(&t, &face, a, b, hidden))
			{
				if((!closest) || (t < closestT))
				{
					closest = &Square(i, j);
					closestT = t;
					closestFace = face;
				}
			}
		}

	(*pFace) = closestFace;

	return closest;
}

LandSquare* Landscape::IntersectingSquare(int* pFace, float* pT, const Vector& a, const Vector& b, BOOL hidden)
{
	LandSquare* closest = NULL;
	float		closestT;
	int         closestFace;

	for(int i=0; i<SQUARES_PER_SIDE; i++)
		for(int j=0; j<SQUARES_PER_SIDE; j++)
		{
			float t;
			int face;
			if(Square(i, j).Intersects(&t, &face, a, b, hidden))
			{
				if((!closest) || (t < closestT))
				{
					closest = &Square(i, j);
					closestT = t;
					closestFace = face;
				}
			}
		}

	(*pFace) = closestFace;
	(*pT) = closestT;

	return closest;
}

// -----------------------------------------------------------------------------

LandSquare& Landscape::SquareAt(float x, float y)
{
	int i = (int)(((x*2.0)/(float)SQUARE_WIDTH+1.0)/2.0+8.0);
	int j = (int)(((y*2.0)/(float)SQUARE_HEIGHT+1.0)/2.0+8.0);
	ASSERT((i>=0) && (i<SQUARES_PER_SIDE));
	ASSERT((j>=0) && (j<SQUARES_PER_SIDE));
	return Square(i, j);
}

const LandSquare& Landscape::SquareAt(float x, float y) const
{
	int i = (int)(((x*2.0)/(float)SQUARE_WIDTH+1.0)/2.0+8.0);
	int j = (int)(((y*2.0)/(float)SQUARE_HEIGHT+1.0)/2.0+8.0);
	ASSERT((i>=0) && (i<SQUARES_PER_SIDE));
	ASSERT((j>=0) && (j<SQUARES_PER_SIDE));
	return Square(i, j);
}

float Landscape::HeightAt(float x, float y)
{
	return SquareAt(x, y).HeightAt(x, y);
}

// -----------------------------------------------------------------------------

static int VertexIndex(int i, int j) { return i*(SQUARES_PER_SIDE+1)+j; }
static int FaceIndex(int i, int j, int n) { return (j*SQUARES_PER_SIDE+i)*2+n; }


Poly* Landscape::CreateMesh(void) const
{
	Poly* pPoly = new Poly(POLYGON_LIST, (SQUARES_PER_SIDE+1)*(SQUARES_PER_SIDE+1), SQUARES_PER_SIDE*SQUARES_PER_SIDE*2);

	for(int j=0; j<SQUARES_PER_SIDE; j++)
		for(int i=0; i<SQUARES_PER_SIDE; i++)
		{
			const LandSquare* pSquare = &Square(i, j);
			pPoly->GetVertex(VertexIndex(i, j)) = Vertex(pSquare->SurfaceVector(1), UV(i, j), RGBA(255, 255, 255));

			if(i == SQUARES_PER_SIDE-1)
				pPoly->GetVertex(VertexIndex(i+1, j)) = Vertex(pSquare->SurfaceVector(1), UV(i+1, j), RGBA(255, 255, 255));

			if(j == SQUARES_PER_SIDE-1)
			{
				pPoly->GetVertex(VertexIndex(i, j+1)) = Vertex(pSquare->SurfaceVector(3), UV(i, j+1), RGBA(255, 255, 255));

				if(i == SQUARES_PER_SIDE-1)
					pPoly->GetVertex(VertexIndex(i+1, j+1)) = Vertex(pSquare->SurfaceVector(2), UV(i+1, j+1), RGBA(255, 255, 255));
			}
		}

	for(j=0; j<=SQUARES_PER_SIDE; j++)
		for(int i=0; i<=SQUARES_PER_SIDE; i++)
		{
			Vector n = Vector(0, 0, 0);
			int count = 0;

			const LandSquare* pNW = ((i>0)&&(j>0))?&Square(i-1, j-1):NULL;
			const LandSquare* pSW = ((i>0)&&(j<SQUARES_PER_SIDE))?&Square(i-1, j):NULL;
			const LandSquare* pNE = ((i<SQUARES_PER_SIDE)&&(j>0))?&Square(i, j-1):NULL;
			const LandSquare* pSE = ((i<SQUARES_PER_SIDE)&&(j<SQUARES_PER_SIDE))?&Square(i, j):NULL;

			if(pNW)
			{
				n += pNW->SurfaceNormal(1).GetVector(); count++;
				if(pNW->SurfaceNWSE()) { n += pNW->SurfaceNormal(0).GetVector(); count++; }
			}

			if(pSW)
			{
				n += pSW->SurfaceNormal(0).GetVector(); count++;
				if(!pSW->SurfaceNWSE()) { n += pSW->SurfaceNormal(1).GetVector(); count++; }
			}

			if(pNE)
			{
				n += pNE->SurfaceNormal(1).GetVector(); count++;
				if(!pNE->SurfaceNWSE()) { n += pNE->SurfaceNormal(0).GetVector(); count++; }
			}

			if(pSE)
			{
				n += pSE->SurfaceNormal(0).GetVector(); count++;
				if(pSE->SurfaceNWSE()) { n += pSE->SurfaceNormal(1).GetVector(); count++; }
			}

			n /= (float)count;
			pPoly->GetVertex(VertexIndex(i, j)).Normal() = Normal(n, -DotProduct(n, pPoly->GetVertex(VertexIndex(i, j)).XYZ()));
		}

	for(j=0; j<SQUARES_PER_SIDE; j++)
		for(int i=0; i<SQUARES_PER_SIDE; i++)
		{
			const LandSquare* pSquare = &Square(i, j);

			PolyFace* pFace1 = &pPoly->GetFace(FaceIndex(i, j, 0));
			PolyFace* pFace2 = &pPoly->GetFace(FaceIndex(i, j, 1));

			if(pSquare->SurfaceNWSE())
			{
				pFace1->vertices[0] = VertexIndex(i, j);
				pFace1->vertices[1] = VertexIndex(i+1, j);
				pFace1->vertices[2] = VertexIndex(i+1, j+1);
				pFace1->normal = Normal(pPoly->GetVertex(pFace1->vertices[0]).XYZ(),
										pPoly->GetVertex(pFace1->vertices[1]).XYZ(),
										pPoly->GetVertex(pFace1->vertices[2]).XYZ());

				pFace2->vertices[0] = VertexIndex(i, j);
				pFace2->vertices[1] = VertexIndex(i+1, j+1);
				pFace2->vertices[2] = VertexIndex(i, j+1);
				pFace2->normal = Normal(pPoly->GetVertex(pFace2->vertices[0]).XYZ(),
										pPoly->GetVertex(pFace2->vertices[1]).XYZ(),
										pPoly->GetVertex(pFace2->vertices[2]).XYZ());
			}
			else
			{
				pFace1->vertices[0] = VertexIndex(i, j);
				pFace1->vertices[1] = VertexIndex(i+1, j);
				pFace1->vertices[2] = VertexIndex(i, j+1);
				pFace1->normal = Normal(pPoly->GetVertex(pFace1->vertices[0]).XYZ(),
										pPoly->GetVertex(pFace1->vertices[1]).XYZ(),
										pPoly->GetVertex(pFace1->vertices[2]).XYZ());

				pFace2->vertices[0] = VertexIndex(i+1, j);
				pFace2->vertices[1] = VertexIndex(i+1, j+1);
				pFace2->vertices[2] = VertexIndex(i, j+1);
				pFace2->normal = Normal(pPoly->GetVertex(pFace2->vertices[0]).XYZ(),
										pPoly->GetVertex(pFace2->vertices[1]).XYZ(),
										pPoly->GetVertex(pFace2->vertices[2]).XYZ());
			}
		}

	return pPoly;
}

// -----------------------------------------------------------------------------

LandSquare::LandSquare(void)
{
	AttachNW(FALSE);
	AttachNE(FALSE);
	AttachSE(FALSE);
	AttachSW(FALSE);

	SurfaceTextured(FALSE);
	SurfaceDrawn(TRUE);

	NorthFaceTextured(FALSE);
	NorthFaceDrawn(FALSE);

	EastFaceTextured(FALSE);
	EastFaceDrawn(FALSE);
}



void LandSquare::Setup(Landscape* pLandscape, int i, int j)
{
	m_pLandscape = pLandscape;

	m_squareI = i;
	m_squareJ = j;

	i-=8;
	j-=8;

	SurfaceHeight(0) = 0;
	SurfaceHeight(1) = 0;
	SurfaceHeight(2) = 0;
	SurfaceHeight(3) = 0;

	SurfaceUV(0) = UV(.33, .33);
	SurfaceUV(1) = UV(.66, .33);
	SurfaceUV(2) = UV(.66, .66);
	SurfaceUV(3) = UV(.33, .66);

	NorthFaceUV(0) = UV(.33, .33);
	NorthFaceUV(1) = UV(.66, .33);
	NorthFaceUV(2) = UV(.66, .66);
	NorthFaceUV(3) = UV(.33, .66);

	EastFaceUV(0) = UV(.33, .33);
	EastFaceUV(1) = UV(.66, .33);
	EastFaceUV(2) = UV(.66, .66);
	EastFaceUV(3) = UV(.33, .66);

	RecomputeNormals();
}

// -----------------------------------------------------------------------------

BOOL LandSquare::SurfaceIntersect(float* pT, const Vector& a, const Vector& b) const
{
	ASSERT(pT);

	if(SurfaceNWSE())
	{
		if(IntersectTriangleSegment(pT, SurfaceVector(0), SurfaceVector(1), SurfaceVector(3), a, b))
			return TRUE;
		else if(IntersectTriangleSegment(pT, SurfaceVector(1), SurfaceVector(2), SurfaceVector(3), a, b))
			return TRUE;
		else
			return FALSE;
	}
	else
	{
		if(IntersectTriangleSegment(pT, SurfaceVector(0), SurfaceVector(1), SurfaceVector(2), a, b))
			return TRUE;
		else if(IntersectTriangleSegment(pT, SurfaceVector(0), SurfaceVector(2), SurfaceVector(3), a, b))
			return TRUE;
		else
			return FALSE;
	}
}



BOOL LandSquare::NorthFaceIntersect(float* pT, const Vector& a, const Vector& b) const
{
	ASSERT(pT);

	if(FromHereN()==NULL) return FALSE;

	Vector v0 = NorthFaceVector(0);
	Vector v1 = NorthFaceVector(1);
	Vector v2 = NorthFaceVector(2);
	Vector v3 = NorthFaceVector(3);
	
	if(IntersectTriangleSegment(pT, v0, v1, v2, a, b))
		return TRUE;
	else if(IntersectTriangleSegment(pT, v0, v2, v3, a, b))
		return TRUE;
	else
		return FALSE;
}



BOOL LandSquare::EastFaceIntersect(float* pT, const Vector& a, const Vector& b) const
{
	ASSERT(pT);

	if(FromHereE()==NULL) return FALSE;

	Vector v0 = EastFaceVector(0);
	Vector v1 = EastFaceVector(1);
	Vector v2 = EastFaceVector(2);
	Vector v3 = EastFaceVector(3);
	
	if(IntersectTriangleSegment(pT, v0, v1, v2, a, b))
		return TRUE;
	else if(IntersectTriangleSegment(pT, v0, v2, v3, a, b))
		return TRUE;
	else
		return FALSE;
}

// -----------------------------------------------------------------------------


BOOL LandSquare::Intersects(float* pT, int* pFace, const Vector& a, const Vector& b, BOOL hidden) const
{
	ASSERT(pT);
	ASSERT(pFace);

	float sT, nT, eT;
	BOOL s, n, e;
	s = SurfaceIntersect(&sT, a, b);
	n = (hidden || NorthFaceDrawn()) && NorthFaceIntersect(&nT, a, b);
	e = (hidden || EastFaceDrawn()) && EastFaceIntersect(&eT, a, b);

	if(!(s | n | e)) return FALSE;

	if(!s) sT = 1.1;
	if(!n) nT = 1.1;
	if(!e) eT = 1.1;

	if(sT <= nT)
	{
		if(sT <= eT)
		{
			(*pT) = sT;
			(*pFace) = 0;
		}
		else
		{
			(*pT) = eT;
			(*pFace) = 2;
		}
	}
	else
	{
		if(nT <= eT)
		{
			(*pT) = nT;
			(*pFace) = 1;
		}
		else
		{
			(*pT) = eT;
			(*pFace) = 2;
		}
	}

	return TRUE;
}

// -----------------------------------------------------------------------------

float LandSquare::HeightAt(float x, float y)
{
	float n;

	if(SurfaceNWSE())
	{
		float nwX = ((m_squareI-8)*2-1)*SQUARE_WIDTH/2;
		float nwY = ((m_squareJ-8)*2-1)*SQUARE_HEIGHT/2;
		float seX = ((m_squareI-8)*2+1)*SQUARE_WIDTH/2;
		float seY = ((m_squareJ-8)*2+1)*SQUARE_HEIGHT/2;
		n = (nwX-x)*(nwY-seY)-(nwY-y)*(nwX-seX);
	}
	else
	{
		float neX = ((m_squareI-8)*2+1)*SQUARE_WIDTH/2;
		float neY = ((m_squareJ-8)*2-1)*SQUARE_HEIGHT/2;
		float swX = ((m_squareI-8)*2-1)*SQUARE_WIDTH/2;
		float swY = ((m_squareJ-8)*2+1)*SQUARE_HEIGHT/2;
		n = (swX-x)*(swY-neY)-(swY-y)*(swX-neX);
	}

	if(n >= 0)
	{
		ASSERT(m_surfaceNormal[0].Y() != 0);
		return -((m_surfaceNormal[0].X() * x) + (m_surfaceNormal[0].Z() * y) + m_surfaceNormal[0].W()) / m_surfaceNormal[0].Y();
	}
	else
	{
		ASSERT(m_surfaceNormal[1].Y() != 0);
		return -((m_surfaceNormal[1].X() * x) + (m_surfaceNormal[1].Z() * y) + m_surfaceNormal[1].W()) / m_surfaceNormal[1].Y();
	}
}

// -----------------------------------------------------------------------------

void LandSquare::Draw(Clipper& clipper)
{
	if(SurfaceDrawn())
	{
		if(AlternateSurface())
			clipper.Context()->SetTexture(m_pLandscape->m_pAlternate);
		else
			clipper.Context()->SetTexture(m_pLandscape->m_pTexture);
		clipper.Context()->SetSolidColor(SurfaceColor().R(), SurfaceColor().G(), SurfaceColor().B());
		VertexType type = SurfaceTextured()?VERTEXTYPE_TC:VERTEXTYPE_C;
		Vertex nw = SurfaceVertex(0);
		nw.RGBA() = RGBA(255, 255, 255);
		Vertex ne = SurfaceVertex(1);
		ne.RGBA() = RGBA(255, 255, 255);
		Vertex se = SurfaceVertex(2);
		se.RGBA() = RGBA(255, 255, 255);
		Vertex sw = SurfaceVertex(3);
		sw.RGBA() = RGBA(255, 255, 255);

		if(SurfaceNWSE())
		{
			if(clipper.Stack()->IsFacing(m_surfaceNormal[0]))
				clipper.DrawTriangle(nw, ne, se, type);
			if(clipper.Stack()->IsFacing(m_surfaceNormal[1]))
				clipper.DrawTriangle(nw, se, sw, type);
		}
		else
		{
			if(clipper.Stack()->IsFacing(m_surfaceNormal[0]))
				clipper.DrawTriangle(nw, ne, sw, type);
			if(clipper.Stack()->IsFacing(m_surfaceNormal[1]))
				clipper.DrawTriangle(ne, se, sw, type);
		}
	}

	if(NorthFaceDrawn() && clipper.Stack()->IsFacing(m_northFaceNormal))
	{
		ASSERT(FromHereN());
		clipper.Context()->SetTexture(m_pLandscape->m_pTexture);
		clipper.Context()->SetSolidColor(NorthFaceColor().R(), NorthFaceColor().G(), NorthFaceColor().B());
		VertexType type = NorthFaceTextured()?VERTEXTYPE_TC:VERTEXTYPE_1;
		Vertex a = NorthFaceVertex(0);
		a.RGBA() = RGBA(255, 255, 255);
		Vertex b = NorthFaceVertex(1);
		b.RGBA() = RGBA(255, 255, 255);
		Vertex c = NorthFaceVertex(2);
		c.RGBA() = RGBA(255, 255, 255);
		Vertex d = NorthFaceVertex(3);
		d.RGBA() = RGBA(255, 255, 255);

		clipper.DrawQuad(a, b, c, d, type);
	}

	if(EastFaceDrawn() && clipper.Stack()->IsFacing(m_eastFaceNormal))
	{
		ASSERT(FromHereE());
		clipper.Context()->SetTexture(m_pLandscape->m_pTexture);
		clipper.Context()->SetSolidColor(EastFaceColor().R(), EastFaceColor().G(), EastFaceColor().B());
		VertexType type = EastFaceTextured()?VERTEXTYPE_TC:VERTEXTYPE_1;
		Vertex a = EastFaceVertex(0);
		a.RGBA() = RGBA(255, 255, 255);
		Vertex b = EastFaceVertex(1);
		b.RGBA() = RGBA(255, 255, 255);
		Vertex c = EastFaceVertex(2);
		c.RGBA() = RGBA(255, 255, 255);
		Vertex d = EastFaceVertex(3);
		d.RGBA() = RGBA(255, 255, 255);

		clipper.DrawQuad(a, b, c, d, type);
	}
}



void LandSquare::DrawWater(Clipper& clipper)
{
	if(!Watered()) return;

	Vertex nw(SurfaceVector(0), WaterColor(0));
	Vertex ne(SurfaceVector(1), WaterColor(1));
	Vertex se(SurfaceVector(2), WaterColor(2));
	Vertex sw(SurfaceVector(3), WaterColor(3));

	nw.Y() = WaterLevel();
	ne.Y() = WaterLevel();
	se.Y() = WaterLevel();
	sw.Y() = WaterLevel();

	clipper.DrawQuad(nw, ne, se, sw, VERTEXTYPE_C);
}



void LandSquare::DrawSurfaceOutline(Clipper& clipper, const RGBA& color, BOOL drawCross, BOOL labelCorners)
{
}



void LandSquare::DrawNorthFaceOutline(Clipper& clipper, const RGBA& color)
{
	Vertex a = NorthFaceVertex(0);
	Vertex b = NorthFaceVertex(1);
	Vertex c = NorthFaceVertex(2);
	Vertex d = NorthFaceVertex(3);

	clipper.OutlineQuad(a, b, c, d, color);
}



void LandSquare::DrawEastFaceOutline(Clipper& clipper, const RGBA& color)
{
	Vertex a = EastFaceVertex(0);
	Vertex b = EastFaceVertex(1);
	Vertex c = EastFaceVertex(2);
	Vertex d = EastFaceVertex(3);

	clipper.OutlineQuad(a, b, c, d, color);
}

// -----------------------------------------------------------------------------

void LandSquare::DrawOutline(Clipper& clipper, const RGBA& color, BOOL drawCross)
{
	Vertex nw = SurfaceVertex(0);
	Vertex ne = SurfaceVertex(1);
	Vertex se = SurfaceVertex(2);
	Vertex sw = SurfaceVertex(3);

	clipper.OutlineQuad(nw, ne, se, sw, color);

	if(drawCross)
		if(SurfaceNWSE())
			clipper.DrawLine(nw, se, VERTEXTYPE_1);
		else
			clipper.DrawLine(ne, sw, VERTEXTYPE_1);

	if(FromHereN())
	{
		Vertex n2 = NorthFaceVertex(2);
		Vertex n3 = NorthFaceVertex(3);

		clipper.DrawLine(nw, n2, VERTEXTYPE_1);
		clipper.DrawLine(n2, n3, VERTEXTYPE_1);
		clipper.DrawLine(ne, n3, VERTEXTYPE_1);
	}

	if(FromHereE())
	{
		Vertex e2 = EastFaceVertex(2);
		Vertex e3 = EastFaceVertex(3);

		clipper.DrawLine(ne, e2, VERTEXTYPE_1);
		clipper.DrawLine(e2, e3, VERTEXTYPE_1);
		clipper.DrawLine(se, e3, VERTEXTYPE_1);
	}
}

// -----------------------------------------------------------------------------

LandSquare* LandSquare::FromHere(int di, int dj) const
{
	di += m_squareI;
	dj += m_squareJ;

	if(di<0) return NULL;
	if(di>=17) return NULL;
	if(dj<0) return NULL;
	if(dj>=17) return NULL;

	return &m_pLandscape->Square(di, dj);
}

// -----------------------------------------------------------------------------

void LandSquare::AdjustHeight(float dNw, float dNe, float dSe, float dSw, BOOL setAttachments)
{
	SurfaceHeight(0) += dNw;
	SurfaceHeight(1) += dNe;
	SurfaceHeight(2) += dSe;
	SurfaceHeight(3) += dSw;

	RecomputeNormals();
	
	LandSquare* pSquare;
	if(setAttachments)
	{
		if(AttachNW())
		{
			if(((pSquare=FromHereNW())!=NULL) && pSquare->AttachSE())
				pSquare->SurfaceHeight(2) += dNw;
			if(((pSquare=FromHereN())!=NULL) && pSquare->AttachSW())
				pSquare->SurfaceHeight(3) += dNw;
			if(((pSquare=FromHereW())!=NULL) && pSquare->AttachNE())
				pSquare->SurfaceHeight(1) += dNw;
		}
		if(AttachNE())
		{
			if(((pSquare=FromHereN())!=NULL) && pSquare->AttachSE())
				pSquare->SurfaceHeight(2) += dNe;
			if(((pSquare=FromHereNE())!=NULL) && pSquare->AttachSW())
				pSquare->SurfaceHeight(3) += dNe;
			if(((pSquare=FromHereE())!=NULL) && pSquare->AttachNW())
				pSquare->SurfaceHeight(0) += dNe;
		}
		if(AttachSE())
		{
			if(((pSquare=FromHereE())!=NULL) && pSquare->AttachSW())
				pSquare->SurfaceHeight(3) += dSe;
			if(((pSquare=FromHereS())!=NULL) && pSquare->AttachNE())
				pSquare->SurfaceHeight(1) += dSe;
			if(((pSquare=FromHereSE())!=NULL) && pSquare->AttachNW())
				pSquare->SurfaceHeight(0) += dSe;
		}
		if(AttachSW())
		{
			if(((pSquare=FromHereW())!=NULL) && pSquare->AttachSE())
				pSquare->SurfaceHeight(2) += dSw;
			if(((pSquare=FromHereSW())!=NULL) && pSquare->AttachNE())
				pSquare->SurfaceHeight(1) += dSw;
			if(((pSquare=FromHereS())!=NULL) && pSquare->AttachNW())
				pSquare->SurfaceHeight(0) += dSw;
		}

		if(FromHereN()) FromHereN()->RecomputeNormals();
		if(FromHereE()) FromHereE()->RecomputeNormals();
		if(FromHereW()) FromHereW()->RecomputeNormals();
		if(FromHereS()) FromHereS()->RecomputeNormals();
	}
}



void LandSquare::SetHeight(float nw, float ne, float se, float sw, BOOL setAttachments)
{
	float dNW = nw - SurfaceHeight(0);
	float dNE = ne - SurfaceHeight(1);
	float dSE = se - SurfaceHeight(2);
	float dSW = sw - SurfaceHeight(3);

	AdjustHeight(dNW, dNE, dSE, dSW, setAttachments);
}

// -----------------------------------------------------------------------------

Vector LandSquare::SurfaceVector(int n) const
{
	ASSERT((n>=0)&&(n<4));
	static float iAdjust[4] = {-1, +1, +1, -1};
	static float jAdjust[4] = {-1, -1, +1, +1};
	return Vector((((float)m_squareI-8)*2+iAdjust[n])*SQUARE_WIDTH/2,
				  SurfaceHeight(n),
				  (((float)m_squareJ-8)*2+jAdjust[n])*SQUARE_HEIGHT/2);
}

Vertex LandSquare::SurfaceVertex(int n) const
{
	// Really should average these normals... Then again, really should redo everything...
	switch(n)
	{
	case 0:		return Vertex(SurfaceVector(n), SurfaceNormal(0), SurfaceUV(n), SurfaceColor());
	case 1:		return Vertex(SurfaceVector(n), SurfaceNormal(0), SurfaceUV(n), SurfaceColor());
	case 2:		return Vertex(SurfaceVector(n), SurfaceNormal(1), SurfaceUV(n), SurfaceColor());
	default:	return Vertex(SurfaceVector(n), SurfaceNormal(1), SurfaceUV(n), SurfaceColor());
	}
}

// -----------------------------------------------------------------------------

Vector LandSquare::NorthFaceVector(int n) const
{
	ASSERT((n>=0)&&(n<4));

	ASSERT(FromHereN());
	switch(n)
	{
	case 0: return SurfaceVector(1);
	case 1: return SurfaceVector(0);
	case 2: return FromHereN()->SurfaceVector(3);
	default: return FromHereN()->SurfaceVector(2);
	}
}

Vertex LandSquare::NorthFaceVertex(int n) const
{
	return Vertex(NorthFaceVector(n), NorthFaceUV(n), NorthFaceColor());
}

// -----------------------------------------------------------------------------

Vector LandSquare::EastFaceVector(int n) const
{
	ASSERT((n>=0)&&(n<4));
	ASSERT(FromHereE());
	switch(n)
	{
	case 0: return SurfaceVector(2);
	case 1: return SurfaceVector(1);
	case 2: return FromHereE()->SurfaceVector(0);
	default: return FromHereE()->SurfaceVector(3);
	}
}

Vertex LandSquare::EastFaceVertex(int n) const
{
	return Vertex(EastFaceVector(n), EastFaceUV(n), EastFaceColor());
}

// -----------------------------------------------------------------------------

void LandSquare::RecomputeNormals(void)
{
	if(SurfaceNWSE())
	{
		m_surfaceNormal[0] = Normal(SurfaceVector(0), SurfaceVector(1), SurfaceVector(2));
		m_surfaceNormal[1] = Normal(SurfaceVector(0), SurfaceVector(2), SurfaceVector(3));
	}
	else
	{
		m_surfaceNormal[0] = Normal(SurfaceVector(0), SurfaceVector(1), SurfaceVector(3));
		m_surfaceNormal[1] = Normal(SurfaceVector(1), SurfaceVector(2), SurfaceVector(3));
	}

	if(FromHereN())
	{
		Vector a = NorthFaceVector(0);
		Vector b = NorthFaceVector(1);
		Vector c = NorthFaceVector(2);
		Vector d = NorthFaceVector(3);

		float dAD = fabs(a.Y() - d.Y());
		float dBC = fabs(b.Y() - c.Y());

		if((dAD < NEAR_ZERO) && (dBC < NEAR_ZERO))
			m_northFaceNormal = Normal(ZERO);
		else if(dAD < NEAR_ZERO)
			m_northFaceNormal = Normal(a, b, c);
		else
			m_northFaceNormal = Normal(a, b, d);
	}
	else
		m_northFaceNormal = Normal(ZERO);

	if(FromHereE())
	{
		Vector a = EastFaceVector(0);
		Vector b = EastFaceVector(1);
		Vector c = EastFaceVector(2);
		Vector d = EastFaceVector(3);

		float dAD = fabs(a.Y() - d.Y());
		float dBC = fabs(b.Y() - c.Y());

		if((dAD < NEAR_ZERO) && (dBC < NEAR_ZERO))
			m_eastFaceNormal = Normal(ZERO);
		else if(dAD < NEAR_ZERO)
			m_eastFaceNormal = Normal(a, b, c);
		else
			m_eastFaceNormal = Normal(a, b, d);
	}
	else
		m_eastFaceNormal = Normal(ZERO);
}

// -----------------------------------------------------------------------------

const BOOL LandSquare::FaceDrawn(int f) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceDrawn();
	case 1: return NorthFaceDrawn();
	default: return EastFaceDrawn();
	}
}

void LandSquare::FaceDrawn(int f, BOOL s)
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: SurfaceDrawn(s); break;
	case 1: NorthFaceDrawn(s); break;
	default: EastFaceDrawn(s); break;
	}
}

const BOOL LandSquare::FaceTextured(int f) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceTextured();
	case 1: return NorthFaceTextured();
	default: return EastFaceTextured();
	}
}

void LandSquare::FaceTextured(int f, BOOL s)
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: SurfaceTextured(s); break;
	case 1: NorthFaceTextured(s); break;
	default: EastFaceTextured(s); break;
	}
}

const UV& LandSquare::FaceUV(int f, int n) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceUV(n);
	case 1: return NorthFaceUV(n);
	default: return EastFaceUV(n);
	}
}

UV& LandSquare::FaceUV(int f, int n)
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceUV(n);
	case 1: return NorthFaceUV(n);
	default: return EastFaceUV(n);
	}
}

const RGBA& LandSquare::FaceColor(int f) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceColor();
	case 1: return NorthFaceColor();
	default: return EastFaceColor();
	}
}

RGBA& LandSquare::FaceColor(int f)
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceColor();
	case 1: return NorthFaceColor();
	default: return EastFaceColor();
	}
}

Vector LandSquare::FaceVector(int f, int n) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceVector(n);
	case 1: return NorthFaceVector(n);
	default: return EastFaceVector(n);
	}
}

Vertex LandSquare::FaceVertex(int f, int n) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceVertex(n);
	case 1: return NorthFaceVertex(n);
	default: return EastFaceVertex(n);
	}
}

BOOL LandSquare::FaceIntersect(int f, float* pT, const Vector& a, const Vector& b) const
{
	ASSERT((f>=0)&&(f<3));
	switch(f)
	{
	case 0: return SurfaceIntersect(pT, a, b);
	case 1: return NorthFaceIntersect(pT, a, b);
	default: return EastFaceIntersect(pT, a, b);
	}
}

// -----------------------------------------------------------------------------

void LandSquare::AttachNW(BOOL attach)
{
	m_attachNW = attach;
	if(attach)
	{
		LandSquare* pSquare;
		if(((pSquare=FromHereNW())!=NULL) && pSquare->AttachSE())
			SurfaceHeight(0) = pSquare->SurfaceHeight(2);
		else if(((pSquare=FromHereN())!=NULL) && pSquare->AttachSW())
			SurfaceHeight(0) = pSquare->SurfaceHeight(3);
		else if(((pSquare=FromHereW())!=NULL) && pSquare->AttachNE())
			SurfaceHeight(0) = pSquare->SurfaceHeight(1);
		RecomputeNormals();
	}
}

void LandSquare::AttachNE(BOOL attach)
{
	m_attachNE = attach;
	if(attach)
	{
		LandSquare* pSquare;
		if(((pSquare=FromHereN())!=NULL) && pSquare->AttachSE())
			SurfaceHeight(1) = pSquare->SurfaceHeight(2);
		else if(((pSquare=FromHereNE())!=NULL) && pSquare->AttachSW())
			SurfaceHeight(1) = pSquare->SurfaceHeight(3);
		else if(((pSquare=FromHereE())!=NULL) && pSquare->AttachNW())
			SurfaceHeight(1) = pSquare->SurfaceHeight(0);
		RecomputeNormals();
	}
}

void LandSquare::AttachSE(BOOL attach)
{
	m_attachSE = attach;
	if(attach)
	{
		LandSquare* pSquare;
		if(((pSquare=FromHereE())!=NULL) && pSquare->AttachSW())
			SurfaceHeight(2) = pSquare->SurfaceHeight(3);
		else if(((pSquare=FromHereS())!=NULL) && pSquare->AttachNE())
			SurfaceHeight(2) = pSquare->SurfaceHeight(1);
		else if(((pSquare=FromHereSE())!=NULL) && pSquare->AttachNW())
			SurfaceHeight(2) = pSquare->SurfaceHeight(0);
		RecomputeNormals();
	}
}

void LandSquare::AttachSW(BOOL attach)
{
	m_attachSW = attach;
	if(attach)
	{
		LandSquare* pSquare;
		if(((pSquare=FromHereW())!=NULL) && pSquare->AttachSE())
			SurfaceHeight(3) = pSquare->SurfaceHeight(2);
		else if(((pSquare=FromHereSW())!=NULL) && pSquare->AttachNE())
			SurfaceHeight(3) = pSquare->SurfaceHeight(1);
		else if(((pSquare=FromHereS())!=NULL) && pSquare->AttachNW())
			SurfaceHeight(3) = pSquare->SurfaceHeight(0);
		RecomputeNormals();
	}
}

// -----------------------------------------------------------------------------
