#include "AdeptHeaders.hpp"

#include "CollisionVolume.hpp"
#include "Entity.hpp"
#include <ElementRenderer\CameraElement.hpp>
#include <ElementRenderer\StateChange.hpp>
#include "Application.hpp"

//#define OBB_BUG "jmalbert"

static ElementRenderer::Element::Callback *Old_Callbacks = NULL;

static ElementRenderer::Element::Callback
	Callbacks[1<<ElementRenderer::Element::CallbackBits]=
{
	NULL,
	CollisionVolume::DrawOBBRejectedCallback,
	CollisionVolume::DrawOBBTraversedCallback,
	CollisionVolume::DrawOBBStruckCallback,
	CollisionVolume::DrawOBBPolygonsTestedCallback,
	NULL,
	NULL,
	NULL
};

static RGBAColor Blue(0.5f, 0.5f, 1.0f, 0.3f);
static RGBAColor Green(0.5f, 1.0f, 0.5f, 0.3f);

static RGBAColor Sparkles[4]=
{
	RGBAColor(1.0f, 0.5f, 0.5f, 0.3f),
	RGBAColor(1.0f, 1.0f, 0.5f, 0.3f),
	RGBAColor(0.5f, 0.5f, 0.5f, 0.3f),
	RGBAColor(1.0f, 1.0f, 1.0f, 0.3f)
};

static bool Show_Line_vs_OBBs=false;
static bool Show_OBB_vs_OBBs=false;

static bool __stdcall Check_ShowLineVsOBBs() {return Show_Line_vs_OBBs;}
static bool __stdcall Check_ShowOBBVsOBBs() {return Show_OBB_vs_OBBs;}

static void
	__stdcall Activate_ShowLineVsOBBs()
{
	Show_Line_vs_OBBs = !Show_Line_vs_OBBs;
	if (Show_Line_vs_OBBs)
	{
		if (Show_OBB_vs_OBBs)
			Show_OBB_vs_OBBs = false;
		else
		{
			Old_Callbacks = ElementRenderer::Element::GetCallbackSet();
			ElementRenderer::Element::UseCallbackSet(Callbacks);
		}
	}
	else
	{
		ElementRenderer::Element::UseCallbackSet(Old_Callbacks);
		Old_Callbacks = NULL;
	}
}

static void
	__stdcall Activate_ShowOBBVsOBBs()
{
	Show_OBB_vs_OBBs = !Show_OBB_vs_OBBs;
	if (Show_OBB_vs_OBBs)
	{
		if (Show_Line_vs_OBBs)
			Show_Line_vs_OBBs = false;
		else
		{
			Old_Callbacks = ElementRenderer::Element::GetCallbackSet();
			ElementRenderer::Element::UseCallbackSet(Callbacks);
		}
	}
	else
	{
		ElementRenderer::Element::UseCallbackSet(Old_Callbacks);
		Old_Callbacks = NULL;
	}
}

DECLARE_TIMER(static, Poly_Ray_Cast);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::InitializeClass()
{
	AddDebuggerMenuItem(
		"Libraries\\Adept\\Show Lines vs. OBBs",
		Check_ShowLineVsOBBs,
		Activate_ShowLineVsOBBs,
		NULL
	);
	AddDebuggerMenuItem(
		"Libraries\\Adept\\Show OBB vs. OBBs",
		Check_ShowOBBVsOBBs,
		Activate_ShowOBBVsOBBs,
		NULL
	);

	Initialize_Timer(Poly_Ray_Cast, "Poly Raycast Time");
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::TerminateClass()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::CollisionVolume():
	Plug(DefaultData),
	m_children(NULL)
{
	Check_Pointer(this);

	//
	//--------------------------------
	// Read in the bounds and material
	//--------------------------------
	//
	m_worldSpaceBounds.localToParent = LinearMatrix4D::Identity;
	m_worldSpaceBounds.sphereRadius = -1.0f;
	m_localSpaceBounds.localToParent = LinearMatrix4D::Identity;
	m_localSpaceBounds.sphereRadius = -1.0f;
	m_material = GrassMaterial;
	m_entity = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::CollisionVolume(
	Entity *entity,
	MemoryStream *stream,
	int version
):
	Plug(DefaultData),
	m_children(NULL)
{
	Check_Pointer(this);
	Check_Object(entity);
	Check_Object(stream);

	//
	//--------------------------------
	// Read in the bounds and material
	//--------------------------------
	//
	*stream >> m_localSpaceBounds;
	Verify(m_localSpaceBounds.axisExtents.x >= 0.0f);
	Verify(m_localSpaceBounds.axisExtents.y >= 0.0f);
	Verify(m_localSpaceBounds.axisExtents.z >= 0.0f);
	*stream >> m_material;
	m_worldSpaceBounds.localToParent = LinearMatrix4D::Identity;
	m_worldSpaceBounds.sphereRadius = -1.0f;

	//
	//------------------------------
	// Read the name if one is there
	//------------------------------
	//
	if (version > 1)
	{
		MString joint_name;
		*stream >> joint_name;
	}
	m_entity = entity;

	//
	//-------------------------------------------------------------------
	// If we are reading version 2 or less, remap the material to the new
	// settings
	//-------------------------------------------------------------------
	//
	if (version < 3)
	{
		switch (m_material&0x1f)
		{
		case 0: //DirtMaterial,
			m_material |= BrownDirtMaterial;
			break;
		case 2: //RockMaterial,
			m_material &= ~0x1f;
			m_material |= RockMaterial;
			break;
		case 3: //SteelMaterial,
			m_material &= ~0x1f;
			m_material |= SteelMaterial;
			break;
		case 4: //BlacktopMaterial,
			m_material &= ~0x1f;
			m_material |= BlacktopMaterial;
			break;
		case 5: //UsMaterial,
			m_material &= ~0x1f;
			m_material |= UsMaterial;
			break;
		case 6: //ThemMaterial,
			m_material &= ~0x1f;
			m_material |= ThemMaterial;
			break;
		case 7: //VehicleMaterial,
			m_material &= ~0x1f;
			m_material |= SteelMaterial;
			break;
		case 8: //GlassMaterial,
			m_material &= ~0x1f;
			m_material |= GlassMaterial;
			break;
		case 9: //BrickMaterial,
			m_material &= ~0x1f;
			m_material |= ConcreteMaterial;
			break;
		case 10: //GrassMaterial,
			m_material &= ~0x1f;
			m_material |= GrassMaterial;
			break;
		case 11: //WoodMaterial,
			m_material &= ~0x1f;
			m_material |= WoodMaterial;
			break;
		case 12: //TreeMaterial,
			m_material &= ~0x1f;
			m_material |= WoodMaterial;
			break;
		case 13: //SwampMaterial,
			m_material &= ~0x1f;
			m_material |= GrassMaterial;
			break;
		case 14: //ConcreteMaterial,
			m_material &= ~0x1f;
			m_material |= ConcreteMaterial;
			break;
		case 15: //RoughMaterial,
			m_material &= ~0x1f;
			m_material |= GrassMaterial;
			break;
		case 16: //SnowMaterial,
			m_material &= ~0x1f;
			m_material |= SnowMaterial;
			break;
		case 17: //UnderbrushMaterial,
			m_material &= ~0x1f;
			m_material |= GrassMaterial;
			break;
		case 18: //ShallowWaterMaterial,
			m_material &= ~0x1f;
			m_material |= WaterMaterial;
			break;
		case 19: //MidWaterMaterial,
			m_material &= ~0x1f;
			m_material |= WaterMaterial;
			break;
		case 20: //DesertMaterial,
			m_material &= ~0x1f;
			m_material |= BrownDirtMaterial;
			break;
		case 21: //FlatSwampMaterial,
			m_material &= ~0x1f;
			m_material |= GrassMaterial;
			break;
		case 22: //ThickSwampMaterial,
			m_material &= ~0x1f;
			m_material |= GrassMaterial;
			break;
		case 23: //RedRockMaterial,
			m_material &= ~0x1f;
			m_material |= RockMaterial;
			break;
		case 24: //GreyDirtMaterial,
			m_material &= ~0x1f;
			m_material |= GreyDirtMaterial;
			break;
		case 25: //DarkDirtMaterial,
			m_material &= ~0x1f;
			m_material |= DarkBrownDirtMaterial;
			break;
		case 26: //DarkDesertMaterial,
			m_material &= ~0x1f;
			m_material |= DarkBrownDirtMaterial;
			break;
		case 27: //DarkRedRockMaterial,
			m_material &= ~0x1f;
			m_material |= DarkRockMaterial;
			break;
		case 28: //DarkRockMaterial,
			m_material &= ~0x1f;
			m_material |= DarkRockMaterial;
			break;
		case 29: //DarkConcreteMaterial,
			m_material &= ~0x1f;
			m_material |= DarkConcreteMaterial;
			break;
		case 30: //DarkGreyDirtMaterial,
			m_material &= ~0x1f;
			m_material |= DarkGreyDirtMaterial;
			break;
		}
	}

	//
	//-----------------------------------------------------------------------
	// Non-armatured objects can read in an arbitrary tree, all of which will
	// be attached to the same entity
	//-----------------------------------------------------------------------
	//
	BYTE count;
	*stream >> count;
	#if defined(_ARMOR)
		if (!count)
			Verify(m_localSpaceBounds.sphereRadius > 0.0f);
	#endif
	while (count-- > 0)
	{
		CollisionVolume *obb = new CollisionVolume(entity, stream, version);
		Check_Object(obb);
		m_children.Add(obb);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::CollisionVolume(
	Entity *main_entity,
	ChildFinder find_function,
	Entity *joint_entity,
	MemoryStream *stream,
	int version
):
	Plug(DefaultData),
	m_children(NULL)
{
	Check_Pointer(this);
	Check_Object(main_entity);
	Check_Pointer(find_function);
	Check_Object(joint_entity);
	Check_Object(stream);

	//
	//--------------------------------
	// Read in the bounds and material
	//--------------------------------
	//
	*stream >> m_localSpaceBounds;
	Verify(m_localSpaceBounds.axisExtents.x >= 0.0f);
	Verify(m_localSpaceBounds.axisExtents.y >= 0.0f);
	Verify(m_localSpaceBounds.axisExtents.z >= 0.0f);
	*stream >> m_material;
	m_worldSpaceBounds.localToParent = LinearMatrix4D::Identity;
	m_worldSpaceBounds.sphereRadius = -1.0f;

	//
	//---------------------------------------------------------------
	// All named OBBs represent parental OBBs and must have no radius
	//---------------------------------------------------------------
	//
	Verify(version > 1);
	MString joint_name;
	*stream >> joint_name;
	if (joint_name)
	{
		Verify(m_localSpaceBounds.sphereRadius == 0.0f);
		joint_entity = (*find_function)(main_entity, joint_name);
		#if defined(OBB_BUG)
			SPEW((OBB_BUG, "Building null node %s", (char*)joint_name));
		#endif
		#if defined(LAB_ONLY)
			if (!joint_entity)
				STOP(("Error - couldn't find joint %s", (char*)joint_name));
		#endif
		if (joint_entity != main_entity)
		{
			Check_Object(joint_entity);
			Verify(!joint_entity->hierarchicalVolume);
			joint_entity->hierarchicalVolume = this;
		}
	}

	//
	//----------------------------------
	// All unnamed OBBs must have bounds
	//----------------------------------
	//
	else
	{
		Verify(m_localSpaceBounds.sphereRadius > 0.0f);
		#if defined(OBB_BUG)
			SPEW((OBB_BUG, "Building %f radius obb node", m_localSpaceBounds.sphereRadius));
		#endif
	}
	m_entity = joint_entity;
	Check_Object(m_entity);

	//
	//------------------------------
	// Read in the hierarchy of OBBs
	//------------------------------
	//
	BYTE count;
	*stream >> count;
	#if defined(_ARMOR)
		if (!count)
			Verify(m_localSpaceBounds.sphereRadius > 0.0f);
	#endif
	while (count-- > 0)
	{
		CollisionVolume *obb =
			new CollisionVolume(main_entity, find_function, joint_entity, stream, version);
		Check_Object(obb);
		m_children.Add(obb);

		//
		//------------------------------------------------------------------------
		// If we don't have a joint name, and the child is attached to our entity,
		// go ahead and multiply his bounds into our space
		//------------------------------------------------------------------------
		//
		if (!joint_name && m_entity==obb->m_entity)
		{
			OBB new_obb;
			new_obb.Multiply(obb->m_localSpaceBounds, m_localSpaceBounds.localToParent);
			obb->m_localSpaceBounds = new_obb;
		}
	}
	#if defined(OBB_BUG)
		if (joint_name.GetLength() > 0)
			SPEW((OBB_BUG, "Done building null node %s", (char*)joint_name));
		else
			SPEW((OBB_BUG, "Done building %f radius obb node", m_localSpaceBounds.sphereRadius));
	#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume::~CollisionVolume()
{
	Check_Object(this);

	//
	//----------------------
	// Delete the child OBBs
	//----------------------
	//
	if (m_entity)
	{
		if (!m_entity->solidVolume && m_entity->hierarchicalVolume == this)
			m_entity->hierarchicalVolume = NULL;
	}
	m_children.DeletePlugs();
}

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

	//
	//---------------------------------
	// Save out the bounds and material
	//---------------------------------
	//
	*stream << m_localSpaceBounds;
	*stream << m_material;
	*stream << MString(GetName());

	//
	//--------------------------------
	// Save out the number of children
	//--------------------------------
	//
	BYTE count = 0;
	ChainIteratorOf<CollisionVolume*> children(&m_children);
	CollisionVolume *volume;
	while ((volume = children.ReadAndNext()) != NULL)
	{
		Check_Object(volume);
		++count;
		Verify(count < 255);
	}
	*stream << count;

	//
	//----------------------
	// Save out the children
	//----------------------
	//
	children.First();
	while ((volume = children.ReadAndNext()) != NULL)
	{
		Check_Object(volume);
		volume->Save(stream);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char* CollisionVolume::GetName()
{
	Check_Object(this); return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::FindEntityToWorld(
	Stuff::LinearMatrix4D *entity_to_world,
	Entity *base,
	const Stuff::LinearMatrix4D &base_to_world
)
{
	Check_Object(this);
	Check_Object(base);

	//
	//--------------------------------------------------------------------------
	// If the base is our entity, just copy the base to world into the entity to
	// world variable
	//--------------------------------------------------------------------------
	//
	if (base == m_entity)
	{
		*entity_to_world = base_to_world;
		return;
	}

	//
	//--------------------------------------------------------------------------
	// Otherwise, we have to concatenate up the entity chain until we get to the
	// base entity
	//--------------------------------------------------------------------------
	//
	LinearMatrix4D entity_to_base = m_entity->GetLocalToParent();
	Entity *entity = m_entity->GetParentEntity();
	while (entity != base)
	{
		Check_Object(entity);
		*entity_to_world = entity_to_base;
		entity_to_base.Multiply(*entity_to_world, entity->GetLocalToParent());
		entity = entity->GetParentEntity();
	}
	entity_to_world->Multiply(entity_to_base, base_to_world);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume* CollisionVolume::CastRay(
	Entity::CollisionQuery *query,
	Entity *base,
	const Stuff::LinearMatrix4D &base_to_world
)
{
	Check_Object(this);
	Check_Object(query);

	//
	//--------------------------------
	// See if the entity can be struck
	//--------------------------------
	//
	if (IsDestroyed())
		return NULL;
	#if defined(LAB_ONLY)
		ElementRenderer::Element *element = m_entity->GetElement();
		Check_Object(element);
	#endif

	//
	//-------------------------------
	// Figure out where our entity is
	//-------------------------------
	//
	LinearMatrix4D entity_to_world;
	FindEntityToWorld(&entity_to_world, base, base_to_world);
	m_worldSpaceBounds.Multiply(m_localSpaceBounds, entity_to_world);

	//
	//-------------------
	// See if we this box
	//-------------------
	//
	Line3D *line = query->m_line;
	Check_Object(line);
	int axis=0;
	Scalar len=-1.0f;
	if (m_worldSpaceBounds.sphereRadius > 0.0f)
	{
		len = line->GetDistanceTo(m_worldSpaceBounds, &axis);
		if (len == -1.0 || len > line->m_length)
		{
			#if defined(LAB_ONLY)
				if (Show_Line_vs_OBBs)
				{
					Verify(element->GetCallbackSet() == Callbacks);
					unsigned callback = element->GetCallbackIndex();
					if (callback < DrawOBBRejected)
						element->SetCallbackIndex(DrawOBBRejected);
				}
			#endif
			return NULL;
		}
		else
		{
			#if defined(LAB_ONLY)
				if (Show_Line_vs_OBBs)
				{
					Verify(element->GetCallbackSet() == Callbacks);
					unsigned callback = element->GetCallbackIndex();
					if (callback < DrawOBBTraversed)
						element->SetCallbackIndex(DrawOBBTraversed);
				}
			#endif
		}
	}

	//
	//----------------------------------------------------------
	// If we did hit the box, and there are no children, we done
	//----------------------------------------------------------
	//
	ChainIteratorOf<CollisionVolume*> children(&m_children);
	if (!children.GetCurrent()) 
	{
		Verify(m_worldSpaceBounds.sphereRadius > 0.0f);
		query->m_material = static_cast<BYTE>(m_material & e_MaterialMask);
		bool exit_now;
		if (!(m_material&e_GoodEnough) && m_entity->GetInterestLevel() == Entity::RenderingInterestLevel)
		{
			RAYCAST_LOGIC("vs. Polygons");
			Start_Timer(Poly_Ray_Cast);
			exit_now = m_entity->GetElement()->CastRay(query);
			Stop_Timer(Poly_Ray_Cast);
			Verify(!exit_now || *query->m_normal * query->m_line->m_direction < -SMALL);
		}
		else
		{
			Verify(len >= 0.0f);
			query->m_line->m_length = len;

			//
			//-------------------------------------------------------------------------------
			// Since we hit the OBB, we have to figure out the new normal.  All the collision
			// told us was which local-space axis was struck, so lets just go get it from the
			// matrix of the world obb
			//-------------------------------------------------------------------------------
			//
			query->m_normal->x = m_worldSpaceBounds.localToParent(axis, 0);
			query->m_normal->y = m_worldSpaceBounds.localToParent(axis, 1);
			query->m_normal->z = m_worldSpaceBounds.localToParent(axis, 2);
			query->m_normal->Normalize(*query->m_normal);
			if (*query->m_normal*line->m_direction >= -SMALL)
				query->m_normal->Negate(*query->m_normal);
			exit_now = true;
			Verify(*query->m_normal * query->m_line->m_direction < -SMALL);
		}
		query->m_data = NULL;
		#if defined(LAB_ONLY)
			if (Show_Line_vs_OBBs)
			{
				Verify(element->GetCallbackSet() == Callbacks);
				if (!(m_material&e_GoodEnough) && m_entity->GetInterestLevel() == Entity::RenderingInterestLevel)
					element->SetCallbackIndex(DrawOBBPolygonsTested);
				else
					element->SetCallbackIndex(DrawOBBStruck);
			}
		#endif
		if (exit_now)
			return this;
	}

	//
	//-------------------------------------------------------------
	// Check all our children, and keep the hit that is the closest
	//-------------------------------------------------------------
	//
	CollisionVolume* result = NULL;
	CollisionVolume *volume;
	while ((volume = children.ReadAndNext()) != NULL)
	{
		Check_Object(volume);
		CollisionVolume *temp = volume->CastRay(query, m_entity, entity_to_world);
		if (temp)
			result = temp;
	}
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
CollisionVolume* CollisionVolume::CollideOBB(
	CollisionVolume *solid,
	Entity *base,
	const Stuff::LinearMatrix4D &base_to_world
)
{
	Check_Object(this);
	Check_Object(solid);

	//
	//--------------------------------
	// See if this is a trivial reject
	//--------------------------------
	//
	if (IsDestroyed())
		return NULL;

	//
	//-------------------------------
	// Figure out where our entity is
	//-------------------------------
	//
	LinearMatrix4D entity_to_world;
	FindEntityToWorld(&entity_to_world, base, base_to_world);
	m_worldSpaceBounds.Multiply(m_localSpaceBounds, entity_to_world);

	//
	//-------------------
	// See if we this box
	//-------------------
	//
	Check_Object(m_entity);
	Check_Object(solid->m_entity);
	#if defined(LAB_ONLY)
		ElementRenderer::Element *our_element = m_entity->GetElement();
		Check_Object(our_element);
		ElementRenderer::Element *their_element = solid->m_entity->GetElement();
		Check_Object(their_element);
	#endif
	if (m_worldSpaceBounds.sphereRadius > 0.0f)
	{
		int axis = solid->m_worldSpaceBounds.FindSeparatingAxis(m_worldSpaceBounds);
		if (axis != OBB::NoSeparation)
		{
			#if defined(LAB_ONLY)
				if (Show_OBB_vs_OBBs)
				{
					Verify(our_element->GetCallbackSet() == Callbacks);
					unsigned callback = our_element->GetCallbackIndex();
					if (callback < DrawOBBRejected)
						our_element->SetCallbackIndex(DrawOBBRejected);
					callback = their_element->GetCallbackIndex();
					if (callback < DrawOBBRejected)
						their_element->SetCallbackIndex(DrawOBBRejected);
				}
			#endif
			return NULL;
		}
	}

	//
	//----------------------------------------------------------
	// If we did hit the box, and there are no children, we done
	//----------------------------------------------------------
	//
	ChainIteratorOf<CollisionVolume*> children(&m_children);
	if (!children.GetCurrent())
	{
		Verify(m_worldSpaceBounds.sphereRadius > 0.0f);
		#if defined(LAB_ONLY)
			if (Show_OBB_vs_OBBs)
			{
				Verify(our_element->GetCallbackSet() == Callbacks);
				our_element->SetCallbackIndex(DrawOBBStruck);
				their_element->SetCallbackIndex(DrawOBBStruck);
			}
		#endif
		return this;
	}

	//
	//-------------------------------------------------------------
	// Check all our children, and keep the hit that is the closest
	//-------------------------------------------------------------
	//
	#if defined(LAB_ONLY)
		if (Show_OBB_vs_OBBs)
		{
			Verify(our_element->GetCallbackSet() == Callbacks);
			unsigned callback = our_element->GetCallbackIndex();
			if (callback < DrawOBBTraversed)
				our_element->SetCallbackIndex(DrawOBBTraversed);
		}
	#endif
	CollisionVolume* result = NULL;
	CollisionVolume *volume;
	while ((volume = children.ReadAndNext()) != NULL)
	{
		Check_Object(volume);
		CollisionVolume *temp = volume->CollideOBB(solid, m_entity, entity_to_world);
		if (temp)
			result = temp;
	}
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::FindSolidsWithin(
	const Stuff::Sphere &sphere,
	WithinCallback callback,
	Entity *original_caller,
	Entity *base,
	const Stuff::LinearMatrix4D &base_to_world
)
{
	Check_Object(this);
	Check_Object(&sphere);
	Check_Pointer(callback);

	//
	//--------------------------------
	// See if this is a trivial reject
	//--------------------------------
	//
	if (IsDestroyed())
		return;

	//
	//-------------------------------
	// Figure out where our entity is
	//-------------------------------
	//
	LinearMatrix4D entity_to_world;
	FindEntityToWorld(&entity_to_world, base, base_to_world);
	m_worldSpaceBounds.Multiply(m_localSpaceBounds, entity_to_world);

	//
	//---------------------------------
	// See if this box is in the sphere
	//---------------------------------
	//
	if (m_worldSpaceBounds.sphereRadius > 0.0f)
	{
		Sphere us(m_worldSpaceBounds);
		if (!sphere.Intersects(us))
			return;
	}

	//
	//-----------------------
	// Check all our children
	//-----------------------
	//
	ChainIteratorOf<CollisionVolume*> children(&m_children);
	if (!children.GetCurrent())
	{
		Verify(m_worldSpaceBounds.sphereRadius > 0.0f);
		(*callback)(this, original_caller);
	}
	else
	{
		CollisionVolume *volume;
		while ((volume = children.ReadAndNext()) != NULL)
		{
			Check_Object(volume);
			volume->FindSolidsWithin(
				sphere,
				callback,
				original_caller,
				m_entity,
				entity_to_world
			);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int CollisionVolume::ReadOBBVersion(MemoryStream *stream)
{
	Check_Object(stream);

	//
	//------------------------------------------------------------------------
	// See if this file has an erf signature.  If so, the next int will be the
	// version number.  If not, assume it is version 1 and rewind the file
	//------------------------------------------------------------------------
	//
	int version = CurrentOBBVersion+1;
	int obb_signature;
	*stream >> obb_signature;
	if (obb_signature == 'OBB#')
		*stream >> version;
	else
		stream->RewindPointer(sizeof(obb_signature));
	if (version > CurrentOBBVersion)
		STOP(("Application must be rebuilt to use this version of OBB data"));
	return version;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::WriteOBBVersion(MemoryStream *stream)
{
	Check_Object(stream);
	*stream << 'OBB#' << static_cast<int>(CurrentOBBVersion);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBRejectedCallback(
	ElementRenderer::CameraElement *camera,
	const ElementRenderer::StateChange *state,
	int culling_state,
	ElementRenderer::Element *element
)
{
	Check_Object(camera);
	Check_Object(state);
	Check_Object(element);

	//
	//--------------------------------
	// Figure out the collision volume
	//--------------------------------
	//
	Entity *entity = Cast_Object(Entity*, element->GetClientData());
	CollisionVolume *solid = entity->GetHierarchicalVolume();
	if (!solid || Show_OBB_vs_OBBs)
		solid = entity->GetSolidVolume();
	if (solid)
	{
		Check_Object(solid);

		//
		//-----------------
		// Draw blue bounds
		//-----------------
		//
		if (solid->m_localSpaceBounds.sphereRadius>0.0f)
		{
			solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
			ElementRenderer::Element::DrawOBB(
				solid->m_worldSpaceBounds,
				Blue,
				camera,
				state,
				0x3f
			);
		}
		else
		{
			ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
			CollisionVolume *child;
			while ((child = children.ReadAndNext()) != NULL)
			{
				Check_Object(child);
				if (child->m_localSpaceBounds.sphereRadius > 0.0f && child->m_entity == solid->m_entity)
				{
					child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
					ElementRenderer::Element::DrawOBB(
						child->m_worldSpaceBounds,
						Blue,
						camera,
						state,
						0x3f
					);
				}
			}
		}
	}
	element->SetCallbackIndex(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBTraversedCallback(
	ElementRenderer::CameraElement *camera,
	const ElementRenderer::StateChange *state,
	int culling_state,
	ElementRenderer::Element *element
)
{
	Check_Object(camera);
	Check_Object(state);
	Check_Object(element);

	//
	//--------------------------------
	// Figure out the collision volume
	//--------------------------------
	//
	Entity *entity = Cast_Object(Entity*, element->GetClientData());
	CollisionVolume *solid = entity->GetHierarchicalVolume();
	if (!solid || Show_OBB_vs_OBBs)
		solid = entity->GetSolidVolume();

	//
	//-------------------------------------------------------------------------
	// This branchs is used by non-articulated objects.  If there are children,
	// draw them first in yellow
	//-------------------------------------------------------------------------
	//
	if (solid)
	{
		Check_Object(solid);
		if (solid->m_localSpaceBounds.sphereRadius > 0.0f)
		{
			ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
			if (children.GetCurrent()==NULL)
			{
				solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
				ElementRenderer::Element::DrawOBB(
					solid->m_worldSpaceBounds,
					Blue,
					camera,
					state,
					0x3f
				);
			}
			else
			{
				CollisionVolume *child;
				while ((child = children.ReadAndNext()) != NULL)
				{
					Check_Object(child);
					if (child->m_entity != solid->m_entity)
						continue;
					Verify(child->m_localSpaceBounds.sphereRadius > 0.0f);
					child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
					ElementRenderer::Element::DrawOBB(
						child->m_worldSpaceBounds,
						Blue,
						camera,
						state,
						0x3f
					);
				}
			}
		}

		//
		//-----------------------------------------------------------------------
		// This branch is used by articulated objects.  If this joint was struck,
		// only draw our children who are also attached to our entity
		//-----------------------------------------------------------------------
		//
		else
		{
			ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
			CollisionVolume *child;
			while ((child = children.ReadAndNext()) != NULL)
			{
				Check_Object(child);
				if (child->m_entity != solid->m_entity)
					continue;

				//
				//---------------------------------------------------------------
				// Check each child to see if it has children itself.  If not, go
				// ahead and draw it
				//---------------------------------------------------------------
				//
				ChainIteratorOf<CollisionVolume*> grandchildren(&child->m_children);
				if (!grandchildren.GetCurrent())
				{
					Verify(child->m_localSpaceBounds.sphereRadius > 0.0f);
					child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
					ElementRenderer::Element::DrawOBB(
						child->m_worldSpaceBounds,
						Blue,
						camera,
						state,
						0x3f
					);
				}
				else
				{
					CollisionVolume *grandchild;
					while ((grandchild = grandchildren.ReadAndNext()) != NULL)
					{
						Check_Object(grandchild);
						Verify(grandchild->m_localSpaceBounds.sphereRadius > 0.0f);
						grandchild->m_worldSpaceBounds.Multiply(grandchild->m_localSpaceBounds, entity->GetLocalToWorld());
						ElementRenderer::Element::DrawOBB(
							grandchild->m_worldSpaceBounds,
							Blue,
							camera,
							state,
							0x3f
						);
					}
				}
			}
		}
	}
	element->SetCallbackIndex(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBStruckCallback(
	ElementRenderer::CameraElement *camera,
	const ElementRenderer::StateChange *state,
	int culling_state,
	ElementRenderer::Element *element
)
{
	Check_Object(camera);
	Check_Object(state);
	Check_Object(element);

	//
	//--------------------------------
	// Figure out the collision volume
	//--------------------------------
	//
	Entity *entity = Cast_Object(Entity*, element->GetClientData());
	CollisionVolume *solid = entity->GetHierarchicalVolume();
	if (!solid || Show_OBB_vs_OBBs)
		solid = entity->GetSolidVolume();

	//
	//-------------------------------------------------------------------------
	// This branchs is used by non-articulated objects.  If there are children,
	// draw them first in yellow
	//-------------------------------------------------------------------------
	//
	if (solid)
	{
		Check_Object(solid);
		if (solid->m_localSpaceBounds.sphereRadius > 0.0f)
		{
			ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
			if (children.GetCurrent()==NULL)
			{
				solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
				ElementRenderer::Element::DrawOBB(
					solid->m_worldSpaceBounds,
					Sparkles[0],
					camera,
					state,
					0x3f
				);
			}
			else
			{
				CollisionVolume *child;
				int sparkle=0;
				while ((child = children.ReadAndNext()) != NULL)
				{
					Check_Object(child);
					if (child->m_entity != solid->m_entity)
						continue;
					Verify(child->m_localSpaceBounds.sphereRadius > 0.0f);
					child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
					ElementRenderer::Element::DrawOBB(
						child->m_worldSpaceBounds,
						Sparkles[(sparkle++)&3],
						camera,
						state,
						0x3f
					);
				}
			}
		}

		//
		//-----------------------------------------------------------------------
		// This branch is used by articulated objects.  If this joint was struck,
		// only draw our children who are also attached to our entity
		//-----------------------------------------------------------------------
		//
		else
		{
			ChainIteratorOf<CollisionVolume*> children(&solid->m_children);
			CollisionVolume *child;
			int sparkle = 0;
			while ((child = children.ReadAndNext()) != NULL)
			{
				Check_Object(child);
				if (child->m_entity != solid->m_entity)
					continue;

				//
				//---------------------------------------------------------------
				// Check each child to see if it has children itself.  If not, go
				// ahead and draw it
				//---------------------------------------------------------------
				//
				ChainIteratorOf<CollisionVolume*> grandchildren(&child->m_children);
				if (!grandchildren.GetCurrent())
				{
					if (child->m_localSpaceBounds.sphereRadius > 0.0f)
					{
						child->m_worldSpaceBounds.Multiply(child->m_localSpaceBounds, entity->GetLocalToWorld());
						ElementRenderer::Element::DrawOBB(
							child->m_worldSpaceBounds,
							Sparkles[(sparkle++)&3],
							camera,
							state,
							0x3f
						);
					}
				}
				else
				{
					CollisionVolume *grandchild;
					while ((grandchild = grandchildren.ReadAndNext()) != NULL)
					{
						Check_Object(grandchild);
						Verify(grandchild->m_localSpaceBounds.sphereRadius > 0.0f);
						grandchild->m_worldSpaceBounds.Multiply(grandchild->m_localSpaceBounds, entity->GetLocalToWorld());
						ElementRenderer::Element::DrawOBB(
							grandchild->m_worldSpaceBounds,
							Sparkles[(sparkle++)&3],
							camera,
							state,
							0x3f
						);
					}
				}
			}
		}
	}
	element->SetCallbackIndex(0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void CollisionVolume::DrawOBBPolygonsTestedCallback(
	ElementRenderer::CameraElement *camera,
	const ElementRenderer::StateChange *state,
	int culling_state,
	ElementRenderer::Element *element
)
{
	Check_Object(camera);
	Check_Object(state);
	Check_Object(element);

	//
	//--------------------------------
	// Figure out the collision volume
	//--------------------------------
	//
	Entity *entity = Cast_Object(Entity*, element->GetClientData());
	CollisionVolume *solid = entity->GetHierarchicalVolume();
	if (!solid || Show_OBB_vs_OBBs)
		solid = entity->GetSolidVolume();

	if (solid)
	{
		Check_Object(solid);
		Verify(solid->m_localSpaceBounds.sphereRadius > 0.0f);
		solid->m_worldSpaceBounds.Multiply(solid->m_localSpaceBounds, entity->GetLocalToWorld());
		ElementRenderer::Element::DrawOBB(
			solid->m_worldSpaceBounds,
			Green,
			camera,
			state,
			0x3f
		);
	}
	element->SetCallbackIndex(0);
}
