#include <MLR\MLRHeaders.hpp>

// the clipper is the "desciption" of the view frustrum
MLRClipper *theClipper;

// contains all necessary information to draw a shape
DrawShapeInformation *drawShapeInfo[2];

LinearMatrix4D *shapeOrigins, *invertedShapeOrigin;

// contains all necessary information to draw an effect
DrawEffectInformation *drawEffectInfo;
LinearMatrix4D *effectOrigins, *invertedEffectOrigin;

LinearMatrix4D cameraOrigin;

YawPitchRange Camera_Direction(0.0f, 0.0f, -10.0f);
Vector2DOf<Scalar> Camera_Shift(0.0f, 0.0f);
Vector3D toBeScaled(2.0f, 2.0f, 2.0f);

MLRState default_state, shapeState[2];

Matrix4D cameraToClip;

RGBAColor fogColor(0.2f, 0.2f, 0.2f, 1.0f);
RGBAColor bgColor(0.1f, 0.1f, 0.1f, 1.0f);

Point3D cardTestPoints[16];
RGBAColor cardTestColors[4];
Vector2DScalar cardTestUVs[16];

Point3D lineTestPoints[24];
RGBAColor lineTestColors[24];

Scalar *pointCloudTest = NULL;
Point3D nGonTestPoints[28];
RGBAColor nGonTestColors[8];

DynamicArrayOf<MLRLight*> sceneLights;

#if 0
MLRPointLight *firstLight = NULL;
#else
//MLRSpotLight *firstLight = NULL;
MLRProjectLight *firstLight = NULL;
#endif
MLRLightMap	*firstLightMap = NULL;

// This function creates out of a frustum a camera-to-clip matrix
void
	SetPerspective(
		Scalar near_clip,
		Scalar far_clip,
		Scalar left_clip,
		Scalar right_clip,
		Scalar top_clip,
		Scalar bottom_clip,
		Matrix4D& cameraToClip
	)
{
	Verify(far_clip - near_clip > SMALL);
	Verify(left_clip - right_clip > SMALL);
	Verify(top_clip - bottom_clip > SMALL);

	//
	//-------------------------------------------------------
	// Calculate the horizontal, vertical, and forward ranges
	//-------------------------------------------------------
	//
	Scalar horizontal_range = APPLY_LEFT_SIGN(1.0f) / (left_clip - right_clip);
	Scalar vertical_range = APPLY_UP_SIGN(1.0f) / (top_clip - bottom_clip);
	Scalar depth_range = APPLY_FORWARD_SIGN(1.0f) / (far_clip - near_clip);

	//
	//------------------------------------------------------------------------
	// Set up the camera to clip matrix.  This matrix takes camera space
	// coordinates and maps them into a homogeneous culling space where valid
	// X, Y, and Z axis values (when divided by W) will all be between 0 and 1
	//------------------------------------------------------------------------
	//
	cameraToClip(LEFT_AXIS, LEFT_AXIS) = near_clip * horizontal_range;
	cameraToClip(LEFT_AXIS, UP_AXIS) = 0.0f;
	cameraToClip(LEFT_AXIS, FORWARD_AXIS) = 0.0f;
	cameraToClip(LEFT_AXIS, 3) = 0.0f;

	cameraToClip(UP_AXIS, LEFT_AXIS) = 0.0f;
	cameraToClip(UP_AXIS, UP_AXIS) = near_clip * vertical_range;
	cameraToClip(UP_AXIS, FORWARD_AXIS) = 0.0f;
	cameraToClip(UP_AXIS, 3) = 0.0f;

	cameraToClip(FORWARD_AXIS, LEFT_AXIS) = -right_clip * horizontal_range;
	cameraToClip(FORWARD_AXIS, UP_AXIS) = -bottom_clip * vertical_range;
	cameraToClip(FORWARD_AXIS, FORWARD_AXIS) = far_clip * depth_range;
	cameraToClip(FORWARD_AXIS, 3) = 1.0f;

	cameraToClip(3, LEFT_AXIS) = 0.0f;
	cameraToClip(3, UP_AXIS) = 0.0f;
	cameraToClip(3, FORWARD_AXIS) = -far_clip * near_clip * depth_range;
	cameraToClip(3, 3) = 0.0f;
}

// This function creates out of a frustum a world-to-clip matrix
void
	SetPerspective(
		Scalar near_clip,
		Scalar far_clip,
		const Radian &horizontal_fov,
		Scalar height_to_width,
		Matrix4D& cameraToClip
	)
{
	Verify(far_clip - near_clip > SMALL);
	Verify(horizontal_fov > SMALL);
	Verify(height_to_width > SMALL);

	//
	//-------------------------------------------------------------
	// Calculate the near plane offsets to the side culling planes
	//-------------------------------------------------------------
	//
	Scalar width = (Scalar)(near_clip * tan(horizontal_fov*0.5f));
	Scalar height = width * height_to_width;
	SetPerspective(near_clip, far_clip, width, -width, height, -height, cameraToClip);
}

// in the next section the data for several effects gets prepared
Stuff::Point3D pointCenter[4] = {
#if 0
	Stuff::Point3D(1.10f, 1.30f, 1.10f),
	Stuff::Point3D(1.20f, 1.20f, 1.20f),
	Stuff::Point3D(1.30f, 1.10f, 1.30f),
	Stuff::Point3D(1.40f, 1.00f, 1.40f)
#else
	Stuff::Point3D(0.0f, 0.0f, 0.0f),
	Stuff::Point3D(0.0f, 0.0f, 0.0f),
	Stuff::Point3D(0.0f, 0.0f, 0.0f),
	Stuff::Point3D(0.0f, 0.0f, 0.0f)
#endif
};

void InitCardCloudTest()
{
	int i, j;
	for(i=0;i<4;i++)
	{
		cardTestColors[i] = RGBAColor((i%2)*0.5f,i*0.25f,(3-i)*0.25f,1.0f);

		cardTestUVs[4*i] = Vector2DScalar(0.0f, 0.0f);
		cardTestUVs[4*i+1] = Vector2DScalar(0.0f, 1.0f);
		cardTestUVs[4*i+2] = Vector2DScalar(1.0f, 1.0f);
		cardTestUVs[4*i+3] = Vector2DScalar(1.0f, 0.0f);

		for(j=0;j<4;j++)
		{
			cardTestPoints[4*i+j] = 
				Point3D(
					Random::GetFraction()-0.5f,
					Random::GetFraction()-0.5f,
					Random::GetFraction()-0.5f
				);
		}
	}
}

void InitNGonCloudTest()
{
	int i, j, num = 7;
	for(i=0;i<4;i++)
	{
		nGonTestColors[2*i] = RGBAColor((i%2)*0.5f,i*0.25f,(3-i)*0.25f,1.0f);
		nGonTestColors[2*i+1] = RGBAColor((i%2)*0.5f,(3-i)*0.25f,i*0.25f,1.0f);

		nGonTestPoints[num*i] = Point3D(0.0f, 0.0f, 0.0f);

		for(j=1;j<num-1;j++)
		{
			nGonTestPoints[num*i+j] = 
				Point3D(
					Random::GetFraction()-0.5f,
					Random::GetFraction()-0.5f,
					Random::GetFraction()-0.5f
				);

			nGonTestPoints[num*i] += cardTestPoints[num*i+j];
		}
		nGonTestPoints[num*i].Multiply(cardTestPoints[num*i], 1.0f/5);
		nGonTestPoints[num*(i+1) - 1] = nGonTestPoints[num*i + 1];
	}
}

void InitPointCloudTest()
{
	pointCloudTest = new Scalar [29];
	Register_Pointer(pointCloudTest);

	*(int *)pointCloudTest = 4;

	pointCloudTest[1] = 1.00f; pointCloudTest[2] = 1.00f; pointCloudTest[3] = 10.0f;
	pointCloudTest[4] = 1.10f; pointCloudTest[5] = 1.10f; pointCloudTest[6] = 11.0f;
	pointCloudTest[7] = 1.20f; pointCloudTest[8] = 1.20f; pointCloudTest[9] = 12.0f;
	pointCloudTest[10] = 1.30f; pointCloudTest[11] = 1.30f; pointCloudTest[12] = 11.0f;

	pointCloudTest[13] = 0.0f; pointCloudTest[14] = 1.0f; pointCloudTest[15] = 1.0f; pointCloudTest[16] = 1.0f;
	pointCloudTest[17] = 1.0f; pointCloudTest[18] = 0.0f; pointCloudTest[19] = 1.0f; pointCloudTest[20] = 1.0f;
	pointCloudTest[21] = 0.0f; pointCloudTest[22] = 1.0f; pointCloudTest[23] = 0.0f; pointCloudTest[24] = 1.0f;
	pointCloudTest[25] = 1.0f; pointCloudTest[26] = 1.0f; pointCloudTest[27] = 0.0f; pointCloudTest[28] = 1.0f;
}

void InitLineCloudTest()
{
	lineTestPoints[0] = Point3D(-1.0, -1.0, -1.0); lineTestPoints[1] = Point3D(1.0, -1.0, -1.0);
	lineTestPoints[2] = Point3D(-1.0, -1.0, -1.0); lineTestPoints[3] = Point3D(-1.0, 1.0, -1.0);
	lineTestPoints[4] = Point3D(-1.0, -1.0, -1.0); lineTestPoints[5] = Point3D(-1.0, -1.0, 1.0);
	lineTestPoints[6] = Point3D(1.0, -1.0, -1.0); lineTestPoints[7] = Point3D(1.0, -1.0, 1.0);
	lineTestPoints[8] = Point3D(1.0, -1.0, -1.0); lineTestPoints[9] = Point3D(1.0, 1.0, -1.0);
	lineTestPoints[10] = Point3D(1.0, -1.0, 1.0); lineTestPoints[11] = Point3D(-1.0, -1.0, 1.0);
	lineTestPoints[12] = Point3D(-1.0, 1.0, -1.0); lineTestPoints[13] = Point3D(-1.0, 1.0, 1.0);
	lineTestPoints[14] = Point3D(-1.0, -1.0, 1.0); lineTestPoints[15] = Point3D(-1.0, 1.0, 1.0);
	lineTestPoints[16] = Point3D(1.0, -1.0, 1.0); lineTestPoints[17] = Point3D(1.0, 1.0, 1.0);
	lineTestPoints[18] = Point3D(1.0, 1.0, -1.0); lineTestPoints[19] = Point3D(1.0, 1.0, 1.0);
	lineTestPoints[20] = Point3D(-1.0, 1.0, 1.0); lineTestPoints[21] = Point3D(1.0, 1.0, 1.0);
	lineTestPoints[22] = Point3D(-1.0, 1.0, -1.0); lineTestPoints[23] = Point3D(1.0, 1.0, -1.0);


	for(int i=0;i<24;i++)
	{
		lineTestColors[i] = RGBAColor (
			Random::GetFraction(),
			Random::GetFraction(),
			Random::GetFraction(),
			1.0f
		);
	}

}
// end of effect initialization

#undef TRACE_ON

void __stdcall InitializeGameEngine()
{
//	Memory_Registration_Enabled=FALSE;
//	Armor_Level=0;
	
	//
	//---------------------
	// Initialize libraries
	//---------------------
	//
	Stuff::InitializeClasses();
	
	MidLevelRenderer::InitializeClasses();

	InitPointCloudTest();
	InitCardCloudTest();
	InitNGonCloudTest();
	InitLineCloudTest();

	//
	//------------------------
	// Set up the texture pool
	//------------------------
	//
	gos_PushCurrentHeap(MidLevelRenderer::Heap);

	TGAFilePool *pool = new TGAFilePool("Assets\\Graphics\\");
	Register_Object(pool);
	MLRTexturePool::Instance = new MLRTexturePool(pool);

	Check_Object(MLRTexturePool::Instance);

	Register_Object(MLRTexturePool::Instance);

	MLRTexturePool::Instance->Add("t1");
	MLRTexturePool::Instance->Add("t1", 8);
	MLRTexturePool::Instance->Add("t1", 4);

	MLRTexturePool::Instance->Add("t2");
	MLRTexturePool::Instance->Add("t3");
	MLRTexturePool::Instance->Add("t4");
	MLRTexturePool::Instance->Add("t5");
	MLRTexturePool::Instance->Add("texture");

	MLRTexturePool::Instance->Add("damage");

	MLRTexturePool::Instance->LoadImages();

	default_state.SetBackFaceOn();
	default_state.SetDitherOn();
	default_state.SetTextureCorrectionOn();
	default_state.SetZBufferCompareOn();
	default_state.SetZBufferWriteOn();

	default_state.SetFilterMode(MLRState::BiLinearFilterMode);

	default_state.SetFogMode(1);

	//
	//--------------------
	// Set up a test light
	//--------------------
	//
	static Stuff::LinearMatrix4D lightToWorld = Stuff::LinearMatrix4D::Identity;
	lightToWorld.BuildTranslation(Point3D(-5.0f, 0.0f, -5.0f));
	lightToWorld.BuildRotation(EulerAngles(0.0f, Pi_Over_4, 0.0f));

	sceneLights.SetLength(1);

#if 0
	firstLight = new MLRPointLight;
#else
	Degree degree(5);
//	firstLight = new MLRSpotLight;
//	firstLight->SetSpreadAngle(degree);
	firstLight = new MLRProjectLight;
	firstLight->InitLineTextureProjection(lineTestPoints, lineTestColors);
#endif
	Register_Object(firstLight);
	sceneLights[0] = firstLight;
	firstLight->SetLightToWorldMatrix(lightToWorld);

	MLRTexture *texture = MLRTexturePool::Instance->Add("lightspot");
	Check_Object(texture);

	firstLightMap = new MLRLightMap(texture);
	Register_Object(firstLightMap);
	firstLight->SetLightMap(firstLightMap);
	
// set up a test cube
#if 1
	#if 0 // different color schemas for the 8 vertices
		RGBAColor colors[8] = {
			RGBAColor(0, 0, 0, 1.0f),
			RGBAColor(0, 0, 0.5f, 1.0f),
			RGBAColor(0, 0.5f, 0, 1.0f),
			RGBAColor(0, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0, 0, 1.0f),
			RGBAColor(0.5f, 0, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f)
		};
	#else
/*
		RGBAColor colors[8] = {
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f),
			RGBAColor(0.5f, 0.5f, 0.5f, 1.0f)
		};
*/
	#endif

	#if 0	// normals only for classic lit cubes
		Vector3D normals[8] = {
			Vector3D(1.0, -1.0, 1.0),
			Vector3D(-1.0, -1.0, 1.0),
			Vector3D(1.0, -1.0, -1.0),
			Vector3D(0.0, -1.0, -1.0),
			Vector3D(-1.0, 1.0, 1.0),
			Vector3D(1.0, 1.0, 1.0),
			Vector3D(1.0, 1.0, -1.0),
			Vector3D(-1.0, 1.0, -1.0)
		};
	#endif

	MLRState state;

	// create 2 states for multi textured cube
	state.SetTextureHandle(texture->GetTextureHandle());
	state.SetRenderDeltaMask(MLRState::TextureMask);

	state.SetFogMode(1);

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

	state.SetFilterMode(MLRState::BiLinearFilterMode);

	MLRState state2 = state;

	state2.SetZBufferWriteOff();
	state2.SetAlphaMode(MLRState::OneOneMode);

	state.SetPriority(MLRState::DefaultPriority);
	state2.SetPriority(MLRState::DefaultPriority + 1);

	texture = MLRTexturePool::Instance->Add("texture");
	state.SetTextureHandle(texture->GetTextureHandle());
	state.SetWireFrameMode(MLRState::WireFrameOffMode);

	state.SetFogMode(1);

	#if 1 // make a cube
		MLRPrimitiveBase *primitive = 
			CreateIndexedTriCube_NoColor_NoLit(0.5f, &state);
	//		CreateIndexedCube_Color_NoLit_2Tex(0.5f, colors, &state, &state2);
	//			CreateIndexedCube_Color_NoLit(0.5f, colors, &state);
	//		CreateIndexedCube_Color_NoLit_DetTex(0.5f, colors, &state, &state2, 0.0f, 0.0f, 4.0f, 4.0f);
	#else
		MLRPrimitiveBase *primitive = new MLR_I_MT_PMesh;

		MLR_I_PMesh *tempPMesh =
			CreateIndexedCube_NoColor_NoLit(0.5f, &state);

		Cast_Pointer(MLR_I_MT_PMesh*, primitive)->Copy(tempPMesh);

		tempPMesh->DetachReference();
	#endif
#endif

#if 1 // make a secound cube
	RGBAColor colors1[8] = {
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f),
		RGBAColor(1.0f, 1.0f, 1.0f, 1.0f)
	};

	#if 0
		Vector3D normals1[8] = {
			Vector3D(1.0, -1.0, 1.0),
			Vector3D(-1.0, -1.0, 1.0),
			Vector3D(1.0, -1.0, -1.0),
			Vector3D(0.0, -1.0, -1.0),
			Vector3D(-1.0, 1.0, 1.0),
			Vector3D(1.0, 1.0, 1.0),
			Vector3D(1.0, 1.0, -1.0),
			Vector3D(-1.0, 1.0, -1.0)
		};
	#endif

	MLRState state1;

// yes we loading "lightspot" a 2nd time but the texture pool should be smart about it
	MLRTexture *lightspot = MLRTexturePool::Instance->Add("lightspot");
	Check_Object(lightspot);

	state1.SetTextureHandle(lightspot->GetTextureHandle());
	state1.SetRenderDeltaMask(MLRState::TextureMask);

	state1.SetFogMode(1);

	MLRPrimitiveBase *primitive1 = CreateIndexedCube_Color_NoLit(0.5f, colors1, &state1);
#endif

// creating the camera-to-clip-space matrix
	SetPerspective(1.0f, 300.0f, Pi_Over_3, 0.75f, cameraToClip);

	shapeOrigins = new LinearMatrix4D;
	*shapeOrigins = LinearMatrix4D::Identity;
	Register_Object(shapeOrigins);

	invertedShapeOrigin = new LinearMatrix4D;
	*invertedShapeOrigin = LinearMatrix4D::Identity;
	Register_Object(invertedShapeOrigin);

//	create a shape
	drawShapeInfo[0] = new DrawShapeInformation;
	Register_Object(drawShapeInfo[0]);

//	drawShapeInfo[0]->shape = new MLRShape(10);
	static RGBAColor half_white(0.5f, 0.5f, 0.5, 0.5f);

	drawShapeInfo[0]->shape = CreateIndexedTriCone_Color_NoLit(2.0f, 4.0f, Pi_Over_8, &RGBAColor::White, &half_white, 2);
	Register_Object(drawShapeInfo[0]->shape);

	drawShapeInfo[0]->clippingFlags.SetClippingState(0x3f);
	drawShapeInfo[0]->shapeToWorld = shapeOrigins;
	drawShapeInfo[0]->worldToShape = invertedShapeOrigin;

	drawShapeInfo[0]->paintMeColor = &RGBAColor::White;

	drawShapeInfo[0]->activeLights = sceneLights.GetData();
	drawShapeInfo[0]->nrOfActiveLights = sceneLights.GetLength();

	shapeOrigins->BuildTranslation(Point3D(0.0f, 0.0f, 0.0f));
	shapeOrigins->BuildRotation(EulerAngles(0.0f, 0.0f, 0.0f));

	invertedShapeOrigin->Invert(*drawShapeInfo[0]->shapeToWorld);

	drawShapeInfo[0]->shape->Add(primitive);
	primitive->DetachReference();

	shapeState[0] = default_state;
	shapeState[0].SetLightingMode(MLRState::LightMapLightingMode);
	drawShapeInfo[0]->state = shapeState[0];

//	create another shape
	drawShapeInfo[1] = new DrawShapeInformation;
	Register_Object(drawShapeInfo[1]);

	drawShapeInfo[1]->shape = new MLRShape(10);
	Register_Object(drawShapeInfo[1]->shape);

	drawShapeInfo[1]->clippingFlags.SetClippingState(0x3f);
	drawShapeInfo[1]->shapeToWorld = shapeOrigins;
	drawShapeInfo[1]->worldToShape = invertedShapeOrigin;

	drawShapeInfo[1]->paintMeColor = &RGBAColor::White;

	shapeOrigins->BuildTranslation(Point3D(0.0f, 0.0f, 0.0f));
//	shapeOrigins->BuildRotation(EulerAngles(0.2f, 0.4f, 0.6f));

	invertedShapeOrigin->Invert(*drawShapeInfo[1]->shapeToWorld);

	drawShapeInfo[1]->shape->Add(primitive1);
	primitive1->DetachReference();
	drawShapeInfo[1]->state = default_state;

// create some effects
	effectOrigins = new LinearMatrix4D;
	*effectOrigins = LinearMatrix4D::Identity;
	Register_Object(effectOrigins);

	invertedEffectOrigin = new LinearMatrix4D;;
	*invertedEffectOrigin = LinearMatrix4D::Identity;
	Register_Object(invertedEffectOrigin);

	drawEffectInfo = new DrawEffectInformation[3];
	Register_Pointer(drawEffectInfo);

	drawEffectInfo[0].effect = new MLRPointCloud(*(int *)pointCloudTest);
	Register_Object(drawEffectInfo[0].effect);

	drawEffectInfo[1].effect = new MLRLineCloud(12);
	Register_Object(drawEffectInfo[1].effect);
	static int linesToDraw = 24;
	drawEffectInfo[1].effect->SetData(&linesToDraw, lineTestPoints, lineTestColors);

	drawEffectInfo[2].effect = new MLRNGonCloud(7, 4);
	Register_Object(drawEffectInfo[2].effect);

	drawEffectInfo[0].clippingFlags.SetClippingState(0x3f);
	drawEffectInfo[0].effectToWorld = &lightToWorld;

	drawEffectInfo[1].clippingFlags.SetClippingState(0x3f);
	drawEffectInfo[1].effectToWorld = shapeOrigins;

	drawEffectInfo[2].clippingFlags.SetClippingState(0x3f);
	drawEffectInfo[2].effectToWorld = effectOrigins;

	drawEffectInfo[0].effect->TurnAllOn();
	drawEffectInfo[1].effect->TurnAllOn();
	drawEffectInfo[2].effect->TurnAllOn();

	drawEffectInfo[1].state = state;

	effectOrigins->BuildTranslation(Point3D(0.0f, 0.0f, 0.0f));
	effectOrigins->BuildRotation(EulerAngles(0.0f, 0.0f, 0.0f));

	invertedEffectOrigin->Invert(*drawEffectInfo->effectToWorld);

//	misc.
	MLRTexturePool::Instance->LoadImages();

	MLRSortByOrder *cameraSorter = new MLRSortByOrder(MLRTexturePool::Instance);
	Register_Object(cameraSorter);

	theClipper = new MLRClipper(0, cameraSorter);
	Register_Object(theClipper);

	cameraOrigin.BuildRotation(
		EulerAngles(Camera_Direction.pitch, Camera_Direction.yaw, 0.0f)
	);
	cameraOrigin.BuildTranslation(Point3D(Camera_Direction));

	GOSVertex::farClipReciprocal = (1.0f-cameraToClip(2, 2))/cameraToClip(3, 2);

	GOSVertex::SetFogTableEntry(1, 5, 20, 0.0f);

	gos_PopCurrentHeap();
}

void __stdcall DoGameLogic()
{
	//
	//---------------
	// Read the mouse
	//---------------
	//
	int
		x_delta,
		y_delta;
	DWORD
		buttons;
	gos_GetMouseInfo(NULL, NULL, &x_delta, &y_delta, NULL, &buttons);
	Scalar
		x_speed = x_delta * 0.01f,
		y_speed = y_delta * 0.01f;

	//
	//------------------------------------------------------
	// If the left Ctrl button is held down, move the camera
	//------------------------------------------------------
	//
	gosEnum_KeyStatus status = gos_GetKeyStatus(KEY_LCONTROL);
	if (status != KEY_FREE && status != KEY_RELEASED)
	{
		Camera_Direction.pitch += y_speed;
		Camera_Direction.yaw -= x_speed;
	}

	//
	//------------------------------------------------------
	// If the left Shift button is held down, pan the camera
	//------------------------------------------------------
	//
	status = gos_GetKeyStatus(KEY_LSHIFT);
	if (status != KEY_FREE && status != KEY_RELEASED)
	{
		Camera_Shift.x += x_speed;
		Camera_Shift.y += y_speed;
	}

	//
	//----------------------------------------------------------------
	// If the left Alt button is held down, zoom the camera in and out
	//----------------------------------------------------------------
	//
	status = gos_GetKeyStatus(KEY_LMENU);
	if (status != KEY_FREE && status != KEY_RELEASED)
	{
		Camera_Direction.range *= 1.0f + 3.0f*y_speed;
	}

	char key = (char)(gos_GetKey()&255);
	switch (key)
	{
		case 'd':
		case 'D':
		{
			MLRTexture *damtex = MLRTexturePool::Instance->Add("damage");

			drawShapeInfo[0]->shape->HurtMe(sceneLights[0]->GetLightToWorldMatrix(), 0.5f, damtex);
		}
		break;
	}

	//
	//----------------------
	// Set the camera matrix
	//----------------------
	//
	cameraOrigin.BuildRotation(
		EulerAngles(Camera_Direction.pitch, Camera_Direction.yaw, 0.0f)
	);
	UnitVector3D
		world_left,
		world_up;
	cameraOrigin.GetLocalLeftInWorld(&world_left);
	cameraOrigin.GetLocalUpInWorld(&world_up);
	Point3D translation(Camera_Direction);
	translation.AddScaled(translation, world_left, Camera_Shift.x);
	translation.AddScaled(translation, world_up, Camera_Shift.y);
	cameraOrigin.BuildTranslation(translation);

	static int i, j, count = 0;

	static YawPitchRoll angles(*shapeOrigins);
	static Point3D posCube(0.0f, 0.0f, 0.0f);

//	posCube.x = (Scalar)(0.4f*sin((Scalar)count/45.0f));
//	posCube.y = (Scalar)(0.2f*cos((Scalar)count/45.0f));
//	posCube.z = (Scalar)(0.4f*cos((Scalar)count/45.0f));

	angles.yaw += 0.002f;
//	angles.pitch += 0.004f;
//	angles.roll += 0.0015f;
//	shapeOrigins->BuildRotation(angles);
	shapeOrigins->BuildTranslation(posCube);

	invertedShapeOrigin->Invert(*drawShapeInfo[0]->shapeToWorld);

	Point3D *points = (Point3D *)(pointCloudTest+1);
	RGBAColor *colors = (RGBAColor *)(pointCloudTest + 1 + (*(int *)pointCloudTest)*3);

#if 1	// animate the points
	for(i=0;i<4;i++)
	{
		points[i].x = pointCenter[i].x;	// + (i+1)*0.4f*sin((Scalar)((i+1)*count)/45.0f);
		points[i].y = pointCenter[i].y;	// + (i+1)*0.4f*cos((Scalar)((i+1)*count)/45.0f);
		points[i].z = pointCenter[i].z;

		colors[i].red = (16.0f*i + (count%256))/256.0f;
		colors[i].green = (32.0f*i + ((count+32)%256))/256.0f;
		colors[i].blue = (64.0f*i + ((count+64)%256))/256.0f;
		colors[i].alpha = (16.0f*i + ((count+128)%256))/256.0f;
	}
#endif

	toBeScaled.x = (Scalar)(1.5f + sin((Scalar)(count)/45.0f));
	toBeScaled.y = (Scalar)(1.5f + cos((Scalar)(count)/45.0f));
	toBeScaled.z = (Scalar)(1.5f + sin((Scalar)(count)/45.0f));

	count++;

	drawEffectInfo[0].effect->SetData((int *)pointCloudTest, points, colors);

// animated the cards
	for(i=0;i<4;i++)
	{
		cardTestColors[i].red = (16.0f*i + (count%256))/256.0f;
		cardTestColors[i].green = (32.0f*i + ((count+32)%256))/256.0f;
		cardTestColors[i].blue = (64.0f*i + ((count+64)%256))/256.0f;
		cardTestColors[i].alpha = (16.0f*i + ((count+128)%256))/256.0f;

		for(j=0;j<4;j++)
		{
			cardTestUVs[4*i+j].x += 0.01f;
			cardTestUVs[4*i+j].y += 0.01f;
		}
	}

	static int nGonTestCount = 4;
	drawEffectInfo[2].effect->SetData(&nGonTestCount, nGonTestPoints, nGonTestColors);
}

void __stdcall UpdateDisplay()
{
	Scalar z = 1.0f;
	theClipper->StartDraw(cameraOrigin, cameraToClip, fogColor, &bgColor, default_state, &z);

	theClipper->DrawShape(drawShapeInfo[0]);

//	theClipper->DrawShape(drawShapeInfo[1]);

	theClipper->DrawEffect(&drawEffectInfo[0]);

	UnitVector3D uv3;
	drawEffectInfo[1].effectToWorld = &firstLight->GetLightToWorldMatrix();
	drawEffectInfo[1].effectToWorld->GetLocalForwardInWorld(&uv3);

	theClipper->DrawEffect(&drawEffectInfo[1]);

//	theClipper->DrawEffect(&drawEffectInfo[2]);

	theClipper->RenderNow();
}

int* bad_boy = (int*)0x16c7be8;

void __stdcall TerminateGameEngine()
{
	Unregister_Object(firstLightMap);

	drawShapeInfo[0]->shape->DetachReference();
	drawShapeInfo[1]->shape->DetachReference();

	Unregister_Object(drawEffectInfo[0].effect);
	delete drawEffectInfo[0].effect;

	Unregister_Object(drawEffectInfo[1].effect);
	delete drawEffectInfo[1].effect;

	Unregister_Object(drawEffectInfo[2].effect);
	delete drawEffectInfo[2].effect;

	Unregister_Pointer(drawEffectInfo);
	delete [] drawEffectInfo;

	Unregister_Object(effectOrigins);
	delete effectOrigins;

	Unregister_Object(invertedEffectOrigin);
	delete invertedEffectOrigin;

	Unregister_Object(drawShapeInfo[0]);
	delete drawShapeInfo[0];

	Unregister_Object(drawShapeInfo[1]);
	delete drawShapeInfo[1];

	Unregister_Object(shapeOrigins);
	delete shapeOrigins;

	Unregister_Object(invertedShapeOrigin);
	delete invertedShapeOrigin;

	Unregister_Object(theClipper);
	delete theClipper;

	Unregister_Pointer(pointCloudTest);
	delete [] pointCloudTest;

	sceneLights.SetLength(0);

	//
	//-------------------
	// Turn off libraries
	//-------------------
	//
//	MemoryBlockBase::UsageReport();
	Unregister_Object(MLRTexturePool::Instance);
	delete MLRTexturePool::Instance;
	MLRTexturePool::Instance = NULL;

	Unregister_Object(firstLight);
	delete firstLight;

	MidLevelRenderer::TerminateClasses();
	Stuff::TerminateClasses();
}


//
// Setup the GameOS structure
//
void __stdcall GetGameOSEnvironment( char* CommandLine )
{
	Environment.applicationName		= "MLR Test";
	Environment.screenWidth				= 640;
	Environment.screenHeight			= 480;
	Environment.fullScreen				= FALSE;
	Environment.bitDepth					= 16;
	Environment.debugLog					= "";			//"DebugLog.txt";
	Environment.spew						= "";			//"GameOS*";

	Environment.UpdateRenderers			= UpdateDisplay;
	Environment.DoGameLogic				= DoGameLogic;
	Environment.InitializeGameEngine	= InitializeGameEngine;
	Environment.TerminateGameEngine	= TerminateGameEngine;

	Environment.FullScreenDevice		= 0;

	Environment.AntiAlias				= 0;			// true/false - Enable full screen antialiasing

//
// Texture infomation
//
	Environment.Texture_S_256			= 8;
	Environment.Texture_S_128			= 0;
	Environment.Texture_S_64			= 8;
	Environment.Texture_S_32			= 0;
	Environment.Texture_S_16			= 0;

	Environment.Texture_K_256			= 2;
	Environment.Texture_K_128			= 0;
	Environment.Texture_K_64			= 0;
	Environment.Texture_K_32			= 0;
	Environment.Texture_K_16			= 0;

	Environment.Texture_A_256			= 0;
	Environment.Texture_A_128			= 0;
	Environment.Texture_A_64			= 0;
	Environment.Texture_A_32			= 0;
	Environment.Texture_A_16			= 0;
}
