Flat functions |
exitfunc | void exitfunc() |
SetupMenus | void SetupMenus() |
apperr | void apperr(string s) |
RethinkBoundingBox | void RethinkBoundingBox() |
ModelDestroy | void ModelDestroy() |
ModelLoadASE | bool ModelLoadASE(cstring fname) |
ModelSelectASE | void ModelSelectASE() |
ModelLoadVRML | bool ModelLoadVRML(cstring fname) |
ModelSelectVRML | void ModelSelectVRML() |
ModelLoadDOF | bool ModelLoadDOF(cstring fname) |
ModelSelectDOF | void ModelSelectDOF() |
ModelExportDOF | void ModelExportDOF() |
EnlightenModel | void EnlightenModel() Some models have poor diffuse lighting Esp. SCGT imported cars |
ScaleModel | void ScaleModel() |
Scale100 | void Scale100() |
Scale1_100 | void Scale1_100() |
OptimizeIndices | void OptimizeIndices() |
GenerateNormals | void GenerateNormals() |
ToggleBurstView | void ToggleBurstView() |
CenterModel | void CenterModel() |
box.DbgPrint | 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(); } |
MapXYtoCtl | static void MapXYtoCtl(int x,int y) Convert X/Y coords to controller input |
event | bool event(QEvent *e) |
class if(e->type!=QEvent | .h |
if | MOTIONNOTIFY) qdbg("event %d; @%d,%d\n",e->type,e->x,e->y); #endif |
Flat functions |
SetupViewport | void SetupViewport(int w,int h) |
GGetDrawable | QDraw *GGetDrawable() |
GGetCV | QCanvas *GGetCV() |
GSwap | void GSwap() |
idlefunc | void idlefunc() |
Setup | void Setup() |
Run | void 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();
}