#include "MLRHeaders.hpp"
#include <MLR\GOSVertex3UV.hpp>

#if defined(TRACE_ENABLED) && defined(MLR_TRACE)
	BitTrace *MLR_BumpyWater_Clip;
#endif

extern DWORD gEnableLightMaps;

//#############################################################################
//############################## Bumpmaped watersurface  ######################
//#############################################################################

DynamicArrayOf<Vector2DScalar>
	*MLR_BumpyWater::texCoords2, *MLR_BumpyWater::texCoords3;
DynamicArrayOf<Vector2DScalar>
	*MLR_BumpyWater::clipExtraTexCoords2, *MLR_BumpyWater::clipExtraTexCoords3;

Stuff::UnitVector3D
	MLR_BumpyWater::sunLight;


MLR_BumpyWater::ClassData*
	MLR_BumpyWater::DefaultData = NULL;

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

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

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

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

	clipExtraTexCoords2 = new DynamicArrayOf<Vector2DScalar> (2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipExtraTexCoords2);

	clipExtraTexCoords3 = new DynamicArrayOf<Vector2DScalar> (2*Limits::Max_Number_Vertices_Per_Mesh);
	Register_Object(clipExtraTexCoords3);

	Vector3D sl(1.0f, -1.0f, -1.0f);

	sl.Normalize(sl);

	sunLight = sl;

	#if defined(TRACE_ENABLED) && defined(MLR_TRACE)
		MLR_BumpyWater_Clip = new BitTrace("MLR_BumpyWater_Clip");
		Register_Object(MLR_BumpyWater_Clip);
	#endif
}

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

	Unregister_Object(texCoords2);
	delete texCoords2;

	Unregister_Object(texCoords3);
	delete texCoords3;

	Unregister_Object(clipExtraTexCoords2);
	delete clipExtraTexCoords2;

	Unregister_Object(clipExtraTexCoords3);
	delete clipExtraTexCoords3;

	#if defined(TRACE_ENABLED) && defined(MLR_TRACE)
		Unregister_Object(MLR_BumpyWater_Clip);
		delete MLR_BumpyWater_Clip;
	#endif
}

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

	referenceState.SetLightingMode(MLRState::LightMapLightingMode);

	facePlanes.SetLength(0);

	if (!dataStore)
		dataStore = new DataStorage;
	Check_Pointer(dataStore);

	if (!colors.GetLength())
	{
		#if COLOR_AS_DWORD
			dataStore->colors.AssignValue(0xFFFFFFFF, coords.GetLength());
		#else
			dataStore->colors.AssignValue(RGBAColor::White, coords.GetLength());
		#endif
		colors.AssignData(dataStore->colors.GetData(), dataStore->colors.GetLength());
	}
	Verify(colors.GetData() == dataStore->colors.GetData());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MLR_BumpyWater::MLR_BumpyWater(ClassData *class_data):
	MLR_I_C_TMesh(class_data)
{
	Check_Pointer(this);

	facePlanes.SetLength(0);

	if (!dataStore)
		dataStore = new DataStorage;
	Check_Pointer(dataStore);

	if (!colors.GetLength())
	{
		#if COLOR_AS_DWORD
			dataStore->colors.AssignValue(0xFFFFFFFF, coords.GetLength());
		#else
			dataStore->colors.AssignValue(RGBAColor::White, coords.GetLength());
		#endif
		colors.AssignData(dataStore->colors.GetData(), dataStore->colors.GetLength());
	}
	Verify(colors.GetData() == dataStore->colors.GetData());
}

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

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

	gos_PushCurrentHeap(PrimitiveHeap);
	MLR_BumpyWater *water = new MLR_BumpyWater(DefaultData, stream, version);
	gos_PopCurrentHeap();

	return water;
}


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

	MLR_I_C_TMesh::Save(stream);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_BumpyWater::Copy(MLR_Water *tMesh)
{
	Check_Object(this);
	Check_Object(tMesh);

	int len;
	const BYTE *_index;
	const Point3D *_coords;
	const Vector2DScalar *_texCoords;
	const ColorType *_colors;

	if(tMesh->dataStore!=NULL && tMesh->dataStore->coords.GetLength()>0)
	{
		tMesh->GetCoordData(&_coords, &len);
		dataStore->coords.SetLength(len);
		Mem_Copy(dataStore->coords.GetData(), _coords, len*sizeof(Point3D), dataStore->coords.GetSize());
		SetCoordData(dataStore->coords.GetData(), len);
	}
	else
	{
		tMesh->GetCoordData(&_coords, &len);
		SetCoordData(_coords, len);
	}

	if(tMesh->dataStore!=NULL && tMesh->dataStore->index.GetLength()>0)
	{
		tMesh->GetIndexData(&_index, &len);
		dataStore->index.SetLength(len);
		Mem_Copy(dataStore->index.GetData(), _index, len*sizeof(BYTE), dataStore->index.GetSize());
		SetIndexData(dataStore->index.GetData(), len);
	}
	else
	{
		tMesh->GetIndexData(&_index, &len);
		SetIndexData(_index, len);
	}

	SetSubprimitiveLengths(NULL, tMesh->GetNumPrimitives());

	if(tMesh->dataStore!=NULL && tMesh->dataStore->texCoords.GetLength()>0)
	{
		tMesh->GetTexCoordData(&_texCoords, &len);
		dataStore->texCoords.SetLength(len);
		Mem_Copy(dataStore->texCoords.GetData(), _texCoords, len*sizeof(Vector2DScalar), dataStore->texCoords.GetSize());
		SetTexCoordData(dataStore->texCoords.GetData(), len);
	}
	else
	{
		tMesh->GetTexCoordData(&_texCoords, &len);
		SetTexCoordData(_texCoords, len);
	}

	texCoords2->SetLength(len);
	texCoords3->SetLength(len);

	referenceState = tMesh->GetReferenceState();
	referenceState.SetBumpMapOn();

	visibleIndexedVerticesKey = false;

	if(tMesh->dataStore!=NULL && tMesh->dataStore->colors.GetLength()>0)
	{
		tMesh->GetColorData(&_colors, &len);
		dataStore->colors.SetLength(len);
		Mem_Copy(dataStore->colors.GetData(), _colors, len*sizeof(ColorType), dataStore->colors.GetSize());
		SetColorData(dataStore->colors.GetData(), len);
	}
	else
	{
		tMesh->GetColorData(&_colors, &len);
		SetColorData(_colors, len);
	}

	facePlanes.SetLength(0);
}

extern int tCounter;


inline DWORD ConvertVectorToColor(UnitVector3D &vect)
{
int r,g,b;

r=(int)(vect.x*0x7f+0x80);	//	x
g=(int)(vect.z*0x7f+0x80);	//	z
b=(int)(vect.y*0x7f+0x80);	//	y
Verify(r<0x100 && g<0x100 && b<0x100);



return   ((0xff&0xff)<<24)|
		 ((r&0xff)<<16)|
		 ((g&0xff)<<8)|
		 ((b&0xff)<<0);
/*
return  0xff808080 +
				((0x7f & (int)(vect.x*127))<<0)+
				((0x7f & (int)(vect.z*127))<<8);
				((0x7f & (int)(vect.y*127))<<16);


return 0xffffffff;
*/
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MLR_BumpyWater::FindBackFace(const Point3D& u, const Normal3D& n)
{
	Check_Object(this);

	Start_Timer(Find_Backface_Time);

	BYTE *iPtr;

	Vector3D v;
	UnitVector3D vn, hn;

	if(numOfTriangles <= 0)
	{
		visible = 0;

		return 0;
	}

	ColorType *cols = dataStore->colors.GetData();
	int i, len = dataStore->colors.GetLength();

	for(i=0;i<len;i++)
	{
		v.Subtract(coords[i], u);

		vn.Normalize(v);

		hn.x = -(vn.x + sunLight.x)*0.5f;
		hn.y = -(vn.y + sunLight.y)*0.5f;
		hn.z = -(vn.z + sunLight.z)*0.5f;

//		Scalar intens =  vn * sunLight;

		#if COLOR_AS_DWORD
			cols[i] = ConvertVectorToColor(hn);

		#else
		#endif
	}

	len = texCoords.GetLength();
	
	Scalar r = 1.0f;

	if(MLRState::GetCanBumpEnvMap() != 0)
	{
		r = bumpRatio;
	}
	else if(MLRState::GetCanBumpDotMap())
	{
		r = bumpNormRatio;
	}

	for(i=0;i<len;i++)
	{
		(*texCoords2)[i][0] = r*texCoords[i][0];
		(*texCoords2)[i][1] = r*texCoords[i][1];

		(*texCoords3)[i][0] = envRatio*texCoords[i][0];
		(*texCoords3)[i][1] = envRatio*texCoords[i][1];
	}

#if 0
	MLRTexture *tex = (*MLRTexturePool::Instance)[referenceState.GetTextureHandle()];
	tex->SetAnimateTexture(true);

	AffineMatrix4D &textureMatrix = tex->GetTextureMatrix();
	textureMatrix(3, 0) = tCounter * 0.0004f;
	textureMatrix(3, 1) = tCounter * 0.0005f;

	tex = (*MLRTexturePool::Instance)[textureNorm];
	tex->SetAnimateTexture(true);

	AffineMatrix4D &textureMatrix2 = tex->GetTextureMatrix();
	textureMatrix2(3, 0) = -tCounter * 0.0002f;
	textureMatrix2(3, 1) = tCounter * 0.0004f;
#endif

	iPtr = testList->GetData();
	memset(iPtr, 1, numOfTriangles);

	visible = 1;

	FindVisibleVertices();

	passes = 1;

	Stop_Timer(Find_Backface_Time);

	return 1;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
int
	MLR_BumpyWater::GetNumPasses()
{
	Check_Object(this);

	return 2;
}
*/

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#define I_SAY_YES_TO_COLOR
#define I_SAY_YES_TO_BUMP_TEXTURE

#undef I_SAY_YES_TO_DETAIL_TEXTURES
#undef I_SAY_YES_TO_TERRAIN2
#undef I_SAY_YES_TO_DUAL_TEXTURES
#undef I_SAY_YES_TO_TERRAIN
#undef I_SAY_YES_TO_LIGHTING
#undef I_SAY_YES_TO_WATER
#undef CALCULATEuvONtheFLY

#define CLASSNAME MLR_BumpyWater
#define CLASSNAMENOCOLOR MLR_I_TMesh

#if defined(TRACE_ENABLED) && defined(MLR_TRACE)
	#define SET_MLR_TMESH_CLIP() MLR_BumpyWater_Clip->Set()
	#define CLEAR_MLR_TMESH_CLIP() MLR_BumpyWater_Clip->Clear()
#else
	#define SET_MLR_TMESH_CLIP()
	#define CLEAR_MLR_TMESH_CLIP()
#endif

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

#include <MLR\MLRTriangleClipping.hpp>

#undef I_SAY_YES_TO_COLOR
#undef I_SAY_YES_TO_BUMP_TEXTURE

#undef CLASSNAME
#undef CLASSNAMENOCOLOR

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MLR_BumpyWater::LightMapLighting(MLRLight *light)
{
	MLRLightMap *lightMap = light->GetLightMap();
	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;

	Scalar waterHeight = coords[0].z;

	BYTE addedVertices = 0;
	BYTE addedIndicies = 0;

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

	switch(light->GetLightType())
	{
		case MLRLight::PointLight:
		{
			Check_Object(lightMap);

			light->GetInShapePosition(lightPosInShape);

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

			Scalar One_Over_Falloff = 1.0f/f;

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

				f = lightPosInShape.z - waterHeight;
				
				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 = Vector3D(0.0f, 1.0f, 0.0f);
					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;
						}
					}
				}
			}
		}
		break;
		case MLRLight::SpotLight:
		{
			int behindCount = 0, falloffCount = 0;

			Check_Object(lightMap);

			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 1
				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 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
#else
				if(tooBig != 0)
				{
					lightMap->SetPolygonMarker(1);
					lightMap->AddUShort(3);

					for(k=0;k<3;k++)
					{
						lightMap->AddCoord(coords[index[k+j]]);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddColor(RGBAColor(0.0f, 0.0f, 0.5f, 1.0f));
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddUVs(0.5f, 0.5f);
		//				DEBUG_STREAM << k << " " << lightMapUVs[k][0] << " " << lightMapUVs[k][0] << "\n";
					}
				} 
				else if(behindCount != 0)
				{
					lightMap->SetPolygonMarker(1);
					lightMap->AddUShort(3);

					for(k=0;k<3;k++)
					{
						lightMap->AddCoord(coords[index[k+j]]);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddColor(RGBAColor(0.5f, 0.0f, 0.0f, 1.0f));
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddUVs(0.5f, 0.5f);
		//				DEBUG_STREAM << k << " " << lightMapUVs[k][0] << " " << lightMapUVs[k][0] << "\n";
					}
				} 
				else	if(behindCount == 0 && (lm == true || CheckForBigTriangles(&lightMapUVs, 3) == true) )
				{
					lightMap->SetPolygonMarker(1);
					lightMap->AddUShort(3);

					for(k=0;k<3;k++)
					{
						lightMap->AddCoord(coords[index[k+j]]);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddColor(lightMapSqFalloffs[k], lightMapSqFalloffs[k], lightMapSqFalloffs[k], 1.0f);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddUVs(lightMapUVs[k][0]+0.5f, lightMapUVs[k][1]+0.5f);
		//				DEBUG_STREAM << k << " " << lightMapUVs[k][0] << " " << lightMapUVs[k][0] << "\n";
					}

				}
				else if(CheckForBigTriangles(&lightMapUVs, 3) == false)
				{
					lightMap->SetPolygonMarker(1);
					lightMap->AddUShort(3);

					for(k=0;k<3;k++)
					{
						lightMap->AddCoord(coords[index[k+j]]);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddColor(errorColor);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddUVs(0.5f, 0.5f);
		//				DEBUG_STREAM << k << " " << lightMapUVs[k][0] << " " << lightMapUVs[k][0] << "\n";
					}
				}
#endif
			}
		}
		break;
		case MLRLight::ProjectLight:
#if 1
		{
			Check_Object(lightMap);

			MLRProjectLight *pLight = Cast_Object(MLRProjectLight*, light);

			light->GetInShapePosition(lightPosInShape);

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

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

//			Scalar One_Over_Falloff = 1.0f/(3.0f*f);
			if(lightPosInShape.y <= SMALL)
			{
				break;
			}
			Scalar oneOverRadius = 1.0f / (pLight->GetTanSpreadAngle() * lightPosInShape.y);
			
			if(pLight->GetFalloff(lightPosInShape.y, falloff))
			{
				for(i=0,j=0,k=0;i<len;i++,j += 3)
				{
					if((*testList)[i] == 0)
					{
						continue;
					}
				
					if(dir.y>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

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

		{
			int behindCount = 0, falloffCount = 0;

			light->GetInShapePosition(lightPosInShape);

			MLRProjectLight *pLight = Cast_Object(MLRProjectLight*, light);
			static Point3D texUV[Limits::Max_Number_Vertices_Per_Mesh];

			Matrix4D m4;
			m4.Multiply(pLight->GetInversMatrix(), pLight->GetProjectMatrix());

			Vector4D v4;

			for(i=0;i<coords.GetLength();i++)
			{
				v4.Multiply(coords[i], m4);
				Scalar rhw = 1.0f/v4.w;

				texUV[i].x = v4.x*rhw;
				texUV[i].y = v4.y*rhw;
				texUV[i].z = v4.z*rhw;
			}

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

				int tooBig = 0;
				for(k=0;k<3;k++)
				{
					(*lightMapUVs)[k][0] = texUV[index[k+j]][0];
					(*lightMapUVs)[k][1] = texUV[index[k+j]][1];

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

					) 
					{
						tooBig++;
					}
*/
					lm = true;
				}

				if(	
					tooBig == 0 &&
					behindCount == 0 && 
					falloffCount < 3 &&
					(lm == true || CheckForBigTriangles(lightMapUVs, 3) == true)
					)
				{
					lightMap->SetPolygonMarker(1);
					lightMap->AddUShort(3);

					for(k=0;k<3;k++)
					{
						lightMap->AddCoord(coords[index[k+j]]);
					}
					for(k=0;k<3;k++)
					{
//						lightMap->AddColor((*lightMapSqFalloffs)[k], (*lightMapSqFalloffs)[k], (*lightMapSqFalloffs)[k], 1.0f);
						lightMap->AddColor(1.0f, 1.0f, 1.0f, 1.0f);
					}
					for(k=0;k<3;k++)
					{
						lightMap->AddUVs((*lightMapUVs)[k][0], (*lightMapUVs)[k][1]);
//						lightMap->AddUVs((*lightMapUVs)[k][0]+0.5f, (*lightMapUVs)[k][1]+0.5f);
//						DEBUG_STREAM << k << " " << lightMapUVs[k][0] << " " << lightMapUVs[k][0] << "\n";
					}
				}
			}
		}
#endif
		break;
		case MLRLight::ShadowLight:
		break;
		default:
			STOP(("MLR_BumpyWater::LightMapLighting: What you want me to do ?"));
		break;
	}

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



void
	MLR_BumpyWater::FindFacePlanes()
{
	Check_Object(this); 

	int i, j, numPrimitives = GetNumPrimitives();
	Vector3D v;

	Verify(index.GetLength() > 0);

	for(i=0,j=0;i<numPrimitives;++i,j+=3)
	{
		facePlanes[i].BuildPlane(
			coords[index[j]],
			coords[index[j+1]],
			coords[index[j+2]]
		);

		;
	}
}

