//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"

#include "physics_shadow.h"
#include "vphysics/player_controller.h"
#include "physics_friction.h"
#include "vphysics/friction.h"

// IsInContact
#include "ivp_mindist.hxx"
#include "ivp_mindist_intern.hxx"
#include "ivp_core.hxx"
#include "ivp_friction.hxx"
#include "ivp_listener_object.hxx"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

struct vphysics_save_cshadowcontroller_t;
struct vphysics_save_shadowcontrolparams_t;


// UNDONE: Try this controller!
//damping is usually 1.0
//frequency is usually in the range 1..16
void ComputePDControllerCoefficients( float *coefficientsOut, const float frequency, const float damping, const float dt )
{
	const float ks = 9.0f * frequency * frequency;
	const float kd = 4.5f * frequency * damping;

	const float scale = 1.0f / ( 1.0f  + kd * dt + ks * dt * dt );

	coefficientsOut[0] = ks  *  scale;
	coefficientsOut[1] = ( kd + ks * dt ) * scale;

	// Use this controller like:
	// speed += (coefficientsOut[0] * (targetPos - currentPos) + coefficientsOut[1] * (targetSpeed - currentSpeed)) * dt
}

void ComputeController( IVP_U_Float_Point &currentSpeed, const IVP_U_Float_Point &delta, float maxSpeed, float maxDampSpeed, float scaleDelta, float damping, IVP_U_Float_Point *pOutImpulse = NULL )
{
	if ( currentSpeed.quad_length() < 1e-6 )
	{
		currentSpeed.set_to_zero();
	}

	// scale by timestep
	IVP_U_Float_Point acceleration;
	if ( maxSpeed > 0 )
	{
		acceleration.set_multiple( &delta, scaleDelta );
		float speed = acceleration.real_length();
		if ( speed > maxSpeed )
		{
			speed = maxSpeed / speed;
			acceleration.mult( speed );
		}
	}
	else
	{
		acceleration.set_to_zero();
	}

	IVP_U_Float_Point dampAccel;
	if ( maxDampSpeed > 0 )
	{
		dampAccel.set_multiple( &currentSpeed, -damping );
		float speed = dampAccel.real_length();
		if ( speed > maxDampSpeed )
		{
			speed = maxDampSpeed / speed;
			dampAccel.mult( speed );
		}
	}
	else
	{
		dampAccel.set_to_zero();
	}
	currentSpeed.add( &dampAccel );
	currentSpeed.add( &acceleration );
	if ( pOutImpulse )
	{
		*pOutImpulse = acceleration;
	}
}


void ComputeController( IVP_U_Float_Point &currentSpeed, const IVP_U_Float_Point &delta, const IVP_U_Float_Point &maxSpeed, float scaleDelta, float damping, IVP_U_Float_Point *pOutImpulse )
{
	// scale by timestep
	IVP_U_Float_Point acceleration;
	acceleration.set_multiple( &delta, scaleDelta );
	
	if ( currentSpeed.quad_length() < 1e-6 )
	{
		currentSpeed.set_to_zero();
	}

	acceleration.add_multiple( &currentSpeed, -damping );

	for(int i=2; i>=0; i--)
	{
		if(IVP_Inline_Math::fabsd(acceleration.k[i]) < maxSpeed.k[i]) 
			continue;
		
		// clip force
		acceleration.k[i] = (acceleration.k[i] < 0) ? -maxSpeed.k[i] : maxSpeed.k[i];
	}

	currentSpeed.add( &acceleration );
	if ( pOutImpulse )
	{
		*pOutImpulse = acceleration;
	}
}


static bool IsOnGround( IVP_Real_Object *pivp )
{
	IPhysicsFrictionSnapshot *pSnapshot = CreateFrictionSnapshot( pivp );
	bool bGround = false;
	while (pSnapshot->IsValid())
	{
		Vector normal;
		pSnapshot->GetSurfaceNormal( normal );
		if ( normal.z < -0.7f )
		{
			bGround = true;
			break;
		}
		
		pSnapshot->NextFrictionData();
	}
	DestroyFrictionSnapshot( pSnapshot );
	return bGround;
}

class CPlayerController : public IVP_Controller_Independent, public IPhysicsPlayerController, public IVP_Listener_Object
{
public:
	CPlayerController( CPhysicsObject *pObject );
	~CPlayerController( void );

	// ipion interfaces
    void do_simulation_controller( IVP_Event_Sim *es,IVP_U_Vector<IVP_Core> *cores);
    virtual IVP_CONTROLLER_PRIORITY get_controller_priority() { return (IVP_CONTROLLER_PRIORITY) (IVP_CP_MOTION+1); }
	virtual const char *get_controller_name() { return "vphysics:player"; }

	void SetObject( IPhysicsObject *pObject );
	void SetEventHandler( IPhysicsPlayerControllerEvent *handler );
	void Update( const Vector& position, const Vector& velocity, float secondsToArrival, bool onground, IPhysicsObject *ground );
	void MaxSpeed( const Vector &velocity );
	bool IsInContact( void );
	virtual bool WasFrozen() 
	{ 	
		IVP_Real_Object *pivp = m_pObject->GetObject();
		IVP_Core *pCore = pivp->get_core();
		return pCore->temporarily_unmovable ? true : false;
	}

	void ForceTeleportToCurrentPosition()
	{
		m_forceTeleport = true;
	}

	int GetShadowPosition( Vector *position, QAngle *angles )
	{
		IVP_U_Matrix matrix;
		
		IVP_Environment *pEnv = m_pObject->GetObject()->get_environment();

		double psi = pEnv->get_next_PSI_time().get_seconds();
		m_pObject->GetObject()->calc_at_matrix( psi, &matrix );
		if ( angles )
		{
			ConvertRotationToHL( matrix, *angles );
		}
		if ( position )
		{
			ConvertPositionToHL( matrix.vv, *position );
		}

		return 1;
	}
	void GetShadowVelocity( Vector *velocity );
	virtual void GetLastImpulse( Vector *pOut )
	{
		ConvertPositionToHL( m_lastImpulse, *pOut );
	}

	virtual void StepUp( float height );
	virtual void Jump();
	virtual IPhysicsObject *GetObject() { return m_pObject; }

	virtual void SetPushMassLimit( float maxPushMass )
	{
		m_pushableMassLimit = maxPushMass;
	}

	virtual void SetPushSpeedLimit( float maxPushSpeed )
	{
		m_pushableSpeedLimit = maxPushSpeed;
	}

	virtual float GetPushMassLimit() { return m_pushableMassLimit; }
	virtual float GetPushSpeedLimit() { return m_pushableSpeedLimit; }

	// Object listener
	virtual void event_object_deleted( IVP_Event_Object *pEvent)
	{
		Assert( pEvent->real_object == m_pGround->GetObject() );
		m_pGround = NULL;
	}
	virtual void event_object_created( IVP_Event_Object *) {}
	virtual void event_object_revived( IVP_Event_Object *) {}
	virtual void event_object_frozen ( IVP_Event_Object *) {}

private:
	void AttachObject( void );
	void DetachObject( void );
	int TryTeleportObject( void );
	void SetGround( CPhysicsObject *pGroundObject );

	CPhysicsObject		*m_pObject;
	IVP_U_Float_Point	m_saveRot;
	CPhysicsObject		*m_pGround;	// Uses object listener to clear - so ok to hold over frames

	IPhysicsPlayerControllerEvent	*m_handler;
	float				m_maxDeltaPosition;
	float				m_dampFactor;
	float				m_secondsToArrival;
	float				m_pushableMassLimit;
	float				m_pushableSpeedLimit;
	IVP_U_Point			m_targetPosition;
	IVP_U_Float_Point	m_groundPosition;
	IVP_U_Float_Point	m_maxSpeed;
	IVP_U_Float_Point	m_currentSpeed;
	IVP_U_Float_Point	m_lastImpulse;
	bool				m_enable : 1;
	bool				m_onground : 1;
	bool				m_forceTeleport : 1;
	bool				m_updatedSinceLast : 5;
};


CPlayerController::CPlayerController( CPhysicsObject *pObject )
{
	m_pGround = NULL;
	m_pObject = pObject;
	m_handler = NULL;
	m_maxDeltaPosition = ConvertDistanceToIVP( 24 );
	m_dampFactor = 1.0f;
	m_targetPosition.k[0] = m_targetPosition.k[1] = m_targetPosition.k[2] = 0;
	m_pushableMassLimit = VPHYSICS_MAX_MASS;
	m_pushableSpeedLimit = 1e4f;
	m_forceTeleport = false;
	AttachObject();
}

CPlayerController::~CPlayerController( void ) 
{
	DetachObject();
}

void CPlayerController::SetGround( CPhysicsObject *pGroundObject )
{
	if ( m_pGround != pGroundObject )
	{
		if ( m_pGround && m_pGround->GetObject() )
		{
			m_pGround->GetObject()->remove_listener_object(this);
		}
		m_pGround = pGroundObject;
		if ( m_pGround && m_pGround->GetObject() )
		{
			m_pGround->GetObject()->add_listener_object(this);
		}
	}
}

void CPlayerController::AttachObject( void )
{
	m_pObject->EnableDrag( false );
	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_Core *pCore = pivp->get_core();
	m_saveRot = pCore->rot_speed_damp_factor;
	pCore->rot_speed_damp_factor = IVP_U_Float_Point( 100, 100, 100 );
	pCore->calc_calc();
	BEGIN_IVP_ALLOCATION();
	pivp->get_environment()->get_controller_manager()->add_controller_to_core( this, pCore );
	END_IVP_ALLOCATION();
	m_pObject->AddCallbackFlags( CALLBACK_IS_PLAYER_CONTROLLER );
}

void CPlayerController::DetachObject( void )
{
	if ( !m_pObject )
		return;

	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_Core *pCore = pivp->get_core();
	pCore->rot_speed_damp_factor = m_saveRot;
	pCore->calc_calc();
	m_pObject->RemoveCallbackFlags( CALLBACK_IS_PLAYER_CONTROLLER );
	m_pObject = NULL;
	pivp->get_environment()->get_controller_manager()->remove_controller_from_core( this, pCore );
	SetGround(NULL);
}

void CPlayerController::SetObject( IPhysicsObject *pObject )
{
	CPhysicsObject *obj = (CPhysicsObject *)pObject;
	if ( obj == m_pObject )
		return;

	DetachObject();
	m_pObject = obj;
	AttachObject();
}

int CPlayerController::TryTeleportObject( void )
{
	if ( m_handler && !m_forceTeleport )
	{
		Vector hlPosition;
		ConvertPositionToHL( m_targetPosition, hlPosition );
		if ( !m_handler->ShouldMoveTo( m_pObject, hlPosition ) )
			return 0;
	}

	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_U_Quat targetOrientation;
	IVP_U_Point outPosition;

	pivp->get_quat_world_f_object_AT( &targetOrientation, &outPosition );

	if ( pivp->is_collision_detection_enabled() )
	{
		m_pObject->EnableCollisions( false );
		pivp->beam_object_to_new_position( &targetOrientation, &m_targetPosition, IVP_TRUE );
		m_pObject->EnableCollisions( true );
	}
	else
	{
		pivp->beam_object_to_new_position( &targetOrientation, &m_targetPosition, IVP_TRUE );
	}
	m_forceTeleport = false;
	return 1;
}

void CPlayerController::StepUp( float height )
{
	if ( height == 0.0f )
		return;

	Vector step( 0, 0, height );

	IVP_Real_Object *pIVP = m_pObject->GetObject();
	IVP_U_Quat world_f_object;
	IVP_U_Point positionIVP, deltaIVP;
	ConvertPositionToIVP( step, deltaIVP );
	pIVP->get_quat_world_f_object_AT( &world_f_object, &positionIVP );
	positionIVP.add( &deltaIVP );
	pIVP->beam_object_to_new_position( &world_f_object, &positionIVP, IVP_TRUE );
}

void CPlayerController::Jump()
{
#if 0
	// float for one tick to allow stepping and jumping to work properly
	IVP_Real_Object *pIVP = m_pObject->GetObject();
	const IVP_U_Point *pgrav = pIVP->get_environment()->get_gravity();
	IVP_U_Float_Point gravSpeed;
	gravSpeed.set_multiple( pgrav, pIVP->get_environment()->get_delta_PSI_time() );
	pIVP->get_core()->speed.subtract( &gravSpeed );
#endif
}

const int MAX_LIST_NORMALS = 8;
class CNormalList
{
public:
	bool IsFull() { return m_Normals.Count() == MAX_LIST_NORMALS; }
	void AddNormal( const Vector &normal )
	{
		if ( IsFull() )
			return;

		for ( int i = m_Normals.Count(); --i >= 0; )
		{
			if ( DotProduct( m_Normals[i], normal ) > 0.99f )
				return;
		}
		m_Normals.AddToTail( normal );
	}

	bool HasPositiveProjection( const Vector &vec )
	{
		for ( int i = m_Normals.Count(); --i >= 0; )
		{
			if ( DotProduct( m_Normals[i], vec ) > 0 )
				return true;
		}
		return false;
	}

	// UNDONE: Handle the case better where we clamp to multiple planes
	// and still have a projection, but don't exceed limitVel.  Currently that will stop.
	// when this is done, remove the ground exception below.
	Vector ClampVector( const Vector &inVector, float limitVel )
	{
		if ( m_Normals.Count() > 2 )
		{
			for ( int i = 0; i < m_Normals.Count(); i++ )
			{
				if ( DotProduct(inVector, m_Normals[i]) > 0 )
				{
					return vec3_origin;
				}
			}
		}
		else
		{
			if ( m_Normals.Count() == 2 )
			{
				Vector crease;
				CrossProduct( m_Normals[0], m_Normals[1], crease );
				float dot = DotProduct( inVector, crease );
				return crease * dot;
			}
			else if (m_Normals.Count() == 1)
			{
				float dot = DotProduct( inVector, m_Normals[0] );
				if ( dot > limitVel )
				{
					return inVector + m_Normals[0]*(limitVel - dot);
				}
			}
		}
		return inVector;
	}
private:
	CUtlVectorFixed<Vector, MAX_LIST_NORMALS>	m_Normals;
};

void CPlayerController::do_simulation_controller( IVP_Event_Sim *es,IVP_U_Vector<IVP_Core> *)
{
	if ( !m_enable )
		return;
	IVP_Real_Object *pivp = m_pObject->GetObject();

	IVP_Core *pCore = pivp->get_core();
	Assert(!pCore->pinned && !pCore->physical_unmoveable);
	// current situation
	const IVP_U_Matrix *m_world_f_core = pCore->get_m_world_f_core_PSI();
	const IVP_U_Point *cur_pos_ws = m_world_f_core->get_position();

	IVP_U_Float_Point baseVelocity;
	baseVelocity.set_to_zero();
	// ---------------------------------------------------------
	// Translation
	// ---------------------------------------------------------

	if ( m_pGround )
	{
		const IVP_U_Matrix *pMatrix = m_pGround->GetObject()->get_core()->get_m_world_f_core_PSI();
		pMatrix->vmult4( &m_groundPosition, &m_targetPosition );
		m_pGround->GetObject()->get_core()->get_surface_speed( &m_groundPosition, &baseVelocity );
		pCore->speed.subtract( &baseVelocity );
	}

	IVP_U_Float_Point delta_position;  delta_position.subtract( &m_targetPosition, cur_pos_ws);

	if (!pivp->flags.shift_core_f_object_is_zero)
	{
		IVP_U_Float_Point shift_cs_os_ws;
		m_world_f_core->vmult3( pivp->get_shift_core_f_object(), &shift_cs_os_ws);
		delta_position.subtract( &shift_cs_os_ws );
	}


	IVP_DOUBLE qdist = delta_position.quad_length();

	// UNDONE: This is totally bogus!  Measure error using last known estimate
	// not current position!
	if ( m_forceTeleport || qdist > m_maxDeltaPosition * m_maxDeltaPosition )
	{
		if ( TryTeleportObject() )
			return;
	}

	// float to allow stepping
	const IVP_U_Point *pgrav = es->environment->get_gravity();
	IVP_U_Float_Point gravSpeed;
	gravSpeed.set_multiple( pgrav, es->delta_time );
	if ( m_onground )
	{
		pCore->speed.subtract( &gravSpeed );
	}

	float fraction = 1.0;
	if ( m_secondsToArrival > 0 )
	{
		fraction = es->delta_time / m_secondsToArrival;
		if ( fraction > 1 )
		{
			fraction = 1;
		}
	}
	if ( !m_updatedSinceLast )
	{
		// we haven't received an update from the game code since the last controller step
		// This means we haven't gotten feedback integrated into the motion plan, so the error may be 
		// exaggerated.  Assume that the first updated tick had valid information, and limit
		// all subsequent ticks to the same size impulses.
		// NOTE: Don't update the saved impulse - so any subsequent ticks will still have the last
		// known good information.
		float len = m_lastImpulse.real_length();
		// cap the max speed to the length of the last known good impulse
		IVP_U_Float_Point tmp;
		tmp.set( len, len, len );
		ComputeController( pCore->speed, delta_position, tmp, fraction / es->delta_time, m_dampFactor, NULL );
	}
	else
	{
		ComputeController( pCore->speed, delta_position, m_maxSpeed, fraction / es->delta_time, m_dampFactor, &m_lastImpulse );
	}
	pCore->speed.add( &baseVelocity );
	m_updatedSinceLast = false;

	// UNDONE: Assumes gravity points down
	Vector lastImpulseHL;
	ConvertPositionToHL( pCore->speed, lastImpulseHL );
	IPhysicsFrictionSnapshot *pSnapshot = CreateFrictionSnapshot( pivp );
	bool bGround = false;
	float invMass = pivp->get_core()->get_inv_mass();
	float limitVel = m_pushableSpeedLimit;
	CNormalList normalList;
	while (pSnapshot->IsValid())
	{
		Vector normal;
		pSnapshot->GetSurfaceNormal( normal );
		if ( normal.z < -0.7f )
		{
			bGround = true;
		}
		// remove this when clamp works better
		if ( normal.z > -0.99f )
		{
			IPhysicsObject *pOther = pSnapshot->GetObject(1);
			if ( !pOther->IsMoveable() || pOther->GetMass() > m_pushableMassLimit )
			{
				limitVel = 0.0f;
			}
			float pushSpeed = DotProduct( lastImpulseHL, normal );
			float contactVel = pSnapshot->GetNormalForce() * invMass;
			float pushTotal = pushSpeed + contactVel;
			if ( pushTotal > limitVel )
			{
				normalList.AddNormal( normal );
			}
		}
		
		pSnapshot->NextFrictionData();
	}
	DestroyFrictionSnapshot( pSnapshot );

	Vector clamped = normalList.ClampVector( lastImpulseHL, limitVel );
	Vector limit = clamped - lastImpulseHL;
	IVP_U_Float_Point limitIVP;
	ConvertPositionToIVP( limit, limitIVP );
	pivp->get_core()->speed.add( &limitIVP );
	m_lastImpulse.add( &limitIVP );

	if ( bGround )
	{
		float gravDt = gravSpeed.real_length();
		// moving down?  Press down with full gravity and no more
		if ( m_lastImpulse.k[1] >= 0 )
		{
			float delta = gravDt - m_lastImpulse.k[1];
			pivp->get_core()->speed.k[1] += delta;
			m_lastImpulse.k[1] += delta;
		}
	}

	// if we have time left, subtract it off
	m_secondsToArrival -= es->delta_time;
	if ( m_secondsToArrival < 0 )
	{
		m_secondsToArrival = 0;
	}
}

void CPlayerController::SetEventHandler( IPhysicsPlayerControllerEvent *handler ) 
{
	m_handler = handler;
}

void CPlayerController::Update( const Vector& position, const Vector& velocity, float secondsToArrival, bool onground, IPhysicsObject *ground )
{
	IVP_U_Point	targetPositionIVP;
	IVP_U_Float_Point targetSpeedIVP;

	ConvertPositionToIVP( position, targetPositionIVP );
	ConvertPositionToIVP( velocity, targetSpeedIVP );
	
	m_updatedSinceLast = true;
	// if the object hasn't moved, abort
	if ( targetSpeedIVP.quad_distance_to( &m_currentSpeed ) < 1e-6 )
	{
		if ( targetPositionIVP.quad_distance_to( &m_targetPosition ) < 1e-6 )
		{
			return;
		}
	}

	m_targetPosition.set( &targetPositionIVP );
	m_secondsToArrival = secondsToArrival < 0 ? 0 : secondsToArrival;
	// Sanity check to make sure the position is good.
#ifdef _DEBUG
	float large = 1024 * 512;
	Assert( m_targetPosition.k[0] >= -large && m_targetPosition.k[0] <= large );
	Assert( m_targetPosition.k[1] >= -large && m_targetPosition.k[1] <= large );
	Assert( m_targetPosition.k[2] >= -large && m_targetPosition.k[2] <= large );
#endif

	m_currentSpeed.set( &targetSpeedIVP );

	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_Core *pCore = pivp->get_core();
	IVP_Environment *pEnv = pivp->get_environment();
	pEnv->get_controller_manager()->ensure_core_in_simulation(pCore);

	m_enable = true;
	// m_onground makes this object anti-grav
	// UNDONE: Re-evaluate this
	m_onground = false;//onground;
	if ( velocity.LengthSqr() <= 0.1f )
	{
		// no input velocity, just go where physics takes you.
		m_enable = false;
		ground = NULL;
	}
	else
	{
		MaxSpeed( velocity );
	}

	CPhysicsObject *pGroundObject = static_cast<CPhysicsObject *>(ground);
	SetGround( pGroundObject );
	if ( m_pGround )
	{
		const IVP_U_Matrix *pMatrix = m_pGround->GetObject()->get_core()->get_m_world_f_core_PSI();
		pMatrix->vimult4( &m_targetPosition, &m_groundPosition );
	}
}

void CPlayerController::MaxSpeed( const Vector &velocity )
{
	IVP_Core *pCore = m_pObject->GetObject()->get_core();
	IVP_U_Float_Point ivpVel;
	ConvertPositionToIVP( velocity, ivpVel );
	IVP_U_Float_Point available = ivpVel;
	
	// normalize and save length
	float length = ivpVel.real_length_plus_normize();

	IVP_U_Float_Point baseVelocity;
	baseVelocity.set_to_zero();

	float dot = ivpVel.dot_product( &pCore->speed );
	if ( dot > 0 )
	{
		ivpVel.mult( dot * length );
		available.subtract( &ivpVel );
	}

	pCore->speed.add( &baseVelocity );
	IVP_Float_PointAbs( m_maxSpeed, available );
}


void CPlayerController::GetShadowVelocity( Vector *velocity )
{
	IVP_Core *core = m_pObject->GetObject()->get_core();
	if ( velocity )
	{
		IVP_U_Float_Point speed;
		speed.add( &core->speed, &core->speed_change );
		if ( m_pGround )
		{
			IVP_U_Float_Point baseVelocity;
			m_pGround->GetObject()->get_core()->get_surface_speed( &m_groundPosition, &baseVelocity );
			speed.subtract( &baseVelocity );
		}
		ConvertPositionToHL( speed, *velocity );
	}
}


bool CPlayerController::IsInContact( void )
{
	IVP_Real_Object *pivp = m_pObject->GetObject();
	if ( !pivp->flags.collision_detection_enabled )
		return false;

	IVP_Synapse_Friction *pfriction = pivp->get_first_friction_synapse();
	while ( pfriction )
	{
		extern IVP_Real_Object *GetOppositeSynapseObject( IVP_Synapse_Friction *pfriction );

		IVP_Real_Object *pobj = GetOppositeSynapseObject( pfriction );
		if ( pobj->flags.collision_detection_enabled )
		{
			// skip if this is a static object
			if ( !pobj->get_core()->physical_unmoveable && !pobj->get_core()->pinned )
			{
				CPhysicsObject *pPhys = static_cast<CPhysicsObject *>(pobj->client_data);
				// If this is a game-controlled shadow object, then skip it.
				// otherwise, we're in contact with something physically simulated
				if ( !pPhys->IsControlledByGame() )
					return true;
			}
		}

		pfriction = pfriction->get_next();
	}

	return false;
}


IPhysicsPlayerController *CreatePlayerController( CPhysicsObject *pObject )
{
	return new CPlayerController( pObject );
}

void DestroyPlayerController( IPhysicsPlayerController *pController )
{
	delete pController;
}

void QuaternionDiff( const IVP_U_Quat &p, const IVP_U_Quat &q, IVP_U_Quat &qt )
{
	IVP_U_Quat q2;

	// decide if one of the quaternions is backwards
	q2.set_invert_unit_quat( &q );
	qt.set_mult_quat( &q2, &p );
	qt.normize_quat();
}


void QuaternionAxisAngle( const IVP_U_Quat &q, Vector &axis, float &angle )
{
	angle = 2 * acos(q.w);
	if ( angle > M_PI )
	{
		angle -= 2*M_PI;
	}

	axis.Init( q.x, q.y, q.z );
	VectorNormalize( axis );
}


void GetObjectPosition_IVP( IVP_U_Point &origin, IVP_Real_Object *pivp )
{
	const IVP_U_Matrix *m_world_f_core = pivp->get_core()->get_m_world_f_core_PSI();
	origin.set( m_world_f_core->get_position() );
	if (!pivp->flags.shift_core_f_object_is_zero)
	{
		IVP_U_Float_Point shift_cs_os_ws;
		m_world_f_core->vmult3( pivp->get_shift_core_f_object(), &shift_cs_os_ws );
		origin.add(&shift_cs_os_ws);
	}
}


bool IsZeroVector( const IVP_U_Point &vec )
{
	return (vec.k[0] == 0.0 && vec.k[1] == 0.0 && vec.k[2] == 0.0 ) ? true : false;
}

float ComputeShadowControllerIVP( IVP_Real_Object *pivp, shadowcontrol_params_t &params, float secondsToArrival, float dt )
{
	// resample fraction
	// This allows us to arrive at the target at the requested time
	float fraction = 1.0;
	if ( secondsToArrival > 0 )
	{
		fraction = dt / secondsToArrival;
		if ( fraction > 1 )
		{
			fraction = 1;
		}
	}

	secondsToArrival -= dt;
	if ( secondsToArrival < 0 )
	{
		secondsToArrival = 0;
	}

	if ( fraction <= 0 )
		return secondsToArrival;

	// ---------------------------------------------------------
	// Translation
	// ---------------------------------------------------------

	IVP_U_Point positionIVP;
	GetObjectPosition_IVP( positionIVP, pivp );

	IVP_U_Float_Point delta_position;
	delta_position.subtract( &params.targetPosition, &positionIVP);

	// BUGBUG: Save off velocities and estimate final positions
	// measure error against these final sets
	// also, damp out 100% saved velocity, use max additional impulses
	// to correct error and damp out error velocity
	// extrapolate position
	if ( params.teleportDistance > 0 )
	{
		IVP_DOUBLE qdist;
		if ( !IsZeroVector(params.lastPosition) )
		{
			IVP_U_Float_Point tmpDelta;
			tmpDelta.subtract( &positionIVP, &params.lastPosition );
			qdist = tmpDelta.quad_length();
		}
		else
		{
			// UNDONE: This is totally bogus!  Measure error using last known estimate
			// not current position!
			qdist = delta_position.quad_length();
		}

		if ( qdist > params.teleportDistance * params.teleportDistance )
		{
			if ( pivp->is_collision_detection_enabled() )
			{
				pivp->enable_collision_detection( IVP_FALSE );
				pivp->beam_object_to_new_position( &params.targetRotation, &params.targetPosition, IVP_TRUE );
				pivp->enable_collision_detection( IVP_TRUE );
			}
			else
			{
				pivp->beam_object_to_new_position( &params.targetRotation, &params.targetPosition, IVP_TRUE );
			}
			delta_position.set_to_zero();
		}
	}

	float invDt = 1.0f / dt;
	IVP_Core *pCore = pivp->get_core();
	ComputeController( pCore->speed, delta_position, params.maxSpeed, params.maxDampSpeed, fraction * invDt, params.dampFactor, &params.lastImpulse );

	params.lastPosition.add_multiple( &positionIVP, &pCore->speed, dt );

	IVP_U_Float_Point deltaAngles;
	// compute rotation offset
	IVP_U_Quat deltaRotation;
	QuaternionDiff( params.targetRotation, pCore->q_world_f_core_next_psi, deltaRotation );

	// convert offset to angular impulse
	Vector axis;
	float angle;
	QuaternionAxisAngle( deltaRotation, axis, angle );
	VectorNormalize(axis);

	deltaAngles.k[0] = axis.x * angle;
	deltaAngles.k[1] = axis.y * angle;
	deltaAngles.k[2] = axis.z * angle;

	ComputeController( pCore->rot_speed, deltaAngles, params.maxAngular, params.maxDampAngular, fraction * invDt, params.dampFactor, NULL );

	return secondsToArrival;
}

void ConvertShadowControllerToIVP( const hlshadowcontrol_params_t &in, shadowcontrol_params_t &out )
{
	ConvertPositionToIVP( in.targetPosition, out.targetPosition );
	ConvertRotationToIVP( in.targetRotation, out.targetRotation );
	out.teleportDistance = ConvertDistanceToIVP( in.teleportDistance );

	out.maxSpeed = ConvertDistanceToIVP( in.maxSpeed );
	out.maxDampSpeed = ConvertDistanceToIVP( in.maxDampSpeed );
	out.maxAngular = ConvertAngleToIVP( in.maxAngular );
	out.maxDampAngular = ConvertAngleToIVP( in.maxDampAngular );

	out.dampFactor = in.dampFactor;
}

void ConvertShadowControllerToHL( const shadowcontrol_params_t &in, hlshadowcontrol_params_t &out )
{
	ConvertPositionToHL( in.targetPosition, out.targetPosition );
	ConvertRotationToHL( in.targetRotation, out.targetRotation );
	out.teleportDistance = ConvertDistanceToHL( in.teleportDistance );

	out.maxSpeed = ConvertDistanceToHL( in.maxSpeed );
	out.maxDampSpeed = ConvertDistanceToHL( in.maxDampSpeed );
	out.maxAngular = ConvertAngleToHL( in.maxAngular );
	out.maxDampAngular = ConvertAngleToHL( in.maxDampAngular );

	out.dampFactor = in.dampFactor;
}

float ComputeShadowControllerHL( CPhysicsObject *pObject, const hlshadowcontrol_params_t &params, float secondsToArrival, float dt )
{
	shadowcontrol_params_t ivpParams;
	ConvertShadowControllerToIVP( params, ivpParams );
	return ComputeShadowControllerIVP( pObject->GetObject(), ivpParams, secondsToArrival, dt );
}

class CShadowController : public IVP_Controller_Independent, public IPhysicsShadowController, public CAlignedNewDelete<16>
{
public:
	CShadowController();
	CShadowController( CPhysicsObject *pObject, bool allowTranslation, bool allowRotation );
	~CShadowController( void );

	// ipion interfaces
    void do_simulation_controller( IVP_Event_Sim *es,IVP_U_Vector<IVP_Core> *cores);
    virtual IVP_CONTROLLER_PRIORITY get_controller_priority() { return IVP_CP_MOTION; }
	virtual const char *get_controller_name() { return "vphysics:shadow"; }

	void SetObject( IPhysicsObject *pObject );
	void Update( const Vector &position, const QAngle &angles, float secondsToArrival );
	void MaxSpeed( float maxSpeed, float maxAngularSpeed );
	virtual void StepUp( float height );
	virtual void SetTeleportDistance( float teleportDistance );
	virtual bool AllowsTranslation() { return m_allowsTranslation; }
	virtual bool AllowsRotation() { return m_allowsRotation; }

	virtual void GetLastImpulse( Vector *pOut )
	{
		ConvertPositionToHL( m_shadow.lastImpulse, *pOut );
	}
	bool IsEnabled() { return m_enabled; }
	void Enable( bool bEnable ) 
	{ 
		m_enabled = bEnable;
	}

	void WriteToTemplate( vphysics_save_cshadowcontroller_t &controllerTemplate );
	void InitFromTemplate( const vphysics_save_cshadowcontroller_t &controllerTemplate );
	
	virtual void SetPhysicallyControlled( bool isPhysicallyControlled ) { m_isPhysicallyControlled = isPhysicallyControlled; }
	virtual bool IsPhysicallyControlled() { return m_isPhysicallyControlled; }

	virtual void UseShadowMaterial( bool bUseShadowMaterial )
	{
		if ( !m_pObject )
			return;

		int current = m_pObject->GetMaterialIndexInternal();
		int target = bUseShadowMaterial ? MATERIAL_INDEX_SHADOW : m_savedMaterialIndex;
		if ( target != current )
		{
			m_pObject->SetMaterialIndex( target );
		}
	}

	virtual void ObjectMaterialChanged( int materialIndex )
	{
		if ( !m_pObject )
			return;
		m_savedMaterialIndex = materialIndex;
	}

	//Basically get the last inputs to IPhysicsShadowController::Update(), returns last input to timeOffset in Update()
	virtual float GetTargetPosition( Vector *pPositionOut, QAngle *pAnglesOut );

	virtual float GetTeleportDistance( void );
	virtual void GetMaxSpeed( float *pMaxSpeedOut, float *pMaxAngularSpeedOut );

private:
	void AttachObject( void );
	void DetachObject( void );

	shadowcontrol_params_t	m_shadow;
	IVP_U_Float_Point	m_saveRot;
	IVP_U_Float_Point	m_savedRI;
	CPhysicsObject		*m_pObject;
	float				m_secondsToArrival;
	float				m_savedMass;
	unsigned int		m_savedFlags;

	unsigned short		m_savedMaterialIndex;
	bool				m_enabled : 1;
	bool				m_allowsTranslation : 1;
	bool				m_allowsRotation : 1;
	bool				m_isPhysicallyControlled : 1;
};

CShadowController::CShadowController()
{ 
	m_shadow.targetPosition.set_to_zero(); 
	m_shadow.targetRotation.init(); 
}

CShadowController::CShadowController( CPhysicsObject *pObject, bool allowTranslation, bool allowRotation )
{
	m_pObject = pObject;
	m_shadow.dampFactor = 1.0f;
	m_shadow.teleportDistance = 0;
	m_shadow.maxDampSpeed = 0;
	m_shadow.maxDampAngular = 0;
	m_shadow.targetPosition.set_to_zero(); 
	m_shadow.targetRotation.init(); 

	m_allowsTranslation = allowTranslation;
	m_allowsRotation = allowRotation;

	m_isPhysicallyControlled = false;
	m_enabled = false;

	AttachObject();
}

CShadowController::~CShadowController( void ) 
{
	DetachObject();
}

void CShadowController::AttachObject( void )
{
	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_Core *pCore = pivp->get_core();
	m_saveRot = pCore->rot_speed_damp_factor;
	m_savedRI = *pCore->get_rot_inertia();
	m_savedMass = pCore->get_mass();
	m_savedMaterialIndex = m_pObject->GetMaterialIndexInternal();

	Vector position;
	QAngle angles;
	m_pObject->GetPosition( &position, &angles );
	ConvertPositionToIVP( position, m_shadow.targetPosition );
	ConvertRotationToIVP( angles, m_shadow.targetRotation );

	UseShadowMaterial( true );

	pCore->rot_speed_damp_factor = IVP_U_Float_Point( 100, 100, 100 );

	if ( !m_allowsRotation )
	{
		IVP_U_Float_Point ri( 1e14f, 1e14f, 1e14f );
		pCore->set_rotation_inertia( &ri );
	}
	if ( !m_allowsTranslation )
	{
		m_pObject->SetMass( VPHYSICS_MAX_MASS );
		//pCore->inv_rot_inertia.hesse_val = 0.0f;
		m_pObject->EnableGravity( false );
	}

	m_savedFlags = m_pObject->CallbackFlags();
	unsigned int flags = m_savedFlags | CALLBACK_SHADOW_COLLISION;
	flags &= ~CALLBACK_GLOBAL_FRICTION;
	flags &= ~CALLBACK_GLOBAL_COLLIDE_STATIC;
	m_pObject->SetCallbackFlags( flags );
	m_pObject->EnableDrag( false );

	pCore->calc_calc();
	pivp->get_environment()->get_controller_manager()->add_controller_to_core( this, pCore );
	m_shadow.lastPosition.set_to_zero();
}

void CShadowController::DetachObject( void )
{
	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_Core *pCore = pivp->get_core();
	// don't bother if we're just doing this to delete the object
	if ( !(m_pObject->GetCallbackFlags() & CALLBACK_MARKED_FOR_DELETE) )
	{
		pCore->rot_speed_damp_factor = m_saveRot;
		pCore->set_mass( m_savedMass );
		m_pObject->SetCallbackFlags( m_savedFlags );
		m_pObject->EnableDrag( true );
		m_pObject->EnableGravity( true );
		UseShadowMaterial( false );
		pCore->set_rotation_inertia( &m_savedRI ); // this calls calc_calc()
	}
	m_pObject = NULL;
	pivp->get_environment()->get_controller_manager()->remove_controller_from_core( this, pCore );
}

void CShadowController::SetObject( IPhysicsObject *pObject )
{
	CPhysicsObject *obj = (CPhysicsObject *)pObject;
	if ( obj == m_pObject )
		return;

	DetachObject();
	m_pObject = obj;
	AttachObject();
}


void CShadowController::StepUp( float height )
{
	Vector step( 0, 0, height );

	IVP_Real_Object *pIVP = m_pObject->GetObject();
	IVP_U_Quat world_f_object;
	IVP_U_Point positionIVP, deltaIVP;
	ConvertPositionToIVP( step, deltaIVP );
	pIVP->get_quat_world_f_object_AT( &world_f_object, &positionIVP );
	positionIVP.add( &deltaIVP );
	pIVP->beam_object_to_new_position( &world_f_object, &positionIVP, IVP_TRUE );
}

void CShadowController::SetTeleportDistance( float teleportDistance )
{
	m_shadow.teleportDistance = ConvertDistanceToIVP( teleportDistance );
}

float CShadowController::GetTeleportDistance( void )
{
	return ConvertDistanceToHL( m_shadow.teleportDistance );
}

void CShadowController::do_simulation_controller( IVP_Event_Sim *es,IVP_U_Vector<IVP_Core> *)
{
	if ( IsEnabled() )
	{
		IVP_Real_Object *pivp = m_pObject->GetObject();
		Assert(!pivp->get_core()->pinned && !pivp->get_core()->physical_unmoveable);

		ComputeShadowControllerIVP( pivp, m_shadow, m_secondsToArrival, es->delta_time );
		if ( m_allowsTranslation )
		{
			// UNDONE: Assumes gravity points down
			const IVP_U_Point *pgrav = pivp->get_environment()->get_gravity();
			float gravDt = pgrav->k[1] * es->delta_time;
			
			if ( m_shadow.lastImpulse.k[1] > gravDt )
			{
				if ( IsOnGround( pivp ) )
				{
					float delta = gravDt - m_shadow.lastImpulse.k[1];
					pivp->get_core()->speed.k[1] += delta;
					m_shadow.lastImpulse.k[1] += delta;
				}
			}
		}

		// if we have time left, subtract it off
		m_secondsToArrival -= es->delta_time;
		if ( m_secondsToArrival < 0 )
		{
			m_secondsToArrival = 0;
		}
	}
	else
	{
		m_shadow.lastPosition.set_to_zero();
	}
}

// NOTE: This isn't a test for equivalent orientations, it's a test for calling update
// with EXACTLY the same data repeatedly
static bool IsEqual( const IVP_U_Point &pt0, const IVP_U_Point &pt1 )
{
	return pt0.quad_distance_to( &pt1 ) < 1e-8f ? true : false;
}

// NOTE: This isn't a test for equivalent orientations, it's a test for calling update
// with EXACTLY the same data repeatedly
static bool IsEqual( const IVP_U_Quat &pt0, const IVP_U_Quat &pt1 )
{
	float delta = fabs(pt0.x - pt1.x);
	delta += fabs(pt0.y - pt1.y);
	delta += fabs(pt0.z - pt1.z);
	delta += fabs(pt0.w - pt1.w);
	return delta < 1e-8f ? true : false;
}

void CShadowController::Update( const Vector &position, const QAngle &angles, float secondsToArrival )
{
	IVP_U_Point targetPosition = m_shadow.targetPosition;
	IVP_U_Quat targetRotation = m_shadow.targetRotation;

	ConvertPositionToIVP( position, m_shadow.targetPosition );
	m_secondsToArrival = secondsToArrival < 0 ? 0 : secondsToArrival;
	ConvertRotationToIVP( angles, m_shadow.targetRotation );

	Enable( true );

	if ( IsEqual( targetPosition, m_shadow.targetPosition ) && IsEqual( targetRotation, m_shadow.targetRotation ) )
		return;

	m_pObject->Wake();
}

float CShadowController::GetTargetPosition( Vector *pPositionOut, QAngle *pAnglesOut )
{
	if( pPositionOut )
		ConvertPositionToHL( m_shadow.targetPosition, *pPositionOut );

	if( pAnglesOut )
		ConvertRotationToHL( m_shadow.targetRotation, *pAnglesOut );

	return m_secondsToArrival;
}

void CShadowController::MaxSpeed( float maxSpeed, float maxAngularSpeed )
{
	// UNDONE: Turn this on when shadow controllers are having velocity updated per frame
	// right now this has the effect of making dampspeed zero by default.
#if 0
	IVP_Core *pCore = m_pObject->GetObject()->get_core();
	{
		// limit additional velocity to that which is not amplifying the current velocity
		float availableSpeed = ConvertDistanceToIVP( maxSpeed );
		float currentSpeed = pCore->speed.real_length();

		m_shadow.maxDampSpeed = min(currentSpeed, availableSpeed);
		m_shadow.maxSpeed = availableSpeed - m_shadow.maxDampSpeed;
	}

	{
		// limit additional velocity to that which is not amplifying the current velocity
		float availableAngularSpeed = ConvertAngleToIVP( maxAngularSpeed );
		float currentAngularSpeed = pCore->rot_speed.real_length();
		m_shadow.maxDampAngular = min(currentAngularSpeed, availableAngularSpeed);
		m_shadow.maxAngular = availableAngularSpeed - m_shadow.maxDampAngular;
	}
#else
	m_shadow.maxSpeed = maxSpeed;
	m_shadow.maxDampSpeed = maxSpeed;
	m_shadow.maxAngular = maxAngularSpeed;
	m_shadow.maxDampAngular = maxAngularSpeed;
#endif
}

void CShadowController::GetMaxSpeed( float *pMaxSpeedOut, float *pMaxAngularSpeedOut )
{
	if( pMaxSpeedOut )
		*pMaxSpeedOut = m_shadow.maxSpeed;

	if( pMaxAngularSpeedOut )
		*pMaxAngularSpeedOut = m_shadow.maxAngular;
}

struct vphysics_save_shadowcontrolparams_t : public hlshadowcontrol_params_t
{
	DECLARE_SIMPLE_DATADESC();
};

BEGIN_SIMPLE_DATADESC( vphysics_save_shadowcontrolparams_t )
	DEFINE_FIELD( targetPosition,		FIELD_POSITION_VECTOR	),
	DEFINE_FIELD( targetRotation,		FIELD_VECTOR	),
	DEFINE_FIELD( maxSpeed,			FIELD_FLOAT	),
	DEFINE_FIELD( maxDampSpeed,		FIELD_FLOAT	),
	DEFINE_FIELD( maxAngular,			FIELD_FLOAT	),
	DEFINE_FIELD( maxDampAngular,		FIELD_FLOAT	),
	DEFINE_FIELD( dampFactor,			FIELD_FLOAT	),
	DEFINE_FIELD( teleportDistance,	FIELD_FLOAT	),
END_DATADESC()

struct vphysics_save_cshadowcontroller_t
{
	CPhysicsObject		*pObject;
	float				secondsToArrival;
	IVP_U_Float_Point	saveRot;
	IVP_U_Float_Point	savedRI;
	IVP_U_Float_Point	currentSpeed;

	float				savedMass;
	int					savedMaterial;
	unsigned int		savedFlags;
	bool				enable;
	bool				allowPhysicsMovement;
	bool				allowPhysicsRotation;
	bool				isPhysicallyControlled;

	hlshadowcontrol_params_t	shadowParams;

	DECLARE_SIMPLE_DATADESC();
};


BEGIN_SIMPLE_DATADESC( vphysics_save_cshadowcontroller_t )
	//DEFINE_VPHYSPTR( pObject ),
	DEFINE_FIELD( secondsToArrival,		FIELD_FLOAT	),
	DEFINE_ARRAY( saveRot.k,				FIELD_FLOAT, 3 ),
	DEFINE_ARRAY( savedRI.k,				FIELD_FLOAT, 3 ),
	DEFINE_ARRAY( currentSpeed.k,			FIELD_FLOAT, 3 ),
	DEFINE_FIELD( savedMass,				FIELD_FLOAT	),
	DEFINE_FIELD( savedFlags,				FIELD_INTEGER	),
	DEFINE_CUSTOM_FIELD( savedMaterial, MaterialIndexDataOps() ),
	DEFINE_FIELD( enable,						FIELD_BOOLEAN	),
	DEFINE_FIELD( allowPhysicsMovement,		FIELD_BOOLEAN	),
	DEFINE_FIELD( allowPhysicsRotation,		FIELD_BOOLEAN	),
	DEFINE_FIELD( isPhysicallyControlled,	FIELD_BOOLEAN	),

	DEFINE_EMBEDDED_OVERRIDE( shadowParams, vphysics_save_shadowcontrolparams_t ),
END_DATADESC()

void CShadowController::WriteToTemplate( vphysics_save_cshadowcontroller_t &controllerTemplate )
{
	controllerTemplate.pObject = m_pObject;
	controllerTemplate.secondsToArrival = m_secondsToArrival;
	controllerTemplate.saveRot = m_saveRot;
	controllerTemplate.savedRI = m_savedRI;
	controllerTemplate.savedMass = m_savedMass;
	controllerTemplate.savedFlags = m_savedFlags;

	controllerTemplate.savedMaterial = m_savedMaterialIndex;
	controllerTemplate.enable = IsEnabled();
	controllerTemplate.allowPhysicsMovement = m_allowsTranslation;
	controllerTemplate.allowPhysicsRotation = m_allowsRotation;
	controllerTemplate.isPhysicallyControlled = m_isPhysicallyControlled;

	ConvertShadowControllerToHL( m_shadow, controllerTemplate.shadowParams );
}

void CShadowController::InitFromTemplate( const vphysics_save_cshadowcontroller_t &controllerTemplate )
{
	m_pObject = controllerTemplate.pObject;
	m_secondsToArrival = controllerTemplate.secondsToArrival;
	m_saveRot = controllerTemplate.saveRot;
	m_savedRI = controllerTemplate.savedRI;
	m_savedMass = controllerTemplate.savedMass;
	m_savedFlags = controllerTemplate.savedFlags;
	m_savedMaterialIndex = controllerTemplate.savedMaterial;

	Enable( controllerTemplate.enable );
	m_allowsTranslation = controllerTemplate.allowPhysicsMovement;
	m_allowsRotation = controllerTemplate.allowPhysicsRotation;
	m_isPhysicallyControlled = controllerTemplate.isPhysicallyControlled;
	
	ConvertShadowControllerToIVP( controllerTemplate.shadowParams, m_shadow );
	m_pObject->GetObject()->get_environment()->get_controller_manager()->add_controller_to_core( this, m_pObject->GetObject()->get_core() );
}


IPhysicsShadowController *CreateShadowController( CPhysicsObject *pObject, bool allowTranslation, bool allowRotation )
{
	return new CShadowController( pObject, allowTranslation, allowRotation );
}



bool SavePhysicsShadowController( const physsaveparams_t &params, IPhysicsShadowController *pIShadow )
{
	vphysics_save_cshadowcontroller_t controllerTemplate;
	memset( &controllerTemplate, 0, sizeof(controllerTemplate) );

	CShadowController *pShadowController = (CShadowController *)pIShadow;
	pShadowController->WriteToTemplate( controllerTemplate );
	params.pSave->WriteAll( &controllerTemplate );
	return true;
}

bool RestorePhysicsShadowController( const physrestoreparams_t &params, IPhysicsShadowController **ppShadowController )
{
	return false;
}

bool RestorePhysicsShadowControllerInternal( const physrestoreparams_t &params, IPhysicsShadowController **ppShadowController, CPhysicsObject *pObject )
{
	vphysics_save_cshadowcontroller_t controllerTemplate;

	memset( &controllerTemplate, 0, sizeof(controllerTemplate) );
	params.pRestore->ReadAll( &controllerTemplate );
	
	// HACKHACK: pass this in
	controllerTemplate.pObject = pObject;

	CShadowController *pShadow = new CShadowController();
	pShadow->InitFromTemplate( controllerTemplate );

	*ppShadowController = pShadow;
	return true;
}

bool SavePhysicsPlayerController( const physsaveparams_t &params, CPlayerController *pPlayerController )
{
	return false;
}

bool RestorePhysicsPlayerController( const physrestoreparams_t &params, CPlayerController **ppPlayerController )
{
	return false;
}

//HACKHACK: The physics object transfer system needs to temporarily detach a shadow controller from an object, then reattach without other repercussions
void ControlPhysicsShadowControllerAttachment_Silent( IPhysicsShadowController *pController, IVP_Real_Object *pivp, bool bAttach )
{
	if( bAttach )
	{
		pivp->get_environment()->get_controller_manager()->add_controller_to_core( (CShadowController *)pController, pivp->get_core() );
	}
	else
	{
		pivp->get_environment()->get_controller_manager()->remove_controller_from_core( (CShadowController *)pController, pivp->get_core() );
	}
}

void ControlPhysicsPlayerControllerAttachment_Silent( IPhysicsPlayerController *pController, IVP_Real_Object *pivp, bool bAttach )
{
	if( bAttach )
	{
		pivp->get_environment()->get_controller_manager()->add_controller_to_core( (CPlayerController *)pController, pivp->get_core() );
	}
	else
	{
		pivp->get_environment()->get_controller_manager()->remove_controller_from_core( (CPlayerController *)pController, pivp->get_core() );
	}
}