void IVP_Controller_Raycast_Airboat::do_steering_wheel(IVP_POS_WHEEL wheel_nr, IVP_FLOAT s_angle)
{
    IVP_Raycast_Airboat_Wheel *wheel = get_wheel(wheel_nr);
	
    wheel->axis_direction_cs.set_to_zero();
    wheel->axis_direction_cs.k[ index_x ] = 1.0f;
    wheel->axis_direction_cs.rotate( IVP_COORDINATE_INDEX(index_y), s_angle);
}
void CPhysics_Car_System_Raycast_Wheels::update_wheel_positions( void )
{
	// Get the car body object.
    IVP_Cache_Object *pCacheObject = car_body->get_cache_object();

	// Get the core (vehicle) matrix.
    IVP_U_Matrix m_core_f_object;    
	car_body->calc_m_core_f_object( &m_core_f_object );
    
    for ( int iWheel = 0; iWheel < n_wheels; ++iWheel )
	{
		// Get the current raycast wheel.
		IVP_Raycast_Car_Wheel *pRaycastWheel = get_wheel( IVP_POS_WHEEL( iWheel ) );

		// Get the position of the wheel in vehicle core space.
		IVP_U_Float_Point hp_cs; 
		hp_cs.add_multiple( &pRaycastWheel->hp_cs, &pRaycastWheel->spring_direction_cs, pRaycastWheel->raycast_dist - pRaycastWheel->wheel_radius );

		// Get the position on vehicle object space (inverse transform).
		IVP_U_Float_Point hp_os; 
		m_core_f_object.vimult4( &hp_cs, &hp_os );

		// Transform the wheel position from object space into world space.
		IVP_U_Point hp_ws; 
		pCacheObject->transform_position_to_world_coords( &hp_os, &hp_ws );
		
		// Apply rotational component.
		IVP_U_Point wheel_cs( &pRaycastWheel->axis_direction_cs );
		IVP_U_Point wheel2_cs( 0 ,0 ,0 ); 
		wheel2_cs.k[index_y] = -1.0;
		wheel2_cs.rotate( IVP_COORDINATE_INDEX( index_x ), pRaycastWheel->angle_wheel );
		
		IVP_U_Matrix3 m_core_f_wheel;
		m_core_f_wheel.init_normized3_col( &wheel_cs, IVP_COORDINATE_INDEX( index_x ), &wheel2_cs );
		
		IVP_U_Matrix3 m_world_f_wheel;
		pCacheObject->m_world_f_object.mmult3( &m_core_f_wheel, &m_world_f_wheel ); // bid hack, assumes cs = os (for rotation);
		
		IVP_U_Quat rot_ws; 
		rot_ws.set_quaternion( &m_world_f_wheel );
		m_pWheels[iWheel]->beam_object_to_new_position( &rot_ws, &hp_ws );
    }

    pCacheObject->remove_reference();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void IVP_Controller_Raycast_Airboat::DoSimulationTurbine( IVP_Raycast_Airboat_Pontoon_Temp *pTempPontoons,
											              IVP_Event_Sim *pEventSim, IVP_Core *pAirboatCore )
{
	// Get the forward vector in world-space.
	IVP_U_Float_Point vecForwardWS; 
    const IVP_U_Matrix *matWorldFromCore = pAirboatCore->get_m_world_f_core_PSI();
	matWorldFromCore->get_col( IVP_COORDINATE_INDEX( index_z ), &vecForwardWS );

	// Forward (Front/Back) force
	IVP_U_Float_Point vecImpulse; 
	vecImpulse.set_multiple( &vecForwardWS, m_flThrust * pEventSim->delta_time );
	pAirboatCore->center_push_core_multiple_ws( &vecImpulse );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void IVP_Controller_Raycast_Airboat::DoSimulationSteering( IVP_Raycast_Airboat_Pontoon_Temp *pTempPontoons,
													       IVP_Core *pAirboatCore, IVP_Event_Sim *pEventSim )
{
	IVP_U_Float_Point vecRightWS; 
    const IVP_U_Matrix *matWorldFromCore = pAirboatCore->get_m_world_f_core_PSI();
	matWorldFromCore->get_col( IVP_COORDINATE_INDEX( index_x ), &vecRightWS );

	IVP_FLOAT flForceRotational = 0.0f;

	// Check for rotation.
	if ( fabsf( m_SteeringAngle ) > 0.01 )
	{
		// Get the steering sign.
		IVP_FLOAT flSteeringSign = m_SteeringAngle < 0.0f ? -1.0f : 1.0f;
		flForceRotational = IVP_RAYCAST_AIRBOAT_STEERING_RATE * pAirboatCore->get_mass() * pEventSim->i_delta_time;
		flForceRotational *= flSteeringSign;
	}

	// General force that will help get us back to zero rotation.
	IVP_FLOAT flRotSpeedSign = pAirboatCore->rot_speed.k[1] < 0.0f ? -1.0f : 1.0f;
	IVP_FLOAT flRotationalDrag = -0.5f * IVP_RAYCAST_AIRBOAT_STEERING_RATE * pAirboatCore->get_mass() * pEventSim->i_delta_time;
	flRotationalDrag *= flRotSpeedSign;
	flForceRotational += flRotationalDrag;
	
	IVP_U_Float_Point vecImpulse; 
	for ( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		IVP_Raycast_Airboat_Wheel *pPontoonPoint = get_wheel( IVP_POS_WHEEL( iPoint ) );
		IVP_Raycast_Airboat_Pontoon_Temp *pTempPontoonPoint = &pTempPontoons[iPoint];

		IVP_FLOAT flPontoonSign = iPoint >= 2 ? -1.0f : 1.0f;
		IVP_FLOAT flForceRot = flForceRotational * flPontoonSign;

		vecImpulse.set_multiple( &vecRightWS, flForceRot * pEventSim->delta_time );

		IVP_U_Float_Point vecPointPosCS, vecPointPosWS;
		vecPointPosCS = pPontoonPoint->raycast_start_cs;
		matWorldFromCore->vmult3( &vecPointPosCS, &vecPointPosWS );
		IVP_U_Point vecPointPositionWS( vecPointPosWS );
		pAirboatCore->push_core_ws( &vecPointPositionWS, &vecImpulse );		
	}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void IVP_Controller_Raycast_Airboat::DoSimulationTurbine(IVP_Event_Sim *pEventSim)
{
	IVP_Core *pAirboatCore = m_pAirboatBody->get_core();

	IVP_FLOAT thrust = m_flThrust;
	if (m_bAirborneIdle || (m_bAirborne && (thrust < 0.0f)))
		thrust *= 0.5f;

	IVP_U_Float_Point vecImpulse;
	pAirboatCore->get_m_world_f_core_PSI()->get_col(IVP_COORDINATE_INDEX(index_z), &vecImpulse);
	IVP_FLOAT flForwardY = vecImpulse.k[1];

	if ((flForwardY < -0.5f) && (thrust > 0.0f))
		thrust *= 1.0f + flForwardY;
	else if ((flForwardY > 0.5f) && (thrust < 0.0f))
		thrust *= 1.0f - flForwardY;

	
	vecImpulse.mult(pAirboatCore->get_mass() * thrust * pEventSim->delta_time);
	pAirboatCore->center_push_core_multiple_ws(&vecImpulse);
}