#include "ElementRendererHeaders.hpp"
#include <MLR\MLRFootstep.hpp>
#include <MLR\MLRSortByOrder.hpp>
#include <MLR\MLRTexturePool.hpp>

//############################################################################
//############################  CameraElement  ###############################
//############################################################################

#if 0
	MidLevelRenderer::MLRTexture *g_BergerName;
	bool s_AbzugFlag = false;
	bool g_AbzugFlag = false;
#endif

ElementRenderer::CameraElement::ClassData*
	ElementRenderer::CameraElement::DefaultData = NULL;

ElementRenderer::Element::SyncMethod
	ElementRenderer::CameraElement::SyncMethods[2]=
{
	SYNC_METHOD(CameraElement, CleanCamera),
	SYNC_METHOD(CameraElement, DirtyCamera)
};

ElementRenderer::Element::CullMethod
	ElementRenderer::CameraElement::CullMethodObject=CULL_METHOD(CameraElement, CameraOnly, Always);

bool ElementRenderer::CameraElement::s_HideSky=false;
static bool __stdcall CheckHideSky() {return ElementRenderer::CameraElement::s_HideSky;}
static void __stdcall EnableHideSky() {ElementRenderer::CameraElement::s_HideSky = !ElementRenderer::CameraElement::s_HideSky;}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::InitializeClass(Stuff::NotationFile *startup_ini)
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			CameraElementClassID,
			"ElementRenderer::CameraElement",
			BaseClass::DefaultData,
			reinterpret_cast<Factory>(&Make)
		);
	Check_Object(DefaultData);

	AddDebuggerMenuItem("Libraries\\Graphics Options\\Hide Sky", CheckHideSky, EnableHideSky, 0 );

	if (startup_ini)
	{
		Check_Object(startup_ini);
		Stuff::Page *page = startup_ini->FindPage("Graphics Options");
		if (page)
		{
			Check_Object(page);
			page->GetEntry("HideSky", &s_HideSky);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::TerminateClass(Stuff::NotationFile *startup_ini)
{
	if (startup_ini)
	{
		Check_Object(startup_ini);
		Stuff::Page *page = startup_ini->SetPage("Graphics Options");
		Check_Object(page);
		page->SetEntry("HideSky", s_HideSky);
	}

	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::CameraElement::CameraElement(
	Stuff::MemoryStream *stream,
	int version,
	ShapeHolder shapes
):
	GroupElement(DefaultData, stream, version, shapes),
	m_scene(NULL)
{
	Check_Pointer(this);

	//
	//----------------------------
	// Initialize member variables
	//----------------------------
	//
	m_state = NeverCullMode | MatrixDirtyFlag;
	CameraElement::SetSyncState();
	CameraElement::SetCullState();
	CameraElement::SetDrawState();

	//
	//-----------------------------
	// Create the attachment to MLR
	//-----------------------------
	//
	gos_PushCurrentHeap(MidLevelRenderer::SorterHeap);
	m_sorter = new MidLevelRenderer::MLRSortByOrder(MidLevelRenderer::MLRTexturePool::Instance);
	Check_Object(m_sorter);
	gos_PopCurrentHeap();

	gos_PushCurrentHeap(MidLevelRenderer::ClipperHeap);
	m_clipper = new MidLevelRenderer::MLRClipper(0, m_sorter);
	Check_Object(m_clipper);
	gos_PopCurrentHeap();

	//
	//-----------------------------------------
	// Get the scene properties from the stream
	//-----------------------------------------
	//
	m_viewingStateChange = new StateChange(stream, version);
	Check_Object(m_viewingStateChange);

	//
	//----------------------------------
	// Get the clip data from the stream
	//----------------------------------
	//
	*stream >> m_nearLeft >> m_nearRight >> m_nearTop >> m_nearBottom;
	*stream >> m_nearClip >> m_farClip;
	SetPerspective(
		m_nearClip,
		m_farClip,
		m_nearLeft,
		m_nearRight,
		m_nearTop,
		m_nearBottom
	);

	//
	//-----------------------------------
	// Get the buffer flags and fog color
	//-----------------------------------
	//
	*stream >> m_clearZBufferOnDraw >> m_clearBackBufferOnDraw;
	if (version < 5)
	{
		Stuff::RGBAColor color;
		*stream >> color;
	}

	m_viewPortLeft = 0.0f;
	m_viewPortTop = 0.0f;
	m_viewPortRight = 1.0f;
	m_viewPortBottom = 1.0f;

	m_acceptingElement = NULL;

	m_drawSky = true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::CameraElement::CameraElement(StateChange *properties):
	GroupElement(DefaultData),
	m_scene(NULL)
{
	Check_Pointer(this);
	Check_Object(properties);

	//
	//----------------------------
	// Initialize member variables
	//----------------------------
	//
	m_state = NeverCullMode | MatrixDirtyFlag;
	CameraElement::SetSyncState();
	CameraElement::SetCullState();
	CameraElement::SetDrawState();
	m_viewingStateChange = properties;


	m_clearZBufferOnDraw = true;
	m_clearBackBufferOnDraw = true;
	m_drawSky = true;
	m_fovAngle = Stuff::Pi_Over_3;

	//m_scene = NULL;
	m_useTintColor = false;
	//
	//--------------
	// Build the sky
	//--------------
	//

	//
	//-----------------------------
	// Create the attachment to MLR
	//-----------------------------
	//
	gos_PushCurrentHeap(MidLevelRenderer::SorterHeap);
	m_sorter = new MidLevelRenderer::MLRSortByOrder(MidLevelRenderer::MLRTexturePool::Instance);
	Check_Object(m_sorter);
	gos_PopCurrentHeap();

	gos_PushCurrentHeap(MidLevelRenderer::ClipperHeap);
	m_clipper = new MidLevelRenderer::MLRClipper(0, m_sorter);
	Check_Object(m_clipper);
	gos_PopCurrentHeap();

	m_viewPortLeft = 0.0f;
	m_viewPortTop = 0.0f;
	m_viewPortRight = 1.0f;
	m_viewPortBottom = 1.0f;

	m_acceptingElement = NULL;

	//sanghoon begin
	m_hsh_special_flag=false;
	//sanghoon end
}

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

void ElementRenderer::CameraElement::DeleteSky()
{
	Element *sky = m_sky.GetCurrent();
	if (sky)
	{
		Check_Object(sky);
		delete sky;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::CameraElement::~CameraElement()
{
	Check_Object(m_clipper);
	delete m_clipper;
	Check_Object(m_viewingStateChange);
	delete m_viewingStateChange;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::CameraElement*
	ElementRenderer::CameraElement::Make(
		Stuff::MemoryStream *stream,
		int version,
		ShapeHolder shapes
	)
{
	Check_Object(stream);

	gos_PushCurrentHeap(g_Heap);
	CameraElement *element = new CameraElement(stream, version, shapes);
	gos_PopCurrentHeap();

	return element;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	BaseClass::Save(stream);

	//
	//--------------------------
	// Save the scene properties
	//--------------------------
	//
	Check_Object(m_viewingStateChange);
	m_viewingStateChange->Save(stream);

	//
	//-------------------
	// Save the clip data
	//-------------------
	//
	*stream << m_nearLeft << m_nearRight << m_nearTop << m_nearBottom;
	*stream << m_nearClip << m_farClip;

	//
	//--------------------------------
	// Save buffer flags and fog color
	//--------------------------------
	//
	*stream << m_clearZBufferOnDraw << m_clearBackBufferOnDraw;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::TestInstance()
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::SetSyncState()
{
	Check_Object(this);

	Verify(GetCullMode() == NeverCullMode);
	m_sync = SyncMethods[IsMatrixDirty()?1:0];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::CleanCameraSyncMethod()
{
	Check_Object(this);
	SYNC_LOGIC("Clean::Camera");

	//
	//------------------------------------------------------------------
	// Concatenated our m_localToWorld from m_localToParent and our parent's
	// m_localToWorld
	//------------------------------------------------------------------
	//
	Check_Object(m_parent);
	m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());

	//
	//--------------------------------------------
	// Transform the local planes into world space
	//--------------------------------------------
	//
	for (int i=0; i<CullingPlaneCount; ++i)
		m_worldCullingPlanes[i].Multiply(
			m_localCullingPlanes[i],
			m_localToWorld
		);

	//
	//--------------------------------------------
	// Prepare 2D culling
	//--------------------------------------------
	//

	m_cullFrustrumXZ[0] = m_localToWorld;

	Stuff::UnitVector3D ur, uu, uf;
	Stuff::Vector3D
		right = Stuff::Vector3D::Identity,
		forward = Stuff::Vector3D::Identity,
		up = Stuff::Vector3D::Identity;

	m_localToWorld.GetLocalRightInWorld(&ur);
	m_localToWorld.GetLocalUpInWorld(&uu);
	m_localToWorld.GetLocalForwardInWorld(&uf);

	right.AddScaled(right, ur, Stuff::Fabs(m_nearRight*m_farClip/m_nearClip));
	up.AddScaled(up, uu, Stuff::Fabs(m_nearTop*m_farClip/m_nearClip));
	forward.AddScaled(forward, uf, Stuff::Fabs(m_farClip));


	m_cullFrustrumXZ[1].Add(forward, up);
	m_cullFrustrumXZ[2].Add(forward, up);

	up.Negate(up);
	m_cullFrustrumXZ[3].Add(forward, up);
	m_cullFrustrumXZ[4].Add(forward, up);

	m_cullFrustrumXZ[2].Add(m_cullFrustrumXZ[2], right);
	m_cullFrustrumXZ[3].Add(m_cullFrustrumXZ[3], right);

	right.Negate(right);
	m_cullFrustrumXZ[1].Add(m_cullFrustrumXZ[1], right);
	m_cullFrustrumXZ[4].Add(m_cullFrustrumXZ[4], right);

	for(i=1;i<5;i++)
	{
		m_cullFrustrumXZ[i].Add(m_cullFrustrumXZ[i], m_cullFrustrumXZ[0]);

		m_cullFrustrumXZ[i].y = 0.0f;
	}
	m_cullFrustrumXZ[0].y = 0.0f;

#if 0
	SPEW(("micgaert", "%f %f %f", m_cullFrustrumXZ[0].x, m_cullFrustrumXZ[0].y, m_cullFrustrumXZ[0].z));
	SPEW(("micgaert", "%f %f %f", m_cullFrustrumXZ[1].x, m_cullFrustrumXZ[1].y, m_cullFrustrumXZ[1].z));
	SPEW(("micgaert", "%f %f %f", m_cullFrustrumXZ[2].x, m_cullFrustrumXZ[2].y, m_cullFrustrumXZ[2].z));
	SPEW(("micgaert", "%f %f %f", m_cullFrustrumXZ[3].x, m_cullFrustrumXZ[3].y, m_cullFrustrumXZ[3].z));
	SPEW(("micgaert", "%f %f %f", m_cullFrustrumXZ[4].x, m_cullFrustrumXZ[4].y, m_cullFrustrumXZ[4].z));
	SPEW(("micgaert", "\n"));
#endif

	//
	//--------------------
	// Update our children
	//--------------------
	//
	Stuff::ChainIteratorOf<Element*> children(&m_group);
	Element *child;
	while ((child = children.ReadAndNext()) != NULL)
	{
		Check_Object(child);
		child->Sync();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::DirtyCameraSyncMethod()
{
	Check_Object(this);
	SYNC_LOGIC("Dirty::Camera");

	//
	//------------------------------------------------------
	// If the local-to-parent matrix is dirty, make it clean
	//------------------------------------------------------
	//
	m_localToParent = m_newLocalToParent;
	MatrixIsClean();
	CleanCameraSyncMethod();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::SetCullState()
{
	Check_Object(this);

	m_cameraCull = CullMethodObject.m_cameraCull;
	m_rayCull = CullMethodObject.m_rayCull;
	m_sphereTest = CullMethodObject.m_sphereTest;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ElementRenderer::CameraElement::CameraOnlyCullMethod(CameraElement *camera)
{
	Check_Object(this);
	Check_Object(camera);

	return (camera != this) ? -1 : 0;
}

//sanghoon begin
void __cdecl ClearTargetCameraBackBuffer();
//sanghoon end

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::DrawScene(bool gosClearedScreen)
{
	Check_Object(this);

	//
	//-----------------------------
	// Tell MLR where the camera is
	//-----------------------------
	//
	ELEMENT_RENDER("DrawScene");
	Start_Timer(Total_Video);
#if 0
	if (g_AbzugFlag)
	{
		m_viewingStateChange->DisableChildTextureHandleControl();
		m_viewingStateChange->DisableChildAlphaControl ();											
		m_viewingStateChange->SetAlphaMode (MidLevelRenderer::MLRState::AlphaMode::OneZeroMode);
		m_viewingStateChange->SetTextureHandle (g_BergerName->GetTextureHandle ());
	}
#endif

	bool secondary = false;
	{
		ELEMENT_RENDER("Setup & Sky");
		Start_Timer(Setup_Sky);
		Check_Object(m_clipper);
		Check_Object(m_viewingStateChange);
		if(m_useTintColor)
		{
			FadeColor = m_tintColor;
		}
		else
		{
			FadeColor = Stuff::RGBAColor::White;
		}

		m_acceptingElement = NULL;

		if (m_clearZBufferOnDraw)
		{
			Stuff::Scalar z=1.0f;
			if (m_clearBackBufferOnDraw)
			{
				m_clipper->StartDraw(
					m_localToWorld,
					m_cameraToClip,
					Stuff::RGBAColor::Unassigned,
					&StateChange::GetFogColor(),
					m_viewingStateChange->GetMLRState(),
					&z
				);

			}
			else
			{
				m_clipper->StartDraw(
					m_localToWorld,
					m_cameraToClip,
					Stuff::RGBAColor::Unassigned,
					NULL,
					m_viewingStateChange->GetMLRState(),
					&z
				);
			}
		}
		else
		{
			if (m_clearBackBufferOnDraw)
			{
				m_clipper->StartDraw(
					m_localToWorld,
					m_cameraToClip,
					Stuff::RGBAColor::Unassigned,
					&StateChange::GetFogColor(),
					m_viewingStateChange->GetMLRState(),
					NULL
				);

			}
			else
			{
				//
				//-----------------------------------------
				//This means that we are a secondary camera
				//-----------------------------------------
				//

				//sanghoon begin
				if(m_hsh_special_flag){
				ClearTargetCameraBackBuffer();
				}
				//sanghoon end

				m_clipper->StartDraw(
					m_localToWorld,
					m_cameraToClip,
					Stuff::RGBAColor::Unassigned,
					NULL,
					m_viewingStateChange->GetMLRState(),
					NULL
				);
		
				 secondary = true;
			}
		}

		//
		//---------------------------------
		//Setup the Viewport for the camera
		//---------------------------------
		//
		MidLevelRenderer::SetViewportScalars(
			m_viewPortTop,
			m_viewPortLeft,
			m_viewPortBottom,
			m_viewPortRight
		);

		if(secondary)
		{
			if(gosClearedScreen==false)
			{
				MidLevelRenderer::ClearZBuffer();
			}
			GetClipper()->SetCurrentState();
		}

		//
		//------------------------------------------------------------------------
		// If the sky is active, draw it first after moving it to where the camera
		// is
		//------------------------------------------------------------------------
		//
		else // Don't update LastCameraPosition for any camera but the main one.
		{
			gosFX::Effect::LastCameraPosition = GetLocalToWorld();
		}
		if (m_sky.GetCurrent()  && !s_HideSky && m_drawSky && m_scene.GetCurrent())
		{
			Element *sky = m_sky.GetCurrent();
			Check_Object(sky);
			Stuff::LinearMatrix4D sky_to_parent = sky->GetLocalToParent();
			sky_to_parent.BuildTranslation(gosFX::Effect::LastCameraPosition);
			sky->SetLocalToParent(sky_to_parent);
			sky->Sync();
			DrawElement(sky, m_viewingStateChange);
		}
			
		Stop_Timer(Setup_Sky);
	}

	//
	//---------------------------------------------
	// Traverse the scene, and tell MLR we are done
	//---------------------------------------------
	//
	if(m_scene.GetCurrent())
	{
		ELEMENT_RENDER("Culling");
		Check_Object(m_scene.GetCurrent());
		Start_Timer(Graph_Traversal);
		DrawElement(m_scene.GetCurrent(), m_viewingStateChange);
		Stop_Timer(Graph_Traversal);
	}

	if(!secondary && (MidLevelRenderer::MLRFootStep::Instance!=NULL) )
	{
		MidLevelRenderer::MLRFootStep::Instance->Draw(m_clipper, GetLocalToWorld());
	}

	{
		ELEMENT_RENDER("Rasterizing");
		Start_Timer(Render);
//sanghoon
extern bool sh_isdeathmode;
		if(!sh_isdeathmode)
		m_clipper->RenderNow();
		Stop_Timer(Render);
	}
	FadeColor = Stuff::RGBAColor::White;

	Stop_Timer(Total_Video);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::CastShadow(
		const Stuff::LinearMatrix4D &shadow_to_world,
		const Stuff::UnitVector3D &sun_in_world,
		Stuff::Scalar radius
	)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::MakeFootStep(
		MidLevelRenderer::MLRShape *shape,
		const Stuff::LinearMatrix4D &foot,
		Stuff::Scalar radius,
		MidLevelRenderer::MLRTexture *tex
	)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::SetScene(Element *scene)
{
	Check_Object(this);

	m_scene.Remove();

	if (scene != NULL)
	{
		m_scene.Add(scene);
	}
}	

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::AdoptSkyElement(Element *sky)
{
	Check_Object(this);
	Check_Object(sky);

	//
	//----------------------------------------------------------------------
	// Make sure that the sky element only writes to the z-buffer and is not
	// fogged
	//----------------------------------------------------------------------
	//
	m_sky.Remove();
	m_sky.Add(sky);
	StateChange *state = sky->GetStateChange();
	if (!state)
	{
		state = new StateChange;
		Check_Object(state);
		sky->AdoptStateChange(state);
	}
	state->DisableBackfaceCulling();
	state->DisableChildBackfaceControl();

	state->SetFogMode(StateChange::DisableFogMode);
	state->DisableChildFogControl();

	state->DisableZBufferWrite();
	state->DisableChildZBufferWriteControl();

	state->DisableZBufferCompare();
	state->DisableChildZBufferCompareControl();

	state->DisableTextureWrap();
	state->DisableChildTextureWrapControl();

	state->SetFilterMode(MidLevelRenderer::MLRState::BiLinearFilterMode);
	state->DisableChildFilterControl();

	state->EnableDrawNow();
	state->DisableChildDrawNowControl();

	sky->SetRootMode();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::SetPerspective(
		Stuff::Scalar near_clip,
		Stuff::Scalar far_clip,
		Stuff::Scalar left_clip,
		Stuff::Scalar right_clip,
		Stuff::Scalar top_clip,
		Stuff::Scalar bottom_clip
	)
{
	Check_Object(this);
	Verify(far_clip - near_clip > Stuff::SMALL);
	Verify(left_clip - right_clip > Stuff::SMALL);
	Verify(top_clip - bottom_clip > Stuff::SMALL);
	m_nearClip = near_clip;
	m_farClip = far_clip;
	m_nearLeft = left_clip;
	m_nearRight = right_clip;
	m_nearTop = top_clip;
	m_nearBottom = bottom_clip;

	//
	//-------------------------------------------------------
	// Calculate the horizontal, vertical, and forward ranges
	//-------------------------------------------------------
	//
	Stuff::Scalar horizontal_range = APPLY_LEFT_SIGN(1.0f) / (left_clip - right_clip);
	Stuff::Scalar vertical_range = APPLY_UP_SIGN(1.0f) / (top_clip - bottom_clip);
	Stuff::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
	//------------------------------------------------------------------------
	//
	m_cameraToClip(LEFT_AXIS, LEFT_AXIS) = near_clip * horizontal_range;
	m_cameraToClip(LEFT_AXIS, UP_AXIS) = 0.0f;
	m_cameraToClip(LEFT_AXIS, FORWARD_AXIS) = 0.0f;
	m_cameraToClip(LEFT_AXIS, 3) = 0.0f;

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

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

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

	//
	//-----------------------------------------------------------------------
	// Now, set up the far and near clip planes.  They are always parallel to
	// the X-Y plane
	//-----------------------------------------------------------------------
	//
	int index = MidLevelRenderer::MLRClippingState::NextBit - MidLevelRenderer::MLRClippingState::NearClipBit - 1;
	m_localCullingPlanes[index].offset = -near_clip;
	m_localCullingPlanes[index].normal = Stuff::UnitVector3D::Backward;

	index = MidLevelRenderer::MLRClippingState::NextBit - MidLevelRenderer::MLRClippingState::FarClipBit - 1;
	m_localCullingPlanes[index].offset = far_clip;
	m_localCullingPlanes[index].normal = Stuff::UnitVector3D::Forward;

	//
	//------------------------------------------------------------------------
	// Set up the left culling plane.  Given right-handed points, create three
	// vertices from the clip data
	//------------------------------------------------------------------------
	//
	Stuff::Scalar growth = far_clip / near_clip;
	Stuff::Scalar far_left = left_clip * growth;
	Stuff::Scalar far_right = right_clip * growth;
	Stuff::Scalar far_top = top_clip * growth;
	Stuff::Scalar far_bottom = bottom_clip * growth;

	Stuff::Point3D
		point1,
		point2,
		point3;
	point1[LEFT_AXIS] = APPLY_LEFT_SIGN(left_clip);
	point1[UP_AXIS] = APPLY_UP_SIGN(bottom_clip);
	point1[FORWARD_AXIS] = APPLY_FORWARD_SIGN(near_clip);
	point2[LEFT_AXIS] = APPLY_LEFT_SIGN(left_clip);
	point2[UP_AXIS] = APPLY_UP_SIGN(top_clip);
	point2[FORWARD_AXIS] = APPLY_FORWARD_SIGN(near_clip);
	point3[LEFT_AXIS] = APPLY_LEFT_SIGN(far_left);
	point3[UP_AXIS] = APPLY_UP_SIGN(top_clip);
	point3[FORWARD_AXIS] = APPLY_FORWARD_SIGN(far_clip);
	index = MidLevelRenderer::MLRClippingState::NextBit - MidLevelRenderer::MLRClippingState::LeftClipBit - 1;
	m_localCullingPlanes[index].BuildPlane(point1, point2, point3);

	//
	//-------------------------------
	// Set up the right culling plane
	//-------------------------------
	//
	point1[LEFT_AXIS] = APPLY_LEFT_SIGN(right_clip);
	point1[UP_AXIS] = APPLY_UP_SIGN(top_clip);
	point2[LEFT_AXIS] = APPLY_LEFT_SIGN(right_clip);
	point2[UP_AXIS] = APPLY_UP_SIGN(bottom_clip);
	point3[LEFT_AXIS] = APPLY_LEFT_SIGN(far_right);
	point3[UP_AXIS] = APPLY_UP_SIGN(far_bottom);
	index = MidLevelRenderer::MLRClippingState::NextBit - MidLevelRenderer::MLRClippingState::RightClipBit - 1;
	m_localCullingPlanes[index].BuildPlane(point1, point2, point3);

	//
	//-----------------------------
	// Set up the top culling plane
	//-----------------------------
	//
	point1[LEFT_AXIS] = APPLY_LEFT_SIGN(left_clip);
	point2[UP_AXIS] = APPLY_UP_SIGN(top_clip);
	point3[UP_AXIS] = APPLY_UP_SIGN(far_top);
	index = MidLevelRenderer::MLRClippingState::NextBit - MidLevelRenderer::MLRClippingState::TopClipBit - 1;
	m_localCullingPlanes[index].BuildPlane(point1, point2, point3);

	//
	//--------------------------------
	// Set up the bottom culling plane
	//--------------------------------
	//
	point1[LEFT_AXIS] = APPLY_LEFT_SIGN(right_clip);
	point1[UP_AXIS] = APPLY_UP_SIGN(bottom_clip);
	point2[LEFT_AXIS] = APPLY_LEFT_SIGN(left_clip);
	point2[UP_AXIS] = APPLY_UP_SIGN(bottom_clip);
	point3[LEFT_AXIS] = APPLY_LEFT_SIGN(far_left);
	point3[UP_AXIS] = APPLY_UP_SIGN(far_bottom);
	index = MidLevelRenderer::MLRClippingState::NextBit - MidLevelRenderer::MLRClippingState::BottomClipBit - 1;
	m_localCullingPlanes[index].BuildPlane(point1, point2, point3);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::SetPerspective(
		Stuff::Scalar near_clip,
		Stuff::Scalar far_clip,
		const Stuff::Radian &horizontal_fov,
		Stuff::Scalar height_to_width
	)
{
	Check_Object(this);
	Verify(far_clip - near_clip > Stuff::SMALL);
	Verify(horizontal_fov > Stuff::SMALL);
	Verify(height_to_width > Stuff::SMALL);

	//
	//-------------------------------------------------------------
	// Calculate the near plane offsets to the side culling planes
	//-------------------------------------------------------------
	//
	Stuff::Scalar width = near_clip * Stuff::Tan(horizontal_fov*0.5f);
	Stuff::Scalar height = width * height_to_width;
	m_fovAngle = horizontal_fov;
	SetPerspective(near_clip, far_clip, width, -width, height, -height);

	
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::CameraElement::GetPerspective(
		Stuff::Scalar &near_clip,
		Stuff::Scalar &far_clip,
		Stuff::Scalar &horizontal_fov,
		Stuff::Scalar &height_to_width
	)
{

	near_clip = m_nearClip;
	far_clip = m_farClip;
	horizontal_fov = Stuff::Arctan(m_nearLeft, m_nearClip)*2.0f;
	height_to_width = m_nearTop / m_nearLeft;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementRenderer::CameraElement::CastCulledRay(CollisionQuery *query)
{
	Check_Object(this);
	Check_Object(query);

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::Element*
	ElementRenderer::CameraElement::FindSmallestElementContainingCulled(SphereTest *test)
{
	Check_Object(this);
	Check_Object(test);

	return NULL;
}
