Module: mrun.cpp

Flat functions
 

exitfuncvoid exitfunc()
SelectTrackvoid SelectTrack(cstring name)
GetIndexstatic int GetIndex(cstring text,int maxIndex)

Request an index between 0 and maxIndex
Returns -1 if the action was cancelled.

SetupMenusvoid SetupMenus()
apperrvoid apperr(string s)
cbProgressstatic bool cbProgress(int cur,int total,cstring text)
LoadTrackvoid LoadTrack()
SaveTrackvoid SaveTrack()
eventbool event(QEvent *e)
DbgPrjvoid DbgPrj(cstring s)
SetupViewportvoid SetupViewport(int w,int h)
DbgPrjDbgPrj("SetupViewport");

#ifdef OBS
matPrj.DbgPrint("Projection");
qdbg("glCtx: %p\n",glXGetCurrentContext());
#endif

#ifdef OBS
float d=1000.0f;
glFrustum(-w/d/2,w/d/2,-h/d/2,h/d/2,1,100000);
#endif
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//glTranslatef(0,0,-d);

fSetup=TRUE;
}

GGetDrawableQDraw *GGetDrawable()
GGetCVQCanvas *GGetCV()
GSwapvoid GSwap()
SetGLColorstatic void SetGLColor(QColor *color)

Local function to convert rgba to float

PaintRopestatic void PaintRope(DVector3 *v1,DVector3 *v2,QColor *col)

Paint rope with poles

PaintTrackvoid PaintTrack(int flags)

Paint the entire track
flags&1; output on debug the timing results

glShadeModelglShadeModel(GL_FLAT);

glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_NORMALIZE);
glDisable(GL_DEPTH_TEST);
#endif

glDisableglDisable(GL_LIGHTING);

glDisable(GL_CULL_FACE);
lDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
endif
glColor3f(1,1,1);
glBegin(GL_LINES);
glVertex3f(vPick.x,vPick.y,vPick.z);
glVertex3f(vPick.x,vPick.y+1.0f,vPick.z);
glColor3f(1,1,0);
glVertex3f(vPickEnd.x,vPickEnd.y,vPickEnd.z);
glVertex3f(vPickEnd.x,vPickEnd.y+1.0f,vPickEnd.z);
// Connect the two
glColor3f(1,1,.5);
glVertex3f(vPick.x,vPick.y+1.0f,vPick.z);
glVertex3f(vPickEnd.x,vPickEnd.y+1.0f,vPickEnd.z);
glEnd();
}
t[2]=tmr->GetMilliSeconds();

// 2D stats; remember matrices for picking
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();

// Stats
fps->FrameRendered();
x=20; y=20;
QCV->Set2D();
QCV->SetColor(255,255,255);
QCV->SetFont(app->GetSystemFont());
sprintf(buf,"FPS: %.2f",fps->GetFPS());
QCV->Text(buf,x,y); y+=20;

if(flags&1)qdbg(" t1: %d ms\n",t[1]);

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

idlefuncvoid idlefunc()
Setupvoid Setup()
Runvoid Run()


/*
 * TrackEd - editor for VRML-type tracks
 * 12-11-00: Created!
 * NOTES:
 * (C) MarketGraph/RvG
 */

#include "main.h"
#pragma hdrstop
#ifdef WIN32
#include <direct.h>
#include <stdlib.h>
#include <stdio.h>
#endif
#include <d3/fps.h>
#include <d3/global.h>
#include <qlib/debug.h>
DEBUG_ENABLE

#define APP_TITLE "Racer TrackEd v1.2"

#define DRW        Q_BC
#define CAR_FNAME  "data/cars/devtest/car.ini"
#define CONTROLS_FILE   "controls.ini"

#define DEBUG_INI  "debug.ini"

// Use infinite grid? (throws car back in place when too far out of line)
//#define USE_INF_GRID

#define PROFILE(n)

// 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

QTimer *tmr;

// Debugging
RDebugInfo *rdbg;

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

// TrackEd vars

// GUI
QTitleBar *title;

// Menu
enum { MAX_IO=3 };
cstring  ioName[MAX_IO]={ "Select track","Load track","Save track" };
QButton *butIO[MAX_IO];
enum { MAX_MODIFY=6 };
cstring  modifyName[MAX_MODIFY]=
{ "Convert SCGT wrl's","(I)mport DOF models",
  // Line operations
  "Goto line start","Declare grid pos","Declare pits pos",
  "Declare timeline"
};
QButton *butModify[MAX_MODIFY];

// Graphics
DGlobal     dglobal;
DGeode     *model;
RTrackVRML *track;
DFPS       *fps;
static DTransformation aTF,*tf;
bool fFog=TRUE;

// Picking
DVector3 vPick,vPickEnd;

// Errors
QMessageHandler defQErr;
cstring sError="Error";

void exitfunc()
{
  int i;
  for(i=0;i<MAX_IO;i++)delete butIO[i];
  for(i=0;i<MAX_MODIFY;i++)delete butModify[i];
  //if(rJoy)delete rJoy;
  if(RMGR)delete RMGR;
  if(model)delete model;
  if(track)delete track;
  delete info;
}

/**********
* Helpers *
**********/
void SelectTrack(cstring name)
{
  char buf[256];
qdbg("SelecTrack(%s)\n",name);

  track->SetName(name);
  sprintf(buf,"%s - %s",APP_TITLE,name);
  title->SetTitle(buf);
}
static int GetIndex(cstring text,int maxIndex)
// Request an index between 0 and maxIndex
// Returns -1 if the action was cancelled.
{
  char buf[256],buf2[128];
  int  n;
  
  sprintf(buf,"%s (0..%d)",text,maxIndex);
  sprintf(buf2,"%d",maxIndex);
 retry:
  if(!QDlgString("Enter an index",buf,buf2,sizeof(buf2)))
    return -1;
  n=atoi(buf2);
  if(n<0||n>maxIndex)
  {
    if(!QMessageBox("Error","Value out of range."))
      return -1;
    else goto retry;
  }
  return n;
}

/********
* MENUS *
********/
void SetupMenus()
{
  QRect r;
  int   i;
  
  // IO
  r.x=Q_BC->GetX()-2; r.y=Q_BC->GetHeight()+Q_BC->GetY()+10;
  r.wid=150; r.hgt=35;
  for(i=0;i<MAX_IO;i++)
  {
    butIO[i]=new QButton(QSHELL,&r,ioName[i]);
    r.x+=r.wid+10;
  }
  
  // Modify
  r.x=Q_BC->GetX()+Q_BC->GetWidth()+10; r.y=Q_BC->GetY()-2;
  for(i=0;i<MAX_MODIFY;i++)
  {
    butModify[i]=new QButton(QSHELL,&r,modifyName[i]);
    r.y+=r.hgt+10;
    // Seperators
    if(i==1)
    {
      // Line ops coming up
      new QLabel(QSHELL,&r,"Line operations");
      r.y+=30;
    }
  }
}

/*********
* ERRORS *
*********/
void apperr(string s)
{
  qdbg("Error '%s'\n",s);
  QMessageBox("Error",s);
  if(defQErr)defQErr(s);
}

/******
* I/O *
******/
static QProgressDialog *dlgProgress;
static bool cbProgress(int cur,int total,cstring text)
{
  dlgProgress->SetProgressText(text);
  dlgProgress->SetProgress(cur,total);
  return dlgProgress->Poll();
}
void LoadTrack()
{
  QRect r(100,100,400,150);
  dlgProgress=new QProgressDialog(QSHELL,&r,"Loading","Loading models.");
    //QProgressDialog::NO_CANCEL);
  dlgProgress->Create();
  track->SetLoadCallback(cbProgress);
  if(!track->Load())
    QMessageBox(sError,"Can't load track!");
  track->SetLoadCallback(0);
  delete dlgProgress; dlgProgress=0;
}
void SaveTrack()
{
  QRect r(100,100,400,100);
  dlgProgress=new QProgressDialog(QSHELL,&r,"Saving",
    "Saving track definition.",QProgressDialog::NO_CANCEL);
  dlgProgress->Create();
  if(!track->Save())
    QMessageBox(sError,"Can't load track!");
  delete dlgProgress; dlgProgress=0;
}


/*********
* EVENTS *
*********/
bool event(QEvent *e)
{
  int gear,n;

  PROFILE(RProfile::PROF_EVENTS);

  static int drag,dragX,dragY;

#ifdef OBS
  if(model)tf=model->GetTransformation();
  else     tf=0;
#endif
  
  if(e->type==QEvent::BUTTONPRESS)
  {
    //if(!drag)
    if(e->win==Q_BC)
    { drag|=(1<<(e->n-1));
      dragX=e->x; dragY=e->y;
      if(e->n==1)
      {
        PickTest(e->x,DRW->GetHeight()-e->y,tf,&vPick);
        vPickEnd=vPick;
      }
    }
  } else if(e->type==QEvent::BUTTONRELEASE)
  {
    drag&=~(1<<(e->n-1));
  } else if(e->type==QEvent::MOTIONNOTIFY)
  {
    if(!tf)return FALSE;
    
    // 2 mouse button version
    if((drag&(1|4))==(1|4))
    { tf->x+=(e->x-dragX)*400./720.;
      tf->y-=(e->y-dragY)*400./576.;
    } else if(drag&1)
    {
      // if(mode==PICK)
      PickTest(e->x,DRW->GetHeight()-e->y,tf,&vPickEnd);
      // else if(mode==CAMERA)
      // { tf->xa+=((float)(e->y-dragY))*300./576.;
      //   tf->ya+=((float)(e->x-dragX))*300./720.;
      // }
    } else if(drag&4)
    {
      // Rotate camera
      tf->xa+=((float)(e->y-dragY))*300./576.;
      tf->ya+=((float)(e->x-dragX))*300./720.;
      //tf->za+=(e->y-dragY)*300./576.;
      //tf->z+=(e->x-dragX);
    }
    dragX=e->x; dragY=e->y;
#ifdef OBS
qdbg("Camera location=(%.2f,%.2f,%.2f)\n",tf->x,tf->y,tf->z);
qdbg("       direction=(%.2f,%.2f,%.2f)\n",tf->xa,tf->ya,tf->za);
#endif
    return TRUE;
  }

  if(e->type==QEvent::CLICK)
  {
    if(e->win==butIO[0])
    {
      // SelectTrack();
    } else if(e->win==butIO[1])
    {
      LoadTrack();
    } else if(e->win==butIO[2])
    {
      SaveTrack();
    } else if(e->win==butModify[0])
    {
      ConvertSCGTwrl();
    } else if(e->win==butModify[1])
    {
      ImportDOFs();
    } else if(e->win==butModify[2])
    {
      // Goto start pos
      tf->x=vPick.x;
      tf->y=vPick.y;
      tf->z=vPick.z;
      // Float above the pick point
      tf->y+=4.0f;
    } else if(e->win==butModify[3])
    {
      // Declare grid position
      n=GetIndex("Select grid position index",
        track->GetGridPosCount()<RTrackVRML::MAX_GRID_POS?
        track->GetGridPosCount():RTrackVRML::MAX_GRID_POS-1);
      if(n!=-1)
      {
        RCarPos *pos;
        pos=&track->gridPos[n]; //GetGridPos(n);
        pos->from=vPick;
        pos->to=vPickEnd;
        if(n==track->GetGridPosCount())track->gridPosCount++;
      }
    } else if(e->win==butModify[4])
    {
      // Declare pit position
      n=GetIndex("Select pit position index",
        track->GetPitPosCount()<RTrackVRML::MAX_GRID_POS?
        track->GetPitPosCount():RTrackVRML::MAX_GRID_POS-1);
      if(n!=-1)
      {
        RCarPos *pos;
        pos=&track->pitPos[n]; //GetPitPos(n);
        pos->from=vPick;
        pos->to=vPickEnd;
        if(n==track->GetPitPosCount())track->pitPosCount++;
      }
    } else if(e->win==butModify[5])
    {
      // Declare time line
      n=GetIndex("Select timeline index",
        track->GetTimeLines()<RTrackVRML::MAX_GRID_POS?
        track->GetTimeLines():RTrackVRML::MAX_GRID_POS-1);
      if(n!=-1)
      {
        if(n<track->GetTimeLines())
        { delete track->GetTimeLine(n);
        } else track->timeLines++;
        // Install new timeline
        track->timeLine[n]=new RTimeLine(&vPick,&vPickEnd);
#ifdef OBS
        RTimeLine *tl;
        tl=track->timeLine[n]; //GetTimeLine(n);
        tl->from=vPick;
        tl->to=vPickEnd;
#endif
        //if(n==track->GetTimeLines())track->timeLines++;
      }
    }
  }
  if(e->type==QEvent::MOTIONNOTIFY)
  {
    //MapXYtoCtl(e->x,e->y);
  } else if(e->type==QEvent::KEYPRESS)
  {
    if(e->n==QK_ESC)
      app->Exit(0);
    else
    if(e->n==QK_I)
    { ImportDOFs();
    } else if(e->n==QK_S)
    {
      // Start position
      tf->x=tf->y=tf->z=0;
      tf->xa=tf->ya=tf->za=0;
      //tf->xa=15;
      tf->y=1.0f;
      //tf->ya=-90;
    }
  }

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

  return FALSE;
}

/***********
* Painting *
***********/
void DbgPrj(cstring s)
{
  DMatrix4 matPrj;
  glGetFloatv(GL_PROJECTION_MATRIX,matPrj.GetM());
  matPrj.DbgPrint(s);
//qdbg("glCtx: %p\n",glXGetCurrentContext());
}
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;
  }
  
  // Calculate projection
//qdbg("w=%d, h=%d\n",w,h);
  //glViewport(0,0,w,h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(50.0,(GLfloat)w/(GLfloat)h,1.0,
    info->GetFloat("gfx.visibility",300.f));
  
  // Remember for later faster projection
  glGetFloatv(GL_PROJECTION_MATRIX,matPrj.GetM());
DbgPrj("SetupViewport");
#ifdef OBS
matPrj.DbgPrint("Projection");
qdbg("glCtx: %p\n",glXGetCurrentContext());
#endif
  
#ifdef OBS
  float d=1000.0f;
  glFrustum(-w/d/2,w/d/2,-h/d/2,h/d/2,1,100000);
#endif
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  //glTranslatef(0,0,-d);
  
  fSetup=TRUE;
}

QDraw *GGetDrawable()
{
  return DRW;
}

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

#ifdef OBS
static void SetGLColor(QColor *color)
// Local function to convert rgba to float
{
  GLfloat cr,cg,cb,ca;
  cr=(GLfloat)color->GetR()/255;
  cg=(GLfloat)color->GetG()/255;
  cb=(GLfloat)color->GetB()/255;
  ca=(GLfloat)color->GetA()/255;
  glColor4f(cr,cg,cb,ca);
}
#endif
#ifdef OBS
static void PaintRope(DVector3 *v1,DVector3 *v2,QColor *col)
// Paint rope with poles
{
  SetGLColor(col);
  glBegin(GL_LINES);
    // Both 
    glVertex3f(v1->x,v1->y,v1->z);
    glVertex3f(v1->x,v1->y+1,v1->z);
    glVertex3f(v2->x,v2->y,v2->z);
    glVertex3f(v2->x,v2->y+1,v2->z);
    // Connection
    glVertex3f(v1->x,v1->y+1,v1->z);
    glVertex3f(v2->x,v2->y+1,v2->z);
  glEnd();
}
#endif

void PaintTrack(int flags)
// Paint the entire track
// flags&1; output on debug the timing results
{
  QVector3 *v;
  static QTimer *tmr;
  int  t[10];
  int  i,x,y;
  char buf[256];
  
  if(!tmr)tmr=new QTimer();
  
  tmr->Reset();
  tmr->Start();
  
  GGetCV()->Select();
  GGetCV()->Enable(QCanvas::IS3D);
  
  SetupViewport(DRW->GetWidth(),DRW->GetHeight());

  t[0]=tmr->GetMilliSeconds();
  // Fog
  if(fFog)
  {
    GLfloat density;
    GLfloat fogColor[4]={ 0.5,0.5,0.5,1.0 };
    
    glEnable(GL_FOG);
    glFogi(GL_FOG_MODE,GL_LINEAR);
    glFogfv(GL_FOG_COLOR,fogColor);
    glFogf(GL_FOG_DENSITY,0.35);
    glFogf(GL_FOG_END,300.0f);
    glHint(GL_FOG_HINT,GL_DONT_CARE);
    
    glClearColor(.5,.5,.5,0);
  } else
  {
    glClearColor(51.f/255.f,93.f/255.f,165.f/255.f,0);
  }
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  //glClear(GL_DEPTH_BUFFER_BIT);
#ifdef ND_DEV
  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 };
  //glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);
  glEnable(GL_DEPTH_TEST);
  //glShadeModel(GL_FLAT);
  glShadeModel(GL_SMOOTH);
  
  // Camera
  //Camera();
  glLightfv(GL_LIGHT0,GL_POSITION,lightPos);
  //glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);
#endif

#ifdef OBS
// Speed test
glShadeModel(GL_FLAT);
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
glDisable(GL_NORMALIZE);
glDisable(GL_DEPTH_TEST);
#endif

  t[1]=tmr->GetMilliSeconds();
  
  // Paint track
  if(track)
  {
    // Camera is negated, angles should be negative
    glRotatef(tf->za,0,0,1);
    glRotatef(tf->xa,1,0,0);
    glRotatef(tf->ya,0,1,0);
    glTranslatef(-tf->x,-tf->y,-tf->z);
    
    track->GetCuller()->CalcFrustumEquations();
    track->Paint();
    track->PaintHidden();
    
    // Paint pick vectors
//#ifdef OBS
glDisable(GL_LIGHTING);
glDisable(GL_CULL_FACE);
//glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
//#endif
    glColor3f(1,1,1);
    glBegin(GL_LINES);
      glVertex3f(vPick.x,vPick.y,vPick.z);
      glVertex3f(vPick.x,vPick.y+1.0f,vPick.z);
      glColor3f(1,1,0);
      glVertex3f(vPickEnd.x,vPickEnd.y,vPickEnd.z);
      glVertex3f(vPickEnd.x,vPickEnd.y+1.0f,vPickEnd.z);
      // Connect the two
      glColor3f(1,1,.5);
      glVertex3f(vPick.x,vPick.y+1.0f,vPick.z);
      glVertex3f(vPickEnd.x,vPickEnd.y+1.0f,vPickEnd.z);
    glEnd();
  }
  t[2]=tmr->GetMilliSeconds();
  
  // 2D stats; remember matrices for picking
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  
  // Stats
  fps->FrameRendered();
  x=20; y=20;
  QCV->Set2D();
  QCV->SetColor(255,255,255);
  QCV->SetFont(app->GetSystemFont());
  sprintf(buf,"FPS: %.2f",fps->GetFPS());
  QCV->Text(buf,x,y); y+=20;
  
  if(flags&1)qdbg("  t1: %d ms\n",t[1]);
  
  PROFILE(RProfile::PROF_SWAP);
  GSwap();

  PROFILE(RProfile::PROF_OTHER);
  
  // Restore matrices
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
//DbgPrj("PaintTrack1");

}

void idlefunc()
{
  PaintTrack();
}

void Setup()
{
  float v;
  
  // D3
  // Use texture pool to save image memory
  dglobal.flags|=DGlobal::USE_TEXTURE_POOL;
  
  // GUI
  title=new QTitleBar(QSHELL,APP_TITLE);
  
  defQErr=QSetErrorHandler(apperr);

  // Create a track and use the latest loaded track as its name
  track=new RTrackVRML();
  SelectTrack(info->GetStringDirect("track.lasttrack"));
  
  // Track FPS
  fps=new DFPS();
  fFog=info->GetInt("gfx.fog");
  
  // Setup transformation
  tf=&aTF;
  // Give it an initial tilt and distance
  tf->xa=90;
  tf->z=-700;
  // Start pos
  tf->x=tf->y=tf->z=0;
  tf->xa=tf->ya=tf->za=0;
  
  // GUI
  SetupMenus();

  // Get window up
  app->RunPoll();
  app->SetIdleProc(idlefunc);
  app->SetExitProc(exitfunc);
  app->SetEventProc(event);
}

void Run()
{
  Setup();
  app->Run();
}