#include "ElementRendererHeaders.hpp"

//#define SCALE_BUG "your_loginname_here"

//############################################################################
//############################  ScalableShapeElement  ################################
//############################################################################

HGOSHEAP
	ElementRenderer::ScalableShapeElement::s_Heap = NULL;

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

ElementRenderer::Element::SyncMethod
	ElementRenderer::ScalableShapeElement::SyncMethods[SyncStateCount]=
{
	//
	// Root mode
	//
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),

	//
	// Sphere Cull
	//
	SYNC_METHOD(ScalableShapeElement,	CleanSphere),
	SYNC_METHOD(ScalableShapeElement,	DirtySphere),
	SYNC_METHOD(ScalableShapeElement,	CleanSphere),
	SYNC_METHOD(ScalableShapeElement,	DirtySphere),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),

	//
	// OBB Cull
	//
	SYNC_METHOD(ScalableShapeElement,	CleanOBB),
	SYNC_METHOD(ScalableShapeElement,	DirtyOBB),
	SYNC_METHOD(ScalableShapeElement,	CleanOBB),
	SYNC_METHOD(ScalableShapeElement,	DirtyOBB),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),

	//
	// Never Cull
	//
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						Clean),
	SYNC_METHOD(Element,						Dirty),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						Defer),

	//
	// Always Cull
	//
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None),
	SYNC_METHOD(Element,						None)
};

ElementRenderer::Element::DrawMethod
	ElementRenderer::ScalableShapeElement::DrawMethods[DrawStateCount]=
{
	//
	// No billboarding
	//
	DRAW_METHOD(ScalableShapeElement,	Inherit),
	DRAW_METHOD(ScalableShapeElement,	Override)
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::InitializeClass()
{
	Verify(!s_Heap);
	s_Heap = gos_CreateMemoryHeap("Scalable Shape", 0, g_LibraryHeap);
	Check_Pointer(s_Heap);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			ScalableShapeElementClassID,
			"ElementRenderer::ScalableShapeElement",
			BaseClass::DefaultData,
			reinterpret_cast<Factory>(&Make)
		);
	Register_Object(DefaultData);
}

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

	Check_Pointer(s_Heap);
	gos_DestroyMemoryHeap(s_Heap);
	s_Heap = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::ScalableShapeElement::ScalableShapeElement(
	Stuff::MemoryStream *stream,
	int version,
	ShapeHolder shapes,
	bool read_scale_from_stream
):
	ShapeElement(DefaultData, stream, version, shapes)
{
	Check_Pointer(this);
	Check_Object(stream);

	unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
	Verify(index < SyncStateCount);
	m_sync = SyncMethods[index];
	index = (m_state>>CullStateBit) & CullStateMask;
	Verify(index < CullStateCount);
	m_cameraCull = CullMethods[index].m_cameraCull;
	m_rayCull = CullMethods[index].m_rayCull;
	m_sphereTest = CullMethods[index].m_sphereTest;
	index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];

	//
	//------------------------
	// Get the scale and color
	//------------------------
	//
	if (read_scale_from_stream)
		*stream >> m_scale >> m_color;
	else
	{
		m_scale.x = 1.0f;
		m_scale.y = 1.0f;
		m_scale.z = 1.0f;
		m_color.red = 1.0f;
		m_color.green = 1.0f;
		m_color.blue = 1.0f;
		m_color.alpha = 1.0f;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::ScalableShapeElement::ScalableShapeElement():
	ShapeElement(DefaultData)
{
	Check_Pointer(this);

	unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
	Verify(index < SyncStateCount);
	m_sync = SyncMethods[index];
	index = (m_state>>CullStateBit) & CullStateMask;
	Verify(index < CullStateCount);
	m_cameraCull = CullMethods[index].m_cameraCull;
	m_rayCull = CullMethods[index].m_rayCull;
	m_sphereTest = CullMethods[index].m_sphereTest;
	index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];

	m_scale.x = 1.0f;
	m_scale.y = 1.0f;
	m_scale.z = 1.0f;
	m_color.red = 1.0f;
	m_color.green = 1.0f;
	m_color.blue = 1.0f;
	m_color.alpha = 1.0f;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::ScalableShapeElement::ScalableShapeElement(ScalableShapeElement *shape):
	ShapeElement(shape, DefaultData)
{
	Check_Pointer(this);

	unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
	Verify(index < SyncStateCount);
	m_sync = SyncMethods[index];
	index = (m_state>>CullStateBit) & CullStateMask;
	Verify(index < CullStateCount);
	m_cameraCull = CullMethods[index].m_cameraCull;
	m_rayCull = CullMethods[index].m_rayCull;
	m_sphereTest = CullMethods[index].m_sphereTest;
	index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];

	m_scale.x = 1.0f;
	m_scale.y = 1.0f;
	m_scale.z = 1.0f;
	m_color.red = 1.0f;
	m_color.green = 1.0f;
	m_color.blue = 1.0f;
	m_color.alpha = 1.0f;
}

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

	gos_PushCurrentHeap(s_Heap);
	ScalableShapeElement *element =
		new ScalableShapeElement(stream, version, shapes, true);
	gos_PopCurrentHeap();

	return element;
}

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

	RegisteredClass::ClassID class_id;
	*stream >> class_id;
	Verify(class_id == ShapeElementClassID);

	gos_PushCurrentHeap(s_Heap);
	ScalableShapeElement *element =
		new ScalableShapeElement(stream, version, shapes, false);
	gos_PopCurrentHeap();

	return element;
}

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

	//
	//-----------------------------
	// Save the shape to the stream
	//-----------------------------
	//
	*stream << m_scale << m_color;
}

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

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

	Verify(
		GetCullMode() != VolumeCullMode
		 || AreBoundsWrong()
		 || IsSyncDeferred()
		 || (
				m_localOBB.sphereRadius>0.0f
				 && (IsMatrixDirty() || m_worldOBB.sphereRadius>0.0f
			)
		)
	);
	Verify(
		GetCullMode() == RootMode || m_parent != NULL || AreBoundsWrong()
	);
	unsigned index = (m_state>>SyncStateBit) & SyncStateMask;
	Verify(index < SyncStateCount);
	m_sync = SyncMethods[index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::CleanSphereSyncMethod()
{
	Check_Object(this);
	Verify(m_localOBB.sphereRadius > 0.0f);
	Verify(
		!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
	);
	SYNC_LOGIC("Sphere::Clean::ScalableShape");

	//
	//-------------------------------
	// Update our m_localToWorld matrix
	//-------------------------------
	//
	Check_Object(m_parent);
	if (!IsLocalToParentIdentity())
		m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
	else
		m_localToWorld = m_parent->GetLocalToWorld();

	//
	//------------------------------
	// Transform the bounding sphere
	//------------------------------
	//
	Stuff::Point3D local_origin(m_localOBB.localToParent);
	local_origin *= m_scale;
	m_worldOBB.localToParent(3,0) =
		local_origin.x*m_localToWorld(0,0)
		 + local_origin.y*m_localToWorld(1,0)
		 + local_origin.z*m_localToWorld(2,0)
		 + m_localToWorld(3,0);
	m_worldOBB.localToParent(3,1) =
		local_origin.x*m_localToWorld(0,1)
		 + local_origin.y*m_localToWorld(1,1)
		 + local_origin.z*m_localToWorld(2,1)
		 + m_localToWorld(3,1);
	m_worldOBB.localToParent(3,2) =
		local_origin.x*m_localToWorld(0,2)
		 + local_origin.y*m_localToWorld(1,2)
		 + local_origin.z*m_localToWorld(2,2)
		 + m_localToWorld(3,2);
	m_worldOBB.sphereRadius = m_localOBB.sphereRadius * m_scale.GetLength();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::DirtySphereSyncMethod()
{
	Check_Object(this);
	Verify(m_localOBB.sphereRadius > 0.0f);
	Verify(
		IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedBySphere()
	);
	SYNC_LOGIC("Sphere::Dirty::ScalableShape");

	//
	//--------------------
	// Update our matrices
	//--------------------
	//
	m_localToParent = m_newLocalToParent;
	Check_Object(m_parent);
	if (!IsLocalToParentIdentity())
		m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
	else
		m_localToWorld = m_parent->GetLocalToWorld();

	//
	//------------------------------
	// Transform the bounding sphere
	//------------------------------
	//
	Stuff::Point3D local_origin(m_localOBB.localToParent);
	local_origin *= m_scale;
	m_worldOBB.localToParent(3,0) =
		local_origin.x*m_localToWorld(0,0)
		 + local_origin.y*m_localToWorld(1,0)
		 + local_origin.z*m_localToWorld(2,0)
		 + m_localToWorld(3,0);
	m_worldOBB.localToParent(3,1) =
		local_origin.x*m_localToWorld(0,1)
		 + local_origin.y*m_localToWorld(1,1)
		 + local_origin.z*m_localToWorld(2,1)
		 + m_localToWorld(3,1);
	m_worldOBB.localToParent(3,2) =
		local_origin.x*m_localToWorld(0,2)
		 + local_origin.y*m_localToWorld(1,2)
		 + local_origin.z*m_localToWorld(2,2)
		 + m_localToWorld(3,2);
	m_worldOBB.sphereRadius = m_localOBB.sphereRadius * m_scale.GetLength();
	MatrixIsClean();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::CleanOBBSyncMethod()
{
	Check_Object(this);
	Verify(
		!IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedByOBB()
	);
	Verify(Stuff::Close_Enough(m_localOBB.sphereRadius, m_localOBB.axisExtents.GetLength(), m_localOBB.sphereRadius*0.02f));
	Verify(
		Stuff::Close_Enough(m_localOBB.localToParent(0,0), 1.0f) &&
		Stuff::Close_Enough(m_localOBB.localToParent(1,1), 1.0f) &&
		Stuff::Close_Enough(m_localOBB.localToParent(2,2), 1.0f)
	);
	SYNC_LOGIC("OBB::Clean::ScalableShape");

	//
	//-------------------------------
	// Update our m_localToWorld matrix
	//-------------------------------
	//
	Check_Object(m_parent);
	if (!IsLocalToParentIdentity())
		m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
	else
		m_localToWorld = m_parent->GetLocalToWorld();

	//
	//------------------------------
	// Transform the bounding sphere
	//------------------------------
	//
	Stuff::LinearMatrix4D scaled_transform = m_localOBB.localToParent;
	scaled_transform(3,0) *= m_scale.x;
	scaled_transform(3,1) *= m_scale.y;
	scaled_transform(3,2) *= m_scale.z;
	m_worldOBB.localToParent.Multiply(scaled_transform, m_localToWorld);
	m_worldOBB.axisExtents.Multiply(m_localOBB.axisExtents, m_scale);
	m_worldOBB.sphereRadius = m_worldOBB.axisExtents.GetLength();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::DirtyOBBSyncMethod()
{
	Check_Object(this);
	Verify(
		IsMatrixDirty() && GetCullMode()==VolumeCullMode && IsBoundedByOBB()
	);
	Verify(Stuff::Close_Enough(m_localOBB.sphereRadius, m_localOBB.axisExtents.GetLength(), m_localOBB.sphereRadius*0.02f));
	Verify(
		Stuff::Close_Enough(m_localOBB.localToParent(0,0), 1.0f) &&
		Stuff::Close_Enough(m_localOBB.localToParent(1,1), 1.0f) &&
		Stuff::Close_Enough(m_localOBB.localToParent(2,2), 1.0f)
	);
	SYNC_LOGIC("OBB::Dirty::ScalableShape");

	//
	//--------------------
	// Update our matrices
	//--------------------
	//
	m_localToParent = m_newLocalToParent;
	Check_Object(m_parent);
	if (!IsLocalToParentIdentity())
		m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
	else
		m_localToWorld = m_parent->GetLocalToWorld();

	//
	//------------------------------
	// Transform the bounding sphere
	//------------------------------
	//
	Stuff::LinearMatrix4D scaled_transform = m_localOBB.localToParent;
	scaled_transform(3,0) *= m_scale.x;
	scaled_transform(3,1) *= m_scale.y;
	scaled_transform(3,2) *= m_scale.z;
	m_worldOBB.localToParent.Multiply(scaled_transform, m_localToWorld);
	m_worldOBB.axisExtents.Multiply(m_localOBB.axisExtents, m_scale);
	m_worldOBB.sphereRadius = m_worldOBB.axisExtents.GetLength();
	#if defined(SCALE_BUG)
		SPEW((SCALE_BUG, "m_worldOBB = "));
		Spew(SCALE_BUG, m_worldOBB);
		SPEW((SCALE_BUG, ""));
	#endif
	MatrixIsClean();
}

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

	STOP(("ScalableShape is not collidable!"));
	return false;
}

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

	STOP(("ScalableShape is not collidable!"));
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::SetDrawState()
{
	Check_Object(this);
	unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::InheritDrawMethod(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);
	#if defined(_ARMOR)
		if (!culling_state)
			Verify(!Cull(camera));
	#endif

	//
	//--------------------------------------
	// Draw our bounds if we are supposed to
	//--------------------------------------
	//
	#if defined(LAB_ONLY)
		Callback *callback = GetCallbackSet();
		unsigned index = GetCallbackIndex();
		if (callback && callback[index])
		{
			Check_Pointer(callback[index]);
			(*callback[index])(camera, inherited_state, culling_state, this);
		}
		else if (s_ShowClippedBounds && GetCullMode() == VolumeCullMode && culling_state)
		{
			Stuff::RGBAColor color(1.0f, 0.5f, 0.5f, 0.3f);
			if (IsBoundedBySphere())
				DrawSphere(m_worldOBB, color, camera, inherited_state, culling_state);
			else
				DrawOBB(m_worldOBB, color, camera, inherited_state, culling_state);
		}
	#endif

	//
	//----------------------------
	// Set up the draw information
	//----------------------------
	//
	MidLevelRenderer::DrawScalableShapeInformation info;
	Check_Object(m_MLRShape);
	info.shape = m_MLRShape;
	info.clippingFlags.SetClippingState(culling_state);
	Check_Object(&m_localToWorld);
	info.shapeToWorld = &m_localToWorld;
	info.worldToShape = NULL;
	info.state = inherited_state->GetMLRState();
	LightList culled_lights;
	info.nrOfActiveLights = CullLights(culled_lights, inherited_state->m_lights);
	info.activeLights = (info.nrOfActiveLights>0) ? &culled_lights[0] : NULL;
	info.scaling = &m_scale;
	info.paintMeColor = &m_color;

	//
	//---------------
	// Draw the shape
	//---------------
	//
	MidLevelRenderer::MLRClipper *clipper = camera->GetClipper();
	Check_Object(clipper);

	Stop_Timer(Graph_Traversal);
	ELEMENT_RENDER("Culling::ScalableShape::Inherited");
	Start_Timer(Draw_Scalable);
	clipper->DrawScalableShape(&info);
	Stop_Timer(Draw_Scalable);
	Start_Timer(Graph_Traversal);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::ScalableShapeElement::OverrideDrawMethod(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);
	#if defined(_ARMOR)
		if (!culling_state)
			Verify(!Cull(camera));
	#endif

	//
	//-------------------
	// Mix the properties
	//-------------------
	//
	Check_Object(m_stateChange);
	StateChange mixed;
	mixed.Mix(*inherited_state, *m_stateChange);
	MixLights(
		mixed.m_lights,
		inherited_state->m_lights,
		m_stateChange->m_lights
	);

	//
	//--------------------------------------
	// Draw our bounds if we are supposed to
	//--------------------------------------
	//
	#if defined(LAB_ONLY)
		Callback *callback = GetCallbackSet();
		unsigned index = GetCallbackIndex();
		if (callback && callback[index])
		{
			Check_Pointer(callback[index]);
			(*callback[index])(camera, &mixed, culling_state, this);
		}
		else if (s_ShowClippedBounds && GetCullMode() == VolumeCullMode && culling_state)
		{
			Stuff::RGBAColor color(1.0f, 0.5f, 0.5f, 0.3f);
			if (IsBoundedBySphere())
				DrawSphere(m_worldOBB, color, camera, &mixed, culling_state);
			else
				DrawOBB(m_worldOBB, color, camera, &mixed, culling_state);
		}
	#endif

	//
	//----------------------------
	// Set up the draw information
	//----------------------------
	//
	MidLevelRenderer::DrawScalableShapeInformation info;
	Check_Object(m_MLRShape);
	info.shape = m_MLRShape;
	info.clippingFlags.SetClippingState(culling_state);
	Check_Object(&m_localToWorld);
	info.shapeToWorld = &m_localToWorld;
	info.worldToShape = NULL;
	info.state = mixed.GetMLRState();
	LightList culled_lights;
	info.nrOfActiveLights = CullLights(culled_lights, mixed.m_lights);
	info.activeLights = (info.nrOfActiveLights>0) ? &culled_lights[0] : NULL;
	info.scaling = &m_scale;
	info.paintMeColor = &m_color;

	//
	//---------------
	// Draw the shape
	//---------------
	//
	MidLevelRenderer::MLRClipper *clipper = camera->GetClipper();
	Check_Object(clipper);

	Stop_Timer(Graph_Traversal);
	ELEMENT_RENDER("Culling::ScalableShape::Overridden");
	Start_Timer(Draw_Scalable);
	clipper->DrawScalableShape(&info);
	Stop_Timer(Draw_Scalable);
	Start_Timer(Graph_Traversal);
}
