Module: mrun.cpp

Flat functions
 

exitfuncvoid exitfunc()
SetupMenusvoid SetupMenus()
apperrvoid apperr(string s)
RethinkBoundingBoxvoid RethinkBoundingBox()
ModelDestroyvoid ModelDestroy()
ModelLoadASEbool ModelLoadASE(cstring fname)
ModelSelectASEvoid ModelSelectASE()
ModelLoadVRMLbool ModelLoadVRML(cstring fname)
ModelSelectVRMLvoid ModelSelectVRML()
ModelLoadDOFbool ModelLoadDOF(cstring fname)
ModelSelectDOFvoid ModelSelectDOF()
ModelExportDOFvoid ModelExportDOF()
EnlightenModelvoid EnlightenModel()

Some models have poor diffuse lighting
Esp. SCGT imported cars

ScaleModelvoid ScaleModel()
Scale100void Scale100()
Scale1_100void Scale1_100()
OptimizeIndicesvoid OptimizeIndices()
GenerateNormalsvoid GenerateNormals()
ToggleBurstViewvoid ToggleBurstView()
CenterModelvoid CenterModel()
box.DbgPrintbox.DbgPrint("Center");

box.GetCenter(&v);
qdbg("Center %f,%f,%f\n",v.x,v.y,v.z);
v.x=-v.x;
v.y=-v.y;
v.z=-v.z;
model->Translate(v.x,v.y,v.z);
RethinkBoundingBox();
}

MapXYtoCtlstatic void MapXYtoCtl(int x,int y)

Convert X/Y coords to controller input

eventbool event(QEvent *e)

class if(e->type!=QEvent
.h

ifMOTIONNOTIFY)

qdbg("event %d; @%d,%d\n",e->type,e->x,e->y);
#endif


Flat functions
 

SetupViewportvoid SetupViewport(int w,int h)
GGetDrawableQDraw *GGetDrawable()
GGetCVQCanvas *GGetCV()
GSwapvoid GSwap()
idlefuncvoid idlefunc()
Setupvoid Setup()
Runvoid Run()


/*
 * Modeler - made for Racer but generically usable actually
 * 05-08-00: Created!
 * 12-11-00: First attempt to do DOF exporting/importing
 * NOTES:
 * - A bit of a mess (derived from Racer itself)
 * (C) MarketGraph/RvG
 */

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

// Trackball? (uses quaternions for more intuitive rotations)
//#define USE_TRACKBALL

#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)

// Controller info
//static QDXJoy *rJoy;
int ctlSteer;
int ctlThrottle;
int ctlBrakes;

// Camera
enum CamTypes
{
  CAM_FOLLOW,                // Just behind the player
  CAM_SIDE,                  // At the side, looking at the wheels
  CAM_FOLLOW_NH,             // Behind the player, always to the Z- axis
  CAM_COCKPIT,               // In the cockpit
  CAM_FRONT,                 // Front view (debug)
  CAM_TOP,                   // Top view
};
int camType=CAM_FOLLOW_NH;

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
};

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

// Modeler vars

// Menu
enum { MAX_IO=4 };
cstring  ioName[MAX_IO]={ "Import ASE","Import VRML",
  "Import DOF","Export DOF" };
QButton *butIO[MAX_IO];
enum { MAX_MODIFY=8 };
cstring  modifyName[MAX_MODIFY]=
{ "Enlighten model","Scale","Scale x100","Scale /100",
  "Optimize","Generate normals","Toggle burst viewing",
  "Center to origin"
};
QButton *butModify[MAX_MODIFY];

// Graphics
DGlobal dglobal;
DGeode *model;
DBoundingBox *bbox;

// Model handling
#ifdef USE_TRACKBALL
QTrackBall *tb;
#endif

// Errors
QMessageHandler defQErr;

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;
#ifdef USE_TRACKBALL
  delete tb;
#endif
  delete info;
}

/********
* 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;
  }
}

/*********
* ERRORS *
*********/
void apperr(string s)
{
  QRect r(100,100,500,150);
  QMessageBox("Error",s,0,&r);
  if(defQErr)defQErr(s);
}

/******
* I/O *
******/
void RethinkBoundingBox()
{
  DBox *b;
  model->GetBoundingBox(bbox->GetBox());
  b=bbox->GetBox();
qdbg("Bounding box: min(%f,%f,%f)-max(%f,%f,%f)\n",
b->min.x,b->min.y,b->min.z,b->max.x,b->max.y,b->max.z);
}
void ModelDestroy()
{
  if(model)delete model;
  model=0;
}
bool ModelLoadASE(cstring fname)
{
  ModelDestroy();
  QCV->Select();
  model=new DGeode(fname);
  if(!DGeodeImportASE(model,fname))
  { qerr("Can't load model '%s'",fname);
    return FALSE;
  }
  RethinkBoundingBox();
  return TRUE;
}
void ModelSelectASE()
{
  char fname[1024];
  if(!QDlgFile("Import model...",info,"modsel",fname))return;
  ModelLoadASE(fname);
}
bool ModelLoadVRML(cstring fname)
{
  ModelDestroy();
  QCV->Select();
  model=new DGeode(fname);
  if(!DGeodeImportVRML(model,fname))
  { qerr("Can't load model '%s'",fname);
    return FALSE;
  }
//model->ScaleVertices(100,100,100);  // $DEV
  RethinkBoundingBox();
  return TRUE;
}
void ModelSelectVRML()
{
  char fname[1024];
  if(!QDlgFile("Import VRML model...",info,"modselvrml",fname))return;
  ModelLoadVRML(fname);
}
bool ModelLoadDOF(cstring fname)
{
  QFile f(fname);
  ModelDestroy();
  QCV->Select();
  model=new DGeode(fname);
  if(!model->ImportDOF(&f))
  { qerr("Can't load model '%s'",fname);
    return FALSE;
  }
  RethinkBoundingBox();
  return TRUE;
}
void ModelSelectDOF()
{
  char fname[1024];
  if(!QDlgFile("Import DOF model...",info,"modseldof",fname))return;
  ModelLoadDOF(fname);
}
void ModelExportDOF()
{
  char fname[1024];
  QFile *f;
  if(!model)return;
  
  if(!QDlgFile("Export to DOF...",info,"exportdof",fname))return;
  f=new QFile(fname,QFile::WRITE);
  if(!model->ExportDOF(f))
  { qerr("Can't export model '%s'",fname);
  }
  delete f;
}

/*********
* MODIFY *
*********/
void EnlightenModel()
// Some models have poor diffuse lighting
// Esp. SCGT imported cars
{
  int i;
  if(!model)return;
  
  for(i=0;i<model->materials;i++)
  {
    model->material[i]->SetDiffuseColor(1,1,1,1);
  }
  model->DestroyLists();
}
void ScaleModel()
{
  QVector3 scale;
  cstring sScale="Scaling";
  char buf[20];
  int  r;

  if(!model)return;
  
  strcpy(buf,"1.0");
  r=QDlgString(sScale,"Enter scale X factor",buf,sizeof(buf));
  if(r!=IDOK)
  {
qdbg("IDOK=%d, r=%d\n",IDOK,r);
    return;
  }
  scale.x=atof(buf);
  strcpy(buf,"1.0");
  if(QDlgString(sScale,"Enter scale Y factor",buf,sizeof(buf))!=IDOK)
    return;
  scale.y=atof(buf);
  strcpy(buf,"1.0");
  if(QDlgString(sScale,"Enter scale Z factor",buf,sizeof(buf))!=IDOK)
    return;
  scale.z=atof(buf);
  
qdbg("Scale %f,%f,%f\n",scale.x,scale.y,scale.z);
  model->ScaleVertices(scale.x,scale.y,scale.z);
  RethinkBoundingBox();
}
void Scale100()
{
  if(!model)return;
  model->ScaleVertices(100,100,100);
  RethinkBoundingBox();
}
void Scale1_100()
{
  if(!model)return;
  model->ScaleVertices(.01f,.01f,.01f);
  RethinkBoundingBox();
}
void OptimizeIndices()
{
  char buf[256];
  int  r1,r2;
  
  if(!model)return;
  r1=DGeodeOptimizeIndices(model);
  r2=DGeodePackIndices(model);
  sprintf(buf,"%d indices equalized, %d vertices removed.\n",r1,r2);
  QMessageBox("Operation completed",buf);
}
void GenerateNormals()
{
  if(!model)return;
  DGeodeRethinkNormals(model);
  QMessageBox("Operation completed","Normals (re)generated.");
}
void ToggleBurstView()
{
  if(!model)return;
  model->SetViewBursts(!model->IsViewBurstsEnabled());
}
void CenterModel()
{
  DBox box;
  DVector3 v;
  
  if(!model)return;
  
  model->GetBoundingBox(&box);
box.DbgPrint("Center");
  box.GetCenter(&v);
qdbg("Center %f,%f,%f\n",v.x,v.y,v.z);
  v.x=-v.x;
  v.y=-v.y;
  v.z=-v.z;
  model->Translate(v.x,v.y,v.z);
  RethinkBoundingBox();
}

/*********
* EVENTS *
*********/
static void MapXYtoCtl(int x,int y)
// Convert X/Y coords to controller input
{
  x-=DRW->GetWidth()/2;
  y-=DRW->GetHeight()/2;
#ifndef WIN32
  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);

  DTransformation *tf;
  static int drag,dragX,dragY;

  if(model)tf=model->GetTransformation();
  else     tf=0;
  
#ifdef OBS
if(e->type!=QEvent::MOTIONNOTIFY)
qdbg("event %d; @%d,%d\n",e->type,e->x,e->y);
#endif

  if(e->type==QEvent::BUTTONPRESS)
  {
    //if(!drag)
    if(e->win==Q_BC)
    { drag|=(1<<(e->n-1));
      dragX=e->x; dragY=e->y;
    }
  } else if(e->type==QEvent::BUTTONRELEASE)
  {
    drag&=~(1<<(e->n-1));
  } else if(e->type==QEvent::MOTIONNOTIFY)
  {
    if(!tf)return FALSE;
    
#ifdef USE_TRACKBALL
    if(drag&1)
    { //qdbg("move %d,%d -> %d,%d\n",dragX,dragY,e->x,e->y);
      tb->Movement(dragX,dragY,e->x,e->y);
    }
#else
    // 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)
    {
      tf->xa+=((float)(e->y-dragY))*300./576.;
      tf->ya+=((float)(e->x-dragX))*300./720.;
    } else if(drag&4)
    {
      tf->za+=(e->y-dragY)*300./576.;
      tf->z+=(e->x-dragX);
    }
#endif
    dragX=e->x; dragY=e->y;
    
    // Copy transformation matrix to its bounding box
    *bbox->GetTransformation()=*tf;
    return TRUE;
  }

  if(e->type==QEvent::CLICK)
  {
    if(e->win==butIO[0])
    {
      ModelSelectASE();
    } else if(e->win==butIO[1])
    {
      ModelSelectVRML();
    } else if(e->win==butIO[2])
    {
      ModelSelectDOF();
    } else if(e->win==butIO[3])
    {
      ModelExportDOF();
    } else if(e->win==butModify[0])
    {
      EnlightenModel();
    } else if(e->win==butModify[1])
    {
      ScaleModel();
    } else if(e->win==butModify[2])
    {
      Scale100();
    } else if(e->win==butModify[3])
    {
      Scale1_100();
    } else if(e->win==butModify[4])
    {
      OptimizeIndices();
    } else if(e->win==butModify[5])
    {
      GenerateNormals();
    } else if(e->win==butModify[6])
    {
      ToggleBurstView();
    } else if(e->win==butModify[7])
    {
      CenterModel();
    }
  }
  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_R)
    { // Report transformation 'matrix'
      if(tf)
        qdbg("tf: @%f,%f,%f angle %f,%f,%f scale %f,%f,%f\n",
          tf->x,tf->y,tf->z,tf->xa,tf->ya,tf->za,tf->xs,tf->ys,tf->zs);
      else
        qdbg("No transformation yet.\n");
    }
  }

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

  return FALSE;
}

/***********
* HANDLING *
***********/
void SetupViewport(int w,int h)
{
  glViewport(0,0,w,h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(65.0,(GLfloat)w/(GLfloat)h,1.0,1000.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

QDraw *GGetDrawable()
{
  return DRW;
}

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

void idlefunc()
{
  QVector3 *v;
  float lightDir[3]={ 0,-.1,-1 };
  //float lightPos[4]={ 1,1,-1,0 };
  //float light0Pos[4]={ 0.0002,-0.0002,-1000,0 };
  //float light0Pos[4]={ 0.0002,-0.0002,1000,0 };
  float light0Pos[4]={ -1,1,1,0 };
  //float light1Pos[4]={ -0.0002,0.0002,-100,0 };
  //float light1Pos[4]={ -0.0002,0.0002,100,0 };
  float light1Pos[4]={ 1,-1,-1,0 };
  float m[16];
  
#ifdef WIN32
  // Task swapping grinds to a halt on Win32, duh!
  QNap(1);
#endif

  GGetCV()->Select();
  SetupViewport(DRW->GetWidth(),DRW->GetHeight());
  //GGetCV()->SetFont(app->GetSystemFont());
  GGetCV()->Set2D();
  GGetCV()->Set3D();
  glClearColor(.1,.1,.4,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);
  glEnable(GL_LIGHT1);
  //glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);
  glEnable(GL_DEPTH_TEST);
  glShadeModel(GL_SMOOTH);
  
#ifdef USE_TRACKBALL
  // Rotate to trackball orientation
  tb->BuildRotateMatrix(m);
  glMultMatrixf(m);
#endif
  
  // Lighting (fixed in relation to the camera pos)
  glLightfv(GL_LIGHT0,GL_POSITION,light0Pos);
  glLightfv(GL_LIGHT1,GL_POSITION,light1Pos);
  {
    float v[4]={ 1,1,1,1 };
    float l1diffuse[4]={ .5,.5,.5,1 };
    glLightfv(GL_LIGHT1,GL_DIFFUSE,l1diffuse);
    glLightfv(GL_LIGHT1,GL_SPECULAR,l1diffuse);
  }
  //glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,lightDir);

  if(model)
  {
//qdbg("model pnt\n");
    //glTranslatef(0,0,-20);
    //QCV->Set3D();
    glPushMatrix();
#ifdef OBS
    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glDisable(GL_LIGHT0);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
#endif
    model->EnableCSG();
//glShadeModel(GL_SMOOTH);
    model->Paint();
    // The box
    bbox->Paint();
    glPopMatrix();
  }
  
  if(rPrefs.bReflections)
  { // Reflections
    glFrontFace(GL_CW);
    glPushMatrix();
    glScalef(1,-1,1);
    //glPixelZoom(1,-1);
    if(model)model->Paint();
    glPopMatrix();
    glFrontFace(GL_CCW);
    //glPixelZoom(1,1);
    //glScalef(1,1,1);
  }

#ifdef OBS
  float ambient[]={ 0.2f,0.2f,0.2f,1.0f };
  float diffuse[]={ 0.8f,0.8f,0.8f,1.0f };
  float specular[]={ 0,0,0,1.0 };
  float emission[]={ 0,0,0,1.0 };
  float shininess=0;

  glMaterialfv(GL_FRONT,GL_AMBIENT,ambient);
  glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuse);
  glMaterialfv(GL_FRONT,GL_SPECULAR,specular);
  glMaterialfv(GL_FRONT,GL_EMISSION,emission);
  glMaterialf(GL_FRONT,GL_SHININESS,shininess);
#endif
  
  //PaintFloor();

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

  PROFILE(RProfile::PROF_OTHER);
  
  if(++rStats.frame==rPrefs.framesMax)
    app->Exit(0);
}

void Setup()
{
  float v;
  
  defQErr=QSetErrorHandler(apperr);

  // GUI
  SetupMenus();

  // Get window up
  app->RunPoll();
  app->SetIdleProc(idlefunc);
  app->SetExitProc(exitfunc);
  app->SetEventProc(event);
  
  // Graphics
  bbox=new DBoundingBox();
  
#ifdef USE_TRACKBALL
  tb=new QTrackBall(Q_BC->GetWidth(),Q_BC->GetHeight());
#endif
}

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