Module: mrun.cpp

Flat functions
 

exitfuncvoid exitfunc()
MapXYtoCtlstatic void MapXYtoCtl(int x,int y)

Convert X/Y coords to controller input

eventbool event(QEvent *e)
SetupViewportvoid SetupViewport(int w,int h)
GGetDrawableQDraw *GGetDrawable()
GGetCVQCanvas *GGetCV()
GSwapvoid GSwap()
PaintVarstatic void PaintVar(cstring name,int value,int *x,int *y)
PaintVarstatic void PaintVar(cstring name,double value,int *x,int *y)
PaintControllerStatusstatic void PaintControllerStatus()

Show different vars
Assumes 2D view has been set up

SceneAnimatevoid SceneAnimate()

Animate all things upto the current time

UpdateControllersvoid UpdateControllers()
idlefuncvoid idlefunc()
glFogfglFogf(GL_FOG_START,20.99f);

glFogf(GL_FOG_END,100.0f);
#endif
glHint(GL_FOG_HINT,GL_DONT_CARE);
glClearColor(fogColor[0],fogColor[1],fogColor[2],0);
} else
{
// Blue sky
glClearColor(51.f/255.f,93.f/255.f,165.f/255.f,0);
}
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
//glCullFace(GL_FRONT);
//glFrontFace(GL_CW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
float lightDir[3]={ 0,-.1,-1 };
//float lightPos[4]={ 1,1,1,0 };
float lightPos[4]={ -1,1,1,0 };
//glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);
glEnable(GL_DEPTH_TEST);

// Camera
car->GetCamera(curCam)->Go();

glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
//glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);

QTRACEQTRACE("idlefunc: RMGR->scene=%p\n",RMGR->scene);

if(rPrefs.bReflections)
{
#ifndef WIN32
glBlendFunc(GL_ONE,GL_CONSTANT_ALPHA_EXT);
glBlendColorEXT(.5,.5,.5,.5);
glEnable(GL_BLEND);
#else
glDisable(GL_BLEND);
#endif
} else
{
glDisable(GL_BLEND);
}

#ifdef DO_DEBUG_POINTS
// Paint dev debug test points in hard color
glColor3f(0,1,0);
glPointSize(2);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glBegin(GL_POINTS);
for(int i=0;i<car->GetWheels();i++)
{
glVertex3f(car->GetWheel(i)->devPoint.x,
car->GetWheel(i)->devPoint.y,
car->GetWheel(i)->devPoint.z);
}
glEnd();
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
#endif

// Paint view (cockpit elements) of current car/camera
// Set 2D view
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glOrtho(0,DRW->GetWidth(),0,DRW->GetHeight(),-1,1);
car->GetViews()->GetView(0)->Paint();

// Info
PaintControllerStatus();

PROFILE(RProfile::PROF_SWAP);
GSwap();

Setupvoid Setup()
Runvoid Run()


/*
 * Racer
 * 05-08-2000: Created!
 * NOTES:
 * - Trying to get it Win32/Banshee/Win2000 compatible
 * (C) MarketGraph/RvG
 */

#include "main.h"
#pragma hdrstop
#include <d3/global.h>
#include <d3/fps.h>
#include <d3/texfont.h>
#include <qlib/timer.h>
#include <qlib/dxjoy.h>
#include <GL/glu.h>
#include <qlib/debug.h>
DEBUG_ENABLE

#define DRW        QSHELL
#define DEBUG_INI  "debug.ini"

//#ifndef WIN32
#define USE_STATS
//#define USE_JOYWATCH
#define USE_LAP_STATS
//#endif

// Green dots to indicate where the wheels are?
//#define DO_DEBUG_POINTS

// Red axis system to indicate world axes?
//#define DO_AXES

//#define FORCE_MOUSE
#ifndef WIN32
// SGI always uses mouse
#define FORCE_MOUSE
#endif

// Profile the simulation?
#define USE_PROFILING

#ifdef USE_PROFILING
#define PROFILE(n)     RMGR->profile->SwitchTo(n)
#else
#define PROFILE(n)
#endif

// Maximum time to emulate per gfx frame
// If this limit would not exist, you could get a frequency of
// simulation that will NEVER draw a frame. (seems like it crashes)
// However, IF this limit is exceeded, slomo will occur. On the other
// hand, it will run at such a low framerate you will not play it at all.
#define MAX_SIMTIME_PER_FRAME 1000

// Graphics
DGlobal   dglobal;
DFPS     *fps;
DTexFont *texfStat;

enum GfxFlags
{
  GFXF_FPS=1
};
int     gfxFlags=GFXF_FPS;

RCar         *car;

// Debugging
RDebugInfo *rdbg;

// Controller info
int ctlSteer;
int ctlThrottle;
int ctlBrakes,ctlClutch;

// Camera
int curCam=0;

struct Statistics
{
  double fps;                // Frames per second
  int    frame;              // Painted frame number
};

struct Prefs
{
  bool bReflections;         // Draw reflection of car?
  int  integrationTime;      // ms per integration step
  int  framesMax;            // After this #frames, auto exit program
  // Fog
  bool   bFog;
  dfloat visibility,
         fogDensity;
};

static Statistics rStats;
static Prefs      rPrefs;
static int  tSimTime;             // Simulated time in milliseconds
static bool isRunning=TRUE;

void exitfunc()
{
  if(RMGR)delete RMGR;
}

/*********
* EVENTS *
*********/
static void MapXYtoCtl(int x,int y)
// Convert X/Y coords to controller input
{
  x-=DRW->GetWidth()/2;
  y-=DRW->GetHeight()/2;
#ifdef FORCE_MOUSE
  if(!(rdbg->flags&RF_NO_STEERING))
  { 
    //if(!RMGR->control[0]->IsOpen())
    { // Mouse
      ctlSteer=-x*3;
    }
  }
  //if(!rJoy->IsOpen())
  {
    // Combined axis simulation
    if(y<0)
    { ctlThrottle=-y*4;
      ctlBrakes=0;
      if(ctlThrottle>1000)
        ctlThrottle=1000;
    } else
    { ctlThrottle=0;
      ctlBrakes=y*4;
      if(ctlBrakes>1000)
        ctlBrakes=1000;
    }
  }
#endif
}

bool event(QEvent *e)
{
  int gear;

  PROFILE(RProfile::PROF_EVENTS);

  if(e->type==QEvent::MOTIONNOTIFY)
  {
#ifdef OBS
    MapXYtoCtl(e->x,e->y);
#endif
  } else if(e->type==QEvent::KEYPRESS)
  {
    if(e->n==QK_ESC)
      app->Exit(0);

    // Camera selection
    if(e->n==QK_1)
    { curCam=0;
    } else if(e->n==QK_2)
    { curCam=1;
    } else if(e->n==QK_3)
    { curCam=2;
    } else if(e->n==QK_4)
    { curCam=3;
    } else if(e->n==QK_5)
    { curCam=4;
    } else if(e->n==QK_6)
    { curCam=5;
    } else if(e->n==QK_7)
    { curCam=6;
    } else if(e->n==QK_8)
    { curCam=7;
    } else if(e->n==QK_9)
    { curCam=8;
    } else if(e->n==QK_0)
    { curCam=9;
    }
    
    if(e->n==(QK_ALT|QK_F))
    {
      // Toggle FPS
      if(!(gfxFlags&GFXF_FPS))
      { // Reset FPS counter
        fps->Reset();
      }
      gfxFlags^=GFXF_FPS;
    } else if(e->n==(QK_SHIFT|QK_R))
    {
      // Move car to first grid location (pole position)
      car->Warp(&RMGR->track->gridPos[0]);
    } else if(e->n==(QK_SHIFT|QK_P))
    {
      // Move car to first pit location
      car->Warp(&RMGR->track->pitPos[0]);
    }
    
    // Gear selection
    if(e->n==QK_UP)
    { gear=car->GetEngine()->GetGear();
      if(gear<car->GetEngine()->GetGears()-1)
        car->GetEngine()->SetGear(gear+1);
    } else if(e->n==QK_DOWN)
    {
      gear=car->GetEngine()->GetGear();
      if(gear>0)
        car->GetEngine()->SetGear(gear-1);
    }
    // Pause
    if(e->n==QK_P)
    { isRunning=!isRunning;
    }
  }

  // Return to other stuff
  PROFILE(RProfile::PROF_OTHER);

  return FALSE;
}

/***********
* Viewport *
***********/
void SetupViewport(int w,int h)
{
  static bool fSetup=FALSE;
  static DMatrix4 matPrj;
  
  if(fSetup)
  {
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(matPrj.GetM());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    return;
  }
  
  QInfo *info=RMGR->GetGfxInfo();
  QRect r;
  
  // Calculate projection
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(50.0,(GLfloat)w/(GLfloat)h,0.1f,
    RMGR->GetGfxInfo()->GetFloat("view.visibility",300.f));
  // Remember for later
  glGetFloatv(GL_PROJECTION_MATRIX,matPrj.GetM());
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
#ifdef FUTURE
  r.x=info->GetInt("view.render.x");
  r.y=info->GetInt("view.render.y");
  r.wid=info->GetInt("view.render.wid");
  r.hgt=info->GetInt("view.render.hgt");
  glViewport(r.x,r.y,r.wid,r.hgt);
  glScissor(r.x,r.y,r.wid,r.hgt);
  glEnable(GL_SCISSOR_TEST);
#endif
  fSetup=TRUE;
}

/***********
* Painting *
***********/
QDraw *GGetDrawable()
{
  return DRW;
}
QCanvas *GGetCV()
{
  return DRW->GetCanvas();
}
void GSwap()
{ DRW->Swap();
}

/*********
* Status *
*********/
static void PaintVar(cstring name,int value,int *x,int *y)
{
  QCanvas *cv=GGetCV();
  char buf[80];
  
  sprintf(buf,"%s: %d",name,value);
  //cv->Text(buf,*x,*y);
  texfStat->Paint(buf,*x,*y);
  *y-=16;
}
static void PaintVar(cstring name,double value,int *x,int *y)
{
  QCanvas *cv=GGetCV();
  char buf[80];
  
  sprintf(buf,"%s: %.3f",name,value);
  //cv->Text(buf,*x,*y);
  texfStat->Paint(buf,*x,*y);
  *y-=16;
}
#define _PV(s,v) PaintVar(s,v,&x,&y)
static void PaintControllerStatus()
// Show different vars
// Assumes 2D view has been set up
{
  QCanvas *cv=GGetCV();
  char buf[80];
  int  x,y;
  
  x=20; y=DRW->GetHeight()-20;
#ifdef OBS
  cv->SetFont(app->GetSystemFont());
  cv->SetColor(255,255,255);
  glDisable(GL_LIGHTING);
#endif
  
#ifdef OBS
  glDisable(GL_LIGHTING);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_BLEND);
#endif
  glDisable(GL_CULL_FACE);
  //glShadeModel(GL_FLAT);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
    
  // FPS
  fps->FrameRendered();
  if(gfxFlags&GFXF_FPS)
  {
    _PV("FPS",fps->GetFPS());
  }
  
#ifdef USE_STATS
  _PV("Real time (s)",RMGR->time->GetRealTime()/1000);
  _PV("Simulated time (s)",RMGR->time->GetSimTime()/1000);
  _PV("Vel (km/h)",(3.6f*car->GetVelocity()->Length())); // m/s => km/h
  _PV("Gear",car->GetEngine()->GetGear());
#ifdef USE_LAP_STATS
  _PV("Lap",RMGR->scene->curLap[0]);
  _PV("Timeline index",RMGR->scene->curTimeLine[0]);
  _PV("1st section time",RMGR->scene->tlTime[0][0]);
#endif
  //_PV("steer",ctlSteer);
  _PV("throttle%",ctlThrottle/10.0f);
  _PV("brakes%",ctlBrakes/10.0f);
  _PV("clutch%",ctlClutch/10.0f);
  //_PV("rpm",car->GetEngine()->GetRPM());
  _PV("wheel(0)-Mz",car->GetWheel(0)->GetAligningTorque());
  //_PV("Joy0 B1",RMGR->controls->joy[0]->button[1]);
  //_PV("Car heading",(float)(car->GetHeading()*RAD_DEG_FACTOR));
#ifdef OBS
  _PV("Slipratio wheel 0 (m/s)",car->GetWheel(0)->GetSlipRatio());
  _PV("Slipratio wheel 2 (m/s)",car->GetWheel(2)->GetSlipRatio());
#endif
#ifdef USE_JOYWATCH
  // Joystick watch
  _PV("Joy0 X",RMGR->controls->joy[0]->x);
  _PV("Joy0 Y",RMGR->controls->joy[0]->y);
  _PV("Joy0 Z",RMGR->controls->joy[0]->z);
  _PV("Joy0 Rx",RMGR->controls->joy[0]->rx);
  _PV("Joy0 Ry",RMGR->controls->joy[0]->ry);
  _PV("Joy0 Rz",RMGR->controls->joy[0]->rz);
  // Controls watching
  _PV("Ctl SteerLeft",RMGR->controls->control[RControls::T_STEER_LEFT]->value);
  _PV("Ctl SteerRight",RMGR->controls->control[RControls::T_STEER_RIGHT]->value);
  _PV("Ctl Throttle",RMGR->controls->control[RControls::T_THROTTLE]->value);
  _PV("Ctl Brakes",RMGR->controls->control[RControls::T_BRAKES]->value);
  _PV("Ctl ShiftUp",RMGR->controls->control[RControls::T_SHIFTUP]->value);
  _PV("Ctl ShiftDown",RMGR->controls->control[RControls::T_SHIFTDOWN]->value);
#endif
#ifdef USE_PROFILING
  // Profiling
  RMGR->profile->Update();
  _PV("CPU% physics",
    RMGR->profile->GetTimePercentageFor(RProfile::PROF_PHYSICS));
  _PV("CPU% graphics",
    RMGR->profile->GetTimePercentageFor(RProfile::PROF_GRAPHICS));
#ifdef ND_NOT_TOO_MUCH
  _PV("CPU% events",
    RMGR->profile->GetTimePercentageFor(RProfile::PROF_EVENTS));
  _PV("CPU% controls",
    RMGR->profile->GetTimePercentageFor(RProfile::PROF_CONTROLS));
  _PV("CPU% swap",
    RMGR->profile->GetTimePercentageFor(RProfile::PROF_SWAP));
  _PV("CPU% other",
    RMGR->profile->GetTimePercentageFor(RProfile::PROF_OTHER));
#endif
#endif

#endif
}

/**************
* Integration *
**************/
void SceneAnimate()
// Animate all things upto the current time
{
  static int lastTime;
  int    curTime,diffTime;
  //double diffTimeDbl;

  // Calculate time from last to current situation
  RMGR->time->Update();
  curTime=RMGR->time->GetRealTime();
  diffTime=curTime-lastTime;
#ifdef OBS
  diffTimeDbl=((double)(curTime-lastTime))/1000.0f;
  rStats.fps=1/diffTimeDbl;
#endif
  
  // Frequency of simulation is constant; the drawing of gfx
  // frames is NOT
  if(diffTime>MAX_SIMTIME_PER_FRAME)
    diffTime=MAX_SIMTIME_PER_FRAME;
  
  // Calculate physics until we are at the last realtime checkpoint
  int integrations=0;
//qdbg("controller input: %d %d %d\n",ctlSteer,ctlThrottle,ctlBrakes);
  car->SetInput(ctlSteer,ctlThrottle,ctlBrakes,ctlClutch);
  //timeSpan=rdbg->timeSpan;
  while(RMGR->time->GetSimTime()<curTime)
  {
    integrations++;
//qdbg("-----------------------\n");
    car->Animate();
  
    // Keep total SIMULATED time (in case of slomo, this is NOT
    // the same as real time)
    RMGR->time->AddSimTime(rPrefs.integrationTime);
    
    // If we want to draw every frame, don't continue calculating
    if(rdbg->flags&RF_DRAW_EVERY_STEP)
      break;
  }
 
  // Update other sim stuff that are out of the integration loop
  car->OnGfxFrame();

  // Audio
  if(integrations>0)
  { if(rdbg->flags&RF_DRAW_EVERY_STEP)
    { // Debug does a bit more audio to get a better feel
      RMGR->audio->Run(RMGR->time->span*integrations*5);
    } else
    { // Actual sim rate
      RMGR->audio->Run(RMGR->time->span*integrations);
    }
  }
  
  // We are now with the simulation near the actual real time
  lastTime=curTime;
}

void UpdateControllers()
{
  rfloat Mz;

  // Get aligning moment
  Mz=car->GetWheel(0)->GetAligningTorque()+car->GetWheel(1)->GetAligningTorque();
  RMGR->controls->SetFFSteerResponse(Mz);
  // Read the new set of controller data
  RMGR->controls->Update();
  // Add both steering controls
  ctlSteer=RMGR->controls->control[RControls::T_STEER_LEFT]->value-
           RMGR->controls->control[RControls::T_STEER_RIGHT]->value;
  ctlThrottle=RMGR->controls->control[RControls::T_THROTTLE]->value;
  ctlBrakes=RMGR->controls->control[RControls::T_BRAKES]->value;
  ctlClutch=RMGR->controls->control[RControls::T_CLUTCH]->value;
//qdbg("throttle=%d, brakes=%d, steer=%d\n",ctlThrottle,ctlBrakes,ctlSteer);
}

void idlefunc()
{
  DVector3 *v;
  
  PROFILE(RProfile::PROF_CONTROLS);

  // Update controller status
  UpdateControllers();

  // Physics
  PROFILE(RProfile::PROF_PHYSICS);
  if(isRunning)
    SceneAnimate();
  
  // Don't paint when not running physics
  if(!isRunning)return;
  
  PROFILE(RProfile::PROF_GRAPHICS);

  GGetCV()->Select();
  SetupViewport(DRW->GetWidth(),DRW->GetHeight());
  GGetCV()->SetFont(app->GetSystemFont());
  
  if(rPrefs.bFog)
  {
    GLfloat density;
    GLfloat fogColor[4]={ .4,.4,.6,1.0 };
    glEnable(GL_FOG);
    glFogi(GL_FOG_MODE,GL_EXP);
    glFogfv(GL_FOG_COLOR,fogColor);
    glFogf(GL_FOG_DENSITY,rPrefs.fogDensity);
    glFogf(GL_FOG_START,10.0f);
    glFogf(GL_FOG_END,rPrefs.visibility*10);
#ifdef OBS
glFogf(GL_FOG_START,20.99f);
glFogf(GL_FOG_END,100.0f);
#endif
    glHint(GL_FOG_HINT,GL_DONT_CARE);
    glClearColor(fogColor[0],fogColor[1],fogColor[2],0);
  } else
  {
    // Blue sky
    glClearColor(51.f/255.f,93.f/255.f,165.f/255.f,0);
  }
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  glEnable(GL_CULL_FACE);
  //glCullFace(GL_FRONT);
  //glFrontFace(GL_CW);
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  float lightDir[3]={ 0,-.1,-1 };
  //float lightPos[4]={ 1,1,1,0 };
  float lightPos[4]={ -1,1,1,0 };
  //glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);
  glEnable(GL_DEPTH_TEST);
  
  // Camera
  car->GetCamera(curCam)->Go();
  
  glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
  //glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);

#ifdef DO_AXES
  v=car->GetPosition();
  RGfxAxes(v->x,v->y+.01,v->z,1);
#endif
  //glTranslatef(0,-1.66,-5.0);
  //glRotatef(15,1,0,0);
#ifdef ND_MOVING_CAM
  static float vx=.05;
  static float y=2.5;
  y+=vx; if(y>5||y<1)vx=-vx;
  gluLookAt(y*2-6,y /*1.66*/,-5.0, 0,0,0, 0,1,0);
#endif
    
  // Track painting
  glShadeModel(GL_FLAT);
  glDisable(GL_LIGHTING);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
  car->SetDefaultMaterial();
  RMGR->GetTrack()->Paint();
  RMGR->GetTrack()->PaintHidden();

  // Cars (lit)
  glEnable(GL_LIGHTING);
  glShadeModel(GL_SMOOTH);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
  car->Paint();
  
  // Unlit geometry coming up
  glShadeModel(GL_FLAT);
  glDisable(GL_LIGHTING);
  glDisable(GL_BLEND);
  glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);
  
  if(rPrefs.bReflections)
  { // Reflections
    glFrontFace(GL_CW);
    glPushMatrix();
    glScalef(1,-1,1);
    //glPixelZoom(1,-1);
    car->Paint();
    glPopMatrix();
    glFrontFace(GL_CCW);
    //glPixelZoom(1,1);
    //glScalef(1,1,1);
  }
  
#ifdef OBS
  // Shadows
  //glFrontFace(GL_CW);
  glPushMatrix();
  glScalef(1,0,1);
  car->Paint();
  glPopMatrix();
  //glFrontFace(GL_CCW);
#endif
  
  // Floor
QTRACE("idlefunc: RMGR->scene=%p\n",RMGR->scene);
  if(rPrefs.bReflections)
  {
#ifndef WIN32
    glBlendFunc(GL_ONE,GL_CONSTANT_ALPHA_EXT);
    glBlendColorEXT(.5,.5,.5,.5);
    glEnable(GL_BLEND);
#else
    glDisable(GL_BLEND);
#endif
  } else
  {
    glDisable(GL_BLEND);
  }
  
#ifdef DO_DEBUG_POINTS
  // Paint dev debug test points in hard color
  glColor3f(0,1,0);
  glPointSize(2);
  glDisable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_DEPTH_TEST);
  glBegin(GL_POINTS);
  for(int i=0;i<car->GetWheels();i++)
  {
    glVertex3f(car->GetWheel(i)->devPoint.x,
               car->GetWheel(i)->devPoint.y,
               car->GetWheel(i)->devPoint.z);
  }
  glEnd();
  glEnable(GL_LIGHTING);
  glEnable(GL_DEPTH_TEST);
#endif
  
  // Paint view (cockpit elements) of current car/camera
  // Set 2D view
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glOrtho(0,DRW->GetWidth(),0,DRW->GetHeight(),-1,1);
  car->GetViews()->GetView(0)->Paint();
  
  // Info
  PaintControllerStatus();
  
  PROFILE(RProfile::PROF_SWAP);
  GSwap();

  PROFILE(RProfile::PROF_OTHER);
  if(rdbg->flags&RF_SLOMO)
  { QNap(rdbg->slomo);
  }
  
  if(++rStats.frame==rPrefs.framesMax)
    app->Exit(0);
}

void Setup()
{
  float v;
  cstring name;
  
  // Get window up
  DRW->GetCanvas()->SetMode(QCanvas::DOUBLEBUF);
  app->RunPoll();
  app->SetIdleProc(idlefunc);
  app->SetExitProc(exitfunc);
  app->SetEventProc(event);

  // Display 'loading...' picture
  rrFullScreenTexture("data/images/racer512.tga");
  
  // Create Racer manager
  new RManager();
  RMGR->Create();

  // Debugging
  //RMGR->infoDebug=new QInfo(DEBUG_INI);
  rdbg=(RDebugInfo*)qcalloc(sizeof(RDebugInfo));
  if(RMGR->infoDebug->GetInt("timing.draw_every_step"))
    rdbg->flags|=RF_DRAW_EVERY_STEP;
  v=RMGR->infoDebug->GetFloat("timing.slomo");
  if(v)
  { rdbg->slomo=v;
    rdbg->flags|=RF_SLOMO;
  }
  if(RMGR->infoDebug->GetInt("controls.steering")==FALSE)
    rdbg->flags|=RF_NO_STEERING;
  if(RMGR->infoDebug->GetInt("stats.wheelinfo")==TRUE)
    rdbg->flags|=RF_WHEEL_INFO;
  
  // Prefs
  rPrefs.integrationTime=RMGR->infoDebug->GetInt("timing.integration_time");
  rPrefs.bReflections=RMGR->infoDebug->GetInt("gfx.reflections");
  rPrefs.framesMax=RMGR->infoDebug->GetInt("timing.frames_max");
  //rdbg->timeSpan=((double)rPrefs.integrationTime)/1000.0f;
  RMGR->time->span=((double)rPrefs.integrationTime)/1000.0f;
  rPrefs.bFog=RMGR->infoGfx->GetInt("fx.fog");
  rPrefs.visibility=RMGR->infoGfx->GetFloat("fx.visibility");
  rPrefs.fogDensity=RMGR->infoGfx->GetFloat("fx.fog_density");
  
  // Create a scene
  RMGR->scene->env=new REnvironment();
  RMGR->scene->Reset();
  
  // Load a track
  rrTrackLoad(RMGR->infoMain->GetStringDirect("ini.track"));
  //RMGR->LoadTrack(RMGR->infoMain->GetStringDirect("ini.track"));
  //RMGR->scene->SetTrack(RMGR->trackVRML);
  
  // Create a car
  name=RMGR->GetMainInfo()->GetStringDirect("dev.car","devtest");
qdbg("Loading car '%s'\n",name);
  car=new RCar(name);
  RMGR->scene->AddCar(car);
  // Move car to first grid location (pole position)
  car->Warp(&RMGR->track->gridPos[0]);
  
  // Start debug info
  ctlSteer=RMGR->infoDebug->GetInt("car.steer");
  ctlThrottle=RMGR->infoDebug->GetInt("engine.throttle");
  ctlBrakes=RMGR->infoDebug->GetInt("engine.brakes");
  ctlClutch=RMGR->infoDebug->GetInt("engine.clutch",1000);
  
  DRW->GetCanvas()->SetMode(QCanvas::DOUBLEBUF);

  // Now that the window is up, we can continue with some things
  RMGR->controls->LoadConfig();
  
  // Graphics
  fps=new DFPS();
  QCV->Select();
  texfStat=new DTexFont("data/fonts/euro16");

  // Start profiling
  RMGR->profile->Reset();
  
  // Start the race!
  RMGR->time->Start();
}

void Run()
{
  // Trigger Win32's qdbg() logging on COM1
  //qlog(QLOG_INFO,"Racer started.");
  Setup();
  app->Run();
}