//  Car.cc - a body with wheels.
//
//  Copyright (C) 2001--2004 Sam Varner
//
//  This file is part of Vamos Automotive Simulator.
//
//  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 2 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, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include <vamos/body/Aerodynamic_Device.h>
#include <vamos/body/Brake.h>
#include <vamos/body/Car.h>
#include <vamos/body/Clutch.h>
#include <vamos/body/Differential.h>
#include <vamos/body/Engine.h>
#include <vamos/body/Fuel_Tank.h>
#include <vamos/body/Gauge.h>
#include <vamos/body/Particle.h>
#include <vamos/body/Suspension.h>
#include <vamos/body/Tire.h>
#include <vamos/body/Transmission.h>
#include <vamos/body/Wheel.h>

#include <cassert>
#include <sstream>
#include <iostream>

using namespace Vamos_Geometry;

//* Class Key_Control
// The Key_Control class handles gradual application of a control
// that's operated by a button, such as the clutch.  If you're using a
// keyboard instead of a joystick, it also handles steering, gas and
// brake.

//** Constructor
Vamos_Body::Key_Control::
Key_Control (bool block) :
  m_block (block),
  m_target_pending (false),
  m_value (0.0),
  m_target (0.0),
  m_next_target (0.0),
  m_rate (0.0),
  m_next_rate (0.0),
  m_delay (0.0),
  m_next_delay (0.0),
  m_time (0.0),
  m_next_time (0.0)
{
}

// Set the target setting of this control.  NEW_TARGET is the desired
// setting.  TIME is how long it should take for the setting to go
// from 0.0 to 1.0 after waiting for DELAY.  `m_rate' is calculated in
// this function.
void Vamos_Body::
Key_Control::target (double new_target, double time, double delay)
{
  if (m_block)
    {
      if (m_value == m_target)
        {
          m_target_pending = false;
        }
      else
        {
          m_target_pending = true;
          m_next_target = new_target;
          m_next_time = time;
          m_next_delay = delay;
          return;
        }
    }

  m_target = new_target;
  m_delay = delay;
  m_time = 0.0;

  if (time != 0.0)
    {
      m_rate = 1.0 / time;
      if (m_target < m_value)
        {
          m_rate = -m_rate;
        }
    }
  else
    {
      // m_rate == 0.0 means that the value will be immediately set to the
      // target.
      m_rate = 0.0;
    }
}

// Update the setting of this control.  The setting move toward
// `m_target' by the ammount `m_rate' * TIME.
double Vamos_Body::
Key_Control::update (double time)
{
  m_time += time;
  if (m_time < m_delay)
    {
      return m_value;
    }

  if (m_rate == 0.0)
    {
      m_value = m_target;
    }
  else
    {
      m_value += m_rate * time;
      if (((m_rate > 0.0) && (m_value > m_target))
          || ((m_rate < 0.0) && (m_value < m_target)))
        {
          m_value = m_target;
          m_rate = 0.0;
        }
    }

  if (m_target_pending && (m_value == m_target))
    {
      target (m_next_target, m_next_time, m_next_delay);
    }

  return m_value;
}

// Go immediately to the target.
void Vamos_Body::
Key_Control::end ()
{
  m_value = m_target;
  m_time = m_delay;
  m_rate = 0.0;
}



//* Class Car

//** Constructor
Vamos_Body::
Car::Car (const Three_Vector& pos) :
  m_chassis (pos),
  mp_drivetrain (0),
  mp_fuel_tank (0),
  m_max_steer_angle (15.0),
  m_steer_exponent (1.0),
  m_slide (0.0),
  m_shift_pending (false),
  m_shift_timer (0.0),
  m_shift_delay (0.2),
  m_new_gear (0),
  m_last_gear (0),
  m_clutch_key_control (true),
  mp_front_particle (0),
  m_distance_traveled (0.0),
  m_field_of_view (60.0),
  m_pan_angle (90.0),
  m_show_dashboard_extras (false),
  m_sector(-1),
  m_abs(false),
  m_abs_time(0.0),
  m_tcs(false),
  m_last_gas(0.0)
{
	controller = 0;
	brakesetting = 0;
	CA = 0.0;
	CW = 0.0;
	m_colpatches[0]=m_colpatches[1]=NULL;
}

extern bool verbose_output;

//** Destructor
// Only the drivetrain member is deleted here.  The rest are deleted
// when the body deletes the particles. 
Vamos_Body::
Car::~Car ()
{
	if (verbose_output)
		std::cout << "Car deinit" << std::endl;
  	
	delete mp_drivetrain;
	delete mp_fuel_tank;
	unsigned int i;
	for( i = 0; i < m_wheels.size(); i++ )
	{
		delete m_wheels[i];
	}
	m_wheels.clear ();
	
	if (verbose_output)
		std::cout << "Car deinit done" << std::endl;
}

void Vamos_Body::
Car::read (std::string data_dir, std::string car_file)
{
	unsigned int i;
	
  // Remember the file name for re-reading.
  if ((data_dir != "") && (car_file != ""))
    {
      m_data_dir = data_dir;
      m_car_file = car_file;
    }

	for( i = 0; i < m_wheels.size(); i++ )
	{
		delete m_wheels[i];
	}
	m_wheels.clear ();
	
	for (std::vector <Particle*>::iterator it = m_chassis.particles ().begin ();
    	it != m_chassis.particles ().end (); it++)
    	delete *it;
	
	m_chassis.particles ().clear ();

/*	if (USE_XML)
  		Car_Reader reader (m_data_dir, "cars/" + m_car_file + "/" + m_car_file + ".xml", this);
*/	if (USE_CAR)
	{
		bool success = LoadCarDefinition (m_data_dir + "cars/" + m_car_file + "/" + m_car_file + ".car");
		if (!success)
		{
			cout << "Error when loading car definition file:  " << m_data_dir + "cars/" + m_car_file + "/" + m_car_file + ".car" << endl;
		}
	}

  // Find the bounding box for the particles.
  std::vector <Particle*>::const_iterator it = m_chassis.particles ().begin ();
  m_crash_box.front = (*it)->position ()[0];
  m_crash_box.back = m_crash_box.front;
  m_crash_box.left = (*it)->position ()[1];
  m_crash_box.right = m_crash_box.left;
  m_crash_box.top = (*it)->position ()[2];
  m_crash_box.bottom = m_crash_box.top;

  mp_front_particle = *it;
  for (; it != m_chassis.particles ().end (); it++)
    {
      const Three_Vector& position = (*it)->position ();
      if (position [0] > m_crash_box.front)
        {
          m_crash_box.front = position [0];
          mp_front_particle = *it;
        }
      else if (position [0] < m_crash_box.back)
        {
          m_crash_box.back = position [0];
        }
      if (position [1] > m_crash_box.left)
        {
          m_crash_box.left = position [1];
        }
      else if (position [1] < m_crash_box.right)
        {
          m_crash_box.right = position [1];
        }
      if (position [2] > m_crash_box.top)
        {
          m_crash_box.top = position [2];
        }
      else if (position [2] < m_crash_box.bottom)
        {
          m_crash_box.bottom = position [2];
        }
    }
}

// Advance the car in time by TIME.  This method assumes that the
// first four members of m_particles are the left-front, right-front,
// left-rear, and right-rear wheels, and that the front wheels are
// steered and the rear wheels are driven.  Re-define this virtual
// function if you want to change these conditions.
void Vamos_Body::
Car::propagate (double time)
{
  // Propagate the key controls.
  m_steer_key_control.update (time);
  m_gas_key_control.update (time);
  m_brake_key_control.update (time);
  m_handbrake_key_control.update (time);
  m_clutch_key_control.update (time);
  m_pan_key_control.update (time);

  // Update the transmission.
  assert (mp_drivetrain);
  if (m_shift_pending)
    {
      m_shift_timer += time;
      if (m_shift_timer > m_shift_delay)
        {
          mp_drivetrain->transmission ()->shift (m_new_gear);
          m_shift_pending = false;
        }
    }

  // Update the throttle.
  assert (mp_fuel_tank);
  double gas = m_gas_key_control.value ();

  // Let the engine know if the fuel tank is empty.
  if (mp_fuel_tank->empty ())
    {
      gas = 0.0;
    }
  mp_drivetrain->engine ()->out_of_gas (mp_fuel_tank->empty ());

  // Update the fuel tank.
  mp_fuel_tank->consume (mp_drivetrain->engine ()->fuel_rate () * time);

  m_slide = 0.0;
  double right_wheel_speed = 0.0;
  double left_wheel_speed = 0.0;
	int count = 0;
	for (std::vector <Wheel*>::iterator it = m_wheels.begin ();
	   it != m_wheels.end ();
	   it++,count++)
	{
		// Steer.
		if ((*it)->steered ())
		{
			(*it)->steer (m_steer_key_control.value ());
		}
		
		
        //Update Brakes!
		brakesetting = m_brake_key_control.value();
		//apply handbrakes
		double handbrakesetting = m_handbrake_key_control.value()*(*it)->get_handbrake_multiplier();
		if (handbrakesetting > brakesetting)
		{
			(*it)->brake (handbrakesetting);
			brakesetting = handbrakesetting;
		}
		else
		{
			if (count == 0)
			{
				//cerr << (*it)->rotational_speed() << ",";
			}
			
			//apply ABS
			if (m_abs)
			{
				m_abs_time += time;
				
				bool errorcorrectalgo = true;
				bool decelerationalgo = false;
				bool mdennyalgo = false;
				bool xtsalgo = false;
				if (errorcorrectalgo)
				{
					//double omegadot_cur = ((*it)->rotational_speed() - (*it)->last_rotational_speed())/time;
					//double omegadot_last = ((*it)->last_rotational_speed() - (*it)->second_last_rotational_speed())/time;
					//double omegadotdot = omegadot_cur - omegadot_last; //find rotational jerk (we only engage when deceleration is decreasing).  this is w''
					
					double sp = (*it)->GetTire()->GetSigmaHat(); //ideal slip ratio given tire loading
					//double uhat = (*it)->GetTire()->GetFx(sp)*0.25; //maximum tire friction given ideal slip ratio
					/*if (count == 0)
						cerr << sp - (*it)->slip() << ",";*/
					
					//only active if brakes commanded past threshold
					if (brakesetting > 0.1)
					{
						double maxspeed = 0;
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							if ((*it2)->rotational_speed() > maxspeed);
								maxspeed = (*it2)->rotational_speed();
						}
						
						//don't engage ABS if all wheels are moving slowly
						if (maxspeed > 6.0)
						{
							double error = -(*it)->slip() - sp;
							//cout << sp << " - " << (*it)->slip() << " = " << error << endl;
							//if (count == 0)	cout << error << endl;
							double thresholdeng = 0.0;
							double thresholddis = -sp/2.0;
	
							if (error > thresholdeng && !(*it)->GetABSState())
							{
								(*it)->SetABSState(true);
								//cout << count << " ABS engaging: " << maxspindiff << endl;
							}
							
							if (error < thresholddis && (*it)->GetABSState())
							{
								(*it)->SetABSState(false);
								//cout << count << " ABS disengaging: " << maxspindiff << endl;
							}
							
							if ((*it)->GetABSState())
							{
								//cout << count << " ABS engage: " << maxspindiff << endl;
								(*it)->brake (0.0);
								//if (count == 3)	cout << 1 << endl;
							}
							else
							{
								//cout << count << " ABS not engaged: " << maxspindiff << endl;
								(*it)->brake (brakesetting);
								//if (count == 3)	cout << 0 << endl;
							}
						}
						else
						{
							(*it)->SetABSState(false);
							(*it)->brake (brakesetting);
						}
					}
					else
					{
						(*it)->SetABSState(false);
						(*it)->brake (brakesetting);
					}
				}
				else if (mdennyalgo) //see http://www.iop.org/EJ/article/0143-0807/26/6/008/ejp5_6_008.pdf
				{
					//only active if brakes commanded past threshold
					if (brakesetting > 0.1)
					{
						double maxspeed = 0;
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							if ((*it2)->rotational_speed() > maxspeed);
								maxspeed = (*it2)->rotational_speed();
						}
						
						//don't engage ABS if all wheels are moving slowly
						//if (maxspeed > 6.0)
						{
							double omegadot_cur = ((*it)->rotational_speed() - (*it)->last_rotational_speed())/time;
							double omegadot_last = ((*it)->last_rotational_speed() - (*it)->second_last_rotational_speed())/time;
							double omegadotdot = omegadot_cur - omegadot_last; //find rotational jerk (we only engage when deceleration is decreasing).  this is w''
							
							double sp = (*it)->GetTire()->GetSigmaHat(); //ideal slip ratio given tire loading
							double uhat = (*it)->GetTire()->GetFx(sp)*0.25; //maximum tire friction given ideal slip ratio
							double v = (*it)->GetTire()->inertia(); //rotational inertia of tire
							double m = (*it)->GetTire()->mass(); //mass of the wheel
							m = 30;
							double Gammahat = (v + 1 - sp)*uhat*0.001; //ideal braking torque in weird unit-less gamma
							double Gamma_to_torque_conversion = m*9.81/v;
							//double ideal_torque = Gammahat * Gamma_to_torque_conversion;
							double brakesetting_max = (*it)->factor_for_brake_torque((Gammahat + 1)*Gamma_to_torque_conversion);
							double brakesetting_min = (*it)->factor_for_brake_torque((Gammahat - 1)*Gamma_to_torque_conversion);
							//brakesetting_max = (*it)->factor_for_brake_torque((20)*Gamma_to_torque_conversion);
							//brakesetting_min = (*it)->factor_for_brake_torque((5)*Gamma_to_torque_conversion);
							//cout << "ideal torque (mdenny): " << Gammahat * Gamma_to_torque_conversion << "," << brakesetting_max << "," << brakesetting_min << " (venzon) " << uhat << "," << (*it)->factor_for_brake_torque(uhat) << endl;
							//cout << "ideal torque: " << uhat << "," << (*it)->factor_for_brake_torque(uhat) << endl;
							//double ideal_brakesetting = (*it)->factor_for_brake_torque(uhat);
							//brakesetting_max = ideal_brakesetting;
							//brakesetting_min = ideal_brakesetting - 0.5;
							
							//cout << "Current sigma hat: " << sp << ", u hat: " << uhat << endl;
							
							//double Gamma = (*it)->brake_torque_for_factor(brakesetting);
							/*double Gamma_min = 500.0;
							double Gamma_max = 1000.0;
							double brakesetting_min = (*it)->factor_for_brake_torque(Gamma_min);
							double brakesetting_max = (*it)->factor_for_brake_torque(Gamma_max);*/
							if (brakesetting_max > 1.0)
								brakesetting_max = 1.0;
							if (brakesetting_min < 0)
								brakesetting_min = 0;
							
							//double abs_period = 0.25;
							//double abs_period = 0.025;
							//double abs_period = 0.07;
							//double abs_period = 1.0;
							
							double coeff = 0.9;
							(*it)->m_absdouble = coeff*(*it)->m_absdouble+(1.0-coeff)*omegadotdot;
							
							if (count == 3)
							{
								cout << (*it)->m_absdouble << "," << omegadotdot << "," << omegadot_cur << "," << (*it)->rotational_speed() << endl;
								if ((*it)->m_absdouble < 0)
								{
									(*it)->SetABSState(true);
									(*it)->brake (0.0);
								}
								else
								{
									(*it)->SetABSState(false);
									(*it)->brake (brakesetting);
								}
							}
							
							/*if (omegadotdot < 0)
							//if (omegadot_cur < 0)
							//if (omegadot_cur < 0 || omegadot_last < 0)
							{
								//braking is occuring and we have hard braking
								
								//cout << brakesetting_min << "," << brakesetting_max << ": " << Gamma << endl;
								
								if (!(*it)->GetABSState())
								{
									if (m_abs_time > abs_period)
									{
										(*it)->SetABSState(true);
										m_abs_time = 0;
									}
										
									(*it)->brake (brakesetting_max);
									//cout << count << ": engage" << endl;
								}
								else
								{
									if (m_abs_time > abs_period)
									{
										(*it)->SetABSState(false);
										m_abs_time = 0;
									}
									
									(*it)->brake (brakesetting_min);
									//cout << count << ": disengage" << endl;
								}
							}
							//else
							else if (m_abs_time > abs_period)
							{
								(*it)->SetABSState(false);
								(*it)->brake (brakesetting);
								m_abs_time = 0;
								//cout << count << ": default disengage" << endl;
							}
							else
							{
								if (!(*it)->GetABSState())
								{
									(*it)->brake (brakesetting_max);
								}
								else
								{
									(*it)->brake (brakesetting_min);
								}
							}*/
							
							/*double thresholdeng = 40.0;
							double thresholddis = 40.0;
							if (deceleration > thresholdeng && !(*it)->GetABSState())
							{
								(*it)->SetABSState(true);
								//cout << count << " ABS engaging: " << maxspindiff << endl;
							}
							
							if (deceleration < thresholddis && (*it)->GetABSState())
							{
								(*it)->SetABSState(false);
								//cout << count << " ABS disengaging: " << maxspindiff << endl;
							}
							
							if ((*it)->GetABSState())
							{
								//cout << count << " ABS engage: " << maxspindiff << endl;
								(*it)->brake (0.0);
							}
							else
							{
								//cout << count << " ABS not engaged: " << maxspindiff << endl;
								(*it)->brake (brakesetting);
							}*/
						}
						/*else
						{
							(*it)->SetABSState(false);
							(*it)->brake (brakesetting);
						}*/
					}
					else
					{
						(*it)->SetABSState(false);
						(*it)->brake (brakesetting);
						//cout << count << ": no-brake disengage" << endl;
					}
				}
				else if (decelerationalgo)
				{
					//only active if brakes commanded past threshold
					if (brakesetting > 0.1)
					{
						double maxspeed = 0;
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							if ((*it2)->rotational_speed() > maxspeed);
								maxspeed = (*it2)->rotational_speed();
						}
						
						//don't engage ABS if all wheels are moving slowly
						if (maxspeed > 6.0)
						{
							//check out rotational deceleration
							double deceleration = ((*it)->last_rotational_speed() - (*it)->rotational_speed())/time;
							/*if (deceleration < 0)
								deceleration = -deceleration;*/
							
							//cout << deceleration << endl;
							
							//only engage if the difference is above thresholdeng, disengage when the difference is under thresholddis
							double thresholdeng = 40.0;
							double thresholddis = 40.0;
							if (deceleration > thresholdeng && !(*it)->GetABSState())
							{
								(*it)->SetABSState(true);
								//cout << count << " ABS engaging: " << maxspindiff << endl;
							}
							
							if (deceleration < thresholddis && (*it)->GetABSState())
							{
								(*it)->SetABSState(false);
								//cout << count << " ABS disengaging: " << maxspindiff << endl;
							}
							
							if ((*it)->GetABSState())
							{
								//cout << count << " ABS engage: " << maxspindiff << endl;
								(*it)->brake (0.0);
							}
							else
							{
								//cout << count << " ABS not engaged: " << maxspindiff << endl;
								(*it)->brake (brakesetting);
							}
						}
						else
						{
							(*it)->SetABSState(false);
							(*it)->brake (brakesetting);
						}
					}
					else
					{
						(*it)->SetABSState(false);
						(*it)->brake (brakesetting);
					}
				}
				else if (xtsalgo)
				{
					//only active if brakes commanded past threshold
					if (brakesetting > 0.1)
					{
						double maxspeed = 0;
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							if ((*it2)->rotational_speed() > maxspeed);
								maxspeed = (*it2)->rotational_speed();
						}
						
						//don't engage ABS if all wheels are moving slowly
						if (maxspeed > 6.0)
						{
							//check tire's speed
							double tirespeed = (*it)->rotational_speed();
							
							cout << count << ": " << tirespeed << endl;
							
							//only engage if the speed is below thresholdeng, disengage when the speed is over thresholddis
							double thresholdeng = 0;
							double thresholddis = 0;
							if (tirespeed < thresholdeng && !(*it)->GetABSState())
							{
								(*it)->SetABSState(true);
								//cout << count << " ABS engaging: " << maxspindiff << endl;
							}
							
							if (tirespeed > thresholddis && (*it)->GetABSState())
							{
								(*it)->SetABSState(false);
								//cout << count << " ABS disengaging: " << maxspindiff << endl;
							}
							
							if ((*it)->GetABSState())
							{
								//cout << count << " ABS engage: " << maxspindiff << endl;
								(*it)->brake (0.0);
							}
							else
							{
								//cout << count << " ABS not engaged: " << maxspindiff << endl;
								(*it)->brake (brakesetting);
							}
						}
						else
						{
							(*it)->SetABSState(false);
							(*it)->brake (brakesetting);
						}
					}
					else
					{
						(*it)->SetABSState(false);
						(*it)->brake (brakesetting);
					}
				}
				else
				{
					//only active if brakes commanded past threshold
					if (brakesetting > 0.1)
					{
						//see if we're spinning slower than the rest of the wheels
						double maxspindiff = 0;
						double myrotationalspeed = (*it)->rotational_speed();
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							//if (it2 != it)
							{
								double spindiff = (*it2)->rotational_speed() - myrotationalspeed;
								if (spindiff > maxspindiff)
									maxspindiff = spindiff;
							}
						}
						
						//only engage if the difference is above thresholdeng, disengage when the difference is under thresholddis
						//cout << maxspindiff << endl;
						double thresholdeng = 10.0;
						double thresholddis = 3.0;
						/*double speedfactor = myrotationalspeed * 0.02;
						if (speedfactor < 1.0)
							speedfactor = 1.0;
						if (speedfactor > 1.0)
							cout << speedfactor << endl;
						if (speedfactor > 2.0)
							speedfactor = 2.0;*/
						if (maxspindiff > thresholdeng && !(*it)->GetABSState())
						{
							(*it)->SetABSState(true);
							//cout << count << " ABS engaging: " << maxspindiff << endl;
						}
						
						if (maxspindiff < thresholddis && (*it)->GetABSState())
						{
							(*it)->SetABSState(false);
							//cout << count << " ABS disengaging: " << maxspindiff << endl;
						}
						
						if ((*it)->GetABSState())
						{
							//cout << count << " ABS engage: " << maxspindiff << endl;
							(*it)->brake (0.0);
						}
						else if (maxspindiff != 0.0)
						{
							//cout << count << " ABS not engaged: " << maxspindiff << endl;
							(*it)->brake (brakesetting);
						}
					}
					else
					{
						(*it)->SetABSState(false);
						(*it)->brake (brakesetting);
					}
				}
				
				if (!mdennyalgo)
					(*it)->SetABSLastState((*it)->GetABSState());
			}
			else
			{
				(*it)->SetABSState(false);
				(*it)->brake (brakesetting);
			}
		}
		
		//cout << count << ": " << brakesetting << endl;

		
		if ((*it)->driven ())
		{
			//do TCS
			if (m_tcs)
			{
				bool accelalgo = false;
				bool errorcorrectalgo = true;
				
				float gasthresh = 0.1;
				
				if (errorcorrectalgo)
				{
					double sp = (*it)->GetTire()->GetSigmaHat(); //ideal slip ratio given tire loading
					//double uhat = (*it)->GetTire()->GetFx(sp)*0.25; //maximum tire friction given ideal slip ratio
					/*if (count == 0)
						cerr << sp - (*it)->slip() << ",";*/
					
					//only active if throttle commanded past threshold
					if (gas > gasthresh)
					{
						//see if we're spinning faster than the rest of the wheels
						double maxspindiff = 0;
						double myrotationalspeed = (*it)->rotational_speed();
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							double spindiff = myrotationalspeed - (*it2)->rotational_speed();
							if (spindiff < 0)
								spindiff = -spindiff;
							if (spindiff > maxspindiff)
								maxspindiff = spindiff;
						}
						
						//don't engage if all wheels are moving at the same rate
						if (maxspindiff > 1.0)
						{
							double error = (*it)->slip() - sp;
							//if (count == 2)	cout << maxspindiff << "," << sp << "," << (*it)->slip() << "," << error << endl;
							//if (count == 2) cerr << (*it)->slip() << ",";
							double thresholdeng = 0.0;
							double thresholddis = -sp/2.0;
	
							if (error > thresholdeng && !(*it)->GetTCSState())
							{
								(*it)->SetTCSState(true);
								//cout << count << " ABS engaging: " << maxspindiff << endl;
							}
							
							if (error < thresholddis && (*it)->GetTCSState())
							{
								(*it)->SetTCSState(false);
								//cout << count << " ABS disengaging: " << maxspindiff << endl;
							}
							
							if ((*it)->GetTCSState())
							{
								//cout << count << " ABS engage: " << maxspindiff << endl;
								//(*it)->brake (1.0);
								//gas = 0.0;
								//gas = m_last_gas - time*error;
								float clutch = m_clutch_key_control.value ();
								if (clutch > 1) clutch = 1;
								if (clutch < 0) clutch = 0;
								gas = gas - error*10.0*clutch;
								if (gas < 0) gas = 0;
								if (gas > 1) gas = 1;
								//if (count == 3)	cout << 1 << endl;
							}
							else
							{
								//cout << count << " ABS not engaged: " << maxspindiff << endl;
								//(*it)->brake (brakesetting);
								//if (count == 3)	cout << 0 << endl;
							}
						}
						/*else
						{
							(*it)->SetTCSState(false);
						}*/
					}
					else
					{
						(*it)->SetTCSState(false);
					}
				}
				else if (accelalgo)
				{
					if (gas > gasthresh && (*it)->last_rotational_speed() != 0.0 && (*it)->rotational_speed() != 0.0)// && engine()->rotational_speed() > 200.0f)
					{
						double acceleration = ((*it)->rotational_speed()-(*it)->last_rotational_speed())/time;
						
						cout << acceleration << ": " << (*it)->rotational_speed() << "," << (*it)->last_rotational_speed() << endl;
						
						float tcseng = 60.0;
						float tcsdis = 40.0;
						
						if (acceleration > tcseng && !(*it)->GetTCSState())
						{
							(*it)->SetTCSState(true);
						}
						
						if (acceleration < tcsdis && (*it)->GetTCSState())
						{
							(*it)->SetTCSState(false);
						}
						
						double brakefactor = (acceleration) * 0.01;
						if (brakefactor < 0) brakefactor = 0;
						if (brakefactor > 1) brakefactor = 1;
						
						if ((*it)->GetTCSState())
						{
							(*it)->brake (brakefactor);
						}
						if ((*it)->GetTCSState() && (*it)->GetTCSLastState())
						{
							//gas = m_last_gas - (time*brakefactor);
						}
						
						if (!(*it)->GetTCSState() && (*it)->GetTCSLastState())
						{
							//gas = m_last_gas;
						}
					}
					else
						(*it)->SetTCSState(false);
				}
				else
				{
					//only apply TCS if the gas pedal is depressed past a threshold
					float rpm_start = 150.0f;
					float rpm_end = 300.0f;
					bool use_rpm = false;
					bool use_clutch = true;
					
					if (gas > gasthresh && (!use_rpm || engine()->rotational_speed() > rpm_start) && (!use_clutch || m_clutch_key_control.value () > 0.0))
					{
						//see if we're spinning faster than the rest of the wheels
						double maxspindiff = 0;
						double myrotationalspeed = (*it)->rotational_speed();
						for (std::vector <Wheel*>::iterator it2 = m_wheels.begin (); it2 != m_wheels.end (); it2++)
						{
							double spindiff = myrotationalspeed - (*it2)->rotational_speed();
							if (spindiff < 0)
								spindiff = -spindiff;
							if (spindiff > maxspindiff)
								maxspindiff = spindiff;
						}
						
						float tcseng = 7.0;
						//float tcsdis = 5.0;
						float tcsdis = 7.0;
						
						if (maxspindiff > tcseng && !(*it)->GetTCSState())
						{
							(*it)->SetTCSState(true);
							//cout << count << " TCS engaging: " << maxspindiff << endl;
						}
						
						if (maxspindiff < tcsdis && (*it)->GetTCSState())
						{
							(*it)->SetTCSState(false);
							//cout << count << " TCS disengaging: " << maxspindiff << endl;
						}
						
						if ((*it)->GetTCSState())
						{
							//cout << count << " TCS engage: " << maxspindiff << endl;
							
							if (!use_rpm)
							{
								if (use_clutch)
									(*it)->brake (m_clutch_key_control.value ());
								else
									(*it)->brake (1.0);
							}
							else
							{
								if (engine()->rotational_speed() > rpm_end)
									(*it)->brake (1.0);
								else
									(*it)->brake((engine()->rotational_speed() - rpm_start)/(rpm_end - rpm_start));
							}
							
							//gas = m_last_gas - 0.1;
							//gas -= 0.1;
						}
						if ((*it)->GetTCSState() && (*it)->GetTCSLastState())
						{
							//gas = 0;
							//gas = 1.0-((engine()->rotational_speed() - rpm_start)/(rpm_end - rpm_start));
							
							if (use_rpm)
								gas = m_last_gas - time*((engine()->rotational_speed() - rpm_start)/(rpm_end - rpm_start));
							else
							{
								//gas = m_last_gas - time;
								
								/*if (engine()->rotational_speed() > engine()->last_rotational_speed())
									gas = (engine()->rotational_speed() - engine()->last_rotational_speed());
								if (gas < 0) gas = 0;
								if (gas > 1) gas = 1;
								gas = 1.0 - gas;
								cout << (engine()->rotational_speed() - engine()->last_rotational_speed()) << endl;*/
								/*if (engine()->rotational_speed() > engine()->last_rotational_speed())
									gas = m_last_gas - 0.02*(engine()->rotational_speed() - engine()->last_rotational_speed());*/
								if (use_clutch)
									gas = m_last_gas - time*m_clutch_key_control.value ();
								else
									gas = m_last_gas - time;
								if (gas < 0) gas = 0;
								if (gas > 1) gas = 1;
								//cout << (engine()->rotational_speed() - engine()->last_rotational_speed()) << endl;
							}
						}
						
						if (!(*it)->GetTCSState() && (*it)->GetTCSLastState())
						{
							//gas = m_last_gas;
						}
					}
					else
						(*it)->SetTCSState(false);
				}
				
				(*it)->SetTCSLastState((*it)->GetTCSState());
			}
			else
				(*it)->SetTCSState(false);
			
			// Apply the driving torque.
			(*it)->drive_torque (mp_drivetrain->torque ((*it)->side ()));
			
			//cout << mp_drivetrain->torque ((*it)->side ()) << endl;
			
			if ((*it)->side () == RIGHT)
			right_wheel_speed = (*it)->rotational_speed ();
			else if ((*it)->side () == LEFT)
			left_wheel_speed = (*it)->rotational_speed ();
		}

		// Sum the sliding speeds of the tires.
		m_slide += (*it)->slide ();
	}
	
	// Update the drivetrain.
	mp_drivetrain->input (gas,
		m_clutch_key_control.value (),
		left_wheel_speed, right_wheel_speed);
	
	m_last_gas = gas;

  // Propagate the base class.
  /*mp_drivetrain->find_forces ();
  m_chassis.find_forces ();

  mp_drivetrain->propagate (time / 2.0);
  m_chassis.propagate (time / 2.0);

  mp_drivetrain->find_forces ();
  m_chassis.find_forces ();

  mp_drivetrain->rewind ();
  m_chassis.rewind ();

  mp_drivetrain->propagate (time);
  m_chassis.propagate (time);
  
  m_chassis.end_timestep ();
  
  m_distance_traveled += 
    m_chassis.rotate_in (m_chassis.cm_velocity ()) [0] * time;*/
  
  int iterations = 1;
  
  int i;
  for (i = 0; i < iterations; i++)
  {
	  m_chassis.find_forces ();
  	  m_chassis.propagate (time);
	  mp_drivetrain->find_forces ();
	  mp_drivetrain->propagate (time/(float) iterations);
	  m_chassis.end_timestep();
	  m_distance_traveled += 
    	m_chassis.rotate_in (m_chassis.cm_velocity ()) [0] * (time/(float)iterations);
  }
}

// Change the steering angle to ANGLE with a time constant of TIME.
void Vamos_Body::
Car::steer (double angle, double time)
{
  double steer_sign = (angle < 0.0) ? -1.0 : 1.0;

  // Apply the non-linearity.
  angle = steer_sign * std::pow (std::abs (angle), m_steer_exponent);

  // Set the maximum angle and speed sensitivity.
  double sens = 1.0 
    / (1.0 + 1.0e-4 * m_steer_speed_sensitivity 
       * m_chassis.cm_velocity ().dot (m_chassis.cm_velocity ()));
  angle *= m_max_steer_angle * sens;
  //printf("%f\n", m_max_steer_angle);
  m_steer_key_control.target (angle, time);
}

// Change the throttle to FACTOR with a time constant of TIME.
void Vamos_Body::
Car::gas (double factor, double time)
{
  m_gas_key_control.target (factor, time);
}

// Change the brakes to FACTOR with a time constant of TIME.
void Vamos_Body::
Car::brake (double factor, double time)
{
  m_brake_key_control.target (factor, time);
}

// Change the handbrakes to FACTOR with a time constant of TIME.
void Vamos_Body::
Car::handbrake (double factor, double time)
{
  m_handbrake_key_control.target (factor, time);
}

// Pan the view.
void Vamos_Body::
Car::pan (double factor, double time)
{
  m_pan_key_control.target (factor * m_pan_angle, time / m_pan_angle);
}

// Shift to the next lower gear.  The chosen gear is returned.
int Vamos_Body::
Car::shift_down ()
{
  return shift (mp_drivetrain->transmission ()->gear () - 1);
}

// Shift to the next higher gear.  The chosen gear is returned.
int Vamos_Body::
Car::shift_up ()
{
  return shift (mp_drivetrain->transmission ()->gear () + 1);
}

// Shift to GEAR.  The chosen gear is returned.
int Vamos_Body::
Car::shift (int gear)
{
  // Do the shift if GEAR is accessible.
  if ((gear <= mp_drivetrain->transmission ()->forward_gears ())
      && (-gear <= mp_drivetrain->transmission ()->reverse_gears ()))
  {
    m_shift_pending = true;
    m_shift_timer = 0.0;
    m_last_gear = mp_drivetrain->transmission ()->gear ();
    m_new_gear = gear;
  }
      
  return m_new_gear;
}

void Vamos_Body::
Car::clutch (double factor, double time)
{
  m_clutch_key_control.target (factor, time, 0.0);
}

// Engage the clutch with a time constant of TIME.
void Vamos_Body::
Car::engage_clutch (double time)
{
  // Wait for the shift timer.
  double delay = m_shift_delay - m_shift_timer;
  m_clutch_key_control.target (1.0, time, delay);
}

// Disengage the clutch with a time constant of TIME.
void Vamos_Body::
Car::disengage_clutch (double time)
{
  // Wait for the shift timer.
  double delay = m_shift_delay - m_shift_timer;
  m_clutch_key_control.target (0.0, time, delay);
}

// Return the pointer to the WHEEL_INDEXth wheel.
Vamos_Body::Wheel* Vamos_Body::Car::
wheel (int wheel_index) const
{
  return m_wheels [wheel_index];
}

// Set the front brake bias to BIAS.
void Vamos_Body::
Car::brake_bias (double bias)
{
  if ((bias >= 0.0) || (bias <= 1.0))
    {
      m_front_brake_bias = bias;
    }
}

// Return the position of the viewpont.
Three_Vector Vamos_Body::
Car::view_position () const
{
  //return m_chassis.position () + m_chassis.rotate_out (m_driver_view);
	return m_driver_view;
}

void Vamos_Body::
Car::start_engine ()
{
  mp_drivetrain->engine ()->start ();
  m_clutch_key_control.end ();
}

// Restore the initial conditions and then set the position to
// POSITION and the orientation to ORIENTATION.
void Vamos_Body::Car::
reset (const Three_Vector& position, const Three_Matrix& orientation)
{
  m_chassis.reset (position, orientation);
  private_reset ();
}

// Restore the initial conditions.
void Vamos_Body::Car::
reset ()
{
  m_chassis.reset ();
  private_reset ();
}

// Perform operations common to both reset() methods. 
void Vamos_Body::Car::
private_reset ()
{
  mp_drivetrain->reset ();
  shift (0);
  start_engine ();
}

void Vamos_Body::
Car::drivetrain (Drivetrain* drive)
{
  assert (drive != 0);
  delete mp_drivetrain;
  mp_drivetrain = drive;
}

// Return true if the position is within the crash box.
bool Vamos_Body::
Car::collision (const Three_Vector& position) const
{
  return m_crash_box.within (m_chassis.transform_in (position));
}

// Return true if the position is within the crash box.
bool Vamos_Body::
Car::Crash_Box::within (const Three_Vector& position) const
{
  return (position [0] < front) && (position [0] > back)
    && (position [1] < left) && (position [1] > right)
    && (position [2] < top) && (position [2] > bottom);
}

//* Class Car_Reader

//** Constructor
/*
Vamos_Body::
Car_Reader::Car_Reader (std::string data_dir, 
                        std::string car_file, 
                        Vamos_Body::Car* car) 
  : m_first_model_for_this_wheel (true),
    m_data_dir (data_dir),
    mp_car (car),
    mp_tachometer (0),
    mp_speedometer (0),
    mp_fuel_gauge (0),
    mp_gear_indicator (0),
    mp_steering_wheel (0),
    m_tachometer_type ("dial"),
    m_speedometer_type ("dial"),
    m_fuel_gauge_type ("dial")
{
  read (data_dir + car_file);
}


Vamos_Body::
Car_Reader::~Car_Reader ()
{
  for (std::vector <Model_Info*>::iterator it = m_models.begin ();
       it != m_models.end ();
       it++)
    {
      delete *it;
    }
}


void Vamos_Body::
Car_Reader::on_start_tag (const Vamos_Geometry::XML_Tag& tag)
{
  //  std::cout << "start " << tag.get_label () << std::endl;
  m_tag = tag.get_label ();
  m_path = m_path + '/' + m_tag;

  const Vamos_Geometry::XML_Tag::Attribute_List& 
    attribs = tag.get_attributes ();

  if ((m_path == "/car/exterior-model") || (m_path == "/car/interior-model"))
    {
      m_strings.clear ();
      m_strings.resize (2);
      m_doubles.resize (1);
      m_doubles [0] = 1.0;
      m_vectors.clear ();
      m_vectors.resize (2);
    }
  else if (m_tag == "view")
    {
      m_vectors.clear ();
      m_doubles.clear ();
      m_doubles.resize (4);
    }
  else if (m_tag == "mirror")
    {
      m_vectors.clear ();
      m_doubles.resize (6);
      m_strings.resize (1);
    }
  else if (m_tag == "steering")
    {
      m_doubles.clear ();
      m_doubles.resize (3);
    }
  else if (m_tag == "dashboard")
    {
      m_ints.clear ();
      m_ints.resize (1);
      m_doubles.clear ();
      m_doubles.resize (12);
      m_strings.clear ();
      m_strings.resize (2);
      m_bools.clear ();
      m_bools.resize (1);
      m_vectors.clear ();
      m_vectors.resize (1);
      m_points.clear ();
      ma_mirrors.clear ();
    }
  else if (m_tag == "on-steering-wheel")
    {
      m_bools [0] = true;
    }
  else if (m_tag == "tachometer")
    {
      if (attribs.size () == 0)
        {
          m_tachometer_type = "dial";
        }
      else
        {
          m_tachometer_type = attribs [0].value;
        }
    }
  else if (m_tag == "speedometer")
    {
      if (attribs.size () == 0)
        {
          m_speedometer_type = "dial";
        }
      else
        {
          m_speedometer_type = attribs [0].value;
        }
    }
  else if (m_tag == "fuel-gauge")
    {
      if (attribs.size () == 0)
        {
          m_fuel_gauge_type = "dial";
        }
      else
        {
          m_fuel_gauge_type = attribs [0].value;
        }
    }
  else if (m_tag == "engine")
    {
      m_doubles.clear ();
      m_doubles.resize (13);
      m_vectors.clear ();
      m_strings.clear ();
      m_strings.resize (1);
    }
	else if (m_tag == "torque-curve")
    {
      m_points.clear ();
      m_bools [0] = true;
      std::istringstream is (attribs [0].value.c_str ());
      is >> m_doubles [9];
    }

  else if (m_tag == "clutch")
    {
      m_doubles.clear ();
    }
  else if (m_tag == "transmission")
    {
      m_doubles.clear ();
      m_gears.clear ();
    }
  else if (m_tag == "differential")
    {
      m_doubles.clear ();
    }
  else if (m_tag == "fuel-tank")
    {
      m_doubles.clear ();
      m_vectors.clear ();
    }
  else if (m_tag == "contact-point")
    {
      m_doubles.resize (3);
      m_strings.resize (1);
      m_vectors.clear ();
    }
  else if (m_tag == "particle")
    {
      m_doubles.resize (1);
      m_vectors.clear ();
    }
  else if (m_tag == "drag")
    {
      m_doubles.resize (2);
      m_vectors.clear ();
    }
  else if (m_tag == "wing")
    {
      m_doubles.resize (6);
      m_vectors.clear ();
    }
  else if (m_tag == "wheel")
    {
      if (m_doubles.size () != 23)
        {
          m_doubles.resize (23);
          m_doubles [8] = 0.0;
          m_doubles [20] = 0.0;
          m_long_parameters.resize (11);
          m_trans_parameters.resize (15);
          m_align_parameters.resize (18);
          m_strings.resize (3);
          m_vectors.resize (5);
        }
      m_strings [0] = attribs [0].value;
      m_strings [1] = attribs [1].value;
      m_bools.clear ();
      m_bools.resize (2);
      m_first_model_for_this_wheel = true;
    }
  else if (m_tag == "steered")
    {
      m_bools [0] = true;
    }
  else if (m_tag == "driven")
    {
      m_bools [1] = true;
    }
}

void Vamos_Body::
Car_Reader::on_end_tag (const Vamos_Geometry::XML_Tag& tag)
{
  //  std::cout << "end " << tag.get_label () << std::endl;
  m_tag = tag.get_label ();

  if ((m_path == "/car/exterior-model") 
      && (m_strings [0] != ""))
    {
      mp_car->exterior_model ("cars/" + m_strings [0],
                              m_doubles [0], m_vectors [0], m_vectors [1]);
    }
  else if ((m_path == "/car/interior-model") 
           && (m_strings [0] != ""))
    {
      mp_car->interior_model (m_data_dir + "cars/" + m_strings [0],
                              m_doubles [0], m_vectors [0], m_vectors [1]);
    }
  else if (m_tag == "view")
    {
      mp_car->set_view (m_vectors [0], m_doubles [0], 
                        m_doubles [1], m_doubles [2], m_doubles [3]);
    }
  else if (m_tag == "mirror")
    {
      mp_car->add_rear_view (m_vectors [0], m_doubles [0], m_doubles [1],
                             m_doubles [2], m_doubles [3], 
                             m_doubles [4], m_doubles [5],
                             m_data_dir + "textures/" + m_strings [0]);
    }
  else if (m_tag == "steering")
    {
      mp_car->max_steer_angle (m_doubles [0]);
      //mp_car->max_steer_angle (60.0);
      mp_car->steer_exponent (m_doubles [1]);
      mp_car->steer_speed_sensitivity (m_doubles [2]);
    }
  else if (m_tag == "mirror-frame")
    {
      ma_mirrors.
        push_back (new Facade (m_doubles [0], m_doubles [1],
                               m_doubles [2], m_doubles [3],
                               m_doubles [4],
                               m_data_dir + "textures/" + m_strings [0]));
    }
  else if (m_tag == "tachometer")
    {
      if (m_tachometer_type == "LED")
        {
          mp_tachometer = 
            new LED_Gauge (m_doubles [0], m_doubles [1], m_doubles [2],
                           m_doubles [3], m_ints [0], 
                           m_doubles [4], m_doubles [5],
                           m_data_dir + "textures/" + m_strings [0],
                           m_bools [0]);
          m_bools [0] = false;
        }
      else if (m_tachometer_type == "digital")
        {
          mp_tachometer = 
            new Digital_Gauge (m_doubles [0], m_doubles [1],
                               m_doubles [2], m_doubles [3],
                               m_doubles [4], m_ints [0],
                               m_data_dir + "textures/" + m_strings [0], 
                               m_bools [0]);
          m_bools [0] = false;
        }
      else
        {
          mp_tachometer = new Dial (m_doubles [0], m_doubles [1],
                                    m_doubles [2], m_doubles [3],
                                    m_doubles [4], m_doubles [5],
                                    m_doubles [6], m_doubles [7],
                                    m_data_dir + "textures/" + m_strings [0], 
                                    m_data_dir + "textures/" + m_strings [1]);
        }
    }
  else if (m_tag == "speedometer")
    {
      if (m_speedometer_type == "digital")
        {
          mp_speedometer = 
            new Digital_Gauge (m_doubles [0], m_doubles [1],
                               m_doubles [2], m_doubles [3],
                               m_doubles [4], m_ints [0],
                               m_data_dir + "textures/" + m_strings [0], 
                               m_bools [0]);
          m_bools [0] = false;
        }
      else
        {
          mp_speedometer = new Dial (m_doubles [0], m_doubles [1],
                                     m_doubles [2], m_doubles [3],
                                     m_doubles [4], m_doubles [5],
                                     m_doubles [6], m_doubles [7],
                                     m_data_dir + "textures/" + m_strings [0], 
                                     m_data_dir + "textures/" + m_strings [1]);
        }
    }
  else if (m_tag == "fuel-gauge")
    {
      if (m_fuel_gauge_type == "digital")
        {
          mp_fuel_gauge = 
            new Digital_Gauge (m_doubles [0], m_doubles [1],
                               m_doubles [2], m_doubles [3],
                               m_doubles [4], m_ints [0],
                               m_data_dir + "textures/" + m_strings [0], 
                               m_bools [0]);
          m_bools [0] = false;
        }
      else
        {
          mp_fuel_gauge = new Dial (m_doubles [0], m_doubles [1],
                                    m_doubles [2], m_doubles [3],
                                    m_doubles [4], m_doubles [5],
                                    m_doubles [6], m_doubles [7],
                                    m_data_dir + "textures/" + m_strings [0], 
                                    m_data_dir + "textures/" + m_strings [1]);
        }
    }
  else if (m_tag == "gear-indicator")
    {
      mp_gear_indicator = 
        new Gear_Indicator (m_doubles [0], m_doubles [1],
                            m_doubles [2], m_doubles [3],
                            m_doubles [4], m_ints [0],
                            m_data_dir + "textures/" + m_strings [0],
                            m_bools [0]);
      m_bools [0] = false;
    }
  else if (m_tag == "gear-shift")
    {
      mp_gear_indicator = 
        new Gear_Shift (m_doubles [0], m_doubles [1],
                        m_doubles [2], m_doubles [3],
                        m_doubles [4], m_vectors [0],
                        m_points,
                        m_data_dir + "textures/" + m_strings [0],
                        m_data_dir + "textures/" + m_strings [1]);
    }
  else if (m_tag == "steering-wheel")
    {
      mp_steering_wheel = 
        new Steering_Wheel (m_doubles [0], m_doubles [1],
                            m_doubles [2], m_doubles [3],
                            m_doubles [4], m_doubles [5],
                            m_doubles [6], m_doubles [7],
                            m_data_dir + "textures/" + m_strings [0]);
    }
  else if (m_tag == "dashboard")
    {
      Dashboard* dash = new Dashboard (m_doubles [8], m_doubles [9],
                                       m_doubles [10], m_doubles [11]);
      for (std::vector <Facade*>::iterator it = ma_mirrors.begin ();
           it != ma_mirrors.end ();
           it++)
        {
          dash->add_facade (*it);
        }
      dash->add_tachometer (mp_tachometer);
      dash->add_speedometer (mp_speedometer);
      dash->add_fuel_gauge (mp_fuel_gauge);
      dash->add_gear_indicator (mp_gear_indicator);
      dash->add_steering_wheel (mp_steering_wheel);
      mp_car->dashboard (dash);
    }
  else if (m_path ==  "/car/dashboard/extras")
    {
      mp_car->show_dashboard_extras (true);
    }
  else if (m_tag == "engine")
    {
      mp_engine = new Engine (m_doubles [0], m_vectors [0], m_doubles [1], 
                              m_doubles [2], m_doubles [3], m_doubles [4], 
                              m_doubles [5], m_doubles [6], m_doubles [7],
                              m_doubles [8]);

		if (m_bools [0])
        {
          mp_engine->set_torque_curve (m_points);
          mp_engine->set_friction (m_doubles [9]);
        }

      mp_car->engine_sound (m_data_dir + "sounds/" + m_strings [0], 
                            m_doubles [9], m_doubles [10],
                            m_doubles [11], m_doubles [12]);
    }

   else if (m_tag == "clutch")
    {
      if (m_doubles.size () != 4)
        {
          error ("clutch requires 4 arguments");
        }
      mp_clutch = new Clutch (m_doubles [0], m_doubles [1], m_doubles [2], 
                              m_doubles [3]);
    }

  else if (m_tag == "transmission")
    {
      if (m_doubles.size () == 4)
        {
          mp_transmission = new Transmission (int (m_doubles [0]),
                                              m_doubles [1],
                                              m_doubles [2]);
          mp_car->shift_delay (m_doubles [3]);
        }
      else
        {
          mp_transmission = new Transmission;
          for (std::vector <std::pair <int, double> >::const_iterator 
                 it = m_gears.begin ();
               it != m_gears.end ();
               it++)
            {
              mp_transmission->gear_ratio (it->first, it->second);
            } 
          mp_car->shift_delay (m_doubles [1]);
        }
    }
  
  else if (m_tag == "differential")
    {
      if (m_doubles.size () != 2)
        {
          error ("differential requires 2 arguments");
        }
      mp_differential = new Differential (m_doubles [0], m_doubles [1]);
    }

  else if (m_tag == "drivetrain")
    {
      delete mp_car->mp_drivetrain;
      mp_car->mp_drivetrain = 
        new Drivetrain (mp_engine, mp_clutch, 
                        mp_transmission, mp_differential);
    }

  else if (m_tag == "fuel-tank")
    {
      if ((m_doubles.size () != 3) || (m_vectors.size () != 1))
        {
          error ("fuel tank requires 1 or 3 elements");
        }
      delete mp_car->mp_fuel_tank;
      mp_car->mp_fuel_tank = new Fuel_Tank (m_vectors [0], m_doubles [0],
                                            m_doubles [1], m_doubles [2]);
    }

  else if (m_tag == "car")
    {
      mp_car->chassis ().particles ().
        push_back (mp_car->mp_drivetrain->engine ());
      mp_car->chassis ().particles ().push_back (mp_car->mp_fuel_tank);
    }

  else if (m_tag == "particle")
    {
      mp_car->chassis ().particles ().
        push_back (new Particle (m_doubles [0], m_vectors [0]));
    }
  else if (m_tag == "contact-point")
    {
      Material::Material_Type material = Material::UNKNOWN;
      if (m_strings [0] == "rubber")
        material = Material::RUBBER;
      else if (m_strings [0] == "metal")
        material = Material::METAL;

      mp_car->chassis ().particles ().
        push_back (new Contact_Point (m_doubles [0], m_vectors [0], material,
                                      m_doubles [1], m_doubles [2]));
    }
  else if (m_tag == "drag")
    {
      mp_car->chassis ().particles ().push_back (new Drag (m_vectors [0], 
                                               m_doubles [0], 
                                               m_doubles [1]));
    }
  else if (m_tag == "wing")
    {
      mp_car->chassis ().particles ().
        push_back (new Wing (m_vectors [0], m_doubles [0], 
                             m_doubles [1], m_doubles [2], 
                             m_doubles [3], m_doubles [4]));
    }
  else if (m_path == "/car/wheel/suspension/model")
    {
      if (m_first_model_for_this_wheel)
        {
          m_models.clear ();
          m_first_model_for_this_wheel = false;
        }
      m_models.push_back (new Model_Info (m_strings [2], m_doubles [21],
                                          m_vectors [3], m_vectors [4]));
    }
  else if (m_tag == "wheel")
    {
      Side side = (m_strings [0] == "left") ? LEFT : RIGHT;
      Suspension* suspension =
        new Suspension (m_vectors [1], m_vectors [2], side, m_doubles [0],
                        m_doubles [1], m_doubles [2], m_doubles [3],
                        m_doubles [4]);
      suspension->camber (m_doubles [5]);
      suspension->caster (m_doubles [6]);
      suspension->toe (m_doubles [7]);
      if (m_doubles [8] != 0.0)
        {
          Suspension* other = static_cast <Suspension*> 
            (*(mp_car->chassis ().particles ().end () - 2));
          assert (other != 0);
          suspension->anti_roll (other, m_doubles [8]);
          m_doubles [8] = 0.0;
        }
      for (std::vector <Model_Info*>::iterator it = m_models.begin ();
           it != m_models.end ();
           it++)
        {
          suspension->set_model ("cars/" + (*it)->file,
                                 (*it)->scale, 
                                 (*it)->translate,
                                 (*it)->rotate);
        }
      mp_car->chassis ().particles ().push_back (suspension->hinge ());
      mp_car->chassis ().particles ().push_back (suspension);

      Tire_Friction friction (m_long_parameters, m_trans_parameters,
                              m_align_parameters);
      Tire tire (m_doubles [9], m_doubles [10], m_doubles [11], friction,
                 m_doubles [12]);

      double bias = m_doubles [17];
      if (m_strings [1] == "rear")
          bias = 1.0 - bias;
      Brake brake (m_doubles [13], m_doubles [14], m_doubles [15], 
                   m_doubles [16], bias);
 
      Wheel* wheel = new Wheel (m_doubles [18], m_vectors [0], 
                                m_doubles [22], m_doubles [20], m_doubles [19],
                                suspension, tire, brake,
                                m_bools [0], m_bools [1], side);
      mp_car->chassis ().particles ().push_back (wheel);
      mp_car->m_wheels.push_back (wheel);
      if (m_slow_model != "")
        {
          std::string stator_path;
          if (m_stator_model != "")
            {
              stator_path = m_data_dir + "cars/" + m_stator_model;
            }
          	if (m_strings [1] == "rear")
			{
          wheel->set_models (settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_rear.joe"),
                             settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_rear.joe"),
                             m_transition,
                             stator_path, m_stator_offset, 
                             m_scale, m_translation, m_rotation);
			}
			else
			{
				wheel->set_models (settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_front.joe"),
                             settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_front.joe"),
                             m_transition,
                             stator_path, m_stator_offset, 
                             m_scale, m_translation, m_rotation);
			}
        }
    }

  m_path = m_path.substr (0, m_path.find_last_of ("/"));
}

void Vamos_Body::
Car_Reader::on_data (std::string data_string)
{
  std::string data = remove_leading_space (data_string);
  if (data.size () == 0)
    {
      return;
    }
  std::istringstream is (data.c_str ());

  if (m_path == "/car/dashboard/position")
    {
      char delim;
      is >> delim >> m_doubles [8] >> delim >> m_doubles [9] 
         >> delim >> m_doubles [10];
    }
  else if (m_path == "/car/dashboard/tilt")
    {
      is >> m_doubles [11];
    }
  else if ((m_path == "/car/dashboard/mirror-frame/position")
           || (m_path == "/car/dashboard/tachometer/position")
           || (m_path == "/car/dashboard/speedometer/position")
           || (m_path == "/car/dashboard/fuel-gauge/position")
           || (m_path == "/car/dashboard/gear-indicator/position")
           || (m_path == "/car/dashboard/gear-shift/position")
           || (m_path == "/car/dashboard/steering-wheel/position"))
    {
      char delim;
      is >> delim >> m_doubles [0] >> delim >> m_doubles [1] 
         >> delim >> m_doubles [2];
    }
  else if ((m_path == "/car/dashboard/tachometer/radius")
           || (m_path == "/car/dashboard/tachometer/width")
           || (m_path == "/car/dashboard/speedometer/radius")
           || (m_path == "/car/dashboard/fuel-gauge/radius")
           || (m_path == "/car/dashboard/steering-wheel/radius"))
    {
      is >> m_doubles [3];
    }
  else if (m_path == "/car/dashboard/tachometer/elements")
    {
      is >> m_ints [0];
    }
  else if (m_path == "/car/dashboard/tachometer/range")
    {
      char delim;
      is >> delim >> m_doubles [4] >> delim >> m_doubles [5];
    }
  else if ((m_path == "/car/dashboard/tachometer/min")
           || (m_path == "/car/dashboard/speedometer/min")
           || (m_path == "/car/dashboard/fuel-gauge/min")
           || (m_path == "/car/dashboard/steering-wheel/min"))
    {
      char delim;
      is >> delim >> m_doubles [4] >> delim >> m_doubles [5];
    }
  else if ((m_path == "/car/dashboard/tachometer/max")
           || (m_path == "/car/dashboard/speedometer/max")
           || (m_path == "/car/dashboard/fuel-gauge/max")
           || (m_path == "/car/dashboard/steering-wheel/max"))
    {
      char delim;
      is >> delim >> m_doubles [6] >> delim >> m_doubles [7];
    }
  else if (m_path == "/car/dashboard/gear-shift/rotation")
    {
      is >> m_vectors [0];
    }
  else if (m_path == "/car/dashboard/gear-shift/stick-positions")
    {
      Two_Point point;
      while (is >> point)
        {
          m_points.push_back (point);
        }
    }
  else if ((m_path == "/car/dashboard/mirror-frame/image")
           || (m_path == "/car/dashboard/tachometer/face")
           || (m_path == "/car/dashboard/tachometer/image")
           || (m_path == "/car/dashboard/speedometer/face")
           || (m_path == "/car/dashboard/speedometer/image")
           || (m_path == "/car/dashboard/fuel-gauge/face")
           || (m_path == "/car/dashboard/fuel-gauge/image")
           || (m_path == "/car/dashboard/gear-indicator/image")
           || (m_path == "/car/dashboard/gear-shift/gate")
           || (m_path == "/car/dashboard/steering-wheel/image"))
    {
      is >> m_strings [0];
    }
  else if ((m_path == "/car/dashboard/tachometer/needle")
           || (m_path == "/car/dashboard/speedometer/needle")
           || (m_path == "/car/dashboard/fuel-gauge/needle")
           || (m_path == "/car/dashboard/gear-shift/stick"))
    {
      is >> m_strings [1];
    }
  else if ((m_path == "/car/dashboard/mirror-frame/size")
           || (m_path == "/car/dashboard/gear-indicator/size")
           || (m_path == "/car/dashboard/gear-shift/size")
           || (m_path == "/car/dashboard/tachometer/size")
           || (m_path == "/car/dashboard/speedometer/size")
           || (m_path == "/car/dashboard/fuel-gauge/size"))
    {
      char delim;
      is >> delim >> m_doubles [3] >> delim >> m_doubles [4];
    }
  else if ((m_path == "/car/dashboard/gear-indicator/numbers")
           || (m_path == "/car/dashboard/tachometer/places")
           || (m_path == "/car/dashboard/speedometer/places")
           || (m_path == "/car/dashboard/fuel-gauge/places"))
    {
      is >> m_ints [0];
    }

  // Particle
  else if (m_path == "/car/particle/mass")
    is >> m_doubles [0];

  // Contact Point
  else if (m_path == "/car/contact-point/mass")
    is >> m_doubles [0];
  else if (m_path == "/car/contact-point/material")
    is >> m_strings [0];
  else if (m_path == "/car/contact-point/friction")
    is >> m_doubles [1];
  else if (m_path == "/car/contact-point/restitution")
    is >> m_doubles [2];

  // Drag
  else if (m_path == "/car/drag/frontal-area")
    is >> m_doubles [0];
  else if (m_path == "/car/drag/drag-coefficient")
    is >> m_doubles [1];

  // Wing
  else if (m_path == "/car/wing/frontal-area")
    is >> m_doubles [0];
  else if (m_path == "/car/wing/drag-coefficient")
    is >> m_doubles [1];
  else if (m_path == "/car/wing/surface-area")
    is >> m_doubles [2];
  else if (m_path == "/car/wing/lift-coefficient")
    is >> m_doubles [3];
  else if (m_path == "/car/wing/efficiency")
    is >> m_doubles [4];

  // Wheel
  else if (m_path == "/car/wheel/position")
    is >> m_vectors [0];
  else if (m_path == "/car/wheel/mass")
    is >> m_doubles [18];
  else if (m_path == "/car/wheel/restitution")
    is >> m_doubles [19];
  else if (m_path == "/car/wheel/roll-height")
    is >> m_doubles [20];

  // Suspension
  else if (m_path == "/car/wheel/suspension/model/file")
    is >> m_strings [2];
  else if (m_path == "/car/wheel/suspension/model/scale")
    is >> m_doubles [21];
  else if (m_path == "/car/wheel/suspension/model/translate")
    is >> m_vectors [3];
  else if (m_path == "/car/wheel/suspension/model/rotate")
    is >> m_vectors [4];
  else if (m_path == "/car/wheel/suspension/position")
    is >> m_vectors [1];
  else if (m_path == "/car/wheel/suspension/hinge")
    is >> m_vectors [2];
  else if (m_path == "/car/wheel/suspension/spring-constant")
    is >> m_doubles [0];
  else if (m_path == "/car/wheel/suspension/bounce")
    is >> m_doubles [1];
  else if (m_path == "/car/wheel/suspension/rebound")
    is >> m_doubles [2];
  else if (m_path == "/car/wheel/suspension/travel")
    is >> m_doubles [3];
  else if (m_path == "/car/wheel/suspension/max-compression-velocity")
    is >> m_doubles [4];
  else if (m_path == "/car/wheel/suspension/camber")
    is >> m_doubles [5];
  else if (m_path == "/car/wheel/suspension/caster")
    is >> m_doubles [6];
  else if (m_path == "/car/wheel/suspension/toe")
    is >> m_doubles [7];
  else if (m_path == "/car/wheel/suspension/anti-roll")
    is >> m_doubles [8];

  // Tire
  else if (m_path == "/car/wheel/tire/radius")
    is >> m_doubles [9];
  else if (m_path == "/car/wheel/tire/offset")
    is >> m_doubles [22];
  else if (m_path == "/car/wheel/tire/rolling-resistance")
    {
      char delim;
      is >> delim >> m_doubles [10] >> delim >> m_doubles [11];
    }
  else if (m_path == "/car/wheel/tire/rotational-inertia")
    is >> m_doubles [12];
  else if (m_path == "/car/wheel/tire/friction/longitudinal")
    {
      for (std::vector <double>::iterator it = m_long_parameters.begin ();
           it != m_long_parameters.end ();
           it++)
        {
          char delim;
          is >> delim >> *it;
        }
    }
  else if (m_path == "/car/wheel/tire/friction/transverse")
    {
      for (std::vector <double>::iterator it = m_trans_parameters.begin ();
           it != m_trans_parameters.end ();
           it++)
        {
          char delim;
          is >> delim >> *it;
        }
    }
  else if (m_path == "/car/wheel/tire/friction/aligning")
    {
      for (std::vector <double>::iterator it = m_align_parameters.begin ();
           it != m_align_parameters.end ();
           it++)
        {
          char delim;
          is >> delim >> *it;
        }
    }

  // Brakes
  else if (m_path == "/car/wheel/brakes/friction")
    is >> m_doubles [13];
  else if (m_path == "/car/wheel/brakes/radius")
    is >> m_doubles [14];
  else if (m_path == "/car/wheel/brakes/area")
    is >> m_doubles [15];
  else if (m_path == "/car/wheel/brakes/max-pressure")
    is >> m_doubles [16];
  else if (m_path == "/car/wheel/brakes/front-bias")
    is >> m_doubles [17];

  // Transmission
  else if (m_path == "/car/drivetrain/transmission/gear-ratio")
    {
      std::pair <int, double> pair;
      char delim;
      is >> delim >> pair.first >> delim >> pair.second;
      m_gears.push_back (pair);
    }

  // View
  else if (m_path == "/car/view/field-width")
    {
      is >> m_doubles [0];
    }
  else if (m_path == "/car/view/near-plane")
    {
      is >> m_doubles [1];
    }
  else if (m_path == "/car/view/far-plane")
    {
      is >> m_doubles [2];
    }
  else if (m_path == "/car/view/pan-angle")
    {
      is >> m_doubles [3];
    }
  // Rear View
  else if (m_path == "/car/mirror/size")
    {
      char delim;
      is >> delim >> m_doubles [0] >> delim >> m_doubles [1];
    }
  else if (m_path == "/car/mirror/direction")
    {
      is >> m_doubles [2];
    }
  else if (m_path == "/car/mirror/field-width")
    {
      is >> m_doubles [3];
    }
  else if (m_path == "/car/mirror/near-plane")
    {
      is >> m_doubles [4];
    }
  else if (m_path == "/car/mirror/far-plane")
    {
      is >> m_doubles [5];
    }
  else if (m_path == "/car/mirror/mask")
    {
      is >> m_strings [0];
    }

  // Steering
  else if (m_path == "/car/steering/max-angle")
    {
      is >> m_doubles [0];
    }
  else if (m_path == "/car/steering/exponent")
    {
      is >> m_doubles [1];
    }
  else if (m_path == "/car/steering/speed-sensitivity")
    {
      is >> m_doubles [2];
    }
  // Engine
  else if (m_path == "/car/drivetrain/engine/mass")
    {
      is >> m_doubles [0];
    }
  else if (m_path == "/car/drivetrain/engine/max-power")
    {
      is >> m_doubles [1];
    }
  else if (m_path == "/car/drivetrain/engine/peak-engine-rpm")
    {
      is >> m_doubles [2];
    }
  else if (m_path == "/car/drivetrain/engine/rpm-limit")
    {
      is >> m_doubles [3];
    }
  else if (m_path == "/car/drivetrain/engine/inertia")
    {
      is >> m_doubles [4];
    }
  else if (m_path == "/car/drivetrain/engine/idle")
    {
      is >> m_doubles [5];
    }
  else if (m_path == "/car/drivetrain/engine/start-rpm")
    {
      is >> m_doubles [6];
    }
  else if (m_path == "/car/drivetrain/engine/stall-rpm")
    {
      is >> m_doubles [7];
    }
  else if (m_path == "/car/drivetrain/engine/fuel-consumption")
    {
      is >> m_doubles [8];
    }
	else if (m_path == "/car/drivetrain/engine/torque-curve")
    {
      Two_Point point;
      while (is >> point)
        {
          m_points.push_back (point);
        }
    }

  else if (m_path == "/car/drivetrain/engine/sound/file")
    {
      is >> m_strings [0];
    }
  else if (m_path == "/car/drivetrain/engine/sound/volume")
    {
      is >> m_doubles [9];
    }
  else if (m_path == "/car/drivetrain/engine/sound/throttle-volume-factor")
    {
      is >> m_doubles [10];
    }
  else if (m_path == "/car/drivetrain/engine/sound/engine-speed-volume-factor")
    {
      is >> m_doubles [11];
    }
  else if (m_path == "/car/drivetrain/engine/sound/pitch")
    {
      is >> m_doubles [12];
    }
  else if ((m_path == "/car/exterior-model/file") 
           || (m_path == "/car/interior-model/file"))
    {
      is >> m_strings [0];
    }
  else if ((m_path == "/car/exterior-model/scale")
           || (m_path == "/car/interior-model/scale"))
    {
      is >> m_doubles [0];
    }
  else if ((m_path == "/car/exterior-model/translate")
           || (m_path == "/car/interior-model/translate"))
    {
      is >> m_vectors [0];
    }
  else if ((m_path == "/car/exterior-model/rotate")
           || (m_path == "/car/interior-model/rotate"))
    {
      is >> m_vectors [1];
      // Convert the rotations from degrees to radians.
      m_vectors [1] *= deg_to_rad (1.0);
    }
  else if (m_path == "/car/wheel/model/slow-file")
    is >> m_slow_model;
  else if (m_path == "/car/wheel/model/fast-file")
    is >> m_fast_model;
  else if (m_path == "/car/wheel/model/stator-file")
    is >> m_stator_model;
  else if (m_path == "/car/wheel/model/transition-speed")
      is >> m_transition;
  else if (m_path == "/car/wheel/model/stator-offset")
      is >> m_stator_offset;
  else if (m_path == "/car/wheel/model/scale")
      is >> m_scale;
  else if (m_path == "/car/wheel/model/translate")
      is >> m_translation;
  else if (m_path == "/car/wheel/model/rotate")
    {
      is >> m_rotation;
      // Convert the rotations from degrees to radians.
      m_rotation [1] *= deg_to_rad (1.0);
    }
  
  else if (m_tag == "position")
    {
      Three_Vector vec;
      is >> vec;
      m_vectors.push_back (vec);
    }
  else
    {
      double arg;
      is >> arg;
      m_doubles.push_back (arg);
    }
}
*/
void Vamos_Body::Car::GetState(Vamos_Geometry::Three_Vector &chassispos, 
		Vamos_Geometry::Three_Matrix &chassisorientation,
		Vamos_Geometry::Three_Vector &chassisvel,
		Vamos_Geometry::Three_Vector &chassisangvel,
		double *suspdisp,
		double *suspcompvel,
		Vamos_Geometry::Three_Vector *whlangvel,
		int &gear,
		double &enginespeed,
		double &clutchspeed,
		double &enginedrag,
		double * tirespeed
		)
{
	//chassis
	
	chassispos = m_chassis.position();
	chassisorientation = m_chassis.orientation();
	chassisvel = m_chassis.cm_velocity();
	chassisangvel = m_chassis.ang_velocity();
	
	int i = 0;
	for (std::vector <Wheel*>::iterator it = m_wheels.begin ();
       it != m_wheels.end ();
       it++)
    {
		if (i < 4)
		{
			//suspenison
			suspdisp[i] = (*it)->suspension()->displacement();
			suspcompvel[i] = (*it)->suspension()->compression_velocity();
			
			//wheels
			whlangvel[i] = (*it)->ang_velocity();
			
			//tires
			tirespeed[i] = (*it)->rotational_speed();
		}
		
		i++;
	}
	
	gear = transmission()->gear();
	enginespeed = engine()->rotational_speed();
	clutchspeed = transmission()->clutch_speed();
	enginedrag = engine()->drag();
}

void Vamos_Body::Car::SetState(Vamos_Geometry::Three_Vector chassispos, 
		Vamos_Geometry::Three_Matrix chassisorientation,
		Vamos_Geometry::Three_Vector chassisvel,
		Vamos_Geometry::Three_Vector chassisangvel,
		double *suspdisp,
		double *suspcompvel,
		Vamos_Geometry::Three_Vector *whlangvel,
		int gear,
		double enginespeed,
		double clutchspeed,
		double enginedrag,
		double * tirespeed
		)
{
	//chassis
	//m_chassis.place(chassispos);
	m_chassis.set_position(chassispos);
	//m_chassis.orient(chassisorientation);
	m_chassis.set_orientation(chassisorientation);
	//m_chassis.cm_velocity(chassisvel);
	m_chassis.set_velocity(chassisvel);
	m_chassis.set_angvel(chassisangvel);
	
	int i = 0;
	for (std::vector <Wheel*>::iterator it = m_wheels.begin ();
       it != m_wheels.end ();
       it++)
    {
		if (i < 4)
		{
			//suspenison
			(*it)->suspension()->set_compression_velocity(suspcompvel[i]);
			(*it)->suspension()->set_displacement(suspdisp[i]);
			//(*it)->suspension()->displace(suspdisp[i]);
			
			//wheels
			(*it)->set_ang_velocity(whlangvel[i]);
			
			//tires
			(*it)->set_rotational_speed(tirespeed[i]);
		}
		
		i++;
	}
	
	transmission()->shift(gear);
	engine()->set_rotational_speed(enginespeed);
	//engine()->speed(enginespeed);
	
	transmission()->set_clutchspeed(clutchspeed);
	engine()->set_drag(enginedrag);
}

bool Vamos_Body::Car::LoadCarDefinition(string carfile)
{
	bool success = true;
	
	Engine* mp_engine;
    Clutch* mp_clutch;
    Transmission* mp_transmission;
    Differential* mp_differential;
	
	//clear any old data
	m_car_definition.Clear();
	m_car_definition.SuppressError(false);
	
	//do all of the file reading
	m_car_definition.Load(carfile);
	
	//load custom car settings
	if (!verbose_output)
		m_car_definition.SuppressError(true);
	m_car_definition.Load(settings.GetSettingsDir() + "/carsettings/" + m_car_file);
	m_car_definition.SuppressError(false);
	
	//load custom parts
	if (!LoadParts())
		success = false;
	
	//start processing
	//m_car_definition.DebugPrint();
	
	string drive = "FWD";
	
	string tstr = "";
	int tint = 0;
	int max_floats = 11;
	float tfloat[max_floats];
	char tempchar[256];
	int i;
	for (i = 0; i < max_floats; i++)
		tfloat[i] = 0;
	float tvec[3];
	tvec[0] = 0;
	tvec[1] = 0;
	tvec[2] = 0;
	Vamos_Geometry::Three_Vector t3v(0,0,0);
	Vamos_Geometry::Three_Vector t3v2(0,0,0);
	
	bool retval;
	
	//get the car's drive parameter, but only if it's valid
	retval = m_car_definition.GetParam("drive", tstr);
	//cout << tstr << endl;
	if (!retval) success = false;
	if (tstr == "RWD" || tstr == "AWD" || tstr == "FWD")
		drive = tstr;
	else
		cout << carfile << ": Invalid car drive parameter (should be something like FWD, RWD, AWD): " << tstr << endl;
	
	retval = m_car_definition.GetParam("driver.view-position", tvec);
	if (!retval) success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	set_view(t3v, 60,0.5,700,60);
	set_hood_view(t3v);
	
	m_car_definition.SuppressError(true);
	retval = m_car_definition.GetParam("driver.hood-mounted-view-position", tvec);
	if (retval)
	{
		t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	}
	else
	{
		t3v.m_vec[0] += 1.0;
		t3v.m_vec[1] = 0.0;
	}
	set_hood_view(t3v);
	m_car_definition.SuppressError(false);
	
	retval = m_car_definition.GetParam("steering.max-angle", tfloat[0]);
	if (!retval) success = false;
	steer_exponent (1.0);
	steer_speed_sensitivity(0);
	max_steer_angle(tfloat[0]);
	
	retval = m_car_definition.GetParam("engine.mass", tfloat[0]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.max-power", tfloat[1]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.peak-engine-rpm", tfloat[2]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.rpm-limit", tfloat[3]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.inertia", tfloat[4]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.idle", tfloat[5]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.start-rpm", tfloat[6]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.stall-rpm", tfloat[7]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.fuel-consumption", tfloat[8]);
	if (!retval) success = false;
	retval = m_car_definition.GetParam("engine.position", tvec);
	if (!retval) success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	mp_engine = new Engine (tfloat [0], t3v, tfloat [1], 
                              tfloat [2], tfloat [3], tfloat [4], 
                              tfloat [5], tfloat [6], tfloat [7],
                              tfloat [8]);
	
	//get torque curve stuff
	bool torque_curve = false;
	m_tpoints.clear();
	Two_Point tpoint;
	m_car_definition.SuppressError(true);
	i = 0;
	sprintf(tempchar, "engine.torque-curve-%02i", i);
	retval = m_car_definition.GetParam(tempchar, tvec);
	//cout << tempchar << endl;
	while (retval && i < 100)
	{
		//cout << tempchar << endl;
		torque_curve = true;
		
		tpoint.x = tvec[0];
		tpoint.y = tvec[1];
		m_tpoints.push_back(tpoint);
		
		i++;
		sprintf(tempchar, "engine.torque-curve-%02i", i);
		retval = m_car_definition.GetParam(tempchar, tvec);
	}
	m_car_definition.SuppressError(false);
	
	//set the torque curve stuff
	if (torque_curve)
	{
		retval = m_car_definition.GetParam("engine.torque-friction", tfloat[0]);
		if (!retval)
			success = false;
		
		mp_engine->set_torque_curve (m_tpoints);
    	mp_engine->set_friction (tfloat[0]);
	}
	
	engine_sound (m_data_dir + "sounds/engine.wav", 
                            0.1, 0.8, 1.0, 0.001);
	
	retval = m_car_definition.GetParam("clutch.sliding", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("clutch.radius", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("clutch.area", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("clutch.max-pressure", tfloat[3]);
	if (!retval)
		success = false;
	mp_clutch = new Clutch (tfloat [0], tfloat [1], tfloat [2], 
                              tfloat [3]);
	
	retval = m_car_definition.GetParam("transmission.shift-delay", tfloat[0]);
	if (!retval)
		success = false;
	shift_delay(tfloat[0]);
	retval = m_car_definition.GetParam("transmission.gears", tint);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("transmission.gear-ratio-r", tfloat[0]);
	if (!retval)
		success = false;
	mp_transmission = new Transmission;
	mp_transmission->gear_ratio(-1, tfloat[0]);
	for (i = 1; i < tint+1; i++)
	{
		sprintf(tempchar, "transmission.gear-ratio-%i", i);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		mp_transmission->gear_ratio(i, tfloat[0]);
	}
	//possible error here?  m_gears is not set
	
	retval = m_car_definition.GetParam("differential.final-drive", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("differential.anti-slip", tfloat[1]);
	if (!retval)
		success = false;
	mp_differential = new Differential (tfloat[0], tfloat[1]);
	
	//we now have all of the important drivetrain pieces, so create the drivetrain
	mp_drivetrain = new Drivetrain(mp_engine, mp_clutch, mp_transmission, mp_differential);
	
	retval = m_car_definition.GetParam("fuel-tank.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("fuel-tank.capacity", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("fuel-tank.volume", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("fuel-tank.fuel-density", tfloat[2]);
	if (!retval)
		success = false;
	delete mp_fuel_tank;
	mp_fuel_tank = new Fuel_Tank (t3v, tfloat [0], tfloat [1], tfloat [2]);
	
	//accumulate front suspension constants
	retval = m_car_definition.GetParam("suspension-front.spring-constant", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.bounce", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.rebound", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.travel", tfloat[3]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.max-compression-velocity", tfloat[4]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.camber", tfloat[5]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.caster", tfloat[6]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.toe", tfloat[7]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-front.anti-roll", tfloat[8]);
	if (!retval)
		success = false;


/*
  else if (m_tag == "wheel")
    {
      Side side = (m_strings [0] == "left") ? LEFT : RIGHT;
      Suspension* suspension =
        new Suspension (m_vectors [1], m_vectors [2], side, m_doubles [0],
                        m_doubles [1], m_doubles [2], m_doubles [3],
                        m_doubles [4]);
      suspension->camber (m_doubles [5]);
      suspension->caster (m_doubles [6]);
      suspension->toe (m_doubles [7]);
      if (m_doubles [8] != 0.0)
        {
          Suspension* other = static_cast <Suspension*> 
            (*(mp_car->chassis ().particles ().end () - 2));
          assert (other != 0);
          suspension->anti_roll (other, m_doubles [8]);
          m_doubles [8] = 0.0;
        }
      for (std::vector <Model_Info*>::iterator it = m_models.begin ();
           it != m_models.end ();
           it++)
        {
          suspension->set_model ("cars/" + (*it)->file,
                                 (*it)->scale, 
                                 (*it)->translate,
                                 (*it)->rotate);
        }
      mp_car->chassis ().particles ().push_back (suspension->hinge ());
      mp_car->chassis ().particles ().push_back (suspension);

      Tire_Friction friction (m_long_parameters, m_trans_parameters,
                              m_align_parameters);
      Tire tire (m_doubles [9], m_doubles [10], m_doubles [11], friction,
                 m_doubles [12]);

      double bias = m_doubles [17];
      if (m_strings [1] == "rear")
          bias = 1.0 - bias;
      Brake brake (m_doubles [13], m_doubles [14], m_doubles [15], 
                   m_doubles [16], bias);
 
      Wheel* wheel = new Wheel (m_doubles [18], m_vectors [0], 
                                m_doubles [22], m_doubles [20], m_doubles [19],
                                suspension, tire, brake,
                                m_bools [0], m_bools [1], side);
      mp_car->chassis ().particles ().push_back (wheel);
      mp_car->m_wheels.push_back (wheel);
      if (m_slow_model != "")
        {
          std::string stator_path;
          if (m_stator_model != "")
            {
              stator_path = m_data_dir + "cars/" + m_stator_model;
            }
          	if (m_strings [1] == "rear")
			{
          wheel->set_models (settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_rear.joe"),
                             settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_rear.joe"),
                             m_transition,
                             stator_path, m_stator_offset, 
                             m_scale, m_translation, m_rotation);
			}
			else
			{
				wheel->set_models (settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_front.joe"),
                             settings.GetFullDataPath("cars/" + mp_car->m_car_file + "/wheel_front.joe"),
                             m_transition,
                             stator_path, m_stator_offset, 
                             m_scale, m_translation, m_rotation);
			}
        }
    }

*/



	//get front right position and hinge position
	retval = m_car_definition.GetParam("suspension-FR.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("suspension-FR.hinge", tvec);
	if (!retval)
		success = false;
	t3v2.m_vec[0] = tvec[0];	t3v2.m_vec[1] = tvec[1];	t3v2.m_vec[2] = tvec[2];
	
	//create front right suspension
	Side side = RIGHT;
	Suspension* suspensionFR =
	new Suspension (t3v, t3v2, side, tfloat [0],
					tfloat [1], tfloat [2], tfloat [3],
					tfloat [4]);
	suspensionFR->camber (tfloat [5]);
	suspensionFR->caster (tfloat [6]);
	suspensionFR->toe (tfloat [7]);
	
	//get front left position and hinge position
	retval = m_car_definition.GetParam("suspension-FL.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("suspension-FL.hinge", tvec);
	if (!retval)
		success = false;
	t3v2.m_vec[0] = tvec[0];	t3v2.m_vec[1] = tvec[1];	t3v2.m_vec[2] = tvec[2];
	
	//create front left suspension
	side = LEFT;
	Suspension* suspensionFL =
	new Suspension (t3v, t3v2, side, tfloat [0],
					tfloat [1], tfloat [2], tfloat [3],
					tfloat [4]);
	suspensionFL->camber (tfloat [5]);
	suspensionFL->caster (tfloat [6]);
	suspensionFL->toe (tfloat [7]);
	
	//assign front anti-roll
	suspensionFL->anti_roll (suspensionFR, tfloat [8]);
	
	
	
	//accumulate rear suspension constants
	retval = m_car_definition.GetParam("suspension-rear.spring-constant", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.bounce", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.rebound", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.travel", tfloat[3]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.max-compression-velocity", tfloat[4]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.camber", tfloat[5]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.caster", tfloat[6]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.toe", tfloat[7]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("suspension-rear.anti-roll", tfloat[8]);
	if (!retval)
		success = false;
	
	//get rear right position and hinge position
	retval = m_car_definition.GetParam("suspension-RR.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("suspension-RR.hinge", tvec);
	if (!retval)
		success = false;
	t3v2.m_vec[0] = tvec[0];	t3v2.m_vec[1] = tvec[1];	t3v2.m_vec[2] = tvec[2];
	
	//create rear right suspension
	side = RIGHT;
	Suspension* suspensionRR =
	new Suspension (t3v, t3v2, side, tfloat [0],
					tfloat [1], tfloat [2], tfloat [3],
					tfloat [4]);
	suspensionRR->camber (tfloat [5]);
	suspensionRR->caster (tfloat [6]);
	suspensionRR->toe (tfloat [7]);
	
	//get rear left position and hinge position
	retval = m_car_definition.GetParam("suspension-RL.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("suspension-RL.hinge", tvec);
	if (!retval)
		success = false;
	t3v2.m_vec[0] = tvec[0];	t3v2.m_vec[1] = tvec[1];	t3v2.m_vec[2] = tvec[2];
	
	//create rear left suspension
	side = LEFT;
	Suspension* suspensionRL =
	new Suspension (t3v, t3v2, side, tfloat [0],
					tfloat [1], tfloat [2], tfloat [3],
					tfloat [4]);
	suspensionRL->camber (tfloat [5]);
	suspensionRL->caster (tfloat [6]);
	suspensionRL->toe (tfloat [7]);
	
	//assign rear anti-roll
	suspensionRL->anti_roll (suspensionRR, tfloat [8]);
	
	
	
	//add suspension elements to the chassis
	chassis ().particles ().push_back (suspensionFL->hinge ());
	chassis ().particles ().push_back (suspensionFL);
	
	chassis ().particles ().push_back (suspensionFR->hinge ());
	chassis ().particles ().push_back (suspensionFR);
	
	chassis ().particles ().push_back (suspensionRL->hinge ());
	chassis ().particles ().push_back (suspensionRL);
	
	chassis ().particles ().push_back (suspensionRR->hinge ());
	chassis ().particles ().push_back (suspensionRR);
	
	
	//read in tire parameters
	std::vector <double> m_long_parameters;
    std::vector <double> m_trans_parameters;
    std::vector <double> m_align_parameters;
	m_long_parameters.resize (11);
    m_trans_parameters.resize (15);
    m_align_parameters.resize (18);
	
	retval = m_car_definition.GetParam("tire-front.radius", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("tire-front.rolling-resistance", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("tire-front.rotational-inertia", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("tire-front.tread", tfloat[3]);
	if (!retval)
		success = false;
	
	//read lateral
	int numinfile;
	for (i = 0; i < 15; i++)
	{
		numinfile = i;
		if (i == 11)
			numinfile = 111;
		else if (i == 12)
			numinfile = 112;
		else if (i > 12)
			numinfile -= 1;
		sprintf(tempchar, "tire-front.a%i", numinfile);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		m_trans_parameters[i] = tfloat[0];
	}
	
	//read longitudinal
	for (i = 0; i < 11; i++)
	{
		sprintf(tempchar, "tire-front.b%i", i);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		m_long_parameters[i] = tfloat[0];
	}
	
	//read aligning
	for (i = 0; i < 18; i++)
	{
		sprintf(tempchar, "tire-front.c%i", i);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		m_align_parameters[i] = tfloat[0];
	}
	
	//create front tires
	Tire_Friction frictionF (m_long_parameters, m_trans_parameters,
                              m_align_parameters);
    Tire tireFL (tfloat [1], tvec [0], tvec [1], frictionF,
                 tfloat [2], tfloat[3]);
	Tire tireFR (tfloat [1], tvec [0], tvec [1], frictionF,
                 tfloat [2], tfloat[3]);
	
	
	retval = m_car_definition.GetParam("tire-rear.radius", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("tire-rear.rolling-resistance", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("tire-rear.rotational-inertia", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("tire-rear.tread", tfloat[3]);
	if (!retval)
		success = false;
	
	//read lateral
	for (i = 0; i < 15; i++)
	{
		numinfile = i;
		if (i == 11)
			numinfile = 111;
		else if (i == 12)
			numinfile = 112;
		else if (i > 12)
			numinfile -= 1;
		sprintf(tempchar, "tire-rear.a%i", numinfile);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		m_trans_parameters[i] = tfloat[0];
	}
	
	//read longitudinal
	for (i = 0; i < 11; i++)
	{
		sprintf(tempchar, "tire-rear.b%i", i);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		m_long_parameters[i] = tfloat[0];
	}
	
	//read aligning
	for (i = 0; i < 18; i++)
	{
		sprintf(tempchar, "tire-rear.c%i", i);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		if (!retval)
			success = false;
		m_align_parameters[i] = tfloat[0];
	}
	
	//create rear tires
	Tire_Friction frictionR (m_long_parameters, m_trans_parameters,
                              m_align_parameters);
    Tire tireRL (tfloat [1], tvec [0], tvec [1], frictionR,
                 tfloat [2], tfloat[3]);
	Tire tireRR (tfloat [1], tvec [0], tvec [1], frictionR,
                 tfloat [2], tfloat[3]);
	
	
	//read front brake params
	retval = m_car_definition.GetParam("brakes-front.friction", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-front.max-pressure", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-front.bias", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-front.radius", tfloat[3]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-front.area", tfloat[4]);
	if (!retval)
		success = false;
	
	//create front brakes
	Brake brakeFL (tfloat [0], tfloat [3], tfloat [4], 
                   tfloat [1], tfloat[2], 0);
	Brake brakeFR (tfloat [0], tfloat [3], tfloat [4], 
                   tfloat [1], tfloat[2], 0);
	
	
	//read rear brake params
	retval = m_car_definition.GetParam("brakes-rear.friction", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-rear.max-pressure", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-rear.bias", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-rear.radius", tfloat[3]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-rear.area", tfloat[4]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("brakes-rear.handbrake", tfloat[5]);
	if (!retval)
		success = false;
	
	//create rear brakes
	Brake brakeRL (tfloat [0], tfloat [3], tfloat [4], 
                   tfloat [1], tfloat[2], tfloat[5]);
	Brake brakeRR (tfloat [0], tfloat [3], tfloat [4], 
                   tfloat [1], tfloat[2], tfloat[5]);
	
	
	//read in wheel data
	retval = m_car_definition.GetParam("wheel-FR.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("wheel-FR.roll-height", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-FR.mass", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-FR.restitution", tfloat[2]);
	if (!retval)
		success = false;
	
	side = RIGHT;
	bool driven;
	if (drive == "FWD" || drive == "AWD")
		driven = true;
	else
		driven = false;
	Wheel* wheelFR = new Wheel (tfloat[1], t3v,
                                0, tfloat[0], tfloat[2],
                                suspensionFR, tireFR, brakeFR,
                                true, driven, side, FRONT);
	wheelFR->set_models (settings.GetFullDataPath("cars/" + m_car_file + "/wheel_front.joe"),
					 settings.GetFullDataPath("cars/" + m_car_file + "/wheel_front.joe"),
					 5.0, "", 0.0, 1.0, Vamos_Geometry::Three_Vector(0,0,0), 
					 Vamos_Geometry::Three_Vector(0,0,0));
				
	
	retval = m_car_definition.GetParam("wheel-FL.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("wheel-FL.roll-height", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-FL.mass", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-FL.restitution", tfloat[2]);
	if (!retval)
		success = false;
	
	side = LEFT;
	if (drive == "FWD" || drive == "AWD")
		driven = true;
	else
		driven = false;
	Wheel* wheelFL = new Wheel (tfloat[1], t3v,
                                0, tfloat[0], tfloat[2],
                                suspensionFL, tireFL, brakeFL,
                                true, driven, side, FRONT);
	wheelFL->set_models (settings.GetFullDataPath("cars/" + m_car_file + "/wheel_front.joe"),
					 settings.GetFullDataPath("cars/" + m_car_file + "/wheel_front.joe"),
					 5.0, "", 0.0, 1.0, Vamos_Geometry::Three_Vector(0,0,0), 
					 Vamos_Geometry::Three_Vector(0,0,0));
	
	retval = m_car_definition.GetParam("wheel-RL.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("wheel-RL.roll-height", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-RL.mass", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-RL.restitution", tfloat[2]);
	if (!retval)
		success = false;
	
	side = LEFT;
	if (drive == "RWD" || drive == "AWD")
		driven = true;
	else
		driven = false;
	Wheel* wheelRL = new Wheel (tfloat[1], t3v,
                                0, tfloat[0], tfloat[2],
                                suspensionRL, tireRL, brakeRL,
                                false, driven, side, BACK);
	wheelRL->set_models (settings.GetFullDataPath("cars/" + m_car_file + "/wheel_rear.joe"),
					 settings.GetFullDataPath("cars/" + m_car_file + "/wheel_rear.joe"),
					 5.0, "", 0.0, 1.0, Vamos_Geometry::Three_Vector(0,0,0), 
					 Vamos_Geometry::Three_Vector(0,0,0));
	
	retval = m_car_definition.GetParam("wheel-RR.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("wheel-RR.roll-height", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-RR.mass", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wheel-RR.restitution", tfloat[2]);
	if (!retval)
		success = false;
	
	side = RIGHT;
	if (drive == "RWD" || drive == "AWD")
		driven = true;
	else
		driven = false;
	Wheel* wheelRR = new Wheel (tfloat[1], t3v,
                                0, tfloat[0], tfloat[2],
                                suspensionRR, tireRR, brakeRR,
                                false, driven, side, BACK);
	wheelRR->set_models (settings.GetFullDataPath("cars/" + m_car_file + "/wheel_rear.joe"),
					 settings.GetFullDataPath("cars/" + m_car_file + "/wheel_rear.joe"),
					 5.0, "", 0.0, 1.0, Vamos_Geometry::Three_Vector(0,0,0), 
					 Vamos_Geometry::Three_Vector(0,0,0));
	
	//add wheels to car
	chassis ().particles ().push_back (wheelFL);
	m_wheels.push_back (wheelFL);
	chassis ().particles ().push_back (wheelFR);
	m_wheels.push_back (wheelFR);
	chassis ().particles ().push_back (wheelRL);
	m_wheels.push_back (wheelRL);
	chassis ().particles ().push_back (wheelRR);
	m_wheels.push_back (wheelRR);
	
	
	
	//read drag
	retval = m_car_definition.GetParam("drag.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("drag.frontal-area", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("drag.drag-coefficient", tfloat[1]);
	if (!retval)
		success = false;
	chassis ().particles ().push_back (new Drag (t3v, 
                                               tfloat [0], 
                                               tfloat [1]));
	
	//calculate drag coefficient. Air density 1.2 is hardcoded
	//in vamos/world/World.cc
	CW += 0.5 * 1.2 * tfloat [0] * tfloat [1];
	
	//read front wing
	retval = m_car_definition.GetParam("wing-front.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("wing-front.frontal-area", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-front.drag-coefficient", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-front.surface-area", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-front.lift-coefficient", tfloat[3]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-front.efficiency", tfloat[4]);
	if (!retval)
		success = false;
	chassis ().particles ().
        push_back (new Wing (t3v, tfloat [0], 
                             tfloat [1], tfloat [2], 
                             tfloat [3], tfloat [4]));

	//calculate aerodynamic downforce coefficient. Air density 1.2 is hardcoded
	//in vamos/world/World.cc
	CA += 0.5 * 1.2 * tfloat [2] * tfloat [3];
	//calculate drag coefficient. Air density 1.2 is hardcoded
	//in vamos/world/World.cc
	CW += 0.5 * 1.2 * tfloat [3] * tfloat [3] * tfloat[2] * (1.0 - tfloat[4]);
	
	//read rear wing
	retval = m_car_definition.GetParam("wing-rear.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("wing-rear.frontal-area", tfloat[0]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-rear.drag-coefficient", tfloat[1]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-rear.surface-area", tfloat[2]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-rear.lift-coefficient", tfloat[3]);
	if (!retval)
		success = false;
	retval = m_car_definition.GetParam("wing-rear.efficiency", tfloat[4]);
	if (!retval)
		success = false;
	chassis ().particles ().
        push_back (new Wing (t3v, tfloat [0], 
                             tfloat [1], tfloat [2], 
                             tfloat [3], tfloat [4]));

	//calculate aerodynamic downforce coefficient. Air density 1.2 is hardcoded
	//in vamos/world/World.cc
	CA += 0.5 * 1.2 * tfloat [2] * tfloat [3];
	//calculate drag coefficient. Air density 1.2 is hardcoded
	//in vamos/world/World.cc
	CW += 0.5 * 1.2 * tfloat [3] * tfloat [3] * tfloat[2] * (1.0 - tfloat[4]);

	//do contact points
	retval = m_car_definition.GetParam("contact-points.mass", tfloat[0]);
	if (!retval)
		success = false;
	m_car_definition.SuppressError(true);
	i = 0;
	sprintf(tempchar, "contact-points.position-%02i", i);
	retval = m_car_definition.GetParam(tempchar, tvec);
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	Material::Material_Type material = Material::METAL;
	while (retval && i < 100)
	{
		chassis ().particles ().push_back (new Contact_Point (tfloat [0], t3v, material,0.5, 0.1));
		//hard coded friction and restitution!!
		
		i++;
		sprintf(tempchar, "contact-points.position-%02i", i);
		retval = m_car_definition.GetParam(tempchar, tvec);
		t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	}
	m_car_definition.SuppressError(false);


	//do particles
	m_car_definition.SuppressError(true);
	i = 0;
	sprintf(tempchar, "particle-%02i.position", i);
	retval = m_car_definition.GetParam(tempchar, tvec);
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	while (retval && i < 100)
	{
		m_car_definition.SuppressError(false);
		sprintf(tempchar, "particle-%02i.mass", i);
		retval = m_car_definition.GetParam(tempchar, tfloat[0]);
		m_car_definition.SuppressError(true);
		
		chassis ().particles ().push_back (new Particle (tfloat [0], t3v));
		
		//cout << tempchar << " = " << tfloat[0] << "," << t3v.m_vec[0] << "," << t3v.m_vec[1] << "," << t3v.m_vec[2] << endl;
		
		i++;
		sprintf(tempchar, "particle-%02i.position", i);
		retval = m_car_definition.GetParam(tempchar, tvec);
		t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	}
	m_car_definition.SuppressError(false);


	//account for the weight of the driver
	retval = m_car_definition.GetParam("driver.position", tvec);
	if (!retval)
		success = false;
	t3v.m_vec[0] = tvec[0];	t3v.m_vec[1] = tvec[1];	t3v.m_vec[2] = tvec[2];
	retval = m_car_definition.GetParam("driver.mass", tfloat[0]);
	if (!retval)
		success = false;
	chassis ().particles ().
        push_back (new Particle (tfloat [0], t3v));

	
	//don't forget to account for the weight of the fuel tank and engine
	chassis ().particles ().push_back (mp_drivetrain->engine ());
	chassis ().particles ().push_back (mp_fuel_tank);
	
	
	//load models.  the data passed here isn't used anymore, but these
	// functions still need to be called so the models get loaded.
	exterior_model ("cars/null", 1.0, Vamos_Geometry::Three_Vector(0,0,0),
					Vamos_Geometry::Three_Vector(0,0,0));
	interior_model ("cars/null", 1.0, Vamos_Geometry::Three_Vector(0,0,0),
					Vamos_Geometry::Three_Vector(0,0,0));
	
	m_tpoints.clear();
	
	return success;
}

bool Vamos_Body::Car::LoadParts()
{
	string tstr;
	int i;
	char tempchar[32];
	bool retval;
	
	m_car_definition.SuppressError(true);
	i = 0;
	sprintf(tempchar, "parts.part-%02i", i);
	retval = m_car_definition.GetParam(tempchar, tstr);
	//cout << tempchar << endl;
	while (retval && i < 100)
	{
		//process part
		LoadPart(tstr);
		
		i++;
		sprintf(tempchar, "parts.part-%02i", i);
		retval = m_car_definition.GetParam(tempchar, tstr);
	}
	m_car_definition.SuppressError(false);
	
	return true;
}

bool Vamos_Body::Car::LoadPart(string partfile)
{
	/*string tstr;
	
	CONFIGFILE partdata (settings.GetFullDataPath("carparts/" + partfile));
	partdata.SuppressError(true);
	
	CONFIGVARIABLE * cur = m_car_definition.GetHead();
	
	bool retval = false;
	
	while (cur != NULL)
	{
		string currentvar = cur->GetFullName();
		retval = partdata.GetParam(currentvar, tstr);
		if (retval)
		{
			string changetype = "";
			partdata.GetParam(currentvar + "-changetype", changetype);
			
			if (changetype == "multiply")
			{
				float currentdata = 0;
				float multval = 0;
				m_car_definition.GetParam(currentvar, currentdata);
				partdata.GetParam(currentvar, multval);
				currentdata *= multval;
				m_car_definition.SetParam(currentvar, currentdata);
			}
			else
			{
				m_car_definition.SetParam(currentvar, tstr);
			}
		}
		
		cur = cur->next;
	}*/
	
	return true;
}

void Vamos_Body::Car::SetColParams(int i, double f1, double f2, double rr, double rd)
{
	if (i < 4 && i >= 0)
	{
		wheel(i)->SetColParams(f1, f2, rr, rd);
	}
}

bool Vamos_Body::Car::ABSActive()
{
	if (!m_abs)
		return false;
	
	for (std::vector <Wheel*>::iterator it = m_wheels.begin (); it != m_wheels.end (); it++)
		if ((*it)->GetABSState())
			return true;
	
	return false;
}

bool Vamos_Body::Car::TCSActive()
{
	if (!m_tcs)
		return false;
	
	for (std::vector <Wheel*>::iterator it = m_wheels.begin (); it != m_wheels.end (); it++)
		if ((*it)->GetTCSState())
			return true;
	
	return false;
}
