/*
**	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/phys3.cpp                             $*
 *                                                                                             *
 *                       Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                     $Modtime:: 1/08/02 5:52p                                               $*
 *                                                                                             *
 *                    $Revision:: 137                                                         $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   Phys3Class::Phys3Class -- Constructor                                                     *
 *   Phys3Class::Phys3Class -- Constructor - from a Definition                                 *
 *   Phys3Class::~Phys3Class -- Destructor                                                     *
 *   Phys3Class::Get_Bounding_Box -- Returns bounding box of the model                         *
 *   Phys3Class::Get_Transform -- Returns the current transform                                *
 *   Phys3Class::Set_Transform -- Sets the current transform                                   *
 *   Phys3Class::Set_Position -- Sets the position                                             *
 *   Phys3Class::Get_Position -- Returns the current position                                  *
 *   Phys3Class::Set_Heading -- Set the heading of this object                                 *
 *   Phys3Class::Get_Heading -- returns the heading of this object                             *
 *   Phys3Class::Set_Slide_Angle -- Sets the maximum angle this object can climb               *
 *   Phys3Class::Get_Slide_Angle -- returns the slide angle                                    *
 *   Phys3Class::Update_Transform -- Recalculate the transform                                 *
 *   Phys3Class::Set_Model -- Set the model being used                                         *
 *   Phys3Class::Update_Cached_Model_Parameters -- Caches some data related to the model       *
 *   Phys3Class::Apply_Impulse -- Apply an impulse to this object                              *
 *   Phys3Class::Apply_Impulse -- Apply an impulse to this object                              *
 *   Phys3Class::Cast_Ray -- Check a ray for intersection with this object                     *
 *   Phys3Class::Cast_AABox -- Check a swept AABox for intersection with this obj              *
 *   Phys3Class::Cast_OBBox -- Check a swept OBBox for intersection with this obj              *
 *   Phys3Class::Intersection_Test -- Check an AABox for intersection with this object         *
 *   Phys3Class::Intersection_Test -- Check an OBBox for intersection with this object         *
 *   Phys3Class::Intersection_Test -- Check a mesh for intersection with this object           *
 *   Phys3Class::Timestep -- Simulate for time dt                                              *
 *   Phys3Class::Get_Ground_State -- validates and returns the gound state structure           *
 *   Phys3Class::Invalidate_Ground_State -- Marks the ground state as dirty                    *
 *   Phys3Class::Check_Ground -- Update the ground state                                       *
 *   Phys3Class::User_Move -- User Move mode.                                                  *
 *   Phys3Class::Ballistic_Move -- Ballistic motion                                            *
 *   Phys3Class::Slide_Move -- Sliding motion, down a slope                                    *
 *   Phys3Class::Normal_Move -- Normal motion, phys3 does nothing (infinite friction)          *
 *   Phys3Class::Collide_Move -- Move in response to someone colliding with me                 *
 *   Phys3Class::Apply_Move -- Apply a movement vector, constrained by world geometry          *
 *   Phys3Class::Debug_Verify_Position -- Verify that I am in a valid position                 *
 *   Phys3Class::Snap_To_Ground -- Following a move, snap back down to the ground              *
 *   Phys3Class::Clip_Move -- Clip a move vector by all of the active contact normals          *
 *   Phys3Class::Get_Shadow_Blob_Box -- Returns the bounding box to use for blob shadows       *
 *   Phys3Class::Get_Collision_Box -- returns the current world space collision box            *
 *   Phys3Class::Push -- Move in response to an animated object colliding with me              *
 *   Phys3Class::Collide -- Move in response to a collision                                    *
 *   Phys3Class::Can_Teleport -- Checks if this object can occupy the specified position       *
 *   Phys3Class::Can_Teleport_And_Stand -- Checks if this object can occupy the specified pos  *
 *   Phys3Class::Find_Teleport_Location -- Searches for a valid position near the given one    *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "phys3.h"
#include "wwdebug.h"
#include "wwhack.h"
#include "wwprofile.h"
#include "wwphystrig.h"
#include "physcoltest.h"
#include "physinttest.h"
#include "colmath.h"
#include "pscene.h"
#include "physcon.h"
#include "physcontrol.h"
#include "bitstream.h"
#include "persistfactory.h"
#include "simpledefinitionfactory.h"
#include "wwphysids.h"
#include "w3d_file.h"


DECLARE_FORCE_LINK(phys3);
const float STEP_HEIGHT = 0.25f;


#define VERBOSE_LOGGING 0

#if VERBOSE_LOGGING
#define VERBOSE_LOG(x) if (/*WWDEBUG_TRIGGER(WWDEBUG_TRIGGER_GENERIC0) &&*/ (strstr(Model->Get_Name(),"lt"))) { WWDEBUG_SAY(x); }
#else
#define VERBOSE_LOG(x)
#endif


/*
** Phys3 Networking constants:
*/
float Phys3Class::_CorrectionTime = 0.5f;
float Phys3Class::_AllowableError = 0.5f;
float Phys3Class::_PopError = 2.5f;


/*
** Phys3HistoryClass Parameters
** Control the implementation of the phys3 history tracking system with the following 
** parameters.
*/
const int		PHYS3_SNAPSHOT_COUNT = 16;					// must be power of 2!
const float		PHYS3_HISTORY_MIN_TIME = 1.25f;			// seconds of history to store

const int		PHYS3_SNAPSHOT_MASK = PHYS3_SNAPSHOT_COUNT - 1; 
const float		PHYS3_SNAPSHOT_INTERVAL = PHYS3_HISTORY_MIN_TIME / PHYS3_SNAPSHOT_COUNT;

#define PHYS3HISTORY_NO_CORRECTION			0
#define PHYS3HISTORY_ABSOLUTE_CORRECTION	0
#define PHYS3HISTORY_LERP_CORRECTION		1


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

	void		Init(const Vector3 & pos,const Vector3 & vel);
	void		Update_History(const Vector3 & pos,const Vector3 & vel, float dt);
	void		Compute_Old_State(float dt,Vector3 * set_pos,Vector3 * set_vel);
	void		Apply_Correction(const Vector3 & pos_correction);

	int		History_Count(void) { return PHYS3_SNAPSHOT_COUNT; }
	Vector3	Get_Historical_Position(int i) { return SnapshotArray[Wrap_Index(HeadIndex + i)].Position; }

	float		Find_Time_Of_Nearest_Point(const Vector3 & pos);
	void		Find_Nearest_Point(const Vector3 & pos,const Vector3 & vel,Vector3 * set_point);

private:

	int		Wrap_Index(int index) { return (index + PHYS3_SNAPSHOT_COUNT) & PHYS3_SNAPSHOT_MASK; }

	class StateSnapshotClass
	{
	public:
		StateSnapshotClass(void) : Position(0,0,0), Velocity(0,0,0), Age(0) { }
		StateSnapshotClass(const Vector3 & pos,const Vector3 & vel, float age) : Position(pos), Velocity(vel), Age(age) { }
		StateSnapshotClass & operator = (const StateSnapshotClass & that) { Position = that.Position; Velocity = that.Velocity; Age = that.Age; }

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

		Vector3				Position;
		Vector3				Velocity;
		float					Age;
	};

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

Phys3HistoryClass::Phys3HistoryClass(void) :
	Initialized(false),
	SnapshotArray(NULL),
	HeadIndex(0)
{
	SnapshotArray = new StateSnapshotClass[PHYS3_SNAPSHOT_COUNT];

	SnapshotArray[1].Age = 10000.0f;
}

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

void Phys3HistoryClass::Init(const Vector3 & pos,const Vector3 & vel)
{
	Initialized = true;
	int next_older_index = Wrap_Index(HeadIndex + 1);
	SnapshotArray[next_older_index].Age = 1000.0f;
	SnapshotArray[next_older_index].Position = pos;
	SnapshotArray[next_older_index].Velocity = vel;
}

void Phys3HistoryClass::Update_History(const Vector3 & pos,const Vector3 & vel, float dt)
{
	int next_older_index = Wrap_Index(HeadIndex + 1);

	/*
	** Special case the first time we insert a state
	*/
	if (SnapshotArray[next_older_index].Age == 0.0f) {
		SnapshotArray[next_older_index].Age = 1000.0f;
		SnapshotArray[next_older_index].Position = pos;
		SnapshotArray[next_older_index].Velocity = vel;
	}

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

	SnapshotArray[HeadIndex].Position = pos;
	SnapshotArray[HeadIndex].Velocity = vel;

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

void Phys3HistoryClass::Compute_Old_State(float t,Vector3 * set_pos,Vector3 * set_vel)
{
	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);
				if (set_pos != NULL) {
					*set_pos = SnapshotArray[tail_index].Position;
				}
				if (set_vel != NULL) {
					*set_vel = SnapshotArray[tail_index].Velocity;
				}
				return;
			}
		
		} else {
			done = true;
		}
	}
	
	int prev_index = Wrap_Index(index - 1);
	int next_index = index;

	StateSnapshotClass lerp;
	float fraction = (t - SnapshotArray[prev_index].Age) / (SnapshotArray[next_index].Age - SnapshotArray[prev_index].Age);
	lerp.Lerp(SnapshotArray[prev_index],SnapshotArray[next_index],fraction);
	if (set_pos != NULL) {
		*set_pos = lerp.Position;
	}
	if (set_vel != NULL) {
		*set_vel = lerp.Velocity;
	}
}

void Phys3HistoryClass::Apply_Correction(const Vector3 & pos_correction)
{
#if PHYS3HISTORY_ABSOLUTE_CORRECTION
	for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT; counter++) {
		int index = Wrap_Index(HeadIndex + counter);
		SnapshotArray[index].Position += pos_correction;
	}
#endif
#if PHYS3HISTORY_LERP_CORRECTION
	for (int counter=0; counter<PHYS3_SNAPSHOT_COUNT; counter++) {
		int index = Wrap_Index(HeadIndex + counter);
		
		float fraction = (PHYS3_HISTORY_MIN_TIME - SnapshotArray[index].Age) / PHYS3_HISTORY_MIN_TIME;
		if (fraction > 0.0f) {
			SnapshotArray[index].Position += fraction * pos_correction;
		}
	}
#endif
}

float Phys3HistoryClass::Find_Time_Of_Nearest_Point(const Vector3 & pos)
{
	/*
	** Find the nearest line segment
	*/
	float min_dist = 10000.0f;
	float time = 0.0f;

	for (int counter=0; counter<PHYS3_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(pos);

		float dist = (point - pos).Length();
		
		if (dist < min_dist) {
			float fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
			min_dist = dist;
			time = WWMath::Lerp(SnapshotArray[index0].Age, SnapshotArray[index1].Age, fraction);
		}
	}

	/*
	** Return the age of the nearest point
	*/
	return time;
}

void Phys3HistoryClass::Find_Nearest_Point(const Vector3 & pos,const Vector3 & vel,Vector3 * set_point)
{
	/*
	** Find the nearest line segment
	*/
	float min_dist = 10000.0f;

	for (int counter=0; counter<PHYS3_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(pos);
		float dist = (point - pos).Length();

		/*
		** Ignore points with velocity more than 90 deg away from server vel
		*/
		float vdot = 0.0f;
		Vector3 history_vel;
		float fraction = 0.0f;
		if (segment.Get_Length() > 0.0f) {
			fraction = (point - segment.Get_P0()).Length() / segment.Get_Length();
		}
		Vector3::Lerp(SnapshotArray[index0].Velocity,SnapshotArray[index1].Velocity,fraction,&history_vel);
		vdot = Vector3::Dot_Product(vel,history_vel); 
		
		if ((dist < min_dist) && (vdot >= 0.0f)) {
			*set_point = point;
			min_dist = dist;
		}
	}
}


void Phys3HistoryClass::StateSnapshotClass::Lerp(const StateSnapshotClass & a, const StateSnapshotClass & b, float fraction)
{
	Vector3::Lerp(a.Position,b.Position,fraction,&Position);
	Vector3::Lerp(a.Velocity,b.Velocity,fraction,&Velocity);
	Age = WWMath::Lerp(a.Age,b.Age,fraction);
}


/***********************************************************************************************
**
** Phys3Class Implementation
**
***********************************************************************************************/
 
/*
** Declare a PersistFactory for Phys3Classes
*/
SimplePersistFactoryClass<Phys3Class,PHYSICS_CHUNKID_PHYS3>	_Phys3Factory;

/*
** Definition factory for Phys3DefClass.  This makes it creatable in the editor
*/
DECLARE_DEFINITION_FACTORY(Phys3DefClass, CLASSID_PHYS3DEF, "Phys3") _Phys3DefDefFactory;

/*
** Chunk ID's for Phys3Class
*/
enum
{
	PHYS3_CHUNK_MOVEABLEPHYS			= 0x00483200,
	PHYS3_CHUNK_VARIABLES,

	PHYS3_VARIABLE_COLLISION_AABOX	= 0x00,
	PHYS3_VARIABLE_ONGROUND,
	PHYS3_VARIABLE_INCOLLISION,
	PHYS3_VARIABLE_HEADING,
	PHYS3_VARIABLE_NORMSPEED,
	PHYS3_VARIABLE_SLIDEANGLE,
	PHYS3_VARIABLE_SLIDENORMALZ,
	PHYS3_VARIABLE_SLIDEANGLETAN,
	PHYS3_VARIABLE_STEPHEIGHT,
	PHYS3_VARIABLE_MOVEMODE,
	PHYS3_VARIABLE_POSITION,
	PHYS3_VARIABLE_VELOCITY
};


static const float DEFAULT_STEP_HEIGHT = 0.25f;						// the distance an object will "step up" over an obstacle
static const float DEFAULT_SLIDE_ANGLE = DEG_TO_RADF(45.0f);	// steepest angle the character can walk up
static const float DEFAULT_NORMALIZED_SPEED = 10.0f;	 

static const float GROUND_DISTANCE = 0.1f;							// On ground if within this distance
static const float GROUND_EPSILON = (GROUND_DISTANCE) / 5.0f;	// Stop at this distance from ground
static const float WALL_EPSILON = 0.5f;//(GROUND_DISTANCE - 0.001f);	// Stop at this distance from walls/slides

static const float MIN_STEP_MOVE = 0.25f;								// only try to step if we could move this distance
static const float MAX_STEP_MOVE_ANGLE_TAN = 1.0f;					// only step if moving at an angle close to the x-y plane

// Debug Vector colors
static const Vector3 VELOCITY_COLOR(1,0,0);				// color for the velocity debug vector
static const Vector3 CONTACT_COLOR(0.25f,0.7f,0.2f);	// color for contact vectors
static const Vector3 GROUND_COLOR(0.0f,1.0f,1.0f);

static inline void Clip_Move(const Vector3 * contacts,int contact_count,Vector3 * move);



/*************************************************************************************
**
** Phys3Class Implementation
**
*************************************************************************************/


/***********************************************************************************************
 * Phys3Class::Phys3Class -- Constructor                                                       *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/15/2000  gth : Created.                                                                 *
 *=============================================================================================*/
Phys3Class::Phys3Class(void) 
{
	CollisionBox.Center.Set(0,0,1);
	CollisionBox.Extent.Set(1,1,1);
	OnGround = false;
	InCollision = false;
	HeadingChanged = false;
	GroundSurface = 0;
	Heading = 0.0f;
	NormSpeed = DEFAULT_NORMALIZED_SPEED;
	SlideAngle = DEFAULT_SLIDE_ANGLE;
	SlideNormalZ = WWMath::Cos(SlideAngle);
	SlideAngleTan = tan(SlideAngle);
	StepHeight = DEFAULT_STEP_HEIGHT;
	MoveMode = NORMAL_MOVE;
	GroundObject = NULL;
	AnimationMove.Set(0,0,0);
	History = NULL;
	LatencyError.Set(0,0,0);
	LastKnownPosition.Set(0,0,0);
	LastKnownVelocity.Set(0,0,0);
	Invalidate_Ground_State();
}	


/***********************************************************************************************
 * Phys3Class::Init -- initialize from a Definition                                            *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/15/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Init(const Phys3DefClass & def)
{
	MoveablePhysClass::Init(def);

	CollisionBox.Center.Set(0,0,1);
	CollisionBox.Extent.Set(1,1,1);
	OnGround = false;
	InCollision = false;
	GroundSurface = 0;
	Heading = 0.0f;
	NormSpeed = def.NormSpeed;
	SlideAngle = def.SlideAngle;
	SlideNormalZ = WWMath::Cos(SlideAngle);
	SlideAngleTan = tan(SlideAngle);
	StepHeight = def.StepHeight;
	MoveMode = NORMAL_MOVE;
	GroundObject = NULL;
	AnimationMove.Set(0,0,0);
	LatencyError.Set(0,0,0);
	LastKnownPosition.Set(0,0,0);
	LastKnownVelocity.Set(0,0,0);

	Update_Cached_Model_Parameters();	
	Invalidate_Ground_State();
}


/***********************************************************************************************
 * Phys3Class::~Phys3Class -- Destructor                                                       *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *=============================================================================================*/
Phys3Class::~Phys3Class(void)
{
	if (History != NULL) {
		delete History;
	}
}


/***********************************************************************************************
 * Phys3Class::Get_Bounding_Box -- Returns bounding box of the model                           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/15/2000  gth : Created.                                                                 *
 *=============================================================================================*/
const AABoxClass & Phys3Class::Get_Bounding_Box(void) const
{
	assert(Model);
	return Model->Get_Bounding_Box();
}


/***********************************************************************************************
 * Phys3Class::Get_Transform -- Returns the current transform                                  *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/15/2000  gth : Created.                                                                 *
 *=============================================================================================*/
const Matrix3D & Phys3Class::Get_Transform(void) const
{
	assert(Model);
	return Model->Get_Transform();
}


/***********************************************************************************************
 * Phys3Class::Set_Transform -- Sets the current transform                                     *
 *                                                                                             *
 *    Note that this "warps" the object to the specified position.  The user is responsible    *
 *    for providing a valid position.                                                          *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/15/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Set_Transform(const Matrix3D & m)
{
	// copy the translation portion of the transform into our state
	m.Get_Translation( &State.Position );

#ifdef WWDEBUG
	if (!State.Position.Is_Valid()) {
		WWDEBUG_SAY(("Phys3Class::Set_Transform got an invalid position: %f, %f, %f\r\n",State.Position.X,State.Position.Y,State.Position.Z));
	}
#endif

	// copy the Z rotation portion of the transform into our heading variable (ugh...)
	Heading = m.Get_Z_Rotation();
	Update_Transform();
	Update_Cull_Box();

	// Wake the object up whenever it moves
	Set_Flag(ASLEEP,false);
	Invalidate_Ground_State();

	Assert_State_Valid();
}


/***********************************************************************************************
 * Phys3Class::Set_Position -- Sets the position                                               *
 *                                                                                             *
 *    This blindly warps the object to the given position.  The user is responsible for        *
 *    providing a valid position.                                                              *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Set_Position(const Vector3 & position)
{
	State.Position = position;
	VERBOSE_LOG(("Phys3::Set_Position %s, (%f, %f, %f)\r\n\r\n",Model->Get_Name(),position.X,position.Y,position.Z));
	Update_Transform(true);
	Update_Cull_Box();
	Set_Flag(ASLEEP,false);
	Invalidate_Ground_State();
}


/***********************************************************************************************
 * Phys3Class::Get_Position -- Returns the current position                                    *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
const Vector3 & Phys3Class::Get_Position(void) const
{
	return State.Position;
}


/***********************************************************************************************
 * Phys3Class::Set_Heading -- Set the heading of this object                                   *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Set_Heading(float heading)
{
	WWASSERT(WWMath::Is_Valid_Float(heading));
	if (heading != Heading) {
		Heading = heading;
		HeadingChanged = true;
	}
	Update_Transform();
}


/***********************************************************************************************
 * Phys3Class::Get_Heading -- returns the heading of this object                               *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
float Phys3Class::Get_Heading(void) const
{
	return Heading;
}


/***********************************************************************************************
 * Phys3Class::Set_Slide_Angle -- Sets the maximum angle this object can climb                 *
 *                                                                                             *
 * If a phys3 object is resting on a slope greater than this angle, it will slide down the     *
 * slope.                                                                                      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Set_Slide_Angle(float angle)
{
	SlideAngle = angle;
	SlideNormalZ = WWMath::Cos(angle);
	SlideAngleTan = tan(angle);
}


/***********************************************************************************************
 * Phys3Class::Get_Slide_Angle -- returns the slide angle                                      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
float Phys3Class::Get_Slide_Angle(void) const
{
	return SlideAngle;
}


/***********************************************************************************************
 * Phys3Class::Update_Transform -- Recalculate the transform                                   *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Update_Transform(bool position_only)
{
	Vector3 pos_dif = Model->Get_Position();
	pos_dif -= State.Position;

	if ((position_only) && (!HeadingChanged)) {
	
		Model->Set_Position(State.Position);
		
	} else {
		
		Matrix3D tm(1);
		tm.Set_Translation(State.Position);
		tm.Rotate_Z(Heading);
		Model->Set_Transform(tm);
	}

	if ( pos_dif.Length2() > WWMATH_EPSILON2 ) {
		Update_Visibility_Status();
	}

	#ifdef WWDEBUG
	for (int j=0;j<3;j++) {

		WWASSERT(WWMath::Is_Valid_Float(State.Position.X));
		WWASSERT(WWMath::Is_Valid_Float(State.Position.Y));
		WWASSERT(WWMath::Is_Valid_Float(State.Position.Z));
		WWASSERT(WWMath::Is_Valid_Float(Heading));
	}
	#endif

	HeadingChanged = false;
}


/***********************************************************************************************
 * Phys3Class::Set_Model -- Set the model being used                                           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Set_Model(RenderObjClass * model)
{
	// Let the base class have the model
	MoveablePhysClass::Set_Model(model);

	// update any members that depend on the model
	Update_Cached_Model_Parameters();

	// Update our culling box
	Update_Cull_Box();
}


/***********************************************************************************************
 * Phys3Class::Update_Cached_Model_Parameters -- Caches some data related to the model         *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Update_Cached_Model_Parameters(void)
{
	// if we don't have a model yet, just return
	if (Model == NULL) {
	
		return;

	} else {

		// Construct our collision box from the model
		RenderObjClass * box = NULL;

		if (Model->Class_ID() == RenderObjClass::CLASSID_DISTLOD) {
			RenderObjClass * lod0 = Model->Get_Sub_Object(0);
			box = lod0->Get_Sub_Object_By_Name("WORLDBOX");
			lod0->Release_Ref();
		} else {
			box = Model->Get_Sub_Object_By_Name("WORLDBOX");
		}
		
		if (box) {

			Matrix3D old_transform = Model->Get_Transform();
			Model->Set_Transform(Matrix3D(1));

			CollisionBox = box->Get_Bounding_Box();
			box->Release_Ref();
			Model->Set_Transform(old_transform);

		}
	}
}


/***********************************************************************************************
 * Phys3Class::Apply_Impulse -- Apply an impulse to this object                                *
 *                                                                                             *
 * As the code comment says... since we changed Phys3 to essentially have infinite friction,   *
 * this function will only work if the object is in the air currently.                         *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Apply_Impulse(const Vector3 & impulse)
{
	// Impluse applied to center of mass simply adds to the linear momentum
	// NOTE: the current algorithm does not maintain velocity uless undergoing
	// ballistic movement... this routine will often have no effect...
	State.Velocity += impulse * MassInv;
}


/***********************************************************************************************
 * Phys3Class::Apply_Impulse -- Apply an impulse to this object                                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Apply_Impulse(const Vector3 & impulse, const Vector3 & wpos)
{
	// Phys3 has only linear momentum so just apply the impulse to the CM...
	Apply_Impulse(impulse);
}


/***********************************************************************************************
 * Phys3Class::Cast_Ray -- Check a ray for intersection with this object                       *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Cast_Ray(PhysRayCollisionTestClass & raytest) 
{
	if (Model->Cast_Ray(raytest)) {
		raytest.CollidedPhysObj = this;
		return true;
	}
	return false;
}


/***********************************************************************************************
 * Phys3Class::Cast_AABox -- Check a swept AABox for intersection with this obj                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Cast_AABox(PhysAABoxCollisionTestClass & boxtest)
{
	AABoxClass wrldbox;
	wrldbox.Extent = CollisionBox.Extent;
	wrldbox.Center = State.Position + CollisionBox.Center;

	if (CollisionMath::Collide(boxtest.Box,boxtest.Move,wrldbox,boxtest.Result)) {
		boxtest.CollidedPhysObj = this;
		return true;
	}
	return false;
}


/***********************************************************************************************
 * Phys3Class::Cast_OBBox -- Check a swept OBBox for intersection with this obj                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest)
{
	AABoxClass wrldbox;
	wrldbox.Extent = CollisionBox.Extent;
	wrldbox.Center = State.Position + CollisionBox.Center;

	if (CollisionMath::Collide(boxtest.Box,boxtest.Move,wrldbox,Vector3(0,0,0),boxtest.Result)) {
		boxtest.CollidedPhysObj = this;
		return true;
	}
	return false;
}


/***********************************************************************************************
 * Phys3Class::Intersection_Test -- Check an AABox for intersection with this object           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Intersection_Test(PhysAABoxIntersectionTestClass & test)
{
	AABoxClass wrldbox;
	wrldbox.Extent = CollisionBox.Extent;
	wrldbox.Center = State.Position + CollisionBox.Center;

	if (CollisionMath::Intersection_Test(wrldbox,test.Box)) {
		test.Add_Intersected_Object(this);
		return true;
	}
	return false;
}


/***********************************************************************************************
 * Phys3Class::Intersection_Test -- Check an OBBox for intersection with this object           *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Intersection_Test(PhysOBBoxIntersectionTestClass & test)
{
	AABoxClass wrldbox;
	wrldbox.Extent = CollisionBox.Extent;
	wrldbox.Center = State.Position + CollisionBox.Center;

	if (CollisionMath::Intersection_Test(wrldbox,test.Box)) {
		test.Add_Intersected_Object(this);
		return true;
	}
	return false;
}


/***********************************************************************************************
 * Phys3Class::Intersection_Test -- Check a mesh for intersection with this object             *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Intersection_Test(PhysMeshIntersectionTestClass & test)
{
	AABoxClass wrldbox;
	wrldbox.Extent = CollisionBox.Extent;
	wrldbox.Center = State.Position + CollisionBox.Center;

	AABoxIntersectionTestClass our_test(wrldbox,test.CollisionType);
	if (test.Mesh->Intersect_AABox(our_test)) {
		test.Add_Intersected_Object(this);
		return true;
	}
	return false;
}


/***********************************************************************************
**
**
** Simulation Code
**
**
***********************************************************************************/


/***********************************************************************************************
 * Phys3Class::Timestep -- Simulate for time dt                                                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Timestep(float dt)
{	
	WWPROFILE("Phys3::Timestep");
	VERBOSE_LOG(("\r\n***** Phys3::Timestep. %s position: %f %f %f\r\n",Model->Get_Name(),State.Position.X,State.Position.Y,State.Position.Z));

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

		/*
		** Debugging, Draw our history if present
		*/
		Vector3 p0 = State.Position;
		Vector3 p1; 
		
		for (int i=1; i<History->History_Count(); i++) {
			p1 = History->Get_Historical_Position(i);	
			DEBUG_RENDER_VECTOR(p0,p1-p0,VELOCITY_COLOR); 
			p0 = p1;
		}
	}

	/*
	** If there is remaining latency error, correct some of it
	*/
	if (LatencyError.Length2() > 0.001f) {

		Vector3 correction;
		float pop_error2 = _PopError * _PopError;
		float allowable_error2 = _AllowableError * _AllowableError;
		float latency_error2 = LatencyError.Length2();

		if (State.Velocity.Length2() > 1.0f) {
			allowable_error2 = 0.0f;
		}

		if (latency_error2 > pop_error2) {
			
			/*
			** Teleport to the network position:
			*/
			Network_Teleport_Correction();

		} else if (latency_error2 > allowable_error2) {

			/*
			** Try to gracefully correct the error:
			*/
			float frame_time = dt;
			float correction_time = _CorrectionTime;
			float correction_fraction;
		
			if (correction_time <= frame_time) {
				correction_fraction = 1.0f;
			} else {
				correction_fraction = WWMath::Clamp(frame_time / correction_time);
			}

			correction_fraction = WWMath::Clamp(correction_fraction,0.0f,0.5f);
			correction = correction_fraction * LatencyError;

			/*
			** Smoothly correct our position
			*/
			Vector3 start = State.Position;
			this->Apply_Move(correction,1.0f,true,false,false);
			Update_Transform();
			Vector3 delta = State.Position - start;
			History->Apply_Correction(delta);

			/*
			** If the correction completely fails, teleport
			*/
			float correction_len = correction.Length();
			float delta_len = delta.Length();
			if ((correction_len > 0.0f) && (delta_len/correction_len < 0.1f)) {
	
				Network_Teleport_Correction();

			} else {
				LatencyError -= delta;
			}
		}
	}

	/*
	** If we're asleep, short circuit
	*/
	if (Is_Asleep() && !Is_User_Control_Enabled()) {
		return;
	}

	Vector3 start_pos = State.Position;
	Vector3 move;
	AABoxClass box;
	CastResultStruct res;

	GroundStateStruct & gs = Get_Ground_State();

	OnGround = gs.OnGround;
	GroundSurface = gs.SurfaceType;
	WWASSERT(GroundSurface >= 0);
	WWASSERT(GroundSurface < SURFACE_TYPE_MAX);

	bool moved = false;

	if (Is_User_Control_Enabled()) {

		MoveMode = USER_OVERRIDE;
		moved = User_Move(dt);

	} else {

		if (!gs.OnGround) {

			MoveMode = BALLISTIC_MOVE;
			moved = Ballistic_Move(dt);

		} else {
			
			if (gs.Normal.Z < SlideNormalZ) {

				MoveMode = SLIDE_MOVE;
				moved = Slide_Move(gs,dt);

			} else {

				MoveMode = NORMAL_MOVE;
				moved = Normal_Move(gs,dt);			

			}
		}
	}

	
	if ((moved) || (HeadingChanged)) {
		WWPROFILE("Phys3Class::Timestep cleanup");
		Update_Transform(true);
		Update_Cull_Box();
		Add_Animation_Move(State.Position - start_pos);

		if (!OnGround) {
			Link_To_Carrier(NULL);
		}
	}

	if (GroundState.OnDynamicObj) {
		Invalidate_Ground_State();
		Set_Flag(ASLEEP,false);
	}


//	DEBUG_RENDER_VECTOR(State.Position,State.Velocity,VELOCITY_COLOR);

	WWASSERT(WWMath::Is_Valid_Float(State.Position.X));
	WWASSERT(WWMath::Is_Valid_Float(State.Position.Y));
	WWASSERT(WWMath::Is_Valid_Float(State.Position.Z));

	WWASSERT(WWMath::Is_Valid_Float(State.Velocity.X));
	WWASSERT(WWMath::Is_Valid_Float(State.Velocity.Y));
	WWASSERT(WWMath::Is_Valid_Float(State.Velocity.Z));

	VERBOSE_LOG((" ***** Phys3::Timestep ended. Position: %f %f %f\r\n\r\n",State.Position.X,State.Position.Y,State.Position.Z));
}


/***********************************************************************************************
 * Phys3Class::Get_Ground_State -- validates and returns the gound state structure             *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
Phys3Class::GroundStateStruct & Phys3Class::Get_Ground_State(void)
{
	WWPROFILE("Phys3::Get_Ground_State");
	if (GroundState.IsDirty == true) {
		GroundState.IsDirty = false;
		AABoxClass box;
		Compute_WS_Collision_Box(State,&box);
		Check_Ground(box,&GroundState,GROUND_DISTANCE);
	}

	VERBOSE_LOG((" GS: OnGround = %d Normal = %f %f %f\r\n",GroundState.OnGround,GroundState.Normal.X,GroundState.Normal.Y,GroundState.Normal.Z));

	return GroundState;
}


/***********************************************************************************************
 * Phys3Class::Invalidate_Ground_State -- Marks the ground state as dirty                      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Invalidate_Ground_State(void)
{
	GroundState.IsDirty = true;
}


/***********************************************************************************************
 * Phys3Class::Check_Ground -- Update the ground state                                         *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Check_Ground(const AABoxClass & box,GroundStateStruct * gs,float check_dist)
{
	WWPROFILE("Check_Ground");
	VERBOSE_LOG(("Phys3::Check_Ground\r\n"));
	CastResultStruct result;
	PhysAABoxCollisionTestClass test(	box,
													Vector3(0,0,-check_dist),
													&result,
													Get_Collision_Group(),
													COLLISION_TYPE_PHYSICAL	);

	Inc_Ignore_Counter();
	PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
	Dec_Ignore_Counter();

	if (result.Fraction < 1.0f) {

		gs->Init_From_Collision_Result(test,result.Fraction * check_dist);
		
		if (!result.StartBad) {
			Link_To_Carrier(test.CollidedPhysObj,test.CollidedRenderObj);
			DEBUG_RENDER_VECTOR(State.Position,gs->Normal,GROUND_COLOR);
			VERBOSE_LOG(("  not intersecting\r\n"));
		}

	} else {
	
		gs->OnGround = false;
		gs->SurfaceType = SURFACE_TYPE_DEFAULT;
		VERBOSE_LOG(("  not on ground!\r\n"));
	}

	if (result.StartBad) {
		DEBUG_RENDER_AABOX(box,Vector3(1,0,0),0.25f);
		VERBOSE_LOG(("  INTERSECTING!\r\n"));
	}

}


/***********************************************************************************************
 * Phys3Class::User_Move -- User Move mode.                                                    *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::User_Move(float dt)
{
	VERBOSE_LOG(("Phys3::User_Move\r\n"));
	if (Controller) {
		
		Vector3 move(Controller->Get_Move_Vector());
		move.Rotate_Z(Heading);

		State.Velocity = NormSpeed * move;
		move = State.Velocity * dt;
		
		return Apply_Move(move,dt);
	}
	return false;
}


/***********************************************************************************************
 * Phys3Class::Ballistic_Move -- Ballistic motion                                              *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Ballistic_Move(float dt)
{		
	// Compute a move vector for the object flying through the air...
	WWPROFILE("Phys3::Ballistic_Move");
	VERBOSE_LOG(("Phys3::Ballistic_Move\r\n"));

	Vector3 move;
	float accel = PhysicsConstants::GravityAcceleration.Z * GravScale;

	Vector3 start_vel = State.Velocity;
	Vector3 start_pos = State.Position;

	move.X = State.Velocity.X * dt;
	move.Y = State.Velocity.Y * dt;
	move.Z = 0.5f * accel * dt * dt + State.Velocity.Z * dt;

	bool moved = Apply_Move(move,dt);

	// Compute X,Y velocities from the actual move that was performed
	State.Velocity.X = (State.Position.X - start_pos.X) / dt;
	State.Velocity.Y = (State.Position.Y - start_pos.Y) / dt;

	// Compute the analytical Z velocity and the ad-hoc Z velocity, the
	// more negative one is the one to keep.  What this does is use the 
	// analytical velocity unless the character hits a roof.
//	if (CollidedThisFrame) {
//		State.Velocity.Z = (State.Position.Z - start_pos.Z) / dt;
//	} else {
		State.Velocity.Z = start_vel.Z + accel * dt;
//	}
	return moved;
}


/***********************************************************************************************
 * Phys3Class::Slide_Move -- Sliding motion, down a slope                                      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Slide_Move(const GroundStateStruct & gs,float dt)
{
	// Compute a move vector which causes the object to slide down the slope...
	WWPROFILE("Phys3::Slide_Move");
	VERBOSE_LOG(("Phys3::Slide_Move\r\n"));

	Vector3 start_pos = State.Position;
	Vector3 move = NormSpeed * dt * gs.Down;

	bool moved = Apply_Move(move,dt,true,false);
	State.Velocity = (State.Position - start_pos) / dt;
	return moved;
}


/***********************************************************************************************
 * Phys3Class::Normal_Move -- Normal motion, phys3 does nothing (infinite grip)                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Normal_Move(const GroundStateStruct & gs,float dt)
{
	WWPROFILE("Phys3::Normal_Move");
	VERBOSE_LOG(("Phys3::Normal_Move\r\n"));
	
	// phys3 doesn't move unless on a slope or falling.
	State.Velocity.Set(0,0,0);
	return false;
}


/***********************************************************************************************
 * Phys3Class::Collide_Move -- Move in response to someone colliding with me                   *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Collide_Move(const Vector3 & requested_move,float dt)
{
	WWPROFILE("Phys3::Collide_Move");
	VERBOSE_LOG(("Phys3::Collide_Move\r\n"));

	// if we're already being considered in a collision (collisions looped back to us) then return
	if (InCollision) {
		return false;
	}
	
	InCollision = true;
	MoveMode = COLLIDE_MOVE;
	Vector3 start_position = State.Position;
	bool moved = Apply_Move(requested_move,dt,true,false);
	
	/*
	** if we moved and we started on the ground, try to stick to the ground
	*/
	if (moved && OnGround) {
		Snap_To_Ground(State.Position - start_position,true);
	}
	InCollision = false;

	State.Velocity = (State.Position - start_position) / dt;
	Add_Animation_Move(State.Position - start_position);
	
	Update_Transform(true);
	Update_Cull_Box();

	return moved;
}


/***********************************************************************************************
 * Phys3Class::Apply_Move -- Apply a movement vector, constrained by world geometry            *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Apply_Move
(
	const Vector3 & input_move,
	float dt,
	bool allow_sliding,
	bool allow_stepping,
	bool stop_on_walkable
)
{
	WWPROFILE("Phys3::Apply_Move");
	VERBOSE_LOG(("Phys3::Apply_Move: %f %f %f\r\n",input_move.X,input_move.Y,input_move.Z));
	
#ifdef WWDEBUG
	if (!input_move.Is_Valid()) {
		WWDEBUG_SAY(("Phys3::Apply_Move called with invalid move vector: %f, %f, %f\r\n",input_move.X,input_move.Y,input_move.Z));
//		WWASSERT(input_move.Is_Valid());
		return false;
	}
#endif

	/*
	** Early exit if we're not really moving!
	*/
	float move_len = input_move.Length();
	if (move_len < WWMATH_EPSILON) {
		if (GroundState.OnGround) {
			Set_Flag(ASLEEP,true);
		}
		VERBOSE_LOG(("  no motion\r\n"));
		return false;
	} else {
		VERBOSE_LOG(("  applying move (%f,%f,%f)\r\n",input_move.X,input_move.Y,input_move.Z));
		Set_Flag(ASLEEP,false);
		Invalidate_Ground_State();
	}

	bool moved = false;
	bool done = false;
	bool collided = false;

	const int MAX_CONTACTS = 4;
	const int MAX_ITERATIONS = 4;
	
	int total_iterations = 0;
	int contact_count = 0;
	Vector3 contacts[MAX_CONTACTS];
	
	Vector3 start_position = State.Position;
	Vector3 move = input_move;
	Vector3 move_dir = move / move_len;

	bool already_stepped = false;

	Inc_Ignore_Counter();
	
	/*
	** Apply Move:
	** 
	** - WHILE (I still have movement to apply)
	**   - sweep our collision box up to the first collision
	**   - IF (I started out intersecting) give up, done
	**   - ELSE 
	**		 - IF (I was able to take the entire move without collision) accept the move, done 
	**	    - ELSE 
	**       - Accept the portion of the move up to the collision
	**       - Call the collision callbacks
	**       - IF (I have no movement remaining OR I'm not allowed to slide) done
	**       - ELSE 
	**			  - If (I can push the obstacle out of my way) push obstacle, go back to top of loop
	**			  - If (I can shatter this object) shatter it, go back to top of loop
	**			  - If (This contact is a slope, and I'm trying to walk up it) modify contact to act like a wall
	**         - Reflect the movement vector so that it does not violate the current contacts
	**
	** Notes:
	** - Try to stop movement some distance (COLLISION_EPSILON) away from collisions.
	** - When approaching slides (slopes that we'll slide off of), try to treat them like walls.
	** - Can't always treat slides like walls since we can fall onto them or walk onto them from the top...
	** - Trying to get stepping for free usually by just always bumping the character up before taking his move
	**
	*/
	
	while (!done) {
		
		if ( ++total_iterations > MAX_ITERATIONS ) {
			VERBOSE_LOG(( "Phys3 Exceeded max iterations!\r\n" ));
			done = true;
		}

		/*
		** Get the collision box for the object
		*/
		AABoxClass box;
		Compute_WS_Collision_Box(State,&box);

		CastResultStruct result;
		PhysAABoxCollisionTestClass test(	box,
														move,
														&result,
														Get_Collision_Group(),
														COLLISION_TYPE_PHYSICAL		);
		
		/*
		** If we're allowed to step up, try to
		*/
		if (OnGround && allow_stepping && !already_stepped) {		

			already_stepped = true;

			/*
			** Bump the box up Z by STEP_HEIGHT
			*/
			test.Translate(Vector3(0,0,STEP_HEIGHT));

			/*
			** Sweep the box
			*/
			PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
			
			/*
			** IF: the 'step' test did not start out intersecting (no low roof) then
			** warp our position up and continue
			**
			** ELSE: Since the step test started out intersecting we must be under a low
			** roof.  In this case, perform the move from our original position; no
			** stepping over obstacles.
			*/
			if (!result.StartBad) {
				
				State.Position.Z += STEP_HEIGHT;
				moved = true;

			} else {

				result.Reset();
				test.Translate(Vector3(0,0,-STEP_HEIGHT));
				PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
			
			}
		
		} else {

			PhysicsSceneClass::Get_Instance()->Cast_AABox(test);

		}
		
		VERBOSE_LOG(("  Checking collision results:  "));
		if (result.StartBad) {
			//WWDEBUG_WARNING(("result.StartBad\n"));
			//PhysicsSceneClass::Get_Instance()->Add_Debug_AABox(box,Vector3(1,0,0));
			VERBOSE_LOG(("StartBad!\r\n"));
			done = true;
		
		} else {

			if (result.Fraction == 1.0f) {

				/*
				** the move was successful, accept it.
				*/
				VERBOSE_LOG(("Entire move accepted.\r\n"));
				State.Position += move;
	
				done = true;
				moved = true;

			} else {

				VERBOSE_LOG(("Collided with %s. normal=(%f,%f,%f)\r\n",test.CollidedRenderObj->Get_Name(),result.Normal.X,result.Normal.Y,result.Normal.Z));
				collided = true;

				/*
				** How far should we stay away from this obstacle
				*/
				float epsilon = GROUND_EPSILON;
				if (result.Normal.Z < SlideNormalZ) {								// TODO: base this on surface type?
					epsilon = WALL_EPSILON;
				}

				/*
				** Try to move up to the obstacle
				*/
				float move_len = result.Fraction * move.Length();
				if (move_len > epsilon) {
					move_len -= epsilon;
					Vector3 direction = move;
					direction.Normalize();

					State.Position += move_len * direction;

					move -= result.Fraction * move;
					dt -= result.Fraction * dt;
					moved = true;
					contact_count = 0;					// since we moved, reset the contacts
				} 
				
				/*
				** If there is no time remaining, no movement remaining,
				** or we're not allowed to "slide" along contacts then we're done
				*/
				if (	(dt < WWMATH_EPSILON) || 
						(move.Length2() < WWMATH_EPSILON) ||
						(allow_sliding == false) ) 
				{
				
					/*
					** we're close enough to done 
					*/
					done = true;
				
				} else {
		
					/*
					** We hit something, call the collision callbacks.
					*/
					WWASSERT(test.CollidedPhysObj != NULL);
					CollisionReactionType reaction = COLLISION_REACTION_DEFAULT;

					CollisionEventClass event;
					event.OtherObj = test.CollidedPhysObj;
					reaction |= Collision_Occurred(event);
					event.OtherObj = this;
					reaction |= test.CollidedPhysObj->Collision_Occurred(event);
					
					if (reaction & COLLISION_REACTION_NO_BOUNCE) {
		
						/*
						** If we're instructed to not bounce, just erase the remaining time
						*/
						dt = 0.0f;
					
					} else {

						/*
						** assume we're going to have to clip the move
						*/
						bool clip_move = true;
						
						/*
						** PUSH: Try to push the obstacle
						*/
						if (clip_move) {
							Phys3Class * p3obj = test.CollidedPhysObj->As_Phys3Class();

							if (p3obj != NULL) {

								/*
								** Compute a move for the object we're contacting which makes it slide out
								** of our way if we're moving at an angle to it.
								*/
								Vector3 dc;
								Vector3::Subtract(p3obj->Get_Position(),State.Position,&dc);
								dc.Normalize();
								float fraction = 1.0f;
								if ((Mass > WWMATH_EPSILON) && (p3obj->Get_Mass() > WWMATH_EPSILON)) {
									fraction = (Mass / (Mass + p3obj->Get_Mass()));
								}
								dc *= fraction * move.Length();

								Dec_Ignore_Counter();
								if (p3obj->Collide_Move(dc,dt)) {
									clip_move = false;
								}
								Inc_Ignore_Counter();
							}
						}
					
						/*
						** SHATTER: If the mesh is shatterable, shatter it.  We are doing this so that 
						** shatterable meshes are purely cosmetic for multiplay.  
						*/
						MeshClass * mesh = NULL;
						if ((test.CollidedRenderObj != NULL) && (test.CollidedRenderObj->Class_ID() == RenderObjClass::CLASSID_MESH)) {
							mesh = (MeshClass*)test.CollidedRenderObj;
						}

						if	(	(mesh != NULL) && 
								(mesh->Get_W3D_Flags() & W3D_MESH_FLAG_SHATTERABLE) && 
								(mesh->Is_Not_Hidden_At_All())) 
						{

							PhysicsSceneClass::Get_Instance()->Shatter_Mesh(	mesh,
																								State.Position,
																								result.Normal,
																								State.Velocity	);
							if (Observer != NULL) {
								Observer->Object_Shattered_Something(this,test.CollidedPhysObj,result.SurfaceType);
							}

							clip_move = false;
						}


						/*
						** CLIP: If the obstacle didn't move out of our way and we didn't step over it, 
						** add the new contact and adjust our move to not hit it again
						*/
						if (clip_move) {

							if (contact_count < MAX_CONTACTS) {
				
								Vector3 normal = result.Normal;
								if (	(normal.Z < SlideNormalZ) && 
										(normal.Z > 0.0f) && 
										(GroundState.OnGround) && 
										(GroundState.Normal.Z > SlideNormalZ) &&
										(MoveMode == BALLISTIC_MOVE)) 
								{
									normal.Z = 0.0f;
									normal.Normalize();
								}
								contacts[contact_count++] = normal;
								DEBUG_RENDER_VECTOR(State.Position,1.5*normal,CONTACT_COLOR);

							} else {

								VERBOSE_LOG(("exceeded max contacts in Phys3::Apply_Move! position: %f, %f, %f\r\n",State.Position.X,State.Position.Y,State.Position.Z));
								done = true;

							}

							Clip_Move(contacts,contact_count,&move);
						}
					}
				}
			}
		}
	}

	Dec_Ignore_Counter();

	return moved;
}


/***********************************************************************************************
 * Phys3Class::Debug_Verify_Position -- Verify that I am in a valid position                   *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
inline bool Phys3Class::Debug_Verify_Position(void)
{
#ifdef VERBOSE_LOGGING
	PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance();

	AABoxClass box;
	Compute_WS_Collision_Box(State,&box);

	NonRefPhysListClass list;

	PhysAABoxIntersectionTestClass test(box,Get_Collision_Group(),COLLISION_TYPE_PHYSICAL,&list);

	Inc_Ignore_Counter();
	bool intersecting = scene->Intersection_Test(test);
	Dec_Ignore_Counter();

	if (intersecting) {
		VERBOSE_LOG(("		%s is intersecting %s!\r\n",Model->Get_Name(),list.Peek_Head()->Peek_Model()->Get_Name()));
	} else {
		VERBOSE_LOG(("    %s not intersecting\r\n",Model->Get_Name()));
	}
	return intersecting;
#else
	return true;
#endif	
}


/***********************************************************************************************
 * Phys3Class::Snap_To_Ground -- Following a move, snap back down to the ground                *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Snap_To_Ground(const Vector3 & actual_move,bool was_stepping)
{
	if (Get_Flag(ASLEEP)) {
		return;
	}
	
	VERBOSE_LOG(("Phys3::Snap_To_Ground\r\n"));

	/*
	** Call this if you started a timestep on the ground and want to stay on the ground...
	** If we were on the ground, we snap down to the ground to ensure that the object does not
	** fly unrealistically off edges, etc.  The logic works like this:
	** - the object may have to snap at least back down to the same altitude it started at
	** - in addition to this, we want to keep the object "stuck" on slopes up to SlideAngle
	** - so, cast its box down this much:  delta_z + delta_xy * tan(SlideAngle)
	** - if what we hit has a slope that less than SlideAngle, snap down!
	*/
	float delta_z = actual_move.Z;
	float delta_xy = WWMath::Sqrt(actual_move.X * actual_move.X + actual_move.Y * actual_move.Y);

	/*
	** If we don't need to snap down, just return
	*/
	if (delta_z + delta_xy > 0.0f) { 
	
		float snap_dist = delta_z + delta_xy * SlideAngleTan + GROUND_DISTANCE;
		
		Inc_Ignore_Counter();

		AABoxClass box;
		Compute_WS_Collision_Box(State,&box);
		CastResultStruct result;
		PhysAABoxCollisionTestClass test(box,Vector3(0,0,-snap_dist),&result,Get_Collision_Group());
		PhysicsSceneClass::Get_Instance()->Cast_AABox(test);

		if ((result.Fraction > 0.0f) && (result.Fraction <= 1.0f) && (!result.StartBad)) {
			
			float snap_down = 0;
			if (result.Normal.Z > SlideNormalZ) {

				/*
				** Case 1: ground is within snap distance and that ground is a walkable slope
				** Snap down to within GROUND_EPSILON of the ground.
				*/
				snap_down = snap_dist * result.Fraction;
				if (snap_down > GROUND_EPSILON) {
					State.Position.Z -= snap_down - GROUND_EPSILON;
				}
				OnGround = true;
				Link_To_Carrier(test.CollidedPhysObj,test.CollidedRenderObj);

				DEBUG_RENDER_VECTOR(State.Position,result.Normal,GROUND_COLOR);
				VERBOSE_LOG(("  now above walkable slope, snapped down %f\r\n",snap_down - GROUND_EPSILON));

			} else {

				/*
				** Case 2: ground is within snap distance but it is not a walkable slope
				**	If we were stepping, we should snap down STEP_HEIGHT if we can.
				*/
				OnGround = false;
				if (was_stepping) {
					// (gth) just let the character snap down to slopes too!
					//snap_down = WWMath::Min(STEP_HEIGHT,snap_dist * result.Fraction);
					if (snap_down > GROUND_EPSILON) {
						State.Position.Z -= snap_down - GROUND_EPSILON;
					}
				}
				
				Link_To_Carrier(NULL);

				VERBOSE_LOG(("  now above non-walkable slope, snapped only up to step-height %f\r\n",snap_down-GROUND_EPSILON));
			}
		
		} else {
			
			/*
			** Case 3: ground is not within snap distance.
			** If we were stepping, snap back down the step distance.
			*/
			OnGround = false;
			if (was_stepping) {
				State.Position.Z -= STEP_HEIGHT;
			}

			VERBOSE_LOG(("  now airborne\r\n"));
		}

		/*
		** Update the GroundState
		*/
		GroundState.Init_From_Collision_Result(test,GROUND_EPSILON);

		Dec_Ignore_Counter();
	}
}


/***********************************************************************************************
 * Phys3Class::Clip_Move -- Clip a move vector by all of the active contact normals            *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Clip_Move(const Vector3 * contacts,int contact_count,Vector3 * move)
{
	WWASSERT(contacts != NULL);
	VERBOSE_LOG(("Phys3::Clip_Move\r\n"));

#ifdef WWDEBUG
	float move_len2 = move->Length2();
#endif
	
	// Loop over all of the contacts.
	//	On each one we modify our move vector to parallel that plane.  
	// Immediately after each modification, we check if the other planes are a problem.  
	// At the first time all planes are satisfied, we're done
	for (int i=0; i<contact_count; i++) {
		
#ifdef WWDEBUG
		if (fabs(contacts[i].Length() - 1.0f) > WWMATH_EPSILON) {
			WWDEBUG_SAY(("Bad Contact Normal: %f %f %f  Length = %f\r\n",contacts[i].X,contacts[i].Y,contacts[i].Z,contacts[i].Length()));
		}
#endif
		if (!( fabs(contacts[i].Length() - 1.0f) < WWMATH_EPSILON )) {
			*move = Vector3(0,0,0);
			return;
		}

		VERBOSE_LOG(("  normal[%d] = %f %f %f\r\n",i,contacts[i].X,contacts[i].Y,contacts[i].Z));
		
		// push the velocity a little bit away from the plane
		float dot = Vector3::Dot_Product(*move,contacts[i]);
		if (dot < 0.0f) {
			Vector3 adjustment = 1.01f * dot * contacts[i];
			*move -= adjustment;
			//WWASSERT(Vector3::Dot_Product(*move,contacts[i]) >= 0.0f);
		}
		
		for (int j=0; j<contact_count; j++) {
			float check = Vector3::Dot_Product(*move,contacts[j]);
			if (check < 0.0f) {
				break;	// this contact isn't happy yet... keep choppin.
			}
		}
		if (j == contact_count) {
			break;	// all contacts are happy!
		}
	}

#ifdef WWDEBUG
	float new_move_len2 = move->Length2();
	WWASSERT(new_move_len2 <= move_len2 + WWMATH_EPSILON);
#endif
}


/***********************************************************************************************
 * Phys3Class::Get_Shadow_Blob_Box -- Returns the bounding box to use for blob shadows         *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Get_Shadow_Blob_Box(AABoxClass * set_obj_space_box)
{
	WWASSERT(set_obj_space_box != NULL);
	*set_obj_space_box = CollisionBox;
}


/***********************************************************************************************
 * Phys3Class::Get_Collision_Box -- returns the current world space collision box              *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   1/4/2001   gth : Created.                                                                 *
 *=============================================================================================*/
void Phys3Class::Get_Collision_Box(AABoxClass * set_box)
{
	WWASSERT(set_box != NULL);
	Compute_WS_Collision_Box(State,set_box);
}

/***********************************************************************************************
 * Phys3Class::Push -- Move in response to an animated object colliding with me                *
 *                                                                                             *
 *    This function is called when StaticAnimPhys objects detect a collision.  The key is that *
 *    we don't want to slide along any contacts; just move as far as we can along the given    *
 *    move vector.                                                                             *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Push(const Vector3 & move)
{
	Vector3 old_pos = State.Position;
	Apply_Move(move,1.0f,false,false);
	Update_Transform(true);
	Update_Cull_Box();

	Vector3 error = (State.Position - old_pos) - move;

#if VERBOSE_LOGGING
	Vector3 actual_move = State.Position - old_pos;
	bool success = (State.Position - old_pos) == move;
	VERBOSE_LOG((" Phys3::Push. Apparent success: %d Error: %f %f %f\r\n",success, error.X,error.Y,error.Z));
#endif

 	return (error.Length2() < WWMATH_EPSILON * WWMATH_EPSILON);
}


/***********************************************************************************************
 * Phys3Class::Collide -- Move in response to a collision                                      *
 *                                                                                             *
 *    This function is similar to the 'Push' function, however the phys3 object is allowed to  *
 *    slide along geometry in order to get out of the way.                                     *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   1/4/2001   gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Collide(const Vector3 & move)
{
	Vector3 old_pos = State.Position;
	Collide_Move(move,1.0f);
	return ((State.Position - old_pos) == move);
}


/***********************************************************************************************
 * Phys3Class::Can_Teleport -- Checks if this object can occupy the specified position         *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Can_Teleport(const Matrix3D &test_tm, bool check_dyn_only, NonRefPhysListClass * result_list)
{
	//
	//	Determine what the world-space collision box for
	// this object would be at the given transform.
	//
	StateStruct test_state;
	test_state.Position.Set (test_tm.Get_Translation ());
	test_state.Velocity.Set (0, 0, 0);
	
	AABoxClass collision_box;
	Compute_WS_Collision_Box (test_state, &collision_box);

	//
	//	Intersect the box with the world.
	//
	PhysAABoxIntersectionTestClass test(collision_box,
													Get_Collision_Group(),
													COLLISION_TYPE_PHYSICAL,
													result_list);

	if (check_dyn_only) {
		test.CheckStaticObjs = false;
	}
	
	Inc_Ignore_Counter ();
	bool intersect = PhysicsSceneClass::Get_Instance ()->Intersection_Test(test);
	Dec_Ignore_Counter ();

	return (intersect == false);
}


/***********************************************************************************************
 * Phys3Class::Can_Teleport_And_Stand -- Checks if this object can occupy the specified pos    *
 *                                                                                             *
 * Also returns an alternate position if the object cannot occupy the specified position.      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Can_Teleport_And_Stand(const Matrix3D &test_tm, Matrix3D *new_tm)
{
	//
	//	Determine what the world-space collision box for
	// this object would be at the given transform.
	//
	StateStruct test_state;
	test_state.Position.Set (test_tm.Get_Translation ());
	test_state.Velocity.Set (0, 0, 0);
	
	AABoxClass collision_box;
	Compute_WS_Collision_Box (test_state, &collision_box);

	Inc_Ignore_Counter ();

	//
	//	Cast a box 'down' from this location.
	//	This checks to make sure it doesn't intersect anything at its
	// starting position, and its not off the edge of a cliff
	//
	CastResultStruct result;
	Vector3 move_vector (0, 0, -5.0F);
	PhysAABoxCollisionTestClass test(	collision_box,
													move_vector,
													&result,
													0,
													COLLISION_TYPE_PHYSICAL);

	PhysicsSceneClass::Get_Instance ()->Cast_AABox (test);

	Dec_Ignore_Counter ();

	//
	//	Did the box land on a suface we can stand on?
	//
	bool retval = false;
	if ((result.StartBad == false) && ((result.Fraction == 1.0F) || (result.Normal.Z >= SlideNormalZ))) {
				
		//
		//	Calculate a new transform for the object
		//
		Vector3 new_pos = test_tm.Get_Translation () + (result.Fraction * move_vector);
		(*new_tm) = test_tm;
		new_tm->Set_Translation (new_pos);

		retval = true;
	}
	
	return retval;
}


/***********************************************************************************************
 * Phys3Class::Find_Teleport_Location -- Searches for a valid position near the given one      *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   9/16/2000  gth : Created.                                                                 *
 *=============================================================================================*/
bool Phys3Class::Find_Teleport_Location
(
	const Vector3 &	start,
	float					radius,
	Vector3 *			out
)
{
	bool retval = false;

	//
	//	Test the actual point the caller passed to us.
	//
	Matrix3D initial_tm (start);
	if (Can_Teleport_And_Stand (initial_tm, &initial_tm)) {
		(*out) = initial_tm.Get_Translation ();
		return true;
	}

	const int MAX_ATTEMPTS	= 10;
	const float MIN_DIST		= 0.1F;
	
	//
	//	Try a number of times to find a valid location
	//
	for (int index = 0; !retval && index < MAX_ATTEMPTS; index ++) {
		
		//
		//	Get a random distance and direction to make the attempt in
		//
		float dist		= WWMath::Random_Float (MIN_DIST, radius);
		float angle		= WWMath::Random_Float (0, DEG_TO_RADF (360));
		float z_delta	= dist * SlideNormalZ;
		
		//
		//	Calculate what the new transform would be at this location
		//
		Vector3 test_pos;
		test_pos.X = start.X + WWMath::Cos (angle) * dist;
		test_pos.Y = start.Y + WWMath::Sin (angle) * dist;
		test_pos.Z = start.Z + z_delta;
		Matrix3D test_tm (test_pos);

		//
		//	Do the test to make sure this is a valid teleport location.
		//
		Matrix3D new_tm (1);
		if (Can_Teleport_And_Stand (test_tm, &new_tm)) {
			(*out) = new_tm.Get_Translation ();
			retval = true;
		}
	}

	return retval;
}

bool Phys3Class::Can_Move_To(const Matrix3D &new_tm)
{
	// 
	// Do a sweep from our current position to the new position.
	// 
	Vector3 start_position = Get_Position();
	Vector3 new_position = new_tm.Get_Translation();
	Vector3 move = new_position - start_position;

	AABoxClass box;
	Compute_WS_Collision_Box(State,&box);

	Inc_Ignore_Counter();
	CastResultStruct result;
	PhysAABoxCollisionTestClass test(	box,
													move,
													&result,
													0,
													COLLISION_TYPE_PHYSICAL		);
	PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
	Dec_Ignore_Counter();

	return (result.Fraction == 1.0f);
}



void Phys3Class::Assert_State_Valid(void)
{
	AABoxClass box;
	Compute_WS_Collision_Box(State,&box);

	CastResultStruct result;
	PhysAABoxCollisionTestClass test(	box,
													Vector3(0,0,0),
													&result,
													0,
													COLLISION_TYPE_PHYSICAL		);

	Inc_Ignore_Counter();
	PhysicsSceneClass::Get_Instance()->Cast_AABox(test);
	Dec_Ignore_Counter();
	
#if VERBOSE_LOGGING
	if (result.StartBad) {
		VERBOSE_LOG(("Phys3 Intersection! ModelName = %s\r\n",Model->Get_Name()));
	}
#endif

	WWASSERT(State.Position.Is_Valid());
	WWASSERT(State.Velocity.Is_Valid());


}

void Phys3Class::Network_State_Update(const Vector3 & pos,const Vector3 & vel)
{
	Vector3 delta = pos - State.Position;
	Set_Position(pos);
	Set_Velocity(vel);
	Snap_To_Ground(delta,false);
	Update_Transform( true );
}


void Phys3Class::Network_Latency_State_Update(const Vector3 & net_pos,const Vector3 & net_vel)
{
	DEBUG_RENDER_AABOX(AABoxClass(CollisionBox.Center + net_pos,CollisionBox.Extent),Vector3(0,1,0),0.25f);

	/*
	** Allocate a history object if needed
	*/
	if (History == NULL) {
		History = new Phys3HistoryClass;
		History->Init(net_pos,net_vel);
	}
	WWASSERT(History != NULL);

	/*
	** Search our history to find the point nearest this server update
	*/
	Vector3 old_pos;
	History->Find_Nearest_Point(net_pos,net_vel,&old_pos);
	LatencyError = net_pos - old_pos;
	LastKnownPosition = net_pos;
	LastKnownVelocity = net_vel;
}

void Phys3Class::Network_Teleport_Correction(void)
{
	Vector3 new_pos = LastKnownPosition;
	Vector3 correction = new_pos - State.Position;

	Set_Position(new_pos);
	Set_Velocity(LastKnownVelocity);
#if 0
	History->Apply_Correction(correction);
#else
	History->Init(LastKnownPosition,LastKnownVelocity);
#endif
	LatencyError.Set(0,0,0);
}


/*************************************************************************************
**
**	Phys3Class Save-Load support
**
*************************************************************************************/

const PersistFactoryClass & Phys3Class::Get_Factory (void) const
{
	return _Phys3Factory;
}

bool Phys3Class::Save (ChunkSaveClass &csave)
{
	csave.Begin_Chunk(PHYS3_CHUNK_MOVEABLEPHYS);
	MoveablePhysClass::Save(csave);
	csave.End_Chunk();

	csave.Begin_Chunk(PHYS3_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_COLLISION_AABOX,CollisionBox);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_ONGROUND,OnGround);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_INCOLLISION,InCollision);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_HEADING,Heading);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_NORMSPEED,NormSpeed);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDEANGLE,SlideAngle);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDENORMALZ,SlideNormalZ);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_SLIDEANGLETAN,SlideAngleTan);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_STEPHEIGHT,StepHeight);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_MOVEMODE,MoveMode);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_POSITION,State.Position);
	WRITE_MICRO_CHUNK(csave,PHYS3_VARIABLE_VELOCITY,State.Velocity);
	csave.End_Chunk();

	return true;
}

bool Phys3Class::Load (ChunkLoadClass &cload)
{
	while (cload.Open_Chunk()) {
		
		switch(cload.Cur_Chunk_ID()) 
		{
			case PHYS3_CHUNK_MOVEABLEPHYS:
				MoveablePhysClass::Load(cload);
				break;

			case PHYS3_CHUNK_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_COLLISION_AABOX,CollisionBox);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_ONGROUND,OnGround);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_INCOLLISION,InCollision);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_HEADING,Heading);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_NORMSPEED,NormSpeed);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDEANGLE,SlideAngle);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDENORMALZ,SlideNormalZ);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_SLIDEANGLETAN,SlideAngleTan);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_STEPHEIGHT,StepHeight);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_MOVEMODE,MoveMode);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_POSITION,State.Position);
						READ_MICRO_CHUNK(cload,PHYS3_VARIABLE_VELOCITY,State.Velocity);
					}
					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();
	}

	Invalidate_Ground_State();
	SaveLoadSystemClass::Register_Post_Load_Callback(this);
	return true;
}

void Phys3Class::On_Post_Load (void)
{
	MoveablePhysClass::On_Post_Load();
	Update_Cached_Model_Parameters();
}



/*************************************************************************************
**
** Phys3Class::GroundStateStruct Implementation
**
*************************************************************************************/
Phys3Class::GroundStateStruct::GroundStateStruct(void) : 
	IsDirty(true),
	OnGround(false),
	OnDynamicObj(false),
	SurfaceType(SURFACE_TYPE_DEFAULT),
	Height(0.0f),
	Normal(0,0,1),
	Down(1,0,0),
	GroundObject(NULL),
	GroundRenderObject(NULL)
{
	Reset();
}

void Phys3Class::GroundStateStruct::Reset(void)
{
	IsDirty = true;
	OnGround = false;
	OnDynamicObj = false;
	SurfaceType = SURFACE_TYPE_DEFAULT;
	Height = 0.0f;
	Normal.Set(0,0,1);
	Down.Set(1,0,0);
	GroundObject = NULL; 
	GroundRenderObject = NULL;
}

void Phys3Class::GroundStateStruct::Init_From_Collision_Result(PhysAABoxCollisionTestClass & test,float height)
{
	Reset();
	IsDirty = false;

	if (test.Result->Fraction < 1.0f) {
		OnGround = true;
		SurfaceType = test.Result->SurfaceType;
		Normal = test.Result->Normal;
		GroundObject = test.CollidedPhysObj;
		GroundRenderObject = test.CollidedRenderObj;
		Height = height;

		if (GroundObject != NULL) {
			if (	(GroundObject->As_HumanPhysClass() != NULL) ||
					(GroundObject->As_RigidBodyClass() != NULL))
			{
				OnDynamicObj = true;
			}
		}

		if (test.Result->StartBad) {
			
			IsDirty = true;
			Normal.Set(0,0,1);
//			VERBOSE_LOG(("ERROR - intersecting object: %s!\r\n",GroundRenderObject->Get_Name()));

		} else {

			if (Normal.Length2() <= 0.0f) {
				WWDEBUG_SAY(("ERROR - detected non-unit normal!\r\n"));
			}
			
			// compute the down vector for this plane
			// down = N x -Z x N
			Vector3 tmp;
			Vector3::Cross_Product(Normal,Vector3(0,0,-1),&tmp);
			Vector3::Cross_Product(tmp,Normal,&(Down));	
			Down.Normalize();
			
//			VERBOSE_LOG(("  on ground, normal=(%f,%f,%f)sliding=%d\r\n",gs->Normal.X,gs->Normal.Y,gs->Normal.Z,(gs->Normal.Z>SlideNormalZ ? 0 : 1)));
		}

	} else {
		OnGround = false;
		OnDynamicObj = false;
		GroundObject = NULL;
		GroundRenderObject = NULL;
		SurfaceType = SURFACE_TYPE_DEFAULT;
	}
}


/***********************************************************************************************
**
** Phys3DefClass Implementation
**
***********************************************************************************************/

/*
** Declare a PersistFactory for Phys3DefClasses 
*/
SimplePersistFactoryClass<Phys3DefClass,PHYSICS_CHUNKID_PHYS3DEF>	_Phys3DefFactory;


/*
** Chunk ID's used by MoveablePhysDefClass
*/
enum 
{
	PHYS3DEF_CHUNK_MOVEABLEPHYSDEF				= 0x04486000,			// (parent class)
	PHYS3DEF_CHUNK_VARIABLES,

	PHYS3DEF_VARIABLE_NORMSPEED					= 0x00,
	PHYS3DEF_VARIABLE_SLIDEANGLE,
	PHYS3DEF_VARIABLE_STEPHEIGHT,
};


Phys3DefClass::Phys3DefClass(void) :
	NormSpeed(DEFAULT_NORMALIZED_SPEED),
	SlideAngle(DEFAULT_SLIDE_ANGLE),
	StepHeight(DEFAULT_STEP_HEIGHT)
{
	// make our parameters editable!
	EDITABLE_PARAM(Phys3DefClass, ParameterClass::TYPE_FLOAT, NormSpeed);
	ANGLE_EDITABLE_PARAM(Phys3DefClass, SlideAngle, DEG_TO_RADF(0.0f), DEG_TO_RADF(90.0f));
	FLOAT_EDITABLE_PARAM(Phys3DefClass, StepHeight, 0.0f, 10.0f);	
}


const PersistFactoryClass & Phys3DefClass::Get_Factory (void) const
{
	return _Phys3DefFactory;
}

uint32 Phys3DefClass::Get_Class_ID (void) const	
{ 
	return CLASSID_PHYS3DEF; 
}

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

bool Phys3DefClass::Save(ChunkSaveClass &csave)
{
	csave.Begin_Chunk(PHYS3DEF_CHUNK_MOVEABLEPHYSDEF);
	MoveablePhysDefClass::Save(csave);
	csave.End_Chunk();

	csave.Begin_Chunk(PHYS3DEF_CHUNK_VARIABLES);
	WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_NORMSPEED,NormSpeed);
	WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_SLIDEANGLE,SlideAngle);
	WRITE_MICRO_CHUNK(csave,PHYS3DEF_VARIABLE_STEPHEIGHT,StepHeight);
	csave.End_Chunk();
	return true;
}


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

		switch(cload.Cur_Chunk_ID()) {

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

			case PHYS3DEF_CHUNK_VARIABLES:
				while (cload.Open_Micro_Chunk()) {
					switch(cload.Cur_Micro_Chunk_ID()) {
						READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_NORMSPEED,NormSpeed);
						READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_SLIDEANGLE,SlideAngle);
						READ_MICRO_CHUNK(cload,PHYS3DEF_VARIABLE_STEPHEIGHT,StepHeight);
					}
					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 Phys3DefClass::Is_Type(const char * type_name)
{
	if (stricmp(type_name,Phys3DefClass::Get_Type_Name()) == 0) {
		return true;
	} else {
		return MoveablePhysDefClass::Is_Type(type_name);
	}
}
