/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : WWPhys                                                       *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/wwphys/rbody.cpp                             $*
 *                                                                                             *
 *                       Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                     $Modtime:: 3/28/02 11:00a                                              $*
 *                                                                                             *
 *                    $Revision:: 119                                                         $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   RigidBodyClass::RigidBodyClass -- Constructor for RigidBodyClass                          *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "rbody.h"
#include "pscene.h"
#include "boxrobj.h"
#include "physcoltest.h"
#include "physinttest.h"
#include "physcon.h"
#include "octbox.h"
#include "bitstream.h"
#include "persistfactory.h"
#include "simpledefinitionfactory.h"
#include "wwphysids.h"
#include "wwhack.h"
#include "wwprofile.h"
#include "hlod.h"
#include "physcontrol.h"
#include "phys3.h"
#include <stdio.h>


DECLARE_FORCE_LINK(rbody);


#define RBODY_DEBUGGING					0
#define RBODY_DEBUG_FILTER				(stricmp(Model->Get_Name(),"V_GDI_ORCA_M") == 0) && (PhysicsSceneClass::Get_Instance()->Is_Debug_Display_Enabled())

#define JITTER_ELIMINATION_CODE		0


float RigidBodyClass::_CorrectionTime = 1.0f;

/***********************************************************************************************
**
** RBodyHistoryClass Implementation
**
***********************************************************************************************/
																						
/*
** RBodyHistoryClass Parameters
** Control the implementation of the phys3 history tracking system with the following 
** parameters.
*/
const int		RBODY_SNAPSHOT_COUNT = 16;					// must be power of 2!
const float		RBODY_HISTORY_MIN_TIME = 0.75f;			// seconds of history to store

const int		RBODY_SNAPSHOT_MASK = RBODY_SNAPSHOT_COUNT - 1; 
const float		RBODY_SNAPSHOT_INTERVAL = RBODY_HISTORY_MIN_TIME / RBODY_SNAPSHOT_COUNT;

#define RBODYHISTORY_NO_CORRECTION			0
#define RBODYHISTORY_ABSOLUTE_CORRECTION	0
#define RBODYHISTORY_LERP_CORRECTION		1


/**
** RBodyHistoryClass
** This class is used to store a history of the state of a RBody object.  The network
** update code uses this history to intelligently update the state of the object when
** a packet is received.  
*/
class RBodyHistoryClass
{
public:
	RBodyHistoryClass(void);
	~RBodyHistoryClass(void);

	void		Init(const RigidBodyStateStruct & state);
	void		Update_History(const RigidBodyStateStruct & state, float dt);
	void		Compute_Old_State(float dt,RigidBodyStateStruct * set_state);
	void		Apply_Correction(const RigidBodyStateStruct & error,float fraction);

	int		History_Count(void) { return RBODY_SNAPSHOT_COUNT; }
	const RigidBodyStateStruct & Get_Historical_State(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)]; }
	const Vector3 & Get_Historical_Position(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)].Position; }

	void		Find_Nearest_State(const RigidBodyStateStruct & input,RigidBodyStateStruct * output);

private:

	int		Wrap_Index(int index) { return (index + RBODY_SNAPSHOT_COUNT) & RBODY_SNAPSHOT_MASK; }
	
	struct StateSnapshotStruct : public RigidBodyStateStruct
	{
	public:
		StateSnapshotStruct(void) : Age(0) { }
		StateSnapshotStruct(const StateSnapshotStruct & that) : RigidBodyStateStruct(that) { Age = that.Age; }
		StateSnapshotStruct & operator = (const StateSnapshotStruct & that) { RigidBodyStateStruct::operator = (that); Age = that.Age; return *this; }
		StateSnapshotStruct & operator = (const RigidBodyStateStruct & that) { RigidBodyStateStruct::operator = (that); Age = 0.0f; return *this; }

		void					Lerp(const StateSnapshotStruct & a, const StateSnapshotStruct & b, float fraction);
		void					Update_Age(float dt) { Age += dt; }

		float					Age;
	};

	StateSnapshotStruct *	SnapshotArray;
	int							HeadIndex;			// history buffer is circular, this is the "head"
};

RBodyHistoryClass::RBodyHistoryClass(void) :
	SnapshotArray(NULL),
	HeadIndex(0)
{
	SnapshotArray = new StateSnapshotStruct[RBODY_SNAPSHOT_COUNT];
}

RBodyHistoryClass::~RBodyHistoryClass(void)
{
	if (SnapshotArray != NULL) {
		delete[] SnapshotArray;
		SnapshotArray = NULL;
	}
}

void RBodyHistoryClass::Init(const RigidBodyStateStruct & state)
{
	int next_older_index = Wrap_Index(HeadIndex + 1);

	SnapshotArray[HeadIndex] = state;
	SnapshotArray[HeadIndex].Age = 0.0f;
	SnapshotArray[next_older_index] = state;
	SnapshotArray[next_older_index].Age = 1000.0f;
}

void RBodyHistoryClass::Update_History(const RigidBodyStateStruct & state, float dt)
{
	int next_older_index = Wrap_Index(HeadIndex + 1);

	/*
	** See if enough time has passed so we need to ratchet forward in the circular buffer
	*/
	if (SnapshotArray[next_older_index].Age + dt > RBODY_SNAPSHOT_INTERVAL) {
		HeadIndex = Wrap_Index(HeadIndex - 1);
		SnapshotArray[HeadIndex].Age = 0;
	}

	SnapshotArray[HeadIndex] = state;

	/*
	** Add age to all existing snapshots
	*/
	for (int i=0; i<RBODY_SNAPSHOT_COUNT; i++) {
		if (i != HeadIndex) {
			SnapshotArray[i].Update_Age(dt);
		}
	}
}

void RBodyHistoryClass::Compute_Old_State(float t,RigidBodyStateStruct * set_state)
{
	WWASSERT(set_state != NULL);
	int index = HeadIndex;
	bool done = false;
	
	while (!done) {
		if (SnapshotArray[index].Age <= t) {
			index = Wrap_Index(index + 1);
			
			/*
			** past the end of our history, just return the oldest known state
			*/
			if (index == HeadIndex) {
				int tail_index = Wrap_Index(HeadIndex - 1);
				*set_state = SnapshotArray[tail_index];
				return;
			}
		
		} else {
			done = true;
		}
	}
	
	int prev_index = Wrap_Index(index - 1);
	int next_index = index;

	float fraction = (t - SnapshotArray[prev_index].Age) / (SnapshotArray[next_index].Age - SnapshotArray[prev_index].Age);
	RigidBodyStateStruct::Lerp(SnapshotArray[prev_index],SnapshotArray[next_index],fraction,set_state);
}

void RBodyHistoryClass::Apply_Correction(const RigidBodyStateStruct & error,float fraction)
{
#if (!RBODY_HISTORY_NO_CORRECTION)
	for (int counter=0; counter<RBODY_SNAPSHOT_COUNT; counter++) {
		int index = Wrap_Index(HeadIndex + counter);

		RigidBodyStateStruct ideal_state = SnapshotArray[index];
		ideal_state.Position += error.Position;
		ideal_state.Orientation = ideal_state.Orientation * error.Orientation;
		ideal_state.LMomentum += error.LMomentum;
		ideal_state.AMomentum += error.AMomentum;

		float lerp_fraction = 0.0f;
#if RBODYHISTORY_ABSOLUTE_CORRECTION
		lerp_fraction = fraction;
#endif
#if RBODYHISTORY_LERP_CORRECTION
		lerp_fraction = fraction * (RBODY_HISTORY_MIN_TIME - SnapshotArray[index].Age) / RBODY_HISTORY_MIN_TIME;
#endif
		RigidBodyStateStruct::Lerp(SnapshotArray[index],ideal_state,lerp_fraction,&(SnapshotArray[index]));
	}
#endif
}

void RBodyHistoryClass::Find_Nearest_State(const RigidBodyStateStruct & input,RigidBodyStateStruct * output)
{
	/*
	** Find the nearest line segment
	*/
	float min_dist = 100000.0f;
	float result_fraction = 0.0f;
	int result_index0 = -1;
	int result_index1 = -1;

	for (int counter=0; counter<RBODY_SNAPSHOT_COUNT-1; counter++) {
		int index0 = Wrap_Index(HeadIndex + counter);
		int index1 = Wrap_Index(index0 + 1);

		LineSegClass segment(SnapshotArray[index0].Position,SnapshotArray[index1].Position);

		Vector3 point = segment.Find_Point_Closest_To(input.Position);
		float dist = (point - input.Position).Length();
		float fraction = 0.0f;
		if (segment.Get_Length() > 0.0f) {
			fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
		}

#if 0
		/*
		** Ignore points with velocity more than 90 deg away from server vel
		*/
		float vdot = 0.0f;
		Vector3 history_vel;
		Vector3::Lerp(SnapshotArray[index0].Velocity,SnapshotArray[index1].Velocity,fraction,&history_vel);
		vdot = Vector3::Dot_Product(vel,history_vel); 
#endif
		
		if ((dist < min_dist) /*&& (vdot >= 0.0f)*/) {
			min_dist = dist;
			result_fraction = fraction;
			result_index0 = index0;
			result_index1 = index1;
		}
	}
	
	WWASSERT((result_index0 >= 0) && (result_index0 < RBODY_SNAPSHOT_COUNT));
	WWASSERT((result_index1 >= 0) && (result_index1 < RBODY_SNAPSHOT_COUNT));
	WWASSERT(output != NULL);
	RigidBodyStateStruct::Lerp(SnapshotArray[result_index0],SnapshotArray[result_index1],result_fraction,output);
}


void RBodyHistoryClass::StateSnapshotStruct::Lerp(const StateSnapshotStruct & a, const StateSnapshotStruct & b, float fraction)
{
	RigidBodyStateStruct::Lerp(a,b,fraction,this);
	Age = WWMath::Lerp(a.Age,b.Age,fraction);
}
																						
																						
																						

				
/***********************************************************************************************
**
** RigidBodyClass Implementation
**
***********************************************************************************************/
float DEFAULT_CONTACT_THICKNESS	= 0.2f;

#define IMPULSE_COLOR		Vector3(1,0,0)
#define LMOMENTUM_COLOR		Vector3(0,1,0)
#define AMOMENTUM_COLOR		Vector3(0,0,1)

												
/*
** Declare a PersistFactory for RigidBodyClasses
*/
SimplePersistFactoryClass<RigidBodyClass,PHYSICS_CHUNKID_RIGIDBODY>	_RigidBodyFactory;


/*
** Chunk ID's used by RigidBodyClass
*/
enum
{
	RBODY_CHUNK_MOVEABLE							= 0x00800900,
	RBODY_CHUNK_VARIABLES,

	RBODY_VARIABLE_ODESYSTEM_PTR				= 0x00,
	RBODY_VARIABLE_IBODY,
	RBODY_VARIABLE_IBODYINV,
	RBODY_VARIABLE_STATE_POSITION,
	RBODY_VARIABLE_STATE_ORIENTATION,
	RBODY_VARIABLE_STATE_LMOMENTUM,
	RBODY_VARIABLE_STATE_AMOMENTUM,
	RBODY_VARIABLE_CONTACT_STIFFNESS,		// OBSOLETE!
	RBODY_VARIABLE_CONTACT_DAMPING,			// OBSOLETE!
	RBODY_VARIABLE_CONTACT_LENGTH,
};										
						

/*
** Rigid Body Dynamics 
** 
** NOTES:
** - No matter what numerical integration technique I use, it seems that the 
**   orientation drifts.  I have to re-normalize frequently so the higher order 
**   integrators are not very attractive (more frequent calls to Compute_Forces...).  
** - Clamping angular velocity to an artificial maximum seems to cause problems 
**   as well.  I'm not sure why but in the test app, it causes "jumpiness" in
**   the simulation.
** - Impact/Contact plan:  Integrate a new state, ignoring collision but computing
**   penalty forces.  Then break that change in state into a change in orientation
**   along with a change in position.  Try the change in orientation, checking for
**   penetration; if penetration occurs, discard the orientation change.  Try
**   the translation, using the swept OBBox.  Compute impact impluse if contact
**   occurs.  When testing for penetration/impact use the normal collision box.
**   When computing penalty/contact forces use a slightly larger box and collect
**   the points inside the box and the box points "behind" the triangles.
**
** May 3, 1999
** - The idea about breaking the state update into separate orientation and translation
**   updates is completely flawed.  It just doesn't work.  Trying to find a new way
**   of detecting collisions without resorting to the binary search through time!
** - Binary searching for the time-of-collision requires a different sort of collision
**   query.  While iterating, it requires a simpler intersection query.  Once finished
**   iterating (found the TOC) it requires a query that will return what point is
**   causing the collision.
**
** June 25, 1999
** - Completely rigid motion may be less efficient in high-poly environments.  For
**   example, a rigid object sliding along the ground will experience a collision (and
**   therefore rewind the integrator) at every single new non-coplanar triangle.
** - On the other hand, the buggy seems to go over curvy surfaces fine since its springs
**   just adjust over time.  Maybe penalty forces are the way after all...  Going to have
**   to try the two-layer box idea...
** - Updating the integrator to a scheme that can handle multiple systems being combined
**   into a single coupled system.  
**
** August 16, 1999
** New Idea: Two-layer box with TOC searching and penalty based contact forces.  General 
** idea is that we let things interpenetrate their outer boxes but not their inner ones.
**
**   pseudo-code:
**   - collect all rigid bodies into a list (later on, we'll break into multiple lists)
**   - integrate them all, computing penalty or real contact forces at each active contact 
**	  - if interpenetration occurs:
**     - search for TOC
**     - rewind (interpolate) all objects to TOC
**     - find the colliding features on the objects involved (edge->edge or vertex->face)
**     - apply collision impulses to the two colliding objects
**     - add contact if resultant relative velocity is less than epsilon
**
**   Derivative calculations contact, constraint forces:
**   - zero each object's force accumulator (or reset it with gravity)
**   - apply all external forces (springs, wind, water)
**   - iterate over all contacts in the sytem, either putting them into a big contact solver
**     or computing a penalty based on their displacement.
**   - add the contact forces to all of the relavent objects
**   - have each object compute its derivatives and add them to the vector
**
**   Finding point of collision:
**   - binary search time until the objects are within some distance of each other
**   - perform an intersection check at time t+dt and record the polygons/boxes that penetrate
**     the outer skin
**   - perform distance calculations on all of those objects (polygons/boxes) at time t
**   - determine if the closest point is caused by a pair of edges or a vertex hitting a face
**   - at the collision point, compute and apply the necessary collision impulse
**   - if the resultant relative velocity is low enough, add a contact constraint/spring to the system
**
** Sept 23, 1999
** Another new idea: Padded boxes.
** - The collision box for an object has four pads on each face (for a total of 24 pads).  Each
**   of these pads can have one contact point.  
** - During each timestep (and sub-timestep) each pad will push out from its face until it hits something
**   or reaches the edge of the outer box.  If it hits something, a contact will be set up which records
**   the position, normal, velocity, and surface parameters.
** - Normally all contacts could behave like damped springs.
** - If the inner box is penetrated, we could search for a time when the outer is penetrated, the inner
**   isn't, and apply impulses to all incoming contact points.
** - Probably need to do some hierarchical culling to the contact checking, try the entire face and
**   if it hits something, then try the four sub-faces.
**
** Oct 14, 1999
** YAHPI	- Yet Another Hacky Physics Idea: Octant Boxes
** - Divide our collision box into 8 octants and detect one contact per octant.  
** - Contact detection will be achieved by using the box-sweeping code on each octant.
** - Octants start out inside the box and sweep along a diagonal some distance
** - At their starting positions these "octant boxes" need to overlap so that when at their
**   fully extended position there aren't gaps around the model.
** ALSO: a key idea is to *never* binary search for the "real" collision.  We just detect
** the collision, revert state, and divide our momentum down in the hopes that the forces
** will handle everything if we come in slower next frame.  This code will need to be improved...
**
** Oct 19, 1999
** Octant boxes are flawed because the contact point for each octant jitters around on the
** object when you're on flat ground.  Since the boxes overlap, the points can all jitter 
** to one side of the CM and then to another side.  This is not good :-)
** Solution: Add contact "hairs" to the corners of the box and "prefer" them whenever the 
** contact "compression" is close to that of its octant box. 
** Remaining/New Problems:
** - Need to compute parameters for these "oct-boxes" automatically which handle any combination
**   of OBBox + Mass
** - Objects can now "tunnel" need to do an extra collision check to prevent this.
** - Need to test the simulation code at the "worst-case" simulation framerate to see if it still
**   stabilizes...
** - Need to handle object-object collisions.  
**
** Oct 26,1999
** - Good success with the octbox + corner contacts idea.  Use both the corner contact and
**   the octant contact when both are present and the octant is closer than the corner.
** - Also still using the "divide down the momentum" approach when there is a collision.  Now
**   the code increases the divisor each frame that the object is stuck.  Not 100% sure if I
**   like this but it is so much faster than doing all of those binary searches through time...
** - Critically damped or overdamped contact springs make the system too stiff.  I use underdamped
**   springs (0.33*critical) to keep everything numerically stable.
** - Had to re-normalize the orientation on every sub-timestep.  It would be nice if we
**   didn't have to do this.  If the guts of the integrator was rotating the orientation
**   instead of adding to it we probably could avoid this.  
** - A first pass at (fake) friction is implemented but commented out.  I'm currently getting 
**   instability if I turn it on and it also never stops the object on slopes, etc.  
** TODO: write a custom integrator which rotates the orientation rather than doing the normal
** state += derivatives*dt...  Is this possible?  It is for Euler integration but we need
** at least MidPoint to keep the simulation stable.
** TODO: when a collision is detected, move the object to the extremeties of its contacts?  This
** should help smooth out collisions?  Will have to recursively try to move any objects in contact
** with this one too...  Probably will need this recursive "fake move" in order to handle obj-obj
** interaction anyway...  Hmmm, this needs more thought.
** TODO: handle obj-obj interaction :-)
**
** Oct 11, 2001
** - Latency handling network code.  Each packet causes us to compute an error between the 
** nearest point in their history; then in the timestep function we try to correct that error.
*/


/***********************************************************************************************
 * RigidBodyClass::RigidBodyClass -- Constructor for RigidBodyClass                            *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   10/15/99   gth : Created.                                                                 *
 *=============================================================================================*/
RigidBodyClass::RigidBodyClass(void) : 
	Box(NULL),
	ContactBox(NULL),
	IBody(1),
	IBodyInv(1),
	Rotation(1),
	IInv(1),
	Velocity(0,0,0),
	AngularVelocity(0,0,0),
	ContactCount(0),
	StickCount(0),
	LastTimestep(0.0f),
	GoToSleepTimer(RBODY_SLEEP_DELAY),
	History(NULL)
{
	State.Position.Set(0,0,0);
	State.Orientation.Make_Identity();
	State.LMomentum.Set(0,0,0);
	State.AMomentum.Set(0,0,0);
	
	LatencyError.Position.Set(0,0,0);
	LatencyError.Orientation.Make_Identity();
	LatencyError.LMomentum.Set(0,0,0);
	LatencyError.AMomentum.Set(0,0,0);

	LastKnownState.Position.Set(0,0,0);
	LastKnownState.Orientation.Make_Identity();
	LastKnownState.LMomentum.Set(0,0,0);
	LastKnownState.AMomentum.Set(0,0,0);

	ContactThickness = DEFAULT_CONTACT_THICKNESS;
}

void RigidBodyClass::Init(const RigidBodyDefClass & def)
{
	MoveablePhysClass::Init(def);

	State.Position.Set(0,0,0);
	State.Orientation.Make_Identity();
	State.LMomentum.Set(0,0,0);
	State.AMomentum.Set(0,0,0);

	LatencyError.Position.Set(0,0,0);
	LatencyError.Orientation.Make_Identity();
	LatencyError.LMomentum.Set(0,0,0);
	LatencyError.AMomentum.Set(0,0,0);

	LastKnownState.Position.Set(0,0,0);
	LastKnownState.Orientation.Make_Identity();
	LastKnownState.LMomentum.Set(0,0,0);
	LastKnownState.AMomentum.Set(0,0,0);

	ContactThickness = DEFAULT_CONTACT_THICKNESS;

	GoToSleepTimer = RBODY_SLEEP_DELAY;
}

RigidBodyClass::~RigidBodyClass(void)
{
	REF_PTR_RELEASE(Box);
	if (ContactBox != NULL) {
		delete ContactBox;
		ContactBox = NULL;
	}
}

const AABoxClass & RigidBodyClass::Get_Bounding_Box(void) const
{
	// TODO: propogate this into Phys?
	WWASSERT(Model);
	return Model->Get_Bounding_Box();
}

const Matrix3D & RigidBodyClass::Get_Transform(void) const
{
	// TODO: propogate this into Phys?
	WWASSERT(Model);
	return Model->Get_Transform();
}

void RigidBodyClass::Set_Transform(const Matrix3D & m)
{
	WWASSERT(Model);

	//TODO: rename this to Teleport!
	//TODO: warp model to desired position and verify that it is non-intersecting
	State.Orientation = Build_Quaternion(m);
	State.Position = m.Get_Translation();
	Model->Set_Transform(m);
	Set_Flag(ASLEEP,false);

	// each time the state changes, update the derived values
	Update_Auxiliary_State();
	Update_Cull_Box();
	Update_Visibility_Status();
}

bool RigidBodyClass::Cast_Ray(PhysRayCollisionTestClass & raytest)
{
	if (Model && Model->Cast_Ray(raytest)) {
		raytest.CollidedPhysObj = this;
		return true;
	}
	return false;
}

bool RigidBodyClass::Cast_AABox(PhysAABoxCollisionTestClass & boxtest)
{
	// only let others collide against what we use to collide with them...
	if (CollisionMath::Collide(boxtest.Box,boxtest.Move,ContactBox->Get_World_Inner_Box(),Vector3(0,0,0),boxtest.Result)) {
		boxtest.CollidedPhysObj = this;
		boxtest.CollidedRenderObj = Box;
		return true;
	}
	return false;
}

bool RigidBodyClass::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest)
{
	// only let others collide against what we use to collide with them...
	if (CollisionMath::Collide(boxtest.Box,boxtest.Move,ContactBox->Get_World_Inner_Box(),Vector3(0,0,0),boxtest.Result)) {
		boxtest.CollidedPhysObj = this;
		boxtest.CollidedRenderObj = Box;
		return true;
	}
	return false;
}


bool RigidBodyClass::Intersection_Test(PhysAABoxIntersectionTestClass & test)
{
	if (CollisionMath::Intersection_Test(ContactBox->Get_World_Inner_Box(),test.Box)) {
		test.Add_Intersected_Object(this);
		return true;
	} 
	return false;
}


bool RigidBodyClass::Intersection_Test(PhysOBBoxIntersectionTestClass & test)
{
	if (CollisionMath::Intersection_Test(ContactBox->Get_World_Inner_Box(),test.Box)) {
		test.Add_Intersected_Object(this);
		return true;
	}
	return false;
}


bool RigidBodyClass::Intersection_Test(PhysMeshIntersectionTestClass & test)
{
	// flip the test around and test our OBBox against the given mesh...
	OBBoxIntersectionTestClass our_test(ContactBox->Get_World_Inner_Box(),test.CollisionType);
	if (test.Mesh->Intersect_OBBox(our_test)) {
		test.Add_Intersected_Object(this);
		return true;
	}
	return false;
}

bool RigidBodyClass::Can_Teleport(const Matrix3D &new_tm, bool check_dyn_only,NonRefPhysListClass * result_list)
{
	bool intersecting = false;

	if (ContactBox) {
		ContactBox->Set_Transform(new_tm);
		intersecting = ContactBox->Is_Intersecting(result_list,!check_dyn_only,true);
		ContactBox->Set_Transform(Get_Transform());

	}
	return !intersecting;
}

bool RigidBodyClass::Can_Teleport_And_Stand(const Matrix3D &new_tm, Matrix3D *out)
{
	*out = new_tm;
	return Can_Teleport(new_tm);
}

void RigidBodyClass::Set_Model(RenderObjClass * model)
{
	MoveablePhysClass::Set_Model(model);
	Update_Cached_Model_Parameters();
}

void RigidBodyClass::Update_Cached_Model_Parameters(void)
{
	// if we don't have a model yet, just return
	if (Model == NULL) return;

	Set_Transform(Model->Get_Transform());

	// cache a pointer to the collision box...
	REF_PTR_RELEASE(Box);
	
	// Try to get the "WorldBox" from the model
	if (Model) {
		RenderObjClass * obj = Model->Get_Sub_Object_By_Name("WorldBox");
		if (obj && obj->Class_ID() == RenderObjClass::CLASSID_OBBOX) {
			REF_PTR_SET(Box,(OBBoxRenderObjClass *)obj);
		}
		REF_PTR_RELEASE(obj);

		// If we didn't finde WorldBox, try to find the LOD named "WorldBox" 
		// The LOD code generates a unique name for the mesh by appending A,B,C, etc to the name.
		// A is the lowest LOD, B is the next, and so on.  Our worldbox is specified in the highest
		// LOD so we have to construct the name by appending 'A'+LodCount to the name... icky
		if ((Box == NULL) && (Model->Class_ID() == RenderObjClass::CLASSID_HLOD)) {
			
			char namebuffer[64];
			sprintf(namebuffer,"WorldBox%c",'A' + ((HLodClass *)Model)->Get_Lod_Count() - 1);

			obj = Model->Get_Sub_Object_By_Name(namebuffer);
			if (obj && obj->Class_ID() == RenderObjClass::CLASSID_OBBOX) {
				REF_PTR_SET(Box,(OBBoxRenderObjClass *)obj);
			}
			REF_PTR_RELEASE(obj);
		}
	}
	
	// Otherwise just create one
	if (Box == NULL) {
		WWDEBUG_SAY(("Missing WorldBox in model: %s\r\n",Model->Get_Name()));
		Box = new OBBoxRenderObjClass(OBBoxClass(Vector3(0,0,0),Vector3(1,1,1)));
		Box->Set_Collision_Type(COLLISION_TYPE_PHYSICAL);
	}

	// Update our contact box
	Matrix3D tm = Get_Transform();
	Model->Set_Transform(Matrix3D(1));

	if (ContactBox != NULL) {
		delete ContactBox;
	}
	ContactBox = new OctBoxClass(*this,Box->Get_Box());
	ContactBox->Set_Thickness(ContactThickness);
	ContactBox->Update_Contact_Parameters();

	Model->Set_Transform(tm);
	ContactBox->Set_Transform(tm);

	// recompute our inertia tensor
	Compute_Inertia();

	// and update our auxiliary state (inertia effects it)
	Update_Auxiliary_State();

	// update our culling box
	Update_Cull_Box();
}

void RigidBodyClass::Get_Velocity(Vector3 * set_vel) const
{
	*set_vel = Velocity;
	Assert_State_Valid();
}

void RigidBodyClass::Get_Angular_Velocity(Vector3 * set_avel) const
{
	*set_avel = AngularVelocity;
	Assert_State_Valid();
}

void RigidBodyClass::Set_Velocity(const Vector3 & newvel)
{
	Assert_State_Valid();

#ifdef WWDEBUG 
	if (newvel.Is_Valid() != true) {
		WWDEBUG_SAY(("someone set an invalid velocity (%8.3f, %8.3f, %8.3f)\r\n",newvel.X,newvel.Y,newvel.Z));
	}
#endif

	Velocity = newvel;
	State.LMomentum = Velocity * Mass;

	Assert_State_Valid();
}

void RigidBodyClass::Set_Angular_Velocity(const Vector3 & newavel)
{
	WWASSERT(WWMath::Is_Valid_Float(newavel.X));
	WWASSERT(WWMath::Is_Valid_Float(newavel.Y));
	WWASSERT(WWMath::Is_Valid_Float(newavel.Z));

#ifdef WWDEBUG 
	if (newavel.Is_Valid() != true) {
		WWDEBUG_SAY(("someone set an invalid angular velocity (%8.3f, %8.3f, %8.3f)\r\n",newavel.X,newavel.Y,newavel.Z));
	}
#endif

	AngularVelocity = newavel;
	Matrix3 I = IInv.Inverse();
	State.AMomentum = I * newavel;
	Assert_State_Valid();
}

void RigidBodyClass::Network_Interpolate_State_Update
(
	const Vector3 & new_pos,
	const Quaternion & new_orientation,
	const Vector3 & new_vel,
	const Vector3 & new_avel,
	float fraction
)
{
	// If we are severely out of sync with the network update, we have to "pop"
	// to the new state.  If needed, we can add more criteria here in the future...
	float characteristic_length2 = 4.0f * ContactBox->Get_Extent_Length2();
	if ((new_pos - State.Position).Length2() > characteristic_length2) {
		fraction = 1.0f;
	}

	// interpolate a new state
	Vector3::Lerp(State.Position,new_pos,fraction,&(State.Position));
	::Fast_Slerp(State.Orientation,State.Orientation,new_orientation,fraction);
	
	Vector3 vel,avel;
	Vector3::Lerp(Velocity,new_vel,fraction,&vel);
	Vector3::Lerp(AngularVelocity,new_avel,fraction,&avel);

	Set_Velocity(vel);
	Set_Angular_Velocity(avel);
	Assert_State_Valid();

	// clear the latency error since we are not using it
	LatencyError.Reset();
	
	// each time the state changes, update the derived values
	Update_Auxiliary_State();
	Model->Set_Transform(Matrix3D(Rotation,State.Position));
	Update_Cull_Box();
	Update_Visibility_Status();
}


void RigidBodyClass::Network_Latency_State_Update
(			
	const Vector3 & new_pos,
	const Quaternion & new_orientation,
	const Vector3 & new_vel,
	const Vector3 & new_avel
)
{
	OBBoxClass debug_box;
	ContactBox->Get_Inner_Box(&debug_box,new_orientation,new_pos);
	DEBUG_RENDER_OBBOX(debug_box,Vector3(1.0f,0,0),0.3f);

	/*
	** Set up the input state
	*/
	LastKnownState.Position = new_pos;
	LastKnownState.Orientation = new_orientation;
	LastKnownState.LMomentum = new_vel * Mass;
	
	Matrix3 I = IInv.Inverse();
	LastKnownState.AMomentum = I * new_avel;

	/*
	** Allocate a history object if needed
	*/
	if (History == NULL) {
		History = new RBodyHistoryClass;
		History->Init(LastKnownState);
	}
	WWASSERT(History != NULL);

	/*
	** Search our history to find the point nearest this server update
	*/
	RigidBodyStateStruct nearest_state;
	History->Find_Nearest_State(LastKnownState,&nearest_state);

	/*
	** Now, compute the correction to apply to our current state
	*/
	LatencyError.Position = LastKnownState.Position - nearest_state.Position;
	LatencyError.Orientation = LastKnownState.Orientation / nearest_state.Orientation;
	LatencyError.LMomentum = LastKnownState.LMomentum - nearest_state.LMomentum;
	LatencyError.AMomentum= LastKnownState.AMomentum - nearest_state.AMomentum;
}


void RigidBodyClass::Network_Latency_Error_Correction(float dt)
{
	if (LatencyError.Position.Length2() > 0.01f) {

#ifdef WWDEBUG
		if (!LatencyError.Is_Valid()) {
			WWDEBUG_SAY(("RigidBodyClass - Invalid Latency Error!\r\n"));
			WWDEBUG_SAY((" position error: %f, %f, %f\r\n",LatencyError.Position.X,LatencyError.Position.Y,LatencyError.Position.Z));
			WWDEBUG_SAY((" orientation error: %f, %f, %f, %f\r\n",LatencyError.Orientation.X, LatencyError.Orientation.Y,LatencyError.Orientation.Z,LatencyError.Orientation.W));
			WWDEBUG_SAY((" L momentum error: %f, %f, %f\r\n",LatencyError.LMomentum.X,LatencyError.LMomentum.Y,LatencyError.LMomentum.Z));
			WWDEBUG_SAY((" A momentum error: %f, %f, %f\r\n",LatencyError.AMomentum.X,LatencyError.AMomentum.Y,LatencyError.AMomentum.Z));
		}
#endif
		if (!LatencyError.Is_Valid()) {
			LatencyError.Reset();
		}

		/*
		** Compute the theoretically fully corrected state
		*/
		RigidBodyStateStruct ideal_state(State);
		ideal_state.Position += LatencyError.Position;
		ideal_state.Orientation = ideal_state.Orientation * LatencyError.Orientation;
		ideal_state.LMomentum += LatencyError.LMomentum;
		ideal_state.AMomentum += LatencyError.AMomentum;

#if 0
	WWDEBUG_SAY(("Rigid Body %s network correction\r\n",Model->Get_Name()));
	WWDEBUG_SAY(("                               position error: %f\r\n",LatencyError.Position.Length()));
	WWDEBUG_SAY(("                               orientation error: %f\r\n",WWMath::Acos(LatencyError.Orientation.W) * 2.0f));
#endif

		/*
		** Decide whether to do the normal smooth correction or to pop
		** We'll pop whenever the position error is large and the 
		** velocity sent from the server is small.
		*/
		const float POP_POSITION_ERROR2 = 3.0f * 3.0f;
		const float POP_MAX_VELOCITY2 = 0.5f * 0.5f;
		const float POP_ANGLE_ERROR = 0.966f;				// cos(theta/2) for 30 degrees

		bool pop = (	(LatencyError.Position.Length2() > POP_POSITION_ERROR2) &&
							((LastKnownState.LMomentum * MassInv).Length2() < POP_MAX_VELOCITY2));

		float cos_half_theta = LatencyError.Orientation.W;

		if (WWMath::Fabs(cos_half_theta) < POP_ANGLE_ERROR) {
			pop = true;			
		}

		if (pop == true) {

			/*
			** Jump to the last given state
			*/
			State = LastKnownState;
			History->Init(LastKnownState);

			/*
			** Clear the latency error
			*/
			LatencyError.Position.Set(0,0,0);
			LatencyError.Orientation.Set(0,0,0);
			LatencyError.LMomentum.Set(0,0,0);
			LatencyError.AMomentum.Set(0,0,0);

		} else {

			/*
			** Blend a fraction of this "ideal" state into the current state
			*/
			float fraction = 0.1f;
			if (_CorrectionTime <= dt) {
				fraction = 1.0f;
			} else {
				fraction = WWMath::Clamp(dt / _CorrectionTime);
			}
			fraction = WWMath::Clamp(fraction,0.0f,0.5f);

			RigidBodyStateStruct::Lerp(State,ideal_state,fraction,&State);
			History->Apply_Correction(LatencyError,fraction);

			/*
			** Recalculate the remaining error
			*/
			LatencyError.Position = ideal_state.Position - State.Position;
			LatencyError.Orientation = ideal_state.Orientation / State.Orientation;
			LatencyError.LMomentum = ideal_state.LMomentum - State.LMomentum;
			LatencyError.AMomentum = ideal_state.AMomentum - State.AMomentum;
		}

		/*
		** each time the state changes, update the derived values
		*/
		Assert_State_Valid();
		Update_Auxiliary_State();
		Model->Set_Transform(Matrix3D(Rotation,State.Position));
		Update_Cull_Box();
		Update_Visibility_Status();
	}
}


void RigidBodyClass::Apply_Impulse(const Vector3 & imp)
{
	// Impluse applied to center of mass simply adds to the linear momentum
	State.LMomentum += imp;
	Velocity = State.LMomentum * MassInv;

	if (Is_Asleep()) {
		Set_Flag(ASLEEP,false);
	}

	Assert_State_Valid();
	DEBUG_RENDER_VECTOR(State.Position,imp,IMPULSE_COLOR);
}

void RigidBodyClass::Apply_Impulse(const Vector3 & imp, const Vector3 & wpos)
{
	// Impluse applied off center, adds to the linear momentum and also
	// adds to the torque.
	Vector3 aimp;
	Vector3::Cross_Product((wpos - State.Position),imp,&aimp);
	
	State.LMomentum += imp;
	State.AMomentum += aimp;

	Velocity = MassInv * State.LMomentum;
	AngularVelocity = IInv * State.AMomentum;

	if (Is_Asleep()) {
		Set_Flag(ASLEEP,false);
	}

	Assert_State_Valid();
#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("Impulse applied: %10.10f %10.10f %10.10f\r\n",imp.X,imp.Y,imp.Z));
		DEBUG_RENDER_VECTOR(wpos,imp,IMPULSE_COLOR);
	}
#endif
}

void RigidBodyClass::Set_Mass(float mass)
{
	// In this function, we need to update the inertia tensor and
	// adjust the linear and angular momentum so that the object
	// retains the same velocity and angular velocity.
	Vector3 vel,avel;
	Get_Velocity(&vel);
	Get_Angular_Velocity(&avel);

	MoveablePhysClass::Set_Mass(mass);
	Compute_Inertia();
	Update_Auxiliary_State();

	Set_Velocity(vel);
	Set_Angular_Velocity(avel);

	ContactBox->Update_Contact_Parameters();
	Assert_State_Valid();
}

void RigidBodyClass::Get_Inertia_Inv(Matrix3 * set_I_inv)
{
	*set_I_inv = IInv;
}

void RigidBodyClass::Set_Inertia(const Matrix3 & I)
{
	Vector3 vel,avel;
	Get_Velocity(&vel);
	Get_Angular_Velocity(&avel);

	IBody = I;
	IBodyInv = IBody.Inverse();
	Update_Auxiliary_State();

	Set_Velocity(vel);
	Set_Angular_Velocity(avel);
}

void RigidBodyClass::Get_Inertia(Matrix3 * I)
{
	WWASSERT(I);
	*I = IBody;
}

void RigidBodyClass::Set_Contact_Parameters(float length)
{
	ContactThickness = length;
	ContactBox->Set_Thickness(ContactThickness);
}

void RigidBodyClass::Get_Contact_Parameters(float * stiffness,float * damping,float * length)
{
	if (stiffness) {
		if (ContactBox) {
			*stiffness = ContactBox->Get_Stiffness();
		} else {
			*stiffness = 0.0f;
		}
	}
	if (damping) {
		if (ContactBox) {
			*damping = ContactBox->Get_Damping();
		} else {
			*damping = 0.0f;
		}
	}
	if (length) {
		*length = ContactThickness;
	}
}



int RigidBodyClass::Compute_Derivatives
(
	float						t,
	StateVectorClass *	test_state,
	StateVectorClass *	dydt,
	int						index
)
{
	if (test_state) {
		Set_State(*test_state,index);
	}

#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("  compute_derivatives: t = %f\r\n",t));
		Dump_State();
	}
#endif

	// time derivitive of position
	(*dydt)[index++] = Velocity[0];
	(*dydt)[index++] = Velocity[1];
	(*dydt)[index++] = Velocity[2];

	// time derivitive of orientation
	Quaternion avel(AngularVelocity.X,AngularVelocity.Y,AngularVelocity.Z,0.0f);
	Quaternion qdot = 0.5 * avel * State.Orientation;
	(*dydt)[index++] = qdot[0];
	(*dydt)[index++] = qdot[1];
	(*dydt)[index++] = qdot[2];
	(*dydt)[index++] = qdot[3];

	// time derivitives of momentum and angular momentum (a.k.a. force and torque)
	Vector3 force(0,0,0);
	Vector3 torque(0,0,0);
	Compute_Force_And_Torque(&force,&torque);

	WWASSERT(force.Is_Valid());
	WWASSERT(torque.Is_Valid());
#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("Force:		%10.10f , %10.10f , %10.10f\r\n",force[0],force[1],force[2]));
		WWDEBUG_SAY(("Torque:	%10.10f , %10.10f , %10.10f\r\n",torque[0],torque[1],torque[2]));
	}
#endif
	
	(*dydt)[index++] = force[0];
	(*dydt)[index++] = force[1];
	(*dydt)[index++] = force[2];

	(*dydt)[index++] = torque[0];
	(*dydt)[index++] = torque[1];
	(*dydt)[index++] = torque[2];
	
#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("  done. \r\n\r\n"));
	}
#endif
	return index;
}

void RigidBodyClass::Get_State(StateVectorClass & set_state)
{
	State.To_Vector(set_state);
}

int RigidBodyClass::Set_State(const StateVectorClass & new_state,int index)
{
	WWPROFILE("RBody::Set_State");
	index = State.From_Vector(new_state,index);
	Update_Auxiliary_State();
	return index;
}

void RigidBodyClass::Set_State(const RigidBodyStateStruct & new_state)
{
	State = new_state;
	Update_Auxiliary_State();
}

void RigidBodyClass::Integrate(float time)
{
	Assert_State_Valid();

	//IntegrationSystem::Euler_Integrate(this,time);
	IntegrationSystem::Midpoint_Integrate(this,time);
	//IntegrationSystem::Runge_Kutta_Integrate(this,time);

	// normalize the orientation since it slowly drifts due to integrator error
	State.Orientation.Normalize();

	Update_Auxiliary_State();
	Assert_State_Valid();
}

void RigidBodyClass::Compute_Inertia(void)
{
	// I'm assuming that the CM is at the origin and the principal axes of inertia
	// are aligned with the coordinate system.  So I'm approximating I by using the 
	// formula for a box which extends both direction in the maximum that the actual
	// box entends in either...
	IBody.Make_Identity();

	if (Box) {
		AABoxClass objbox;
		Box->Get_Obj_Space_Bounding_Box(objbox);
		float dx = 2.0f * objbox.Extent.X + WWMath::Fabs(objbox.Center.X);
		float dy = 2.0f * objbox.Extent.Y + WWMath::Fabs(objbox.Center.Y);
		float dz = 2.0f * objbox.Extent.Z + WWMath::Fabs(objbox.Center.Z);
		IBody[0][0] = (1.0f / 12.0f) * Mass * (dz*dz + dy*dy);
		IBody[1][1] = (1.0f / 12.0f) * Mass * (dx*dx + dz*dz);
		IBody[2][2] = (1.0f / 12.0f) * Mass * (dx*dx + dy*dy);
	}
	IBodyInv = IBody.Inverse();
}

void RigidBodyClass::Update_Auxiliary_State(void)
{
	WWPROFILE("Rbody::Assert_State_Valid");
	Assert_State_Valid();

	State.Orientation.Normalize();

	// compute Rotation,IInv,Velocity,AngularVelocity
	Rotation = Build_Matrix3(State.Orientation);
	IInv = Rotation * IBodyInv * Rotation.Transpose();
	Velocity = MassInv * State.LMomentum;
	AngularVelocity = IInv * State.AMomentum;
	if (ContactBox) {
		ContactBox->Set_Transform(State.Orientation,State.Position);
	}

	Assert_State_Valid();
}

void RigidBodyClass::Compute_Force_And_Torque(Vector3 * force,Vector3 * torque)
{
	WWPROFILE("RigidBodyClass::Compute_Force_And_Torque");

	// NOTE: derived classes should perform their force calculations first and
	// then call us so because contact forces are computed at the end of
	// this routine.
	
	// NOTE: need to optimize this routine!

	// add in gravity
	*force += GravScale * Mass * PhysicsConstants::GravityAcceleration;


	// Add damping
	Vector3 vel_dir = Velocity;
	if (vel_dir.Length2() > WWMATH_EPSILON) {
		vel_dir.Normalize();
		
// DEBUG DEBUG
#pragma message ("(gth) HACK! zeroing bad velocities.")
const float MAX_VEL = 500.0f;
const float MAX_ACCEL = 100.0f;
if (	(Velocity.Is_Valid() == false) || 
		(force->Is_Valid() == false) ||
		(Velocity.Length2() > MAX_VEL * MAX_VEL) ||
		(force->Length() * MassInv > MAX_ACCEL) )
{
	WWDEBUG_SAY(("clearing velocity, model=%s vel=(%f,%f,%f)\r\n",Model->Get_Name(),Velocity.X,Velocity.Y,Velocity.Z));
	Velocity.Set(0,0,0);
	AngularVelocity.Set(0,0,0);
	State.LMomentum.Set(0,0,0);
	State.AMomentum.Set(0,0,0);
	force->Set(0,0,0);
	torque->Set(0,0,0);
	vel_dir.Set(0,0,0);
}

		RigidBodyDefClass * def = Get_RigidBodyDef();
		*force -= def->AerodynamicDragCoefficient * Vector3::Dot_Product(Velocity,Velocity) * vel_dir;

		WWASSERT(force->Is_Valid());
	}

	Vector3 a_dir = AngularVelocity;

// DEBUG DEBUG
#pragma message ("(gth) HACK! zeroing bad angular velocities.")
const float MAX_AVEL = 5.0f * 2.0f * WWMATH_PI;
if (a_dir.Length2() > MAX_AVEL * MAX_AVEL) {
	WWDEBUG_SAY(("clearing angluar velocity, model=%s avel=(%f,%f,%f)\r\n",Model->Get_Name(),AngularVelocity.X,AngularVelocity.Y,AngularVelocity.Z));
	Velocity.Set(0,0,0);
	AngularVelocity.Set(0,0,0);
	State.LMomentum.Set(0,0,0);
	State.AMomentum.Set(0,0,0);
	force->Set(0,0,0);
	torque->Set(0,0,0);
	a_dir.Set(0,0,0);
}

	if (a_dir.Length2() > WWMATH_EPSILON) {
		a_dir.Normalize();
		WWASSERT((a_dir.Length() - 1.0f) < WWMATH_EPSILON);
		*torque -= PhysicsConstants::AngularDamping * Vector3::Dot_Product(AngularVelocity,AngularVelocity) * a_dir;
		WWASSERT(torque->Is_Valid());
	}

	// TODO: boyancy forces, force fields
	// Detect contacts.
	if (Get_RigidBodyDef()->CollisionDisabled == false) {

		ContactBox->Compute_Contacts(false);
		
		for (int i=0; i<ContactBox->Get_Contact_Count(); i++) {

			const CastResultStruct & contact = ContactBox->Get_Contact(i);
			Vector3 r;
			Vector3::Subtract(contact.ContactPoint,State.Position,&r);

			/*
			** Compute Contact Force
			*/
			Vector3 pdot;
			Compute_Point_Velocity(contact.ContactPoint,&pdot);

			float dx = 1.0f - contact.Fraction;
			float dv = Vector3::Dot_Product(pdot,contact.Normal);			
			
			float force_magnitude = ContactBox->Get_Stiffness()*dx - ContactBox->Get_Damping()*dv;
			WWASSERT(WWMath::Is_Valid_Float(force_magnitude));
			Vector3 contact_force = force_magnitude * contact.Normal;
			
			Vector3 contact_torque;
			Vector3::Cross_Product(r,contact_force,&contact_torque);

#ifdef WWDEBUG
			if ((contact_force.Is_Valid() == false) || (contact_torque.Is_Valid() == false)) {
				WWDEBUG_SAY(("bad contact: normal=(%8.3f,%8.3f,%8.3f) r=(%8.3f,%8.3f,%8.3f) dx=%8.3f dv=%8.3f\n",
					contact.Normal.X,contact.Normal.Y,contact.Normal.Z,r.X,r.Y,r.Z,dx,dv));
				contact_force.Set(0,0,0);
				contact_torque.Set(0,0,0);
			}
#endif
	
			/*
			** If we are contacting a phys3 object, push it away from us.  If we
			** can't push it away, then exert a contact force.
			*/
			Phys3Class * p3obj = ContactBox->Peek_Contacted_Object(i)->As_Phys3Class();
			bool resolved = false;
			if (p3obj != NULL) {
				resolved = Push_Phys3_Object_Away(p3obj,contact);
			}
			if (resolved == false) {
				*force += contact_force;
				*torque += contact_torque;
			}

			/*
			** Friction/Drag to bring the object to rest when contacting the ground
			*/
			int contact_count = ContactBox->Get_Contact_Count();
			float contact_weight = Get_Weight() / contact_count;
			Vector3 gravity(0.0f,0.0f,-contact_weight);
			
			if (Get_Flag(FRICTION_DISABLED) == false) {
			
				Vector3 tan_vel = pdot - Vector3::Dot_Product(pdot,contact.Normal)*contact.Normal;
				float tan_vel_magnitude = tan_vel.Length2();

				float friction_coefficient =	PhysicsConstants::Get_Contact_Friction_Coefficient(
														PhysicsConstants::DYNAMIC_OBJ_TYPE_TIRE,
														contact.SurfaceType	);

				if (tan_vel_magnitude > WWMATH_EPSILON * WWMATH_EPSILON) {
					
					/*
					** Friction force points opposite the point's tangential motion
					*/
					tan_vel_magnitude = WWMath::Sqrt(tan_vel_magnitude);
					Vector3 tan_vel_dir = tan_vel / tan_vel_magnitude;
					
					/*
					** The magnitude is the friction coefficient times the portion of the weight supported
					** by this contact.  As the velocity approaches zero, this force approaches zero
					*/
					float friction_magnitude = friction_coefficient * WWMath::Min(0.1f * tan_vel_magnitude * contact_weight,contact_weight);
					Vector3 friction_force = -friction_magnitude * tan_vel_dir;

					/*
					** The active contacts divy up the gravitational force to oppose
					*/
					if (contact.Normal.Z > 0.0f) {
						friction_force -= gravity - Vector3::Dot_Product(gravity,contact.Normal) * contact.Normal;
					}

					/*
					** Finally, clamp the force to the friction circle
					*/
					float max_friction_force = friction_coefficient * contact_weight;
					if (friction_force.Length2() > max_friction_force * max_friction_force) {  
						float flen = friction_force.Length();
						friction_force /= flen;
						friction_force *= 0.5f * max_friction_force;
					}

					/*
					** Compute the torque and add the force and torque into the accumulators
					*/
					Vector3 friction_torque;
					Vector3::Cross_Product(r,friction_force,&friction_torque);

					*force += friction_force;
					*torque += friction_torque;
					DEBUG_RENDER_VECTOR(contact.ContactPoint,friction_force,Vector3(1,0,0));
				}
			}

			DEBUG_RENDER_VECTOR(contact.ContactPoint,contact_force / Get_Mass(),Vector3(0,1,0));
		}
	}

	/*
	** HACK!  Never let the force or torque completely reflect the angular or linear momentum
	** clamp them to a value that at most, drives the momentum to zero
	**
	** NOTE: this doesn't work, I'm just leaving it here in case I get a new idea
	** which will make it work...
	*/
#if JITTER_ELIMINATION_CODE
	Vector3 max_delta_lmomentum = - 1.0f * State.LMomentum / LastTimestep;
	Vector3 max_delta_amomentum = - 1.0f * State.AMomentum / LastTimestep;
	
	Vector3 old_force = *force;
	Vector3 old_torque = *torque;

	if ((max_delta_lmomentum.X < 0) && (force->X < max_delta_lmomentum.X)) force->X = max_delta_lmomentum.X;
	if ((max_delta_lmomentum.X > 0) && (force->X > max_delta_lmomentum.X)) force->X = max_delta_lmomentum.X;
	if ((max_delta_lmomentum.Y < 0) && (force->Y < max_delta_lmomentum.Y)) force->Y = max_delta_lmomentum.Y;
	if ((max_delta_lmomentum.Y > 0) && (force->Y > max_delta_lmomentum.Y)) force->Y = max_delta_lmomentum.Y;
	if ((max_delta_lmomentum.Z < 0) && (force->Z < max_delta_lmomentum.Z)) force->Z = max_delta_lmomentum.Z;
	if ((max_delta_lmomentum.Z > 0) && (force->Z > max_delta_lmomentum.Z)) force->Z = max_delta_lmomentum.Z;
	
	if ((max_delta_amomentum.X < 0) && (torque->X < max_delta_amomentum.X)) torque->X = max_delta_amomentum.X;
	if ((max_delta_amomentum.X > 0) && (torque->X > max_delta_amomentum.X)) torque->X = max_delta_amomentum.X;
	if ((max_delta_amomentum.Y < 0) && (torque->Y < max_delta_amomentum.Y)) torque->Y = max_delta_amomentum.Y;
	if ((max_delta_amomentum.Y > 0) && (torque->Y > max_delta_amomentum.Y)) torque->Y = max_delta_amomentum.Y;
	if ((max_delta_amomentum.Z < 0) && (torque->Z < max_delta_amomentum.Z)) torque->Z = max_delta_amomentum.Z;
	if ((max_delta_amomentum.Z > 0) && (torque->Z > max_delta_amomentum.Z)) torque->Z = max_delta_amomentum.Z;

	WWASSERT(1.00001f * old_force.Length2() >= force->Length2());
	WWASSERT(1.00001f * old_torque.Length2() >= torque->Length2());
#endif

	WWASSERT(WWMath::Is_Valid_Float(force->X));
	WWASSERT(WWMath::Is_Valid_Float(force->Y));
	WWASSERT(WWMath::Is_Valid_Float(force->Z));

}

void RigidBodyClass::Compute_Point_Velocity(const Vector3 & p,Vector3 * pdot)
{
	// REMEMBER: p is assumed to be in world coordinates!  pdot is as well.
	// pdot = velocity + CROSS(angular_velocity , r)
	Vector3 r;
	
	Vector3::Subtract(p,State.Position,&r);
	Vector3::Cross_Product(AngularVelocity,r,pdot);
	Vector3::Add(*pdot,Velocity,pdot);
}

bool RigidBodyClass::Is_Colliding(const Vector3 & point, const Vector3 & normal)
{
	Vector3 padot;
	Compute_Point_Velocity(point,&padot);
	float vrel = Vector3::Dot_Product(normal,padot);
	return vrel < 0.0f;
}

void RigidBodyClass::Set_Moving_Collision_Region(float dt)
{
	AABoxClass region;
	ContactBox->Get_Outer_Bounds(&region);		// start with bounds of collision box

	// Recenter and enlarge to contain our velocity (scaled by a bit)
	Vector3 move = 1.5f * Velocity * dt;
	region.Center += 0.5f * move;
	region.Extent.X += WWMath::Fabs(move.X);
	region.Extent.Y += WWMath::Fabs(move.Y);
	region.Extent.Z += WWMath::Fabs(move.Z);

	// Now, scale to account for any rotational effects
	region.Extent *= 2.0f;

	PhysicsSceneClass::Get_Instance()->Set_Collision_Region(region,Get_Collision_Group());
}

void RigidBodyClass::Set_Stationary_Collision_Region(void)
{
	AABoxClass region;
	ContactBox->Get_Outer_Bounds(&region);		// start with bounds of collision box
	PhysicsSceneClass::Get_Instance()->Set_Collision_Region(region,Get_Collision_Group());
}


bool RigidBodyClass::Can_Go_To_Sleep(float dt)
{
	/*
	** RigidBodies go to sleep if their oct-box is resting on at least three contacts and
	** their velocities are below some thresh-hold and their controller isn't doing anything.
	*/
	if ((Controller != NULL) && (Controller->Is_Inactive() != true)) {
		GoToSleepTimer = RBODY_SLEEP_DELAY;
		return false;
	}
	
	const float VEL_THRESHHOLD = 0.05f;
	const float AVEL_THRESHHOLD = 0.05f;

	float max_lmomentum2 = Mass * VEL_THRESHHOLD * VEL_THRESHHOLD;
	float max_amomentum2 = IBody[1][1] * AVEL_THRESHHOLD * AVEL_THRESHHOLD;
	bool tried_to_sleep = false;

	if ((ContactBox->ContactCount >= 3) || (Get_RigidBodyDef()->CollisionDisabled)) {
		
		if ((State.LMomentum.Length2() < max_lmomentum2) && 
			(State.AMomentum.Length2() < max_amomentum2) ) 
		{
			tried_to_sleep = true;
			if (GoToSleepTimer < 0.0f) {
				return true;
			}
		} 
	}

	if (tried_to_sleep) {
		GoToSleepTimer -= dt;
	} else {
		GoToSleepTimer = RBODY_SLEEP_DELAY;
	}

	return false;
}

void RigidBodyClass::Compute_Impact(const CastResultStruct & result,Vector3 * impulse)
{
	Compute_Impact(result.ContactPoint,result.Normal,impulse);	
}

void RigidBodyClass::Compute_Impact(const Vector3 & point,const Vector3 & normal,Vector3 * impulse)
{
	// NOTE: this is only a temporary solution, it assumes we are colliding with
	// an immovable object (infinite mass)...
	Vector3 padot;
	Compute_Point_Velocity(point,&padot);
	float vrel = Vector3::Dot_Product(normal,padot);
	
	if (vrel > 0.0f) {
	
		// moving away
		impulse->Set(0,0,0);	

	} else {
		
		// collision
		float num = -(1.0f + Elasticity) * vrel;
		Vector3 ra = point - State.Position;
		
		Vector3 tmp1,tmp2;
		Vector3::Cross_Product(ra,normal,&tmp1);
		Matrix3::Rotate_Vector(IInv,tmp1,&tmp2);
		Vector3::Cross_Product(tmp2,ra,&tmp1);
		float den = MassInv + Vector3::Dot_Product(normal,tmp1);
		
		if (WWMath::Fabs(den) > WWMATH_EPSILON) {
			float mag = num / den;
			*impulse = mag * normal;
		} else {
			WWASSERT(0);
			impulse->Set(0,0,0);	
		}
	}

	WWASSERT(WWMath::Is_Valid_Float(impulse->X));
	WWASSERT(WWMath::Is_Valid_Float(impulse->Y));
	WWASSERT(WWMath::Is_Valid_Float(impulse->Z));

}

void RigidBodyClass::Assert_State_Valid(void) const
{
	WWASSERT(State.Position.Is_Valid());
	WWASSERT(State.Orientation.Is_Valid());
	WWASSERT(State.LMomentum.Is_Valid());
	WWASSERT(State.LMomentum.Is_Valid());
	WWASSERT(Velocity.Is_Valid());
	WWASSERT(AngularVelocity.Is_Valid());
}

inline void RigidBodyClass::Dump_State(void) const
{
	WWDEBUG_SAY(("Position:		%10.10f , %10.10f , %10.10f \r\n",State.Position.X,State.Position.Y,State.Position.Z));
	WWDEBUG_SAY(("Orientation:	%10.10f , %10.10f , %10.10f , %10.10f\r\n",State.Orientation.X,State.Orientation.Y,State.Orientation.Z,State.Orientation.W));
	WWDEBUG_SAY(("LMomentum:	%10.10f , %10.10f , %10.10f \r\n",State.LMomentum.X,State.LMomentum.Y,State.LMomentum.Z));
	WWDEBUG_SAY(("AMomentum:	%10.10f , %10.10f , %10.10f \r\n",State.AMomentum.X,State.AMomentum.Y,State.AMomentum.Z));
	WWDEBUG_SAY(("ContactBox intersecting: %d\r\n",(ContactBox->Is_Intersecting() ? 1 : 0)));
	WWDEBUG_SAY(("\r\n"));
}

void RigidBodyClass::Timestep(float dt)
{	
	WWPROFILE("RigidBody::Timestep");
	LastTimestep = dt;

// DEBUG DEBUG
Vector3 vel0 = Velocity;
Vector3 avel0 = AngularVelocity;

	/*
	** Update our history buffer
	*/
	if (History != NULL) {
		History->Update_History(State,dt);

		/*
		** Debugging, Draw our history if present
		*/
		OBBoxClass box;
		for (int i=1; i<History->History_Count(); i++) {
		
			const RigidBodyStateStruct & state = History->Get_Historical_State(i);
			ContactBox->Get_Inner_Box(&box,state.Orientation,state.Position);
			DEBUG_RENDER_OBBOX(box,Vector3(0,1.0f,0),0.05f);
		}
	}

	/*
	** If we have any latency error, attempt to correct it
	*/
	Assert_State_Valid();
	if (LatencyError.Position.Length2() > 0.001f) {
		Network_Latency_Error_Correction(dt);
	}
	Assert_State_Valid();


	/*
	** If we're currently asleep, see if we need to wake up.
	*/
	if (Is_Asleep()) {
		if ((Controller != NULL) && (Controller->Is_Inactive() == false)) {
			Set_Flag(ASLEEP,false);
		} else {
			return;
		}
	}

	if (Is_Immovable()) {
		return;
	}

	WWASSERT(Model);
	WWASSERT(ContactBox);

	Inc_Ignore_Counter();

#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("------------------------------\r\n"));
		WWDEBUG_SAY(("RigidBody Timestep Begin (dt=%f).\r\n",dt));
		WWDEBUG_SAY((" Beginning State:\r\n"));
		Dump_State();
	}
#endif

	/*
	** Set our active collision region
	*/
	Set_Moving_Collision_Region(dt);

	/*
	** Repeat until we eat all of the time
	*/
	const int MAX_COLLISIONS = 10;
	int collisions = 0;
	float remaining_time = dt;
	float timestep;
	
	while ((remaining_time > 0.0f) && (collisions < MAX_COLLISIONS)) {

		Assert_State_Valid();
		WWPROFILE("RigidBodyClass integration loop");
		timestep = remaining_time;

		/*
		** Integrate the state of the object
		*/
		RigidBodyStateStruct oldstate = State;
		Integrate(timestep);

		/*
		** Check the final state of the object for collision.
		*/
		if (!ContactBox->Is_Intersecting()) {
		
			/*
			** Not intersecting so we're accepting the entire move
			*/
			remaining_time -= timestep;
			StickCount = 0;

		} else {

			WWPROFILE("Impulsive Collision Handling");

			StickCount++;

			/*
			** Search for a state where we can get valid contact points
			*/
			bool found = Find_State_In_Contact_Zone(oldstate);

			/*
			** Ad-hoc impact algorithm.  Killing angular momentum, clipping
			** linear momentum to the contact normals.
			*/
			State.AMomentum /= (float)StickCount+1;

			if (found) {
				ContactBox->Compute_Contacts();
				
				if (ContactBox->Get_Contact_Count() > 0) {

					Vector3 contact_centroid(0,0,0);
					
					for (int ci=0; ci<ContactBox->Get_Contact_Count(); ci++) {
			
						const CastResultStruct & contact = ContactBox->Get_Contact(ci);

						/*
						** Eliminate any momentum not tangential to this contact normal
						*/
						float dot = Vector3::Dot_Product(State.LMomentum,contact.Normal);
						if (dot < 0.0f) {
							
							Vector3 impulse = -1.01f * dot * contact.Normal;

							/*
							** If the object we collided with is a dynamic object, apply an impulse
							** to it as well.
							*/
							PhysClass * other_obj = ContactBox->Peek_Contacted_Object(ci);

							if ((other_obj != NULL) && (other_obj->As_RigidBodyClass() != NULL)) {
							
								RigidBodyClass * other_rbody = other_obj->As_RigidBodyClass();
								float fraction = Mass / (Mass + other_rbody->Get_Mass());
									
								State.LMomentum += fraction * impulse;
								other_rbody->Apply_Impulse(-(1.0f - fraction) * impulse, contact.ContactPoint);

							} else if ((other_obj != NULL) && (other_obj->As_Phys3Class() != NULL)) {
								
								if (!Push_Phys3_Object_Away(other_obj->As_Phys3Class(),contact)) {
									State.LMomentum += impulse;
								}
								remaining_time = 0.0f;	// I don't want to resolve a crowd chain reaction all in one frame

							} else {

								State.LMomentum += impulse;

							}

						}

						/*
						** Accumulate the contact centroid
						*/
						contact_centroid += contact.ContactPoint;
					}

					/*
					** Apply an instantaneous change to the angular momentum to make the physics
					** feel more realistic.  Compute the impluse that was applied to the linear 
					** momentum and apply a fraction of it to the angular momentum at the contact 
					** centroid.  (of course, this is all "ad-hoc", not true rigid-body dynamics...)
					*/
					Vector3 impulse = 0.3f * (State.LMomentum - oldstate.LMomentum);
					contact_centroid /= ContactBox->Get_Contact_Count();
					
					Vector3 r = contact_centroid - State.Position;
					Vector3 a_impulse;
					Vector3::Cross_Product(r,impulse,&a_impulse);

					State.AMomentum += a_impulse;


					/*
					** Done, update the rest of the rigid body state
					*/
					Update_Auxiliary_State();

#if RBODY_DEBUGGING
				if (RBODY_DEBUG_FILTER) {
					WWDEBUG_SAY((" Intersection occured, found state in contact zone.\r\n"));
					WWDEBUG_SAY((" Resulting new state:\r\n"));
					Dump_State();
				}
#endif
				}

			} else {
				
				/*
				** If all else fails: 
				** Fall back to the old, kill the momentum and try to let the
				** contact spring forces sort it out.
				*/
#if RBODY_DEBUGGING
				if (RBODY_DEBUG_FILTER) {
					WWDEBUG_SAY(("Rigid Body Object: %s is intersecting (%f,%f,%f).\n",Model->Get_Name(),State.Position.X,State.Position.Y,State.Position.Z));
					WWDEBUG_SAY(("Reverting to previous position: (%f,%f,%f)\r\n",oldstate.Position.X,oldstate.Position.Y,oldstate.Position.Z));
				}
#endif

				State.LMomentum /= (float)(StickCount + 1);
				State.Position = oldstate.Position;
				State.Orientation = oldstate.Orientation;
				remaining_time = 0.0f;
				Update_Auxiliary_State();

				// We've reverted the state, now display the total force and torque that is causing us to "stick"
				#ifdef WWDEBUG
				Vector3 force(0,0,0);
				Vector3 torque(0,0,0);
				Compute_Force_And_Torque(&force,&torque);
			
				DEBUG_RENDER_VECTOR(State.Position,force,Vector3(0,1,0));
				DEBUG_RENDER_VECTOR(State.Position,force,Vector3(0,0,1));
				#endif	

#if RBODY_DEBUGGING
				if (RBODY_DEBUG_FILTER) {
					WWDEBUG_SAY((" Un-handled intersection!  StickCount = %d\r\n",StickCount));
					WWDEBUG_SAY((" Reverted State:\r\n"));
					Dump_State();
				}
#endif

			}
		}
		collisions++;
	}

#if 0 // DEBUG DEBUG DEBUG
	if (ContactBox->Is_Intersecting()) {
		State = BackupState;
		goto doitagain;
	}
#endif

	DEBUG_RENDER_VECTOR(State.Position,Velocity,LMOMENTUM_COLOR);
	DEBUG_RENDER_VECTOR(State.Position,AngularVelocity,AMOMENTUM_COLOR);	

#ifdef WWDEBUG
	if (ContactBox->Is_Intersecting()) {
		OBBoxClass box;
		ContactBox->Get_Inner_Box(&box);
		DEBUG_RENDER_OBBOX(box,Vector3(1,0,0),0.3f);
	}
#endif

	Dec_Ignore_Counter();
	Model->Set_Transform(Matrix3D(Rotation,State.Position));
	Update_Cull_Box();
	Update_Visibility_Status();

	/*
	** Release our active collision region
	*/
	PhysicsSceneClass::Get_Instance()->Release_Collision_Region();

	/*
	** See if we can go to sleep and stop simulating!
	*/
	if (Can_Go_To_Sleep(dt)) {
		Set_Flag(ASLEEP,true);
	}

// DEBUG DEBUG	
	Vector3 vel_change = Velocity - vel0;
	Vector3 avel_change = AngularVelocity - avel0;
	const float VEL_CHANGE_SPIKE = 10.0f;
	const float AVEL_CHANGE_SPIKE = 10.0f;

	if (	(vel_change.Length() > VEL_CHANGE_SPIKE) ||
			(avel_change.Length() > AVEL_CHANGE_SPIKE))
	{
		WWDEBUG_SAY(("***** Velocity spike detected during RBody::Timestep!  "));
		WWDEBUG_SAY(("initial vel: %f    final vel: %f\r\n",vel0.Length(),Velocity.Length()));
		WWDEBUG_SAY(("initial avel: %f   final avel: %f\r\n",avel0.Length(),AngularVelocity.Length()));
	}
}


bool RigidBodyClass::Push_Phys3_Object_Away(Phys3Class * p3obj,const CastResultStruct & contact)
{
	/*
	** Notify any observer of the phys3 object that this impact has happened
	*/
	CollisionEventClass event;
	event.CollisionResult = &contact;
	event.CollidedRenderObj = NULL;
	event.OtherObj = this;
	p3obj->Collision_Occurred(event);

	/*
	** Compute a movement vector to push the phy3 object away
	*/
	Vector3 move = p3obj->Get_Transform().Get_Translation() - State.Position;
	move.Normalize();

	Vector3 point_vel;
	Compute_Point_Velocity(p3obj->Get_Transform().Get_Translation(),&point_vel);
	float pvel_relative = Vector3::Dot_Product(point_vel,move);
	if (pvel_relative > 0.0f) {
		pvel_relative *= LastTimestep;
	} else {
		pvel_relative = 0.0f;
	}

	move *= ContactBox->Get_Thickness() * ((1.0f - contact.Fraction) + pvel_relative);
	
	Dec_Ignore_Counter();
	p3obj->Collide(move);
	Inc_Ignore_Counter();

#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("  Pushing Phys3 Object %x away (%f, %f, %f)\r\n",p3obj,move.X,move.Y,move.Z));
	}
#endif
	
	/*
	** Check if the object is now out of collision with us.  If the external game
	** logic kills the phys3 object, its collision group will be changed so we check
	** for that first.
	*/
	if (PhysicsSceneClass::Get_Instance()->Do_Groups_Collide(p3obj->Get_Collision_Group(),Get_Collision_Group())) {
		AABoxClass p3box;
		p3obj->Get_Collision_Box(&p3box);
		OBBoxClass rbox;
		ContactBox->Get_Outer_Box(&rbox);

#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("  completely clear of our outer collision box\r\n"));
	}
#endif
		return (CollisionMath::Overlap_Test(rbox,p3box) == CollisionMath::OUTSIDE);

	} else {

#if RBODY_DEBUGGING
	if (RBODY_DEBUG_FILTER) {
		WWDEBUG_SAY(("  still intersecting our outer collision box\r\n"));
	}
#endif
		return true;
	}
}


void RigidBodyClass::Assert_Not_Intersecting(void)
{
	Inc_Ignore_Counter();

	OBBoxClass obbox = Box->Get_Box();
	CastResultStruct result;
	PhysOBBoxCollisionTestClass test(	obbox,
													Vector3(0,0,0),
													&result,
													Get_Collision_Group(),
													COLLISION_TYPE_PHYSICAL);
	PhysicsSceneClass::Get_Instance()->Cast_OBBox(test);
//	WWASSERT(result.StartBad != true);			
	if (result.StartBad) {
		WWDEBUG_SAY(("   !!!!!!!!!!!!!!!!!!!!!!!!!   Rigid Body %s intersecting!\r\n",Model->Get_Name()));
	} else {
		WWDEBUG_SAY(("   not intersecting\r\n"));
	}

	Dec_Ignore_Counter();
}

void RigidBodyClass::Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box)
{
	WWASSERT(set_obj_space_box != NULL);
	if (Box) {
		Box->Get_Obj_Space_Bounding_Box(*set_obj_space_box);
	} else {
		MoveablePhysClass::Get_Shadow_Blob_Box(set_obj_space_box);
	}
}


bool RigidBodyClass::Find_State_In_Contact_Zone
(
	const RigidBodyStateStruct & oldstate
)
{
	RigidBodyStateStruct state0 = oldstate;
	RigidBodyStateStruct state1 = State;
	RigidBodyStateStruct teststate;
	
	bool done = false;
	bool success = false;
	int iteration_count = 0;
	const int MAX_ITERATIONS = 5;

	/*
	** Binary search for a state where the box is close enough to 
	** the terrain to generate contacts but the inner box is not
	** intersecting.
	*/
	while (!done) {
		RigidBodyStateStruct::Lerp(state0,state1,0.5f,&teststate);
		Set_State(teststate);
		
		if (ContactBox->Is_In_Contact_Zone()) {
			if (ContactBox->Is_Intersecting()) {
				state1 = teststate;
			} else {
				success = true;
				done = true;
			}
		} else {
			state0 = teststate;
		}
		iteration_count++;
		if (iteration_count > MAX_ITERATIONS) {
			done = true;
		}
	}
	
	return success;
}


bool RigidBodyClass::Push(const Vector3 & move)
{
	Vector3 old_pos = State.Position;

	/*
	** Create a collision test which sweeps our collision box along the given move vector
	*/
	OBBoxClass box = ContactBox->Get_World_Inner_Box();
	CastResultStruct result;
	PhysOBBoxCollisionTestClass test(	box,
													move,
													&result,
													Get_Collision_Group(),
													COLLISION_TYPE_PHYSICAL );
		
 	/*
	** Sweep the box
	*/
	Inc_Ignore_Counter();
	PhysicsSceneClass::Get_Instance()->Cast_OBBox(test);
	Dec_Ignore_Counter();

	/*
	** Move as far as we were allowed
	*/
	if (!result.StartBad) {
		
		if (result.Fraction > 0.0f) {
			State.Position += result.Fraction * move;
		}

		/*
		** Update the rest of our parameters (TODO: clean this process up)
		*/
		Set_Flag(ASLEEP,false);
		Update_Auxiliary_State();
		Update_Visibility_Status();

		Matrix3D m(Rotation,State.Position);
		Model->Set_Transform(m);
		if (ContactBox) {
			ContactBox->Set_Transform(m);
		}

		return (result.Fraction == 1.0f);
	
	} else {
	
		return false;

	}
}


/***********************************************************************************
**
** Save-Load System
**
***********************************************************************************/
const PersistFactoryClass & RigidBodyClass::Get_Factory (void) const
{
	return _RigidBodyFactory;
}

bool RigidBodyClass::Save (ChunkSaveClass &csave)
{
	csave.Begin_Chunk(RBODY_CHUNK_MOVEABLE);
	MoveablePhysClass::Save(csave);
	csave.End_Chunk();
	
	ODESystemClass * ode_ptr = (ODESystemClass *)this;
	csave.Begin_Chunk(RBODY_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_ODESYSTEM_PTR,ode_ptr);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_IBODY,IBody);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_IBODYINV,IBodyInv);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_POSITION,State.Position);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_ORIENTATION,State.Orientation);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_LMOMENTUM,State.LMomentum);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_STATE_AMOMENTUM,State.AMomentum);
	WRITE_MICRO_CHUNK(csave,RBODY_VARIABLE_CONTACT_LENGTH,ContactThickness);
	csave.End_Chunk();
	return true;
}

bool RigidBodyClass::Load (ChunkLoadClass &cload)
{
	ODESystemClass * odesys = NULL;

	while (cload.Open_Chunk()) {
		
		switch(cload.Cur_Chunk_ID()) 
		{
			case RBODY_CHUNK_MOVEABLE:
				MoveablePhysClass::Load(cload);
				break;

			case RBODY_CHUNK_VARIABLES:
			
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_ODESYSTEM_PTR,odesys);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_IBODY,IBody);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_IBODYINV,IBodyInv);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_POSITION,State.Position);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_ORIENTATION,State.Orientation);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_LMOMENTUM,State.LMomentum);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_STATE_AMOMENTUM,State.AMomentum);
						READ_MICRO_CHUNK(cload,RBODY_VARIABLE_CONTACT_LENGTH,ContactThickness);

					}
					cload.Close_Micro_Chunk();	
				}
				break;

			default:
				WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
				break;
		}
		
		cload.Close_Chunk();
	}

	
	if (odesys != NULL) {
		SaveLoadSystemClass::Register_Pointer(odesys,(ODESystemClass *)this);
	}

	SaveLoadSystemClass::Register_Post_Load_Callback(this);
	Update_Auxiliary_State();

	return true;
}


void RigidBodyClass::On_Post_Load(void)
{
	Update_Cached_Model_Parameters();
}


/***********************************************************************************************
**
** RigidBodyDefClass Implementation
**
***********************************************************************************************/

/*
** Persist factory for RigidBodyDefClass's
*/
SimplePersistFactoryClass<RigidBodyDefClass,PHYSICS_CHUNKID_RIGIDBODYDEF>	_RigidBodyDefFactory;

/*
** Definition factory for RigidBodyDefClass.  This makes it show up in the editor
*/
DECLARE_DEFINITION_FACTORY(RigidBodyDefClass, CLASSID_RIGIDBODYDEF, "RigidBody") _RigidBodyDefDefFactory;

/*
** Chunk ID's used by RigidBodyDefClass
*/
enum 
{
	RIGIDBODYDEF_CHUNK_MOVEABLEPHYSDEF						= 0x01106650,			// (parent class)
	RIGIDBODYDEF_CHUNK_VARIABLES,

	RIGIDBODYDEF_VARIABLE_AERODYNAMICDRAGCOEFFICIENT	= 0x00,
	RIGIDBODYDEF_VARIABLE_COLLISIONDISABLED,
};


RigidBodyDefClass::RigidBodyDefClass(void) :
	AerodynamicDragCoefficient(0.0f),
	CollisionDisabled(false)
{
	// make our parameters editable
	FLOAT_EDITABLE_PARAM(RigidBodyDefClass, AerodynamicDragCoefficient, 0.0f, 100.0f);
}

uint32 RigidBodyDefClass::Get_Class_ID (void) const	
{ 
	return CLASSID_RIGIDBODYDEF; 
}

PersistClass * RigidBodyDefClass::Create(void) const
{
	RigidBodyClass * obj = NEW_REF(RigidBodyClass,());
	obj->Init(*this);
	return obj;
}

const PersistFactoryClass & RigidBodyDefClass::Get_Factory (void) const
{
	return _RigidBodyDefFactory;
}

bool RigidBodyDefClass::Save(ChunkSaveClass &csave)
{
	csave.Begin_Chunk(RIGIDBODYDEF_CHUNK_MOVEABLEPHYSDEF);
	MoveablePhysDefClass::Save(csave);
	csave.End_Chunk();
	
	csave.Begin_Chunk(RIGIDBODYDEF_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK(csave,RIGIDBODYDEF_VARIABLE_AERODYNAMICDRAGCOEFFICIENT,AerodynamicDragCoefficient);
	WRITE_MICRO_CHUNK(csave,RIGIDBODYDEF_VARIABLE_COLLISIONDISABLED,CollisionDisabled);
	csave.End_Chunk();
	return true;
}

bool RigidBodyDefClass::Load(ChunkLoadClass &cload)
{
	while (cload.Open_Chunk()) {

		switch(cload.Cur_Chunk_ID()) {

			case RIGIDBODYDEF_CHUNK_MOVEABLEPHYSDEF:
				MoveablePhysDefClass::Load(cload);
				break;

			case RIGIDBODYDEF_CHUNK_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK(cload,RIGIDBODYDEF_VARIABLE_AERODYNAMICDRAGCOEFFICIENT,AerodynamicDragCoefficient);
						READ_MICRO_CHUNK(cload,RIGIDBODYDEF_VARIABLE_COLLISIONDISABLED,CollisionDisabled);
					}
					cload.Close_Micro_Chunk();
				}
				break;


			default:
				WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
				break;
		}

		cload.Close_Chunk();
	}
	return true;
}

bool RigidBodyDefClass::Is_Type(const char * type_name)
{
	if (stricmp(type_name,RigidBodyDefClass::Get_Type_Name()) == 0) {
		return true;
	} else {
		return MoveablePhysDefClass::Is_Type(type_name);
	}
}











