class DGeob | .h |
constructor | DGeob(DGeode *_geode) |
constructor | DGeob(DGeob *master,int flags) Create a geode based on another geode Does a reference for the geometry data; the vertices are NOT copied flags&1; don't reference materials; create a copy |
destructor | ~DGeob() |
Flat functions |
DumpArray | static void DumpArray(cstring s,float *a,int count,int elts) 'elts' is number of elements per thing |
class DGeob | .h |
Destroy | void Destroy() Free all associated arrays |
DestroyList | void DestroyList() Free the display list |
DestroyNormals | void DestroyNormals() If any normals were generated/loaded, they are thrown away Notice this can be faster for geobs that don't use lighting, as they don't need normals (CULL_FACE is done based on the vertices). For example, Racer's SCGT tracks don't do lighting and are drawn faster without supplying normals. |
GetMaterialForID | DMaterial *GetMaterialForID(int id) Returns material to use given id 'id' This is harder than it sounds, since multi-subobject materials use the submaterials for material id, and with normal materials (like SCGT cars use after VRL->VRML->MAX->ASE) without submaterials just use the material from the materialRef Returns 0 if no material exists (!) BUGS: nested multi/sub materials are not supported, just 1 deep |
GetBurstForFace | int GetBurstForFace(int n) Given face #n, return the burst index in which this face belongs. Returns -1 if no burst was found |
GetBoundingBox | void GetBoundingBox(DBox *b) Stores the bounding box info in 'b' |
ExportDOF | bool ExportDOF(QFile *f) Export the geob into a file It doesn't include the original vertices, just the faces. All writing is done in PC (Big-endian) format. 25-12-00; GOB1, GOB0 used non-indexed primitives/faces (obsolete!) |
ImportDOF | bool ImportDOF(QFile *f) Read a geob from a file 25-12-00; GOB1, much more flexible for future enhancements |
ScaleVertices | void ScaleVertices(float x,float y,float z) Scale all vertices by an amount BUGS: - Assumes model is made of triangles! |
Translate | void Translate(float x,float y,float z) Translate all vertices Only works for indexed primitives |
Paint | void Paint() |
/*
* DGeob - big loaded geomobjects (from 3D Studio Max / LightWave etc)
* 23-12-00: Detached from dgeode.cpp. Support for indexed painting.
* 25-12-00: All polygons must be triangles from now on; too much
* code depends on it. GOB1 format replaces GOB0.
* 03-03-01: Usage of display list is now optional; it doesn't do much
* when you only have a vertex array, and sucks up memory! (MonzaF12K)
* NOTES:
* - Indexing has benefits with regards to lighting; may be cached.
* BUGS:
* - Triangulate doesn't work
* (C) MarketGraph/RVG
*/
#include <d3/d3.h>
#pragma hdrstop
#include <d3/matrix.h>
#include <qlib/debug.h>
DEBUG_ENABLE
// Indexed face sets instead of flat big arrays?
#define USE_INDEXED_FACES
// Use displays lists to speed up?
//#define USE_DISPLAY_LISTS
#define N_PER_V 3 // Coordinates per vertex
#define N_PER_F 9 // Coordinates per face (triangles)
#define N_PER_TV 2 // Coordinates used per texture vertex (2D maps)
#define N_PER_TF 6 // Tex Coordinates per tex face
#undef DBG_CLASS
#define DBG_CLASS "DGeob"
/********
* Ctors *
********/
DGeob::DGeob(DGeode *_geode)
{
geode=_geode;
// Init vars
vertices=faces=0;
tvertices=tfaces=0;
vertex=0;
face_v=0; face_nrm=0;
tvertex=0;
tface_v=0;
nrmVector=0;
nrmVectors=0;
// Indexed face sets
index=0;
indices=0;
normal=0;
normals=0;
bursts=0;
materialRef=0;
list=0;
face_v_elements=0;
face_nrm_elements=0;
tface_v_elements=0;
paintFlags=0;
}
DGeob::DGeob(DGeob *master,int flags)
// Create a geode based on another geode
// Does a reference for the geometry data; the vertices are NOT copied
// flags&1; don't reference materials; create a copy
{
int i;
vertices=master->vertices;
faces=master->faces;
tvertices=master->tvertices;
tfaces=master->tfaces;
vertex=master->vertex;
face_v=master->face_v;
face_nrm=master->face_nrm;
tvertex=master->tvertex;
tface_v=master->tface_v;
nrmVectors=master->nrmVectors;
nrmVector=master->nrmVector;
index=master->index;
indices=master->indices;
normal=master->normal;
normals=master->normals;
face_v_elements=master->face_v_elements;
face_nrm_elements=master->face_nrm_elements;
tface_v_elements=master->tface_v_elements;
paintFlags=master->paintFlags;
list=master->list;
geode=master->geode;
// Clone bursts
bursts=master->bursts;
for(i=0;i<bursts;i++)
{ burstStart[i]=master->burstStart[i];
burstCount[i]=master->burstCount[i];
burstMtlID[i]=master->burstMtlID[i];
burstVperP[i]=master->burstVperP[i];
}
materialRef=master->materialRef;
}
DGeob::~DGeob()
{
Destroy();
}
/**********
* HELPERS *
**********/
static void DumpArray(cstring s,float *a,int count,int elts)
// 'elts' is number of elements per thing
{
int i;
qdbg("%s: ",s);
for(i=0;i<count;i++)
{
qdbg("%.2f ",a[i]);
if((i%elts)==elts-1)qdbg(", ");
}
qdbg("\n");
}
/*************
* Destroying *
*************/
void DGeob::Destroy()
// Free all associated arrays
{
if(vertex) { qfree(vertex); vertex=0; }
if(face_v) { qfree(face_v); face_v=0; }
if(face_nrm) { qfree(face_nrm); face_nrm=0; }
if(tface_v) { qfree(tface_v); tface_v=0; }
if(tvertex) { qfree(tvertex); tvertex=0; }
if(nrmVector){ qfree(nrmVector); nrmVector=0; }
if(index) { qfree(index); index=0; }
if(normal) { qfree(normal); normal=0; }
// Reset counts
vertices=0;
faces=0;
tvertices=0;
tfaces=0;
face_v_elements=0;
face_nrm_elements=0;
tface_v_elements=0;
indices=0;
normals=0;
bursts=0;
DestroyList();
}
void DGeob::DestroyList()
// Free the display list
{
if(list)
{
glDeleteLists(list,1);
list=0;
}
}
void DGeob::DestroyNormals()
// If any normals were generated/loaded, they are thrown away
// Notice this can be faster for geobs that don't use lighting, as they
// don't need normals (CULL_FACE is done based on the vertices).
// For example, Racer's SCGT tracks don't do lighting and are drawn faster
// without supplying normals.
{
if(normal)
{ qfree(normal); normal=0;
normals=0;
DestroyList();
}
}
/**************
* Information *
**************/
DMaterial *DGeob::GetMaterialForID(int id)
// Returns material to use given id 'id'
// This is harder than it sounds, since multi-subobject materials
// use the submaterials for material id, and with normal materials
// (like SCGT cars use after VRL->VRML->MAX->ASE) without submaterials
// just use the material from the materialRef
// Returns 0 if no material exists (!)
// BUGS: nested multi/sub materials are not supported, just 1 deep
{
DMaterial *mat;
int n;
//qdbg("DGeob:GetMat4ID; materials=%d, id=%d\n",geode->materials,id);
if(!geode->materials)
{ // Default material
QTRACE("No materials defined; returning 0.\n");
return 0;
} else if(geode->material[materialRef])
{
if(!geode->material[materialRef]->submaterials)
{ // Use topmost material
n=materialRef;
if(n<0)n=0; else if(n>=geode->materials)n=geode->materials-1;
return geode->material[n];
} else
{
//qdbg("DGeob:GetMat4ID; multisub: materials=%d, id=%d\n",geode->materials,id);
// Use material in a multi/sub object material (see 3D Studio Max)
// You CAN define less submaterials in Max then there are material ID's.
// Max seems to spread the materials over the id's. Here I truncate
// the material id's to keep it within range.
n=id;
if(n<0)n=0;
else if(n>=geode->material[materialRef]->submaterials)
n=geode->material[materialRef]->submaterials-1;
//qdbg(" using %d\n",n);
return geode->material[materialRef]->submaterial[n];
}
}
return 0;
}
int DGeob::GetBurstForFace(int n)
// Given face #n, return the burst index in which this face
// belongs.
// Returns -1 if no burst was found
{
int i;
for(i=0;i<bursts;i++)
{
if(n<burstStart[i]+burstCount[i])
return i;
}
return -1;
}
void DGeob::GetBoundingBox(DBox *b)
// Stores the bounding box info in 'b'
{
int i;
dfloat *p;
if(!vertices)
{
b->min.SetToZero();
b->max.SetToZero();
return;
}
b->min.x=b->min.y=b->min.z=99999.0f;
b->max.x=b->max.y=b->max.z=-99999.0f;
p=vertex;
for(i=0;i<vertices;i++)
{
if(*p<b->min.x)b->min.x=*p;
if(*p>b->max.x)b->max.x=*p;
p++;
if(*p<b->min.y)b->min.y=*p;
if(*p>b->max.y)b->max.y=*p;
p++;
if(*p<b->min.z)b->min.z=*p;
if(*p>b->max.z)b->max.z=*p;
p++;
}
}
/******
* DOF *
******/
bool DGeob::ExportDOF(QFile *f)
// Export the geob into a file
// It doesn't include the original vertices, just the faces.
// All writing is done in PC (Big-endian) format.
// 25-12-00; GOB1, GOB0 used non-indexed primitives/faces (obsolete!)
{
int i,len,off,off2;
int n;
f->Write("GOB1",4);
off=f->Tell();
len=0; len=QHost2PC_L(len); f->Write(&len,sizeof(len));
// Header
f->Write("GHDR",4);
len=QHost2PC_L(3*sizeof(int)); f->Write(&len,sizeof(len));
n=QHost2PC_L(flags); f->Write(&n,sizeof(n));
n=QHost2PC_L(paintFlags); f->Write(&n,sizeof(n));
n=QHost2PC_L(materialRef); f->Write(&n,sizeof(n));
// Indices
f->Write("INDI",4);
len=QHost2PC_L(indices*sizeof(dindex)+sizeof(int));
f->Write(&len,sizeof(len));
n=QHost2PC_L(indices); f->Write(&n,sizeof(n));
QHost2PC_SA(index,indices);
f->Write(index,indices*sizeof(dindex));
QPC2Host_SA(index,indices);
// Vertices
f->Write("VERT",4);
len=QHost2PC_L(vertices*sizeof(dfloat)*3+sizeof(int));
f->Write(&len,sizeof(len));
n=QHost2PC_L(vertices); f->Write(&n,sizeof(n));
QHost2PC_FA(vertex,vertices*3);
f->Write(vertex,vertices*sizeof(dfloat)*3);
QPC2Host_FA(vertex,vertices*3);
// Texture vertices (optional)
if(tvertex)
{
f->Write("TVER",4);
len=QHost2PC_L(tvertices*sizeof(dfloat)*2+sizeof(int));
f->Write(&len,sizeof(len));
n=QHost2PC_L(tvertices); f->Write(&n,sizeof(n));
QHost2PC_FA(tvertex,tvertices*2);
f->Write(tvertex,tvertices*sizeof(dfloat)*2);
QPC2Host_FA(tvertex,tvertices*2);
}
// Normals
if(normal)
{
f->Write("NORM",4);
len=QHost2PC_L(normals*sizeof(dfloat)*3+sizeof(int));
f->Write(&len,sizeof(len));
n=QHost2PC_L(normals); f->Write(&n,sizeof(n));
QHost2PC_FA(normal,normals*3);
f->Write(normal,normals*sizeof(dfloat)*3);
QPC2Host_FA(normal,normals*3);
}
// Bursts
f->Write("BRST",4);
len=QHost2PC_L(bursts*4*sizeof(int)+sizeof(int));
f->Write(&len,sizeof(len));
n=QHost2PC_L(bursts); f->Write(&n,sizeof(n));
QHost2PC_IA(burstStart,bursts);
QHost2PC_IA(burstCount,bursts);
QHost2PC_IA(burstMtlID,bursts);
QHost2PC_IA(burstVperP,bursts);
f->Write(burstStart,bursts*sizeof(int));
f->Write(burstCount,bursts*sizeof(int));
f->Write(burstMtlID,bursts*sizeof(int));
f->Write(burstVperP,bursts*sizeof(int));
QPC2Host_IA(burstStart,bursts);
QPC2Host_IA(burstCount,bursts);
QPC2Host_IA(burstMtlID,bursts);
QPC2Host_IA(burstVperP,bursts);
// End chunk
f->Write("GEND",4);
// Record size of entire geob
off2=f->Tell();
f->Seek(off);
len=QHost2PC_L(off2-off); f->Write(&len,sizeof(len));
// Return to previous write position for future writes
f->Seek(off2);
return TRUE;
}
bool DGeob::ImportDOF(QFile *f)
// Read a geob from a file
// 25-12-00; GOB1, much more flexible for future enhancements
{
char buf[8];
int len,n,i;
// Destroy any old geob
Destroy();
// Read GEOB identifier ("GOB1")
f->Read(buf,4);
f->Read(&n,sizeof(n)); len=QPC2Host_L(n);
// Ignore old GOB0 geobs (obsolete)
if(!strncmp(buf,"GOB0",4))
{
f->Seek(len,QFile::S_CUR);
return FALSE;
}
// Read chunks
while(1)
{
f->Read(buf,4); buf[4]=0;
if(f->IsEOF())break;
if(!strcmp(buf,"GEND"))break;
// All chunks have a length
f->Read(&n,sizeof(n)); len=QPC2Host_L(n);
//qdbg("Chunk '%s' len %d, filepos %d\n",buf,len,f->Tell());
if(!strcmp(buf,"GHDR"))
{
f->Read(&n,sizeof(n)); flags=QPC2Host_L(n);
f->Read(&n,sizeof(n)); paintFlags=QPC2Host_L(n);
f->Read(&n,sizeof(n)); materialRef=QPC2Host_L(n);
} else if(!strcmp(buf,"INDI"))
{
f->Read(&n,sizeof(n)); indices=QPC2Host_L(n);
index=(dindex*)qcalloc(indices*sizeof(dindex));
if(!index)
{nomem:
qwarn("DGeob:ImportDOF(); out of memory");
return FALSE;
}
f->Read(index,indices*sizeof(dindex));
QPC2Host_SA(index,indices);
} else if(!strcmp(buf,"VERT"))
{
f->Read(&n,sizeof(n)); vertices=QPC2Host_L(n);
vertex=(float*)qcalloc(vertices*sizeof(dfloat)*3);
if(!vertex)goto nomem;
f->Read(vertex,vertices*sizeof(dfloat)*3);
QPC2Host_FA(vertex,vertices*3);
} else if(!strcmp(buf,"NORM"))
{
f->Read(&n,sizeof(n)); normals=QPC2Host_L(n);
normal=(float*)qcalloc(normals*sizeof(dfloat)*3);
if(!normal)goto nomem;
f->Read(normal,normals*sizeof(dfloat)*3);
QPC2Host_FA(normal,normals*3);
} else if(!strcmp(buf,"TVER"))
{
f->Read(&n,sizeof(n)); tvertices=QPC2Host_L(n);
tvertex=(float*)qcalloc(tvertices*sizeof(dfloat)*2);
if(!tvertex)goto nomem;
f->Read(tvertex,tvertices*sizeof(dfloat)*2);
QPC2Host_FA(tvertex,tvertices*2);
} else if(!strcmp(buf,"BRST"))
{
f->Read(&n,sizeof(n)); bursts=QPC2Host_L(n);
f->Read(burstStart,bursts*sizeof(int));
f->Read(burstCount,bursts*sizeof(int));
f->Read(burstMtlID,bursts*sizeof(int));
f->Read(burstVperP,bursts*sizeof(int));
QPC2Host_IA(burstStart,bursts);
QPC2Host_IA(burstCount,bursts);
QPC2Host_IA(burstMtlID,bursts);
QPC2Host_IA(burstVperP,bursts);
} else
{
// Skip unknown chunk
qdbg("Skipping unknown geob chunk '%s'\n",buf);
f->Seek(len,QFile::S_CUR);
}
}
return TRUE;
}
/**********
* SCALING *
**********/
void DGeob::ScaleVertices(float x,float y,float z)
// Scale all vertices by an amount
// BUGS:
// - Assumes model is made of triangles!
{
int i;
// On more than 1 occasion, accidental scaling by 0
// had the model disappear, resulting in debugging frenzy,
// so warn for that.
if(x==0.0f&&y==0.0f&&z==0.0f)
qwarn("DGeob:ScaleVertices() scaling by 0; model will be invisible");
// Vertices themselves
for(i=0;i<vertices;i++)
{ vertex[i*3+0]*=x;
vertex[i*3+1]*=y;
vertex[i*3+2]*=z;
}
// Faces that where created out of vertices
if(face_v)
for(i=0;i<faces;i++)
{ face_v[i*N_PER_F+0]*=x;
face_v[i*N_PER_F+1]*=y;
face_v[i*N_PER_F+2]*=z;
face_v[i*N_PER_F+3]*=x;
face_v[i*N_PER_F+4]*=y;
face_v[i*N_PER_F+5]*=z;
face_v[i*N_PER_F+6]*=x;
face_v[i*N_PER_F+7]*=y;
face_v[i*N_PER_F+8]*=z;
}
// Rebuild display list upon the next Paint()
DestroyList();
}
/******************
* Transformations *
******************/
void DGeob::Translate(float x,float y,float z)
// Translate all vertices
// Only works for indexed primitives
{
int i;
if(!vertex)return;
for(i=0;i<vertices;i++)
{ vertex[i*3+0]+=x;
vertex[i*3+1]+=y;
vertex[i*3+2]+=z;
}
// Rebuild display list upon the next Paint()
DestroyList();
}
/********
* Paint *
********/
void DGeob::Paint()
{
int i,first;
// Client state caching
bool csNormal=FALSE;
#ifdef USE_DISPLAY_LISTS
// If the object has been put in a display list, use that
if(list)
{
//qdbg("DGeob::Paint(); glCallList(%d)\n",list);
glCallList(list);
//qdbg(" done\n",list);
return;
}
// Generate a display list for the object
list=glGenLists(1);
glNewList(list,GL_COMPILE_AND_EXECUTE);
//qdbg("DGeob::Paint(); glNewList(%d)\n",list);
#endif
#ifdef OBS
qdbg("DGeob::Paint (this=%p), master geode=%p\n",this,geode);
qdbg("bursts=%d\n",bursts);
#endif
// Enable arrays
glEnableClientState(GL_VERTEX_ARRAY);
// Draw all bursts
for(i=0;i<bursts;i++)
{
//qdbg("paint burst %d, mref=%d, count=%d\n",i,materialRef,burstCount[i]);
#ifdef OBS
qdbg("geode=%p, matRef=%d\n",geode,materialRef);
qdbg("geode->mat[ref]=%p\n",geode->material[materialRef]);
#endif
// Install material in OpenGL engine
DMaterial *mat=GetMaterialForID(burstMtlID[i]);
if(mat)
{
mat->PrepareToPaint();
//glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
}
// Check for debug viewing options
if(geode->IsViewBurstsEnabled())
{
// Test mode to recognize bursts
float diffuse[4],val;
int r;
glDisable(GL_TEXTURE_2D);
// Generate nice color
r=list&7;
val=(float)(rand()&127);
if(r&1)diffuse[0]=1.0f; else diffuse[0]=val/256.0f;
if(r&2)diffuse[1]=1.0f; else diffuse[1]=val/256.0f;
if(r&4)diffuse[2]=1.0f; else diffuse[2]=val/256.0f;
diffuse[3]=0.0f;
qdbg("diffuse mat %f,%f,%f\n",diffuse[0],diffuse[1],diffuse[2]);
glMaterialfv(GL_FRONT,GL_DIFFUSE,diffuse);
}
// Test wireframe
//glPolygonMode(GL_FRONT,GL_LINE);
#ifdef OBS_DO_BEFOREHAND
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
#endif
first=burstStart[i];
// Install vertex array
glVertexPointer(3,GL_FLOAT,0,vertex);
// Install normal array, if present
if(normal)
{
if(!csNormal)
{ glEnableClientState(GL_NORMAL_ARRAY);
csNormal=TRUE;
}
glNormalPointer(GL_FLOAT,0,normal);
} else
{ // Turn of normal array
if(csNormal)
{ glDisableClientState(GL_NORMAL_ARRAY);
csNormal=FALSE;
}
}
// Install texture coordinate array if applicable
if(tvertex)
{
int firstTFace;
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2,GL_FLOAT,0,tvertex);
}
// Decide how to draw
if(burstVperP[i]==3)
{ // Most common; triangles
glDrawElements(GL_TRIANGLES,burstCount[i]/3,GL_UNSIGNED_SHORT,
&index[burstStart[i]/3]);
} else if(burstVperP[i]==4)
{
glDrawElements(GL_QUADS,burstCount[i]/4,GL_UNSIGNED_SHORT,
&index[burstStart[i]/4]);
} else
{
#ifdef USE_INDEXED_FACES
qwarn("DGeob; draw poly NYI\n");
#else
// Assume N-polygon; hopefully only 1 polygon is defined
glDrawArrays(GL_POLYGON,0,burstCount[i]);
#endif
}
}
// Turn off
glDisableClientState(GL_VERTEX_ARRAY);
if(csNormal)glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
#ifdef USE_DISPLAY_LIST
glEndList();
#endif
}