void CPhysicsSpring::AttachListener()
{
	if ( !(m_pObjStart->GetCallbackFlags() & CALLBACK_NEVER_DELETED) )
	{
		m_pObjStart->GetObject()->add_listener_object( this );
	}

	if ( !(m_pObjEnd->GetCallbackFlags() & CALLBACK_NEVER_DELETED) )
	{
		m_pObjEnd->GetObject()->add_listener_object( this );
	}
}
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();
	
	m_pObject->SetMaterialIndex( MATERIAL_INDEX_SHADOW );

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

	if ( !m_allowPhysicsRotation )
	{
		IVP_U_Float_Point ri( 1e15f, 1e15f, 1e15f );
		pCore->set_rotation_inertia( &ri );
	}
	if ( !m_allowPhysicsMovement )
	{
		m_pObject->SetMass( 1e6f );
		//pCore->inv_rot_inertia.hesse_val = 0.0f;
		m_pObject->EnableGravity( false );
	}

	pCore->calc_calc();
	pivp->get_environment()->get_controller_manager()->add_controller_to_core( this, pCore );
	m_shadow.lastPosition.set_to_zero();
}
int CPlayerController::TryTeleportObject( void )
{
	if ( m_handler )
	{
		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 );
	}

	return 1;
}
void CPhysicsSpring::DetachListener()
{
	if ( !(m_pObjStart->GetCallbackFlags() & CALLBACK_NEVER_DELETED) )
	{
		m_pObjStart->GetObject()->remove_listener_object( this );
	}

	if ( !(m_pObjEnd->GetCallbackFlags() & CALLBACK_NEVER_DELETED) )
	{
		m_pObjEnd->GetObject()->remove_listener_object( this );
	}

	m_pObjStart = NULL;
	m_pObjEnd = NULL;
	m_pSpring = NULL;
}
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);
				
				// skip if this is a shadow object
				// UNDONE: Is this a hack? need a better way to detect shadows?
				if ( !(pPhys->GetCallbackFlags() & CALLBACK_SHADOW_COLLISION) )
					return true;
			}
		}

		pfriction = pfriction->get_next();
	}

	return false;
}
    void event_object_revived( IVP_Event_Object *pEvent )
	{
		CPhysicsObject *pObject = static_cast<CPhysicsObject *>(pEvent->real_object->client_data);
		if ( !pObject )
			return;

		int sleepState = pObject->GetSleepState();
		
		pObject->NotifyWake();

		// asleep, but already in active list
		if ( sleepState == OBJ_STARTSLEEP )
			return;

		// don't track static objects (like the world).  That way we only track objects that will move
		if ( pObject->GetObject()->get_movement_state() != IVP_MT_STATIC )
		{
			Assert(pObject->GetActiveIndex()==0xFFFF);
			if ( pObject->GetActiveIndex()!=0xFFFF)
				return;

			int index = m_activeObjects.AddToTail( pObject );
			pObject->SetActiveIndex( index );
		}
		if ( m_pCallback )
		{
			m_pCallback->ObjectWake( pObject );
		}
	}
void CPhysicsMotionController::DetachObject( IPhysicsObject *pObject )
{
	CPhysicsObject *pPhys = static_cast<CPhysicsObject *>(pObject);
	IVP_Real_Object *pIVP = pPhys->GetObject();
	IVP_Core *core = pIVP->get_core();

	RemoveCore(core);
}
void CPhysicsSpring::GetEndpoints( Vector* worldPositionStart, Vector* worldPositionEnd )
{
	IVP_U_Point world_coords;

	if ( worldPositionStart )
	{
		const IVP_Anchor *anchor = m_pSpring->get_actuator_anchor(0);
		TransformLocalToIVP( anchor->object_pos, world_coords, m_pObjStart->GetObject(), true );
		ConvertPositionToHL( world_coords, *worldPositionStart );
	}

	if ( worldPositionEnd )
	{
		const IVP_Anchor *anchor = m_pSpring->get_actuator_anchor(1);
		TransformLocalToIVP( anchor->object_pos, world_coords, m_pObjEnd->GetObject(), true );
		ConvertPositionToHL( world_coords, *worldPositionEnd );
	}
}
void CPlayerController::AttachObject( void )
{
	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();
	pivp->get_environment()->get_controller_manager()->add_controller_to_core( this, pCore );
}
void CShadowController::DetachObject( void )
{
	IVP_Real_Object *pivp = m_pObject->GetObject();
	IVP_Core *pCore = pivp->get_core();
	pCore->rot_speed_damp_factor = m_saveRot;
	pCore->set_mass( m_savedMass );
	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 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_Environment *pIVPEnv = pivp->get_environment();
	CPhysicsEnvironment *pVEnv = (CPhysicsEnvironment *)pIVPEnv->client_data;

	float psiScale = pVEnv->GetInvPSIScale();
	if ( !psiScale )
		return;

	IVP_Core *pCore = pivp->get_core();
	// 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();

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

	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 ( qdist > m_maxDeltaPosition * m_maxDeltaPosition )
	{
		if ( TryTeleportObject() )
			return;
	}

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

	ComputeController( pCore->speed, delta_position, m_maxSpeed, psiScale / es->delta_time, m_dampFactor );
}
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 = NULL;
	pivp->get_environment()->get_controller_manager()->remove_controller_from_core( this, pCore );
}
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::InitFromTemplate( const vphysics_save_cshadowcontroller_t &controllerTemplate )
{
	m_pObject = controllerTemplate.pObject;

	m_secondsToArrival = controllerTemplate.secondsToArrival;
	m_saveRot = controllerTemplate.saveRot;
	m_savedRI = controllerTemplate.savedRI;
	m_currentSpeed = controllerTemplate.currentSpeed;
	m_savedMass = controllerTemplate.savedMass;
	m_savedMaterialIndex = controllerTemplate.savedMaterial;
	m_enable = controllerTemplate.enable;
	m_allowPhysicsMovement = controllerTemplate.allowPhysicsMovement;
	m_allowPhysicsRotation = controllerTemplate.allowPhysicsRotation;
	
	ConvertShadowControllerToIVP( controllerTemplate.shadowParams, m_shadow );
	m_pObject->GetObject()->get_environment()->get_controller_manager()->add_controller_to_core( this, m_pObject->GetObject()->get_core() );
}
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();

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

	IVP_Float_PointAbs( m_maxSpeed, available );
}
void CShadowController::do_simulation_controller( IVP_Event_Sim *es,IVP_U_Vector<IVP_Core> *)
{
	if ( m_enable )
	{
		IVP_Real_Object *pivp = m_pObject->GetObject();

		ComputeShadowControllerIVP( pivp, m_shadow, m_secondsToArrival, es->delta_time );

		// 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();
	}
}
void CPlayerController::Update( const Vector& position, const Vector& velocity, bool onground, IPhysicsObject *ground )
{
	IVP_U_Point	targetPositionIVP;
	IVP_U_Float_Point targetSpeedIVP;

	ConvertPositionToIVP( position, targetPositionIVP );
	ConvertPositionToIVP( velocity, targetSpeedIVP );
	
	// 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 = targetPositionIVP;
	m_currentSpeed = 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 );
	}
}
void CPhysicsMotionController::AttachObject( IPhysicsObject *pObject )
{
	Assert(pObject);
	// BUGBUG: Sometimes restore comes back with a NULL, REVISIT
	if ( !pObject || pObject->IsStatic() )
		return;

	CPhysicsObject *pPhys = static_cast<CPhysicsObject *>(pObject);
	IVP_Real_Object *pIVP = pPhys->GetObject();
	IVP_Core *pCore = pIVP->get_core();

#if DEBUG
	int index = m_coreList.Find(pCore);
	if ( m_coreList.IsValidIndex(index) )
	{
		Msg("Attached core twice!!!\n");
		return;
	}
#endif

	m_coreList.AddToTail( pCore );
	pCore->add_core_controller( (IVP_Controller *)this );
}
void CShadowController::MaxSpeed( const Vector &velocity, const AngularImpulse &angularVelocity )
{
	IVP_Core *pCore = m_pObject->GetObject()->get_core();

	// limit additional velocity to that which is not amplifying the current velocity
	IVP_U_Float_Point ivpVel;
	ConvertPositionToIVP( velocity, ivpVel );
	IVP_U_Float_Point available = ivpVel;

	m_currentSpeed = ivpVel;
	
	// normalize and save length
	float length = ivpVel.real_length_plus_normize();

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

	// same for angular, project on to current and remove redundant (amplifying) input
	IVP_U_Float_Point ivpAng;
	ConvertAngularImpulseToIVP( angularVelocity, ivpAng );
	IVP_U_Float_Point availableAng = ivpAng;
	float lengthAng = ivpAng.real_length_plus_normize();

	float dotAng = ivpAng.dot_product( &pCore->rot_speed );
	if ( dotAng > 0 )
	{
		ivpAng.mult( dotAng * lengthAng );
		availableAng.subtract( &ivpAng );
	}

	IVP_Float_PointAbs( m_shadow.maxAngular, availableAng );
}
	//-----------------------------------------------------------------------------
	// Purpose: This walks the objects in the environment and generates friction events
	//			for any scraping that is occurring.
	//-----------------------------------------------------------------------------
	void ProcessActiveObjects( IVP_Environment *pEnvironment, IPhysicsCollisionEvent *pEvent )
	{
		// FIXME: Is this correct? Shouldn't it do next PSI - lastScrape?
		float nextTime = pEnvironment->get_old_time_of_last_PSI().get_time();
		float delta = nextTime - m_lastScrapeTime;

		// only process if we have done a PSI
		if ( delta < pEnvironment->get_delta_PSI_time() )
			return;

		float t = 1.0f;
		t /= delta;
		if ( t > 10.0f )
			t = 10.0f;

		m_lastScrapeTime = nextTime;

		// UNDONE: This only calls friciton for one object in each pair.
		// UNDONE: Split energy in half and call for both objects?
		// UNDONE: Don't split/call if one object is static (like the world)?
		for ( int i = 0; i < m_activeObjects.Count(); i++ )
		{
			CPhysicsObject *pObject = m_activeObjects[i];
			IVP_Real_Object *ivpObject = pObject->GetObject();
			
			// no friction callbacks for this object
			if ( ! (pObject->GetCallbackFlags() & CALLBACK_GLOBAL_FRICTION) )
				continue;

			// UNDONE: IVP_Synapse_Friction is supposed to be opaque.  Is there a better way
			// to implement this?  Using the friction listener is much more work for the CPU
			// and considers sleeping objects.
			IVP_Synapse_Friction *pfriction = ivpObject->get_first_friction_synapse();
			while ( pfriction )
			{
				IVP_Contact_Point *contact = pfriction->get_contact_point();
				IVP_Synapse_Friction *pOpposite = GetOppositeSynapse( pfriction );
				IVP_Real_Object *pobj = pOpposite->get_object();
				CPhysicsObject *pScrape = (CPhysicsObject *)pobj->client_data;

				// friction callbacks for this object?
				if ( pScrape->GetCallbackFlags() & CALLBACK_GLOBAL_FRICTION )
				{
					float energy = IVP_Contact_Point_API::get_eliminated_energy( contact );
					if ( energy ) 
					{
						IVP_Contact_Point_API::reset_eliminated_energy( contact );
						// scrape with an estimate for the energy per unit mass
						// This assumes that the game is interested in some measure of vibration
						// for sound effects.  This also assumes that more massive objects require
						// more energy to vibrate.
						energy = energy * t * ivpObject->get_core()->get_inv_mass();

						if ( energy > 0.05f )
						{
							int hitSurface = physprops->RemapIVPMaterialIndex( pOpposite->get_material_index() );
							CPhysicsFrictionData data(pfriction);
							pEvent->Friction( pObject, ConvertEnergyToHL(energy), pObject->GetMaterialIndexInternal(), hitSurface, &data );
						}
					}
				}
				pfriction = pfriction->get_next();
			}
		}
	}