#include "MLRHeaders.hpp"
#include "Compost\TerrainTextureLogistic.hpp"

//#define HUNT_FOR_THE_RED_TILE "micgaert"

extern DWORD gEnableLightMaps;

//#############################################################################
//## MLRTerrain with no color no lighting w/ detail texture, uv's from xyz  ###
//#############################################################################

DynamicArrayOf<Vector2DScalar>
	*MLR_Terrain2::detailTexCoords;

MLR_Terrain2::ClassData*
	MLR_Terrain2::DefaultData = NULL;

extern DynamicArrayOf<Vector2DScalar> *lightMapUVs;
extern DynamicArrayOf<Scalar> *lightMapSqFalloffs;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::InitializeClass()
{
	Verify(!DefaultData);
	Verify(gos_GetCurrentHeap() == StaticHeap);
	DefaultData =
		new ClassData(
			MLR_Terrain2ClassID,
			"MidLevelRenderer::MLR_Terrain2",
			MLR_I_DeT_TMesh::DefaultData,
			(MLRPrimitiveBase::Factory)&Make
		);
	Register_Object(DefaultData);

	detailTexCoords = new DynamicArrayOf<Vector2DScalar> (Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(detailTexCoords);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Unregister_Object(detailTexCoords);
	delete detailTexCoords;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLR_Terrain2::MLR_Terrain2(
	ClassData *class_data,
	MemoryStream *stream,
	int version
):
	MLR_I_DeT_TMesh(class_data, stream, version)
{
	Check_Pointer(this);
	Check_Pointer(stream);

	*stream >> tileX >> tileZ;
	*stream >> maxDepth >> maxAllDepth;

	int i;
	if(version>4)
	{
		Scalar *fptr = &frame[0][0];

		for(int i=0;i<32;i++)
		{
			*stream >> *fptr++;
		}
	}
	else
	{
		Scalar xOffset, zOffset, xGrid, zGrid;

		*stream >> xOffset >> zOffset;
		*stream >> xGrid >> zGrid;

		frame[0][0] = xOffset;
		frame[0][1] = zOffset;
		frame[0][2] = xOffset + 8*xGrid;
		frame[0][3] = zOffset + 8*zGrid;

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

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

	for(i=0;i<8;i++)
	{
		OneOverX[i] = 1.0f/(frame[i][2] - frame[i][0]);
		OneOverZ[i] = 1.0f/(frame[i][3] - frame[i][1]);
	}

#ifdef BORDERPIXEL
	*stream >> borderPixelFun;
#else
	Scalar dummy;
	*stream >> dummy;
#endif

	BYTE textureFlags, mask = 1;

	*stream >> textureFlags;

	textureFlags &= 0xff >> (7 - Compost::TerrainTextureLogistic::GetResolution());

	for(i=0;i<8;i++)
	{
		if(textureFlags | mask)
		{
			textures[i] = 1;
		}
		else
		{
			textures[i] = 0;
		}
		mask <<= 1;
	}

	Check_Object(MLRTexturePool::Instance);
	MLRTexture *orgTexture = (*MLRTexturePool::Instance)[referenceState.GetTextureHandle()];
	Check_Object(orgTexture);
	const char *texName = orgTexture->GetTextureName();
	char texRoot[1024], name[1024];

	int len;
	if((len = strlen(texName)) > 0)
	{
		Verify(len>8);
		int d = texName[len-6] - '0';

		strncpy(texRoot, texName, len-7);
		texRoot[len-7] = '\0';

		textures[d] = referenceState.GetTextureHandle();

		MLRTexture *texture;

		BYTE mask = 1;
		for(i=0;i<8;i++)
		{
			if(textureFlags & mask)
			{
				sprintf(name, "%s_%1d_%02x%02x", texRoot, i, (7-tileX)/(1<<(maxAllDepth-i)), (7-tileZ)/(1<<(maxAllDepth-i)));

				texture = (*MLRTexturePool::Instance)(name, 0);
				if (!texture)
				{
					texture = MLRTexturePool::Instance->Add(name, 0);
				}
				Check_Object(texture);

				texture->SetHint(orgTexture->GetHint());

				textures[i] = texture->GetTextureHandle();
			}
			else
			{
				textures[i] = 0;
			}
			mask <<= 1;
		}
		i++;
		for(;i<8;i++)
		{
			textures[i] = 0;
		}
	}
	else
	{
		for(int i=0;i<8;i++)
		{
			textures[i] = 0;
		}
	}

	Verify(textures[0]!=0);

	currentDepth = -1;

	SetCurrentDepth(0);

	referenceState.SetLightingMode(MLRState::LightMapLightingMode);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLR_Terrain2::MLR_Terrain2(ClassData *class_data):
	MLR_I_DeT_TMesh(class_data)
{
	Check_Pointer(this);

	tileX = 0;
	tileZ = 0;
	maxDepth = 0;
	maxAllDepth = 0;

#ifdef BORDERPIXEL
	borderPixelFun = 0.0f;
#endif

	Scalar *fptr = &frame[0][0];

	int i;
	for(i=0;i<32;i++)
	{
		*fptr++ = 0.0f;
	}

	for(i=0;i<8;i++)
	{
		textures[i] = 0;
		OneOverX[i] = 1.0f;
		OneOverZ[i] = 1.0f;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLR_Terrain2::~MLR_Terrain2()
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLR_Terrain2*
	MLR_Terrain2::Make(
		MemoryStream *stream,
		int version
	)
{
	Check_Object(stream);

	gos_PushCurrentHeap(PrimitiveHeap);
	MLR_Terrain2 *terrain = new MLR_Terrain2(DefaultData, stream, version);
	gos_PopCurrentHeap();

	return terrain;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::Save(MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);

#ifndef CALCULATEuvONtheFLY
	dynamicUVs.SetLength(0);
#endif
	texCoords.AssignData(NULL, 0);
	MLR_I_DeT_TMesh::Save(stream);
	CalculateUVs();

	*stream << tileX << tileZ;
	*stream << maxDepth << maxAllDepth;

	Scalar *fptr = &frame[0][0];

	for(int i=0;i<32;i++)
	{
		*stream << *fptr++;
	}

#ifdef BORDERPIXEL
	*stream << borderPixelFun;
#else
	*stream << 0.0f;
#endif

	BYTE textureFlags = 0;

//	HACK
/*
	Check_Object(MLRTexturePool::Instance);
	MLRTexture *orgTexture = (*MLRTexturePool::Instance)[referenceState.GetTextureHandle()];
	const char *texName = orgTexture->GetTextureName();
	char texRoot[1024], name[1024];

	int len;
	if((len = strlen(texName)) > 0)
	{
		Verify(len>8);
		int i, d = texName[len-6] - '0';
		Verify(d==maxDepth);
		strncpy(texRoot, texName, len-7);
		texRoot[len-7] = '\0';

		textures[d] = referenceState.GetTextureHandle();

		MLRTexture *texture;

		BYTE mask = 1;
		for(i=0;i<d;i++)
		{
			sprintf(name, "%s_%1d_%02x%02x", texRoot, i, tileX/(1<<(maxAllDepth-i)), tileZ/(1<<(maxAllDepth-i)));

			Verify((1<<i)>(tileX/(1<<(maxAllDepth-i))));
			Verify((1<<i)>(tileZ/(1<<(maxAllDepth-i))));
//				SPEW(("micgaert", "%s", name));

			texture = (*MLRTexturePool::Instance)(name, 0);
			if (texture)
			{
				textureFlags |= mask;
				textures[i] = texture->GetTextureHandle();
			}
			mask <<= 1;
		}
	}
	Verify(textures[0]!=0);
*/
	textureFlags = 3;
//	HACK


	*stream << textureFlags;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::SetCurrentDepth(BYTE d)
{
	if(d == currentDepth)
	{
		return;
	}
	else
	{
		Verify(d <= maxAllDepth);

		BYTE dt;
		dt = d<maxDepth ? d : maxDepth;

		while(dt>0 && textures[dt]==0)
		{
			dt--;
		}
		currentDepth = dt;
	}
	CalculateUVs();
#if defined(LAB_ONLY) && defined(HUNT_FOR_THE_RED_TILE)
	MLRTexture *tex = (*MLRTexturePool::Instance)[textures[currentDepth]];
	GOSImage *image = tex->GetImage();

	if(image->GetHandle())
	{
		const char *tex_name = gos_GetTextureName(image->GetHandle());

		if(*tex_name!='l')
		{
			SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "<2"));
		}
		else
		{
			int lev, tX, tZ;

			sscanf(tex_name, "l:%d r:%d c:%d", &lev, &tZ, &tX);

			if((tZ&0x7)!=(7-tileZ) || (tX&0x7)!=(7-tileX))
			{
				SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "T2 l:%d r:%d c:%d - N %d %d", lev, tZ, tX, (7-tileZ), (7-tileX)));
			}
			else
			{
				SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "T2 l:%d r:%d c:%d - Y", lev, tZ, tX));
			}
		}
	}
	else
	{
		SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "?"));
	}
#endif

	referenceState.SetTextureHandle(textures[currentDepth]);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::SetLevelTexture(int lev, int handle)
{
	Check_Object(this);
	Verify(lev>=0 && lev<8);
	textures[lev] = handle;

	if(lev==currentDepth)
	{
#if defined(LAB_ONLY) && defined(HUNT_FOR_THE_RED_TILE)
		MLRTexture *tex = (*MLRTexturePool::Instance)[textures[currentDepth]];
		GOSImage *image = tex->GetImage();

		if(image->GetHandle())
		{
			const char *tex_name = gos_GetTextureName(image->GetHandle());

			if(*tex_name!='l')
			{
				SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "<2"));
			}
			else
			{
				int lev, tX, tZ;

				sscanf(tex_name, "l:%d r:%d c:%d", &lev, &tZ, &tX);

				if((tZ&0x7)!=(7-tileZ) || (tX&0x7)!=(7-tileX))
				{
					SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "T2 l:%d r:%d c:%d - N %d %d", lev, tZ, tX, (7-tileZ), (7-tileX)));
				}
				else
				{
					SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "T2 l:%d r:%d c:%d - Y", lev, tZ, tX));
				}
			}
		}
		else
		{
			SPEWALWAYS((HUNT_FOR_THE_RED_TILE, "?"));
		}
#endif
		referenceState.SetTextureHandle(textures[currentDepth]);
	}
 }

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::CalculateUVs()
{
#ifdef CALCULATEuvONtheFLY
	return;
#else
	if(texCoords.GetLength() != coords.GetLength())
	{
		gos_PushCurrentHeap(PrimitiveHeap);
		dynamicUVs.SetLength(coords.GetLength());
		texCoords.AssignData(dynamicUVs.GetData(), dynamicUVs.GetLength());
		gos_PopCurrentHeap();
	}

	Scalar maxX = frame[currentDepth][2];
	Scalar maxZ = frame[currentDepth][3];

	for(int i=0;i<texCoords.GetLength();i++)
	{
	#ifdef BORDERPIXEL
		dynamicUVs[i][0] = borderPixelFun + (1.0f-2*borderPixelFun)*(maxX - coords[i].x)*OneOverX[currentDepth];
		dynamicUVs[i][1] = borderPixelFun + (1.0f-2*borderPixelFun)*(maxZ - coords[i].z)*OneOverZ[currentDepth];
	#else
		dynamicUVs[i][0] = (maxX - coords[i].x)*OneOverX[currentDepth];
		dynamicUVs[i][1] = (maxZ - coords[i].z)*OneOverZ[currentDepth];
	#endif

		Verify(texCoords[i][0]>=0.0 && texCoords[i][0]<=1.0);
		Verify(texCoords[i][1]>=0.0 && texCoords[i][1]<=1.0);
	}
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::CalculateUV(int i, Scalar &u, Scalar &v)
{
	Scalar maxX = frame[currentDepth][2];
	Scalar maxZ = frame[currentDepth][3];

#ifdef BORDERPIXEL
	u = borderPixelFun + (1.0f-2*borderPixelFun)*(maxX - coords[i].x)*OneOverX[currentDepth];
	v = borderPixelFun + (1.0f-2*borderPixelFun)*(maxZ - coords[i].z)*OneOverZ[currentDepth];
#else
	u = (maxX - coords[i].x)*OneOverX[currentDepth];
	v = (maxZ - coords[i].z)*OneOverZ[currentDepth];
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

#define I_SAY_YES_TO_DETAIL_TEXTURES
#define I_SAY_YES_TO_TERRAIN2
#undef I_SAY_YES_TO_DUAL_TEXTURES
#undef I_SAY_YES_TO_COLOR
#undef I_SAY_YES_TO_TERRAIN
#undef I_SAY_YES_TO_LIGHTING

#define CLASSNAME MLR_Terrain2

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	This include contains follwing functions:
//	void MLR_Terrain2::TransformNoClip(Matrix4D*, GOSVertexPool*);
//	int MLR_Terrain2::Clip(MLRClippingState, GOSVertexPool*);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <MLR\MLRTriangleClipping.hpp>

#undef I_SAY_YES_TO_DETAIL_TEXTURES
#undef I_SAY_YES_TO_TERRAIN2

#undef CLASSNAME

extern bool
	CheckForBigTriangles(DynamicArrayOf<Vector2DScalar> *lightMapUVs, int stride);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLRPrimitiveBase*
	MLR_Terrain2::StepOnMe(const Stuff::LinearMatrix4D& foot, Stuff::Scalar radius, MLRTexture *tex)
{
	Point3D stepLoc;

	stepLoc = foot;

	UnitVector3D dir, rot;

	foot.GetLocalForwardInWorld(&dir);
	rot = dir;
	rot.y = 0.0f;
	rot.Normalize(rot);

	Scalar oneOverRadius = 0.5f / radius;

	int i, j, k, tooBig, nrOfPoints=0;
	bool lm;
	Scalar bigUV = MLRState::GetMaxUV();

	//
	//------------------------------------------------------------------------
	// Look at each polygon in the mesh to see if it could accept the lightmap
	//------------------------------------------------------------------------
	//
	for(i=0,j=0,k=0;i<numOfTriangles;i++,j += 3)
	{
/*
		if((*testList)[i] == 0)
			continue;
		Scalar f = dir*facePlanes[i].normal;
		if(f>=0.0f)
			continue;
*/
		lm = false;
		tooBig = 0;
		for(k=0;k<3;k++)
		{
			Vector3D vec(coords[index[k+j]]);
			vec -= stepLoc;

			Scalar u, v;
			u = vec.x * oneOverRadius;
			v = vec.z * oneOverRadius;
			(*lightMapUVs)[k][0] = u*rot.z - v*rot.x;
			(*lightMapUVs)[k][1] = v*rot.z + u*rot.x;

			if(
				(*lightMapUVs)[k][0] >= -0.5f && (*lightMapUVs)[k][0] <= 0.5f &&
				(*lightMapUVs)[k][1] >= -0.5f && (*lightMapUVs)[k][1] <= 0.5f

			) 
			{
				lm = true;
			}
			if(
				bigUV > 0.0f &&
				(
					(*lightMapUVs)[k][0] < -bigUV || (*lightMapUVs)[k][0] > bigUV ||
					(*lightMapUVs)[k][1] < -bigUV || (*lightMapUVs)[k][1] > bigUV
				)
			) 
			{
				tooBig++;
			}
		}

		if(	
			tooBig == 0 
			&& ((lm == true)	|| CheckForBigTriangles(lightMapUVs, 3) == true)
		)
		{
			for(k=0;k<3;k++,nrOfPoints++)
			{
				SetClipCoord(coords[index[k+j]], nrOfPoints);
				(*lightMapUVs)[k][0] += 0.5f;
				(*lightMapUVs)[k][1] += 0.5f;
				SetClipTexCoord((*lightMapUVs)[k], nrOfPoints);
			}
		}
	}

	if(nrOfPoints>0)
	{
		Verify(nrOfPoints%3==0);

		gos_PushCurrentHeap(PrimitiveHeap);
		MLR_I_TMesh *ret = new MLR_I_TMesh();
		Register_Object(ret);

		ret->FlashClipCoords(nrOfPoints);
		ret->FlashClipTexCoords(nrOfPoints);
		ret->TheIndexer(nrOfPoints);

		ret->SetSubprimitiveLengths(NULL, nrOfPoints/3);
		ret->FindFacePlanes();

		gos_PopCurrentHeap();

		MLRState state;

		state = referenceState;
		state.SetAlphaMode(MLRState::AlphaInvAlphaMode);
		state.SetPriority(1);
		state.SetBackFaceOn();
		state.SetZBufferCompareOn();
		state.SetTextureCorrectionOn();
		if(tex!=NULL)
		{
			state.SetTextureHandle(tex->GetTextureHandle());
		}
		else
		{
			state.SetTextureHandle(0);
		}
		state.SetTextureWrapMode(MLRState::TextureClamp);

		ret->SetReferenceState(state);
		return ret;
	}
	
	
	return NULL;
}

extern RGBAColor errorColor;
extern bool
	CheckForBigTriangles(DynamicArrayOf<Vector2DScalar> *lightMapUVs, int stride);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_Terrain2::LightMapLighting(MLRLight *light)
{
	MLRLightMap *lightMap = light->GetLightMap();
	Check_Object(lightMap);

	if( (!lightMap) || !gEnableLightMaps || lightMap->IsFull()==true)
	{
		return;
	}

	int i, j, k, len = numOfTriangles;
	LinearMatrix4D matrix = LinearMatrix4D::Identity;
	Point3D lightPosInShape, hitPoint;
	UnitVector3D up, left, forward;
	bool lm;
	Scalar falloff = 1.0f, distance;

	BYTE addedVertices = 0;
	BYTE addedIndicies = 0;

	int tooBig = 0;
	Scalar bigUV = MLRState::GetMaxUV();

	switch(light->GetLightType())
	{
		case MLRLight::PointLight:
		{
			light->GetInShapePosition(lightPosInShape);

			Scalar n, f;
			Cast_Object(MLRPointLight*, light)->GetFalloffDistance(n, f);

			Scalar One_Over_Falloff = 0.5f/f;

			for(i=0,j=0,k=0;i<len;i++,j += 3)
			{
				if((*testList)[i] == 0)
				{
					continue;
				}

				f = facePlanes[i].GetDistanceTo(lightPosInShape);
				
				lm = false;

				tooBig = 0;
				for(k=0;k<3;k++)
				{
					Vector3D vec(coords[index[k+j]]);
					vec-=lightPosInShape;

					(*lightMapUVs)[k][0] = vec.x * One_Over_Falloff;
					(*lightMapUVs)[k][1] = vec.z * One_Over_Falloff;

					falloff = 1.0f;

					if(
						(*lightMapUVs)[k][0] >= -0.5f && (*lightMapUVs)[k][0] <= 0.5f &&
						(*lightMapUVs)[k][1] >= -0.5f && (*lightMapUVs)[k][1] <= 0.5f

					) 
					{
						lm = true;
					}
					if(
						bigUV > 0.0f &&
						(
							(*lightMapUVs)[k][0] < -bigUV || (*lightMapUVs)[k][0] > bigUV ||
							(*lightMapUVs)[k][1] < -bigUV || (*lightMapUVs)[k][1] > bigUV
						)
					) 
					{
						tooBig++;
					}
				}

				if(tooBig==0 && (lm == true || CheckForBigTriangles(lightMapUVs, 3) == true))
				{
#if 0
					Vector3D vec(coords[index[j]]);
					SPEW(("micgaert", "\nvertex1 = %f,%f,%f", vec.x, vec.y, vec.z));
					vec = coords[index[j+1]];
					SPEW(("micgaert", "vertex2 = %f,%f,%f", vec.x, vec.y, vec.z));
					vec = coords[index[j+2]];
					SPEW(("micgaert", "vertex3 = %f,%f,%f", vec.x, vec.y, vec.z));
					vec = facePlanes[i].normal;
					SPEW(("micgaert", "normal = %f,%f,%f", vec.x, vec.y, vec.z));
					SPEW(("micgaert", "forward = %f,%f,%f", forward.x, forward.y, forward.z));
					SPEW(("micgaert", "distance = %f", f));
					SPEW(("micgaert", "light = %f,%f,%f", lightPosInShape.x, lightPosInShape.y, lightPosInShape.z));
					SPEW(("micgaert", "projection = %f,%f,%f", hitPoint.x, hitPoint.y, hitPoint.z));
#endif

					Scalar sq_falloff
						= falloff*falloff*light->GetIntensity();
					
					RGBAColor color;
					light->GetColor(color);

					color.red *= sq_falloff;
					color.green *= sq_falloff;
					color.blue *= sq_falloff;
					color.alpha = 1.0f;

					for(k=0;k<3;k++)
					{
						(*lightMapUVs)[k][0] += 0.5f;
						(*lightMapUVs)[k][1] += 0.5f;

						if(
							lightMap->AddIndex(addedIndicies++, addedVertices)==false ||
#if COLOR_AS_DWORD
							lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], GOSCopyColor(&color))==false
#else	//	COLOR_AS_DWORD
							lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], color)==false
#endif	//	COLOR_AS_DWORD
						)
						{
							return;
						}

						if(addedIndicies >= 252 && addedVertices > 0)
						{
							lightMap->AddMesh(referenceState.GetPriority()+1, addedVertices, addedIndicies);
							addedIndicies = 0;
							addedVertices = 0;
						}
		//				DEBUG_STREAM << k << " " << lightMapUVs[k][0] << " " << lightMapUVs[k][0] << "\n";
					}
				}
			}
		}
		break;
		case MLRLight::SpotLight:
		{
			int behindCount = 0, falloffCount = 0;

			light->GetInShapePosition(matrix);
			lightPosInShape = matrix;

			Scalar tanSpeadAngle = Cast_Object(MLRSpotLight*, light)->GetTanSpreadAngle();

#ifndef TOP_DOWN_ONLY
			matrix.GetLocalLeftInWorld(&left);
			matrix.GetLocalUpInWorld(&up);
			matrix.GetLocalForwardInWorld(&forward);
#else
			forward = UnitVector3D(0.0f, -1.0f, 0.0);
			up = UnitVector3D(1.0f, 0.0f, 0.0);
			left = UnitVector3D(0.0f, 0.0f, 1.0);
#endif

			Verify(Small_Enough(up*left));

			for(i=0,j=0,k=0;i<len;i++,j += 3)
			{
				behindCount = 0;
				falloffCount = 0;

				if((*testList)[i] == 0)
				{
					continue;
				}

				lm = false;

				if(!facePlanes[i].IsSeenBy(lightPosInShape))
				{
					continue;
				}

#if SPEW_AWAY
				Scalar maxX, maxZ;
				Scalar minX, minZ;

				minX = maxX = coords[index[j]].x;
				minZ = maxZ = coords[index[j]].z;

				if(minX>coords[index[j+1]].x)
				{
					minX = coords[index[j+1]].x;
				}
				if(minX>coords[index[j+2]].x)
				{
					minX = coords[index[j+2]].x;
				}
				if(minZ>coords[index[j+1]].z)
				{
					minZ = coords[index[j+1]].z;
				}
				if(minX>coords[index[j+2]].z)
				{
					minZ = coords[index[j+2]].z;
				}
				if(maxX<coords[index[j+1]].x)
				{
					maxX = coords[index[j+1]].x;
				}
				if(maxX<coords[index[j+2]].x)
				{
					maxX = coords[index[j+2]].x;
				}
				if(maxZ<coords[index[j+1]].z)
				{
					maxZ = coords[index[j+1]].z;
				}
				if(maxX<coords[index[j+2]].z)
				{
					maxZ = coords[index[j+2]].z;
				}

				if(lightPosInShape.x > minX && lightPosInShape.x < maxX && lightPosInShape.z > minZ && lightPosInShape.z < maxZ)
				{
					SPEW(("micgaert", "On Target !!"));
				}
#endif

				tooBig = 0;
				for(k=0;k<3;k++)
				{
					Vector3D vec;
					Scalar oneOver;

					vec.Subtract(coords[index[k+j]], lightPosInShape);

#ifndef TOP_DOWN_ONLY
					distance = (vec*forward);
#else
					distance = -vec.y;
#endif

#if SPEW_AWAY
					SPEW(("micgaert", "vertex%d = %f,%f,%f", k, coords[index[k+j]].x, coords[index[k+j]].y, coords[index[k+j]].z));
					SPEW(("micgaert", "distance = %f", distance));
#endif
					if(distance > SMALL)
					{
						if(Cast_Object(MLRInfiniteLightWithFalloff*, light)->GetFalloff(distance, falloff) == false)
						{
							falloffCount++;
						}
						(*lightMapSqFalloffs)[k] = falloff*falloff*light->GetIntensity();

						oneOver
#if 0
							= 1.0f/(2.0f*distance*tanSpeadAngle);
#else
							= OneOverApproximate(2.0f*distance*tanSpeadAngle);
#endif
					}
					else
					{
						behindCount++;
						oneOver = 1.0f/50.0f;
						(*lightMapSqFalloffs)[k] = 0.0f;
#if SPEW_AWAY
						SPEW(("micgaert", "Behind"));
#endif
					}

#ifndef TOP_DOWN_ONLY
					(*lightMapUVs)[k][0] = (left*vec) * oneOver;
					(*lightMapUVs)[k][1] = -(up*vec) * oneOver;
#else
					(*lightMapUVs)[k][0] = vec.x * oneOver;
					(*lightMapUVs)[k][1] = -vec.z * oneOver;
#endif

#if SPEW_AWAY
					SPEW(("micgaert", "uv%d = %f,%f", k, (*lightMapUVs)[k][0], (*lightMapUVs)[k][1]));
#endif
					if(
						(*lightMapUVs)[k][0] >= -0.5f && (*lightMapUVs)[k][0] <= 0.5f &&
						(*lightMapUVs)[k][1] >= -0.5f && (*lightMapUVs)[k][1] <= 0.5f

					) 
					{
						lm = true;
					}
					if(
						bigUV > 0.0f &&
						(
							(*lightMapUVs)[k][0] < -bigUV || (*lightMapUVs)[k][0] > bigUV ||
							(*lightMapUVs)[k][1] < -bigUV || (*lightMapUVs)[k][1] > bigUV
						)

					) 
					{
						tooBig++;
					}
				}
				if(	
					tooBig == 0 
					&& behindCount < 3  
					&& falloffCount < 3 
 					&& ((lm == true)	|| CheckForBigTriangles(lightMapUVs, 3) == true)
				)
				{
					for(k=0;k<3;k++)
					{
						RGBAColor color;

						color.red = (*lightMapSqFalloffs)[k];
						color.green = (*lightMapSqFalloffs)[k];
						color.blue = (*lightMapSqFalloffs)[k];
						color.alpha = 1.0f;

						(*lightMapUVs)[k][0] += 0.5f;
						(*lightMapUVs)[k][1] += 0.5f;

						if(
							lightMap->AddIndex(addedIndicies++, addedVertices)==false ||
#if COLOR_AS_DWORD
							lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], GOSCopyColor(&color))==false
#else	//	COLOR_AS_DWORD
							lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], color)==false
#endif	//	COLOR_AS_DWORD
						)
						{
							return;
						}

						if(addedIndicies >= 252 && addedVertices > 0)
						{
							lightMap->AddMesh(referenceState.GetPriority()+1, addedVertices, addedIndicies);
							addedIndicies = 0;
							addedVertices = 0;
						}
					}
	#if SPEW_AWAY
					SPEW(("micgaert", "See the Light !"));
	#endif
				}
	#if SPEW_AWAY
				Vector3D vec = facePlanes[i].normal;
				SPEW(("micgaert", "normal = %f,%f,%f", vec.x, vec.y, vec.z));
				SPEW(("micgaert", "forward = %f,%f,%f", forward.x, forward.y, forward.z));
				SPEW(("micgaert", "left = %f,%f,%f", left.x, left.y, left.z));
				SPEW(("micgaert", "up = %f,%f,%f", up.x, up.y, up.z));
				SPEW(("micgaert", "light = %f,%f,%f\n", lightPosInShape.x, lightPosInShape.y, lightPosInShape.z));
	#endif
			}
		}
		break;
#define TOP_DOWN_ONLY
		case MLRLight::ProjectLight:
		{
			MLRProjectLight *pLight = Cast_Object(MLRProjectLight*, light);
			
			light->GetInShapePosition(lightPosInShape);


			Scalar n, f;
			pLight->GetFalloffDistance(n, f);

			Scalar ray_distance = light->GetIntensity();

			UnitVector3D dir, rot;
			light->GetInShapeDirection(dir);

			lightPosInShape.AddScaled(lightPosInShape, dir, ray_distance);

			rot = dir;
			rot.y = 0.0f;

			rot.Normalize(rot);

			if(ray_distance <= SMALL)
			{
				break;
			}
//			Scalar One_Over_Falloff = 1.0f/(3.0f*f);
			Scalar oneOverRadius = 0.5f / (pLight->GetTanSpreadAngle() * ray_distance);
			
			if(pLight->GetFalloff(ray_distance, falloff))
			{
				for(i=0,j=0,k=0;i<len;i++,j += 3)
				{
					if((*testList)[i] == 0)
					{
						continue;
					}

/*
					f = dir*facePlanes[i].normal;
					
					if(f>0.0f)
					{
						continue;
					}
*/
					lm = false;

					tooBig = 0;
					for(k=0;k<3;k++)
					{
						Vector3D vec(coords[index[k+j]]);
						vec-=lightPosInShape;

						Scalar u, v;
						u = vec.x * oneOverRadius;
						v = vec.z * oneOverRadius;
						(*lightMapUVs)[k][0] = u*rot.z - v*rot.x;
						(*lightMapUVs)[k][1] = v*rot.z + u*rot.x;


						if(
							(*lightMapUVs)[k][0] >= -0.5f && (*lightMapUVs)[k][0] <= 0.5f &&
							(*lightMapUVs)[k][1] >= -0.25f && (*lightMapUVs)[k][1] <= 0.75f

						) 
						{
							lm = true;
						}
						if(
							bigUV > 0.0f &&
							(
								(*lightMapUVs)[k][0] < -bigUV || (*lightMapUVs)[k][0] > bigUV ||
								(*lightMapUVs)[k][1] < -bigUV || (*lightMapUVs)[k][1] > bigUV
							)
						) 
						{
							tooBig++;
						}
					}

					if(tooBig==0 && (lm == true || CheckForBigTriangles(lightMapUVs, 3) == true))
					{
	#if 0
						Vector3D vec(coords[index[j]]);
						SPEW(("micgaert", "\nvertex1 = %f,%f,%f", vec.x, vec.y, vec.z));
						vec = coords[index[j+1]];
						SPEW(("micgaert", "vertex2 = %f,%f,%f", vec.x, vec.y, vec.z));
						vec = coords[index[j+2]];
						SPEW(("micgaert", "vertex3 = %f,%f,%f", vec.x, vec.y, vec.z));
						vec = facePlanes[i].normal;
						SPEW(("micgaert", "normal = %f,%f,%f", vec.x, vec.y, vec.z));
						SPEW(("micgaert", "forward = %f,%f,%f", forward.x, forward.y, forward.z));
						SPEW(("micgaert", "distance = %f", f));
						SPEW(("micgaert", "light = %f,%f,%f", lightPosInShape.x, lightPosInShape.y, lightPosInShape.z));
						SPEW(("micgaert", "projection = %f,%f,%f", hitPoint.x, hitPoint.y, hitPoint.z));
	#endif

						Scalar sq_falloff
							= falloff*falloff;	//*light->GetIntensity();

						RGBAColor color;
						light->GetColor(color);

						color.red *= sq_falloff;
						color.green *= sq_falloff;
						color.blue *= sq_falloff;
						color.alpha = 1.0f;

						for(k=0;k<3;k++)
						{
							(*lightMapUVs)[k][0] += 0.5f;
							(*lightMapUVs)[k][1] += 0.25f;

							if(
								lightMap->AddIndex(addedIndicies++, addedVertices)==false ||
#if COLOR_AS_DWORD
								lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], GOSCopyColor(&color))==false
#else	//	COLOR_AS_DWORD
								lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], color)==false
#endif	//	COLOR_AS_DWORD
							)
							{
								return;
							}

							if(addedIndicies >= 252 && addedVertices > 0)
							{
								lightMap->AddMesh(referenceState.GetPriority()+1, addedVertices, addedIndicies);
								addedIndicies = 0;
								addedVertices = 0;
							}
						}

					}
				}
			}
		}
		break;
		case MLRLight::ShadowLight:
		{
			MLRShadowLight *shadow = Cast_Object(MLRShadowLight*, light);
			
			light->GetInShapePosition(lightPosInShape);

			UnitVector3D dir, rot;
			light->GetInShapeDirection(dir);
			rot = dir;
			rot.y = 0.0f;
			rot.Normalize(rot);

			//
			//--------------------------------------------------
			// If the light is at or below zero to us, ignore it
			//--------------------------------------------------
			//
			if(lightPosInShape.y <= SMALL)
			{
				break;
			}
			Scalar oneOverRadius = 0.5f / shadow->GetFalloffFar();

			//
			//------------------------------------------------------------------------
			// Look at each polygon in the mesh to see if it could accept the lightmap
			//------------------------------------------------------------------------
			//
			for(i=0,j=0,k=0;i<len;i++,j += 3)
			{
				if((*testList)[i] == 0)
					continue;
				Scalar f = dir*facePlanes[i].normal;
				if(f>=0.0f)
					continue;

				lm = false;
				tooBig = 0;
				for(k=0;k<3;k++)
				{
					Vector3D vec(coords[index[k+j]]);
					vec -= lightPosInShape;

					Scalar u, v;
					u = vec.x * oneOverRadius;
					v = vec.z * oneOverRadius;
					(*lightMapUVs)[k][0] = u*rot.z - v*rot.x;
					(*lightMapUVs)[k][1] = v*rot.z + u*rot.x;

					if(
						(*lightMapUVs)[k][0] >= -0.5f && (*lightMapUVs)[k][0] <= 0.5f &&
						(*lightMapUVs)[k][1] >= -0.5f && (*lightMapUVs)[k][1] <= 0.5f

					) 
					{
						lm = true;
					}
					if(
						bigUV > 0.0f &&
						(
							(*lightMapUVs)[k][0] < -bigUV || (*lightMapUVs)[k][0] > bigUV ||
							(*lightMapUVs)[k][1] < -bigUV || (*lightMapUVs)[k][1] > bigUV
						)
					) 
					{
						tooBig++;
					}
				}

				if(tooBig==0 && (lm == true || CheckForBigTriangles(lightMapUVs, 3) == true))
				{
#if 0
					Vector3D vec(coords[index[j]]);
					SPEW(("micgaert", "\nvertex1 = %f,%f,%f", vec.x, vec.y, vec.z));
					vec = coords[index[j+1]];
					SPEW(("micgaert", "vertex2 = %f,%f,%f", vec.x, vec.y, vec.z));
					vec = coords[index[j+2]];
					SPEW(("micgaert", "vertex3 = %f,%f,%f", vec.x, vec.y, vec.z));
					vec = facePlanes[i].normal;
					SPEW(("micgaert", "normal = %f,%f,%f", vec.x, vec.y, vec.z));
					SPEW(("micgaert", "forward = %f,%f,%f", forward.x, forward.y, forward.z));
					SPEW(("micgaert", "distance = %f", f));
					SPEW(("micgaert", "light = %f,%f,%f", lightPosInShape.x, lightPosInShape.y, lightPosInShape.z));
					SPEW(("micgaert", "projection = %f,%f,%f", hitPoint.x, hitPoint.y, hitPoint.z));
#endif
					RGBAColor color;
					color = RGBAColor::White;
					color.alpha = shadow->GetIntensity();

					for(k=0;k<3;k++)
					{
						(*lightMapUVs)[k][0] += 0.5f;
						(*lightMapUVs)[k][1] += 0.5f;

						if(
							lightMap->AddIndex(addedIndicies++, addedVertices)==false ||
#if COLOR_AS_DWORD
							lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], GOSCopyColor(&color))==false
#else	//	COLOR_AS_DWORD
							lightMap->AddVertex(addedVertices++, coords[index[k+j]], (*lightMapUVs)[k], color)==false
#endif	//	COLOR_AS_DWORD
						)
						{
							return;
						}

						if(addedIndicies >= 252 && addedVertices > 0)
						{
							lightMap->AddMesh(referenceState.GetPriority()+1, addedVertices, addedIndicies);
							addedIndicies = 0;
							addedVertices = 0;
						}

					}

				}
			}
		}
		break;
		default:
			STOP(("MLR_Terrain2::LightMapLighting: What you want me to do ?"));
		break;
	}

	if(addedIndicies && addedVertices)
	{
		lightMap->AddMesh(referenceState.GetPriority()+1, addedVertices, addedIndicies);
	}
}