Module: rcontrol.cpp

class RControl
.h

constructorRControl()
destructor~RControl()
Updatevoid Update()

Flat functions
 

ifif(joy==RR_CONTROLLER_MOUSE)

qdbg("mouse control Update; button=%d, axis=%d\n",button,axis);
#endif
if(axis>=0)
{
// Axis range
if(joy==RR_CONTROLLER_MOUSE)
{
int x,y;
app->GetMouseXY(&x,&y);
// Scale to -1000..1000
x-=RMGR->resWidth/2;
x=x*2000/RMGR->resWidth;
//y=RMGR->resHeight-1-y-RMGR->resHeight/2;
y-=RMGR->resHeight/2;
y=y*2000/RMGR->resHeight;
switch(axis)
{
case AXIS_X: v=x; break;
case AXIS_Y: v=y; break;
default: v=0; break;
}
dbg("mouse control Update; v=%d, x=%d,y=%d\n",v,x,y);
} else
{
// Joystick
switch(axis)
{
case AXIS_X: v=joy->x; break;
case AXIS_Y: v=joy->y; break;
case AXIS_Z: v=joy->z; break;
case AXIS_RX: v=joy->rx; break;
case AXIS_RY: v=joy->ry; break;
case AXIS_RZ: v=joy->rz; break;
default: v=0; break;
}
}
// Keep within range
if(v<min)v=min;
else if(v>max)v=max;
// Bring up to 0-base
v-=base;
if(range!=1000&&range!=0)
{
// Scale to virtual range (1000)
v=v*1000/range;
}
// Adjust for range
//...
} else if(button>=0)
{
// Look at button
if(joy==RR_CONTROLLER_MOUSE)
{
int b=(app->GetMouseButtons()>>1);
if(b&(1<<button))v=1000;
else v=0;
dbg("button %d? b=%d, v=%d\n",button,b,v);
} else
{
// Joystick
if(joy->button[button])v=1000;
else v=0;
}
}


class RControls
.h

constructorRControls()
destructor~RControls()
LoadControllervoid LoadController(QInfo *info,cstring path,RControl *ctl)

Read a controller config from the info file

LoadConfigbool LoadConfig(cstring fname)

Read controls configuration file
'fname' may be 0, in which case a default controller file is searched for
in data/ctrlsets

Updatevoid Update()

Poll all used controllers; don't use too often please,
might be quite slow for old joysticks.

SetFFSteerResponsevoid SetFFSteerResponse(rfloat steeringTorque)

Set feedback forces
'steeringTorque' gives the torque that pulls the steering wheel (Nm)



/*
 * RControl
 * 05-10-00: Created!
 * 19-10-00: Implemented for real and actually using it.
 * 18-12-00: Preset files in data/ctrlsets (use Config to set)
 * NOTES:
 * - Keeps the logic for obtaining controller input, whether from joysticks
 * or keyboard
 * - Is quite a bit based on the number 1000 (hardcoded ranges).
 * (c) Dolphinity/Ruud van Gaal
 */

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

// Default directory for presets
#define CTRLSET_DIR   "data/ctrlsets"

// Default preset file (hopefully present in CTRLSET_DIR)
#define DEF_CTRL_FILE	"genjoy.ini"

/***********
* RCONTROL *
***********/
RControl::RControl()
{
  joy=0;
  axis=-1;
  button=-1;
  min=0; max=1000;
  base=0; range=0;
  flags=0;
  value=0;
}
RControl::~RControl()
{
}

void RControl::Update()
{
  int v;

  if(!joy)return;
//if(joy==RR_CONTROLLER_MOUSE)return;
  
#ifdef OBS
if(joy==RR_CONTROLLER_MOUSE)
qdbg("mouse control Update; button=%d, axis=%d\n",button,axis);
#endif
  if(axis>=0)
  {
    // Axis range
    if(joy==RR_CONTROLLER_MOUSE)
    {
      int x,y;
      app->GetMouseXY(&x,&y);
      // Scale to -1000..1000
      x-=RMGR->resWidth/2;
      x=x*2000/RMGR->resWidth;
      //y=RMGR->resHeight-1-y-RMGR->resHeight/2;
      y-=RMGR->resHeight/2;
      y=y*2000/RMGR->resHeight;
      switch(axis)
      {
        case AXIS_X:  v=x; break;
        case AXIS_Y:  v=y; break;
        default: v=0; break;
      }
//qdbg("mouse control Update; v=%d, x=%d,y=%d\n",v,x,y);
    } else
    {
      // Joystick
      switch(axis)
      {
        case AXIS_X:  v=joy->x; break;
        case AXIS_Y:  v=joy->y; break;
        case AXIS_Z:  v=joy->z; break;
        case AXIS_RX: v=joy->rx; break;
        case AXIS_RY: v=joy->ry; break;
        case AXIS_RZ: v=joy->rz; break;
        default: v=0; break;
      }
    }
    // Keep within range
    if(v<min)v=min;
    else if(v>max)v=max;
    // Bring up to 0-base
    v-=base;
    if(range!=1000&&range!=0)
    {
      // Scale to virtual range (1000)
      v=v*1000/range;
    }
    // Adjust for range
    //...
  } else if(button>=0)
  {
    // Look at button
    if(joy==RR_CONTROLLER_MOUSE)
    {
      int b=(app->GetMouseButtons()>>1);
      if(b&(1<<button))v=1000;
      else             v=0;
//qdbg("button %d? b=%d, v=%d\n",button,b,v);
    } else
    {
      // Joystick
      if(joy->button[button])v=1000;
      else v=0;
    }
  }

  // Negation
  if(flags&RControl::NEGATE)v=1000-v;
  
  // Store value
  value=v;
}

/************
* RCONTROLS *
************/
RControls::RControls()
{
  int i;

  joys=0;
  for(i=0;i<MAX_CONTROLLER_TYPE;i++)
    control[i]=0;
  for(i=0;i<MAX_CONTROLLER;i++)
  {
    fxAlign[i]=fxFriction[i]=fxInertia[i]=0;
    flags[i]=0;
    ffMaxTorque[i]=ffMaxForce[i]=ffDelay[i]=0;
  }
}
RControls::~RControls()
{
  int i;
  // Delete all effects
  for(i=0;i<MAX_CONTROLLER;i++)
  {
    if(fxAlign[i])delete fxAlign[i];
    if(fxFriction[i])delete fxFriction[i];
    if(fxInertia[i])delete fxInertia[i];
  }
  for(i=0;i<joys;i++)
  {
    if(joy[i]!=0&&joy[i]!=RR_CONTROLLER_MOUSE)delete joy[i];
  }
}

void RControls::LoadController(QInfo *info,cstring path,RControl *ctl)
// Read a controller config from the info file
{
  char buf[256];
  int index;
  qstring s;

  // Get controller or notice if none is used for this control
  // (may be keyboard still)
  sprintf(buf,"%s.controller",path);
  index=info->GetInt(buf);
  if(index<0)
  { ctl->joy=0;
    goto no_ctl;
  } else
  { if(index>joys-1)index=joys-1;
    ctl->joy=joy[index];
  }
  // Axis
  sprintf(buf,"%s.axis",path);
  info->GetString(buf,s);
  if(s=="x")ctl->axis=RControl::AXIS_X;
  else if(s=="y")ctl->axis=RControl::AXIS_Y;
  else if(s=="z")ctl->axis=RControl::AXIS_Z;
  else if(s=="rx")ctl->axis=RControl::AXIS_RX;
  else if(s=="ry")ctl->axis=RControl::AXIS_RY;
  else if(s=="rz")ctl->axis=RControl::AXIS_RZ;
  else
  { //qwarn("Unknown controller axis type for '%s'",buf);
    ctl->axis=RControl::AXIS_NONE;
  }
  
  // Axis range
  int min,max;
  sprintf(buf,"%s.min",path);
  min=info->GetInt(buf,0);
  sprintf(buf,"%s.max",path);
  max=info->GetInt(buf,1000);

  // Calculate base axis value and range
  if(min<max)
  {
    ctl->base=min;
    ctl->range=max-min;
    ctl->flags&=~RControl::NEGATE;
  } else
  {
    int t;
    ctl->base=max;
    ctl->range=min-max;
    ctl->flags|=RControl::NEGATE;
    // Swap min/max
    t=min;
    min=max;
    max=t;
  }
  ctl->min=min;
  ctl->max=max;
/*qdbg("RControls:LoadController: min=%d, max=%d, base=%d,"
" range=%d, flags=%d\n",
ctl->min,ctl->max,ctl->base,ctl->range,ctl->flags);*/
  // Button?
  int button;
  sprintf(buf,"%s.button",path);
  button=info->GetInt(buf,-1);
  ctl->button=button;
  // Take precedence over axis if a button is specified
  if(ctl->button!=-1)
    ctl->axis=RControl::AXIS_NONE;
//qdbg("ctl->button=%d\n",button);

  // Manual negate?
  sprintf(buf,"%s.negate",path);
  if(info->GetInt(buf))
    ctl->flags|=RControl::NEGATE;

no_ctl:;
  // Check for keys...
}

bool RControls::LoadConfig(cstring fname)
// Read controls configuration file
// 'fname' may be 0, in which case a default controller file is searched for
// in data/ctrlsets
{
  QInfo *info;
  int i;
  char buf[256];

  if(!fname)
  {
    // Search for selected controller file
    RMGR->GetMainInfo()->GetString("ini.controls",buf,DEF_CTRL_FILE);
qdbg("RControls: indirection to '%s'\n",buf);
    info=new QInfo(RFindFile(buf,CTRLSET_DIR));
  } else
  {
    // Explicit controller file
    qwarn("RControls: explicit controller file given; this is obsolete");
    info=new QInfo(RFindFile(fname));
  }

  joys=info->GetInt("controllers.count");
  // Only 1 support currently (QDXJoy)
  if(joys>1)
  {
    qwarn("Current limit of controllers is just 1 (more specified in %s)",
      fname);
    joys=1;
  }
  if(joys>MAX_CONTROLLER)
  { qwarn("Too many controllers (max=4) specified in controller file (%s)",
      fname);
    joys=MAX_CONTROLLER;
  }

  // Open all used controllers
  for(i=0;i<joys;i++)
  {
    int index;
    
    // Check for special controllers, like the mouse or keyboard
    sprintf(buf,"controllers.controller%d.mouse",i);
    if(info->GetInt(buf))
    {
      joy[i]=RR_CONTROLLER_MOUSE;
    } else
    {
      // Game controller
      sprintf(buf,"controllers.controller%d.index",i);
      index=info->GetInt(buf);
      // Flags
      sprintf(buf,"controllers.controller%d.force_feedback",i);
      if(info->GetInt(buf))
      {
        flags[i]|=FORCE_FEEDBACK;
        // Get FF settings
        sprintf(buf,"controllers.controller%d.max_torque",i);
        ffMaxTorque[i]=info->GetInt(buf);
        sprintf(buf,"controllers.controller%d.max_force",i);
        ffMaxForce[i]=info->GetInt(buf);
        sprintf(buf,"controllers.controller%d.delay",i);
        ffDelay[i]=info->GetInt(buf);
      }
      // Open joystick
      joy[i]=new QDXJoy(index);
      if(!joy[i]->Open())
      { qerr("Can't open controller %d (gamecontroller index %d)",i,index);
      }
    }
  }

  // Read all controls
  cstring ctlName[]=
  { "steerleft","steerright","throttle","brakes","shiftup","shiftdown",
    "clutch"
  };
  for(i=0;i<MAX_CONTROLLER_TYPE;i++)
  {
    control[i]=new RControl();
    if(!control[i])break;
    sprintf(buf,"controls.%s",ctlName[i]);
    LoadController(info,buf,control[i]);
  }

  // Acquire all controllers
  for(i=0;i<joys;i++)
  {
    // Open only real game controllers
    if(joy[i]==RR_CONTROLLER_MOUSE)continue;
    
    if(flags[i]&FORCE_FEEDBACK)
    {
      if(!joy[i]->IsForceFeedback())
      {
        qwarn("Controller %d can't do force feedback; disabling FF",i);
        flags[i]&=~FORCE_FEEDBACK;
      } else
      {
        // Use the force, Luke!
        // Disable autocentering BEFORE acquiring device (!)
        joy[i]->DisableAutoCenter();
        fxAlign[i]=new QDXFFEffect(joy[i]);
        fxAlign[i]->SetupConstantForce();
        //fxAlign[i]->Start();
      }
    }
    joy[i]->Acquire();
  }
  
  return TRUE;
}

void RControls::Update()
// Poll all used controllers; don't use too often please,
// might be quite slow for old joysticks.
{
  int i;

  for(i=0;i<joys;i++)
  {
    if(joy[i]!=0&&joy[i]!=RR_CONTROLLER_MOUSE&&joy[i]->IsOpen())
    {
      joy[i]->Poll();
    }
  }
  for(i=0;i<MAX_CONTROLLER_TYPE;i++)
  {
    control[i]->Update();
  }
}

/*****************
* FORCE FEEDBACK *
*****************/
void RControls::SetFFSteerResponse(rfloat steeringTorque)
// Set feedback forces
// 'steeringTorque' gives the torque that pulls the steering wheel (Nm)
{
  int i;
  rfloat fx,fy;
  
  // Calculate steering force from torque
  // Find FF controllers
  for(i=0;i<joys;i++)
  {
    if(!(flags[i]&FORCE_FEEDBACK))continue;
    
    // Check for force to be valid
    if(!ffMaxTorque[i])continue;
    
    fx=steeringTorque*ffMaxForce[i]/ffMaxTorque[i];
    if(fx<-ffMaxForce[i])fx=-ffMaxForce[i];
    else if(fx>ffMaxForce[i])fx=ffMaxForce[i];
    fy=0;
//qdbg("RControls::SetFFResponse(); fx=%f, fy=%f\n",fx,fy);
    // Update the aligning effect
    fxAlign[i]->UpdateConstantForce(fx,fy);
  }
}