Module: rsusp.cpp

class RSuspension
.h

constructorRSuspension(RCar *_car)
destructor~RSuspension()
Destroyvoid Destroy()
DisableBoundingBoxvoid DisableBoundingBox()
EnableBoundingBoxvoid EnableBoundingBox()
Initvoid Init()
Loadbool Load(QInfo *info,cstring path)
Resetvoid Reset()

After a car warp for example

Paintvoid Paint()
PreAnimatevoid PreAnimate()
CalcForcesvoid CalcForces()
ApplyForcesvoid ApplyForces()
Integratevoid Integrate()
ApplyWheelTranslationbool ApplyWheelTranslation(DVector3 *translation, DVector3 *velocity)

The wheel is moving; stretch or compress the spring
If the translation leads to a physically impossible state,
FALSE is returned and 'translation' contains the translation
needed to get the wheel inside physically possible limits.
Otherwise the wheel
could move farther than the suspension will allow.
We should signal this to the driver perhaps; a hard bump stop!
'velocity' contains the wheel velocity with respect to the car



/*
 * Racer - suspension
 * 03-09-00: Created! (16:39:01)
 * 12-12-00: Bounding box support.
 * 23-02-01: Roll centers introduced; car was highly unstable at >80 km/h.
 * NOTES:
 * BUGS:
 * - You may enter a state where a bounce starts which never ends; perhaps
 * a problem with the integration (x=x+v+1/2a might be better), or with
 * the high (vertical) spring rate of a tire. More damping must be introduced
 * somewhere as the shocks don't seem to.
 * (C) MarketGraph/RvG
 */

#include <racer/racer.h>
#include <qlib/debug.h>
#pragma hdrstop
#include <math.h>
#include <d3/geode.h>
DEBUG_ENABLE

// Local trace
//#define LTRACE

#undef  DBG_CLASS
#define DBG_CLASS "RSuspension"

RSuspension::RSuspension(RCar *_car)
{
  car=_car;
  length=0;
  restLength=0;
  minLength=maxLength=0;
  k=0;
  slices=0;
  position.SetToZero();
  rollCenter.SetToZero();
  pistonVelocity.SetToZero();
  forceBody.SetToZero();
  forceSpring.SetToZero();
  Init();
 
#ifdef RR_GFX_OGL
  quad=gluNewQuadric();
#endif
  model=0;
  bbox=0;
}
RSuspension::~RSuspension()
{
  Destroy();
}

void RSuspension::Destroy()
{
#ifdef RR_GFX_OGL
  if(quad){ gluDeleteQuadric(quad); quad=0; }
#endif
  if(model){ delete model; model=0; }
  if(bbox){ delete bbox; bbox=0; }
}

/**********
* Attribs *
**********/
void RSuspension::DisableBoundingBox()
{
  flags&=~DRAW_BBOX;
}
void RSuspension::EnableBoundingBox()
{
  flags|=DRAW_BBOX;
}

/**********
* Loading *
**********/
void RSuspension::Init()
{
  flags=0;
}
bool RSuspension::Load(QInfo *info,cstring path)
{
  char buf[128],fname[256];
  int  index;
  
  Destroy();

  // Deduce index by path
  if(path[0])
    index=path[strlen(path)-1]-'0';
  else
    index=0;

  // Location; offset to car center of geometry
  sprintf(buf,"%s.x",path);
  position.x=info->GetFloat(buf,(index&1)?-.4f:.4f);
  sprintf(buf,"%s.y",path);
  position.y=info->GetFloat(buf);
  sprintf(buf,"%s.z",path);
  position.z=info->GetFloat(buf,(index<2)?-1.0f:1.0f);

  // Roll center (default: X=center of car, Y at ground, Z at susp location)
  sprintf(buf,"%s.roll_center.x",path);
  rollCenter.x=info->GetFloat(buf);
  sprintf(buf,"%s.roll_center.y",path);
  rollCenter.y=info->GetFloat(buf);
  sprintf(buf,"%s.roll_center.z",path);
  rollCenter.z=info->GetFloat(buf,position.z);
  // Roll center is relative to suspension point
  rollCenter.Add(&position);
  
  // Physical attribs
  sprintf(buf,"%s.restlen",path);
  restLength=info->GetFloat(buf,.5f);
  sprintf(buf,"%s.minlen",path);
  minLength=info->GetFloat(buf,0.1f);
  sprintf(buf,"%s.maxlen",path);
  maxLength=info->GetFloat(buf,1.0f);
  sprintf(buf,"%s.k",path);
  k=info->GetFloat(buf,7500.0f);
  
  // Damper
  sprintf(buf,"%s.bump_rate",path);
  bumpRate=info->GetFloat(buf,2000.0f);
  sprintf(buf,"%s.rebound_rate",path);
  reboundRate=info->GetFloat(buf,2200.0f);

  // Gfx
  sprintf(buf,"%s.radius",path);
  radius=info->GetFloat(buf,.04f);
  sprintf(buf,"%s.slices",path);
  slices=info->GetInt(buf,4);
  
  // Model (or stub gfx)
  model=new RModel(car);
  model->Load(info,path);
  if(!model->IsLoaded())
    quad=gluNewQuadric();
  bbox=new DBoundingBox();
  bbox->EnableCSG();
#ifdef FUTURE
  bbox->GetBox()->size.x=radius*2;
  bbox->GetBox()->size.y=restLength*2;
  bbox->GetBox()->size.z=radius*2;
#endif

  // Deduce others
  length=restLength;
  
  return TRUE;
}

void RSuspension::Reset()
// After a car warp for example
{
//qdbg("RSusp:Reset, length=%f, restLen=%f\n",length,restLength);
  length=restLength;
  pistonVelocity.SetToZero();
  forceBody.SetToZero();
  forceWheel.SetToZero();
  forceSpring.SetToZero();
  forceDamper.SetToZero();
}

/********
* Paint *
********/
void RSuspension::Paint()
{
#ifdef RR_GFX_OGL
  float col[]={ .4,.4,.4 };

  glPushMatrix();
  
  // Location
  glTranslatef(position.GetX(),position.GetY(),position.GetZ());

  if(flags&DRAW_BBOX)
  {
    // Draw the bounding box in the model-type rotation
    // (the stub must rotate so the cylinder is at the local Z-axis)
    bbox->Paint();
  }

  // Discs are painted at Z=0
  glRotatef(90,1,0,0);
  
  // Primitives
  if(model!=0&&model->IsLoaded())
  {
    model->Paint();
    goto skip_quad;
  }
  
  car->SetDefaultMaterial();
  
#ifdef OBS
  // Center point for quad
  glTranslatef(0,0,-restLength/2);
#endif
  
  glMaterialfv(GL_FRONT,GL_DIFFUSE,col);
  // Draw cylinder with caps
//qdbg("radius=%f, length=%f, slices=%d\n",radius,length,slices);
  gluCylinder(quad,radius,radius,length,slices,1);
  gluQuadricOrientation(quad,GLU_INSIDE);
  gluDisk(quad,0,radius,slices,1);
  gluQuadricOrientation(quad,GLU_OUTSIDE);
  glTranslatef(0,0,length);
  gluDisk(quad,0,radius,slices,1);
  
 skip_quad:
  
  glPopMatrix();
#endif
}

/**********
* Physics *
**********/
void RSuspension::PreAnimate()
{
  forceBody.SetToZero();
  forceWheel.SetToZero();
}

void RSuspension::CalcForces()
{
  DVector3 force;
  double damperRate;

//qdbg("RSusp:CF; force=%.2f,%.2f,%.2f\n",force.x,force.y,force.z);
 
  // Calculate spring force (because of stretching/compressing)
  force.x=0;
  force.y=k*(length-restLength);
  force.z=0;
#ifdef LTRACE
qdbg("RSusp: force.y=%f, k=%f, restLength=%f, length=%f\n",
force.y,k,restLength,length);
#endif

  // Calculate force of the damper
//qdbg("  piston v=%f\n",pistonVelocity.y);
  if(pistonVelocity.y<0)
  { // Stretching
//qdbg("    stretching\n");
    damperRate=reboundRate;
  } else
  {
    // Compressing
//qdbg("    compressing\n");
    damperRate=bumpRate;
  }
  // Force is proportional to damper rate and velocity
  forceDamper=-damperRate*pistonVelocity;
#ifdef LTRACE
qdbg("  pistonVelocity=%.2f,.%2f,.%2f\n",
pistonVelocity.x,pistonVelocity.y,pistonVelocity.z);
qdbg("  damper force=%.2f,%.2f,%.2f\n",
forceDamper.x,forceDamper.y,forceDamper.z);
#endif

  // Total force
  force+=forceDamper;
  
  // Total acts on 2 points; body and wheel
  //forceBody=-force;		// Future unary operator
  forceBody.x=-force.x;
  forceBody.y=-force.y;
  forceBody.z=-force.z;
  forceWheel=force;
#ifdef LTRACE
qdbg("RSusp:CF; forceWheel=%.2f,%.2f,%.2f\n",
forceWheel.x,forceWheel.y,forceWheel.z);
#endif
}
void RSuspension::ApplyForces()
{
}

void RSuspension::Integrate()
{
  // Look at the suspension movement
  //...
}

/*******************
* External effects *
*******************/
bool RSuspension::ApplyWheelTranslation(DVector3 *translation,
bool RSuspension::ApplyWheelTranslation(DVector3 *translation,  DVector3 *velocity)
// The wheel is moving; stretch or compress the spring
// If the translation leads to a physically impossible state,
// FALSE is returned and 'translation' contains the translation
// needed to get the wheel inside physically possible limits.
// Otherwise the wheel
// could move farther than the suspension will allow.
// We should signal this to the driver perhaps; a hard bump stop!
// 'velocity' contains the wheel velocity with respect to the car
{
  // Y changes length of suspension
  length-=translation->y;
  // Maximum reached?
  if(length<minLength)
  {
#ifdef LTRACE
qdbg("RSusp:AWT; l<min: length=%f, minLen=%f, org translateY=%f, tY=%f\n",
length,minLength,translation->y,minLength-length);
#endif
    translation->y=length-minLength;
    length=minLength;
    pistonVelocity.SetToZero();
    return FALSE;
  } else if(length>maxLength)
  {
#ifdef LTRACE
qdbg("RSusp:AWT; l>max: length=%f, minLen=%f, org translateY=%f, tY=%f\n",
length,minLength,translation->y,minLength-length);
#endif
    translation->y=maxLength-length;
    length=maxLength;
    pistonVelocity.SetToZero();
    return FALSE;
  }
  
  // Velocity of wheel wrt car
  // As the wheel is connected to the suspension vertically,
  // the piston velocity is the same
  pistonVelocity=*velocity;
#ifdef LTRACE
qdbg("RSusp::ApplyWheelTrans; pistonVelocity=%.2f,%.2f,%.2f\n",
pistonVelocity.x,pistonVelocity.y,pistonVelocity.z);
#endif
  return TRUE;
}