Flat functions |
DGeobImportASE | bool DGeobImportASE(DGeob *g,QFile *f) Read a GEOMOBJECT from the ASE file Assumes the pointer is at the opening { of the object |
DGeodeImportASE | bool DGeodeImportASE(DGeode *g,cstring fname) A very flat importer for a subset of .ASE files Doesn't support multiple material depths |
/*
* DGeode - ASE format importing
* 26-12-00: Detached from dgeob.cpp/dgeode.cpp.
* (C) MarketGraph/RVG
*/
#include <d3/d3.h>
#pragma hdrstop
#include <qlib/app.h>
#include <d3/matrix.h>
#include <d3/geode.h>
#include <qlib/debug.h>
DEBUG_ENABLE
// Indexed face sets instead of flat big arrays?
#define USE_INDEXED_FACES
#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"
/*************
* ASE Import *
*************/
bool DGeobImportASE(DGeob *g,QFile *f)
// Read a GEOMOBJECT from the ASE file
// Assumes the pointer is at the opening { of the object
{
DBG_C_FLAT("DGeobImportASE")
int nest;
qstring cmd,ns;
int i,n;
float x,y,z;
float cr,cg,cb;
int v1,v2,v3;
DMaterial *curMat; // Current material being edited
int curMatIndex,curSubMatIndex; // Tree-ing materials 1 deep
#ifndef USE_INDEXED_FACES
int vnrmIndex; // Normal vertex index
#endif
int curMtlID;
int curFace;
DMatrix4 matNode;
DVector3 vec,vecT;
dfloat *tempTVertex=0;
qdbg("DGeob: import (geob=@%p)\n",g);
nest=0;
curMtlID=-1;
curFace=0;
#ifndef USE_INDEXED_FACES
vnrmIndex=0;
#endif
matNode.SetIdentity();
while(1)
{
f->GetString(cmd);
// We don't expect EOF
if(f->IsEOF())return FALSE;
if(cmd=="{")
{ nest++;
} else if(cmd=="}")
{ nest--;
// Detect last closing brace for GEOMOBJECT
if(nest==0)
break;
// Node details
//
} else if(cmd=="*NODE_NAME")
{
f->GetString(ns);
qdbg("Node name '%s'\n",(cstring)ns);
} else if(cmd=="*TM_ROW0")
{
f->GetString(ns); vec.x=atof(ns);
f->GetString(ns); vec.y=atof(ns);
f->GetString(ns); vec.z=atof(ns);
matNode.SetX(&vec);
} else if(cmd=="*TM_ROW1")
{
f->GetString(ns); vec.x=atof(ns);
f->GetString(ns); vec.y=atof(ns);
f->GetString(ns); vec.z=atof(ns);
matNode.SetY(&vec);
} else if(cmd=="*TM_ROW2")
{
f->GetString(ns); vec.x=atof(ns);
f->GetString(ns); vec.y=atof(ns);
f->GetString(ns); vec.z=atof(ns);
matNode.SetZ(&vec);
// Vertices, faces, texture vertices and texture faces
//
} else if(cmd=="*MESH_NUMVERTEX")
{ f->GetString(ns);
n=Eval(ns);
qdbg("%d vertices\n",n);
g->vertices=n;
g->vertex=(float*)qcalloc(sizeof(float)*g->vertices*3);
if(!g->vertex)qerr("?vertex");
// Indexed face set normals
g->normals=g->vertices;
g->normal=(float*)qcalloc(sizeof(float)*g->normals*3);
if(!g->normal)qerr("?normal");
} else if(cmd=="*MESH_VERTEX")
{ f->GetString(ns); n=Eval(ns);
f->GetString(ns); x=atof(ns);
f->GetString(ns); y=atof(ns);
f->GetString(ns); z=atof(ns);
// 3DS Max to OpenGL axis system
g->vertex[n*N_PER_V+0]=x;
g->vertex[n*N_PER_V+1]=z; //z;
g->vertex[n*N_PER_V+2]=-y; //-y;
//qdbg("vertex %d: %f %f %f\n",n,x,y,z);
} else if(cmd=="*MESH_NUMFACES")
{ f->GetString(ns);
n=Eval(ns);
qdbg("%d faces\n",n);
// Only triangles are expected (for ASE files)
#ifndef USE_INDEXED_FACES
faces=n;
face_v_elements=faces*N_PER_F;
face_v=(float*)qcalloc(sizeof(float)*faces*N_PER_F);
if(!face_v)qerr("?face_v");
face_nrm_elements=faces*N_PER_F;
face_nrm=(float*)qcalloc(sizeof(float)*faces*N_PER_F);
if(!face_nrm)qerr("?face_nrm");
#endif
g->indices=n*3;
g->index=(dindex*)qcalloc(sizeof(dindex)*g->indices);
if(!g->index)qerr("DGeob:ImportASE out of memory (index)");
} else if(cmd=="*MESH_FACE")
{ f->GetString(ns); n=atoi(ns);
curFace=n;
//qdbg("MESH_FACE %d\n",n);
// Read vertex indices
f->GetString(ns); f->GetString(ns); v1=atoi(ns);
f->GetString(ns); f->GetString(ns); v2=atoi(ns);
f->GetString(ns); f->GetString(ns); v3=atoi(ns);
// Store indices
g->index[n*3+0]=v1;
g->index[n*3+1]=v2;
g->index[n*3+2]=v3;
#ifndef USE_INDEXED_FACES
// Store direct vertex information (old way)
for(i=0;i<N_PER_V;i++)
{
face_v[n*N_PER_F+0+i]=vertex[v1*N_PER_V+i];
face_v[n*N_PER_F+3+i]=vertex[v2*N_PER_V+i];
face_v[n*N_PER_F+6+i]=vertex[v3*N_PER_V+i];
}
#endif
// Keep track of face ranges with the same material id
f->GetString(ns); f->GetString(ns); // AB, BC, CA edge flags
f->GetString(ns); f->GetString(ns);
f->GetString(ns); f->GetString(ns);
// Mesh smoothing
f->GetString(ns);
// Get all groups until *MESH_MTLID
while(1)
{ f->GetString(ns);
if(f->IsEOF())break;
if(ns=="*MESH_MTLID")break;
}
f->GetString(ns); // MTL id
n=atoi(ns);
if(n!=curMtlID)
{
// New material ID range
if(g->bursts>=DGeob::MAX_FACE_BURST)
{ qwarn("DGeode:ImportASE(): max face burst (%d) maxed out",g->bursts);
} else
{ g->burstStart[g->bursts]=curFace*3*3; // Assumes tris
g->burstCount[g->bursts]=0;
g->burstMtlID[g->bursts]=n;
g->burstVperP[g->bursts]=3; // Assumes tris only
g->bursts++;
curMtlID=n;
//qdbg("new burst; bursts=%d\n",bursts);
}
}
g->burstCount[g->bursts-1]+=3*3; // Count #consecutive faces (tri's)
//qdbg(" updated burstCount=%d\n",burstCount[bursts-1]);
//if((n%1000)==0)qdbg("face %d: %d %d %d\n",n,v1,v2,v3);
} else if(cmd=="*MESH_VERTEXNORMAL")
{
// A vertex normal; we don't use face normals
//qdbg("vertexNormal %d\n",vnrmIndex);
f->GetString(ns); n=atoi(ns);
f->GetString(ns); vec.x=atof(ns);
f->GetString(ns); vec.y=atof(ns);
f->GetString(ns); vec.z=atof(ns);
// Transform normal through node matrix
matNode.TransformVector(&vec,&vecT);
// Store in the indexed face set normal array
g->normal[n*3+0]=-vecT.x;
g->normal[n*3+1]=-vecT.z;
g->normal[n*3+2]=vecT.y;
#ifndef USE_INDEXED_FACES
// Store as drawarray form (old)
n=vnrmIndex;
face_nrm[vnrmIndex*3+0]=-vecT.x;
face_nrm[vnrmIndex*3+1]=-vecT.z;
face_nrm[vnrmIndex*3+2]=vecT.y;
vnrmIndex++;
//if((vnrmIndex%1000)==0)qdbg("facenrm %d: %d %d %d\n",n,v1,v2,v3);
#endif
} else if(cmd=="*MESH_NUMTVERTEX")
{ f->GetString(ns);
n=Eval(ns);
qdbg("%d texture vertices\n",n);
g->tvertices=n;
if(n)
{
tempTVertex=(float*)qcalloc(sizeof(float)*g->tvertices*N_PER_V);
if(!tempTVertex)qerr("?tempTVertex");
// 2D coordinates as end result
g->tvertex=(float*)qcalloc(sizeof(float)*g->tvertices*2);
if(!g->tvertex)qerr("?tvertex");
}
} else if(cmd=="*MESH_TVERT")
{
f->GetString(ns); n=Eval(ns);
f->GetString(ns); x=atof(ns);
f->GetString(ns); y=atof(ns);
f->GetString(ns); z=atof(ns);
tempTVertex[n*N_PER_V+0]=x;
tempTVertex[n*N_PER_V+1]=y;
tempTVertex[n*N_PER_V+2]=z;
//qdbg("tvertex %d: %f %f %f\n",n,x,y,z);
} else if(cmd=="*MESH_NUMTVFACES")
{ f->GetString(ns);
n=Eval(ns);
qdbg("%d texture faces\n",n);
#ifndef USE_INDEXED_FACES
tfaces=n;
// Assumes all triangles here
tface_v_elements=tfaces*N_PER_TF;
tface_v=(float*)qcalloc(sizeof(float)*tface_v_elements);
if(!tface_v)qerr("?tface_v");
#endif
} else if(cmd=="*MESH_TFACE")
{ int burst;
dfloat xScale,yScale,scale[2],offset[2];
DMaterial *mat=0;
f->GetString(ns); n=atoi(ns);
f->GetString(ns); v1=atoi(ns);
f->GetString(ns); v2=atoi(ns);
f->GetString(ns); v3=atoi(ns);
// UVW tiling
burst=g->GetBurstForFace(n*9); // Assume triangles; 3x3 floats/v
if(burst!=-1)
{
mat=g->GetMaterialForID(g->burstMtlID[burst]);
// Calculate scaling to apply to texture coordinates
xScale=mat->uvwUtiling;
yScale=mat->uvwVtiling;
// Give it a nice form for the 'for' loop below
scale[0]=xScale;
scale[1]=yScale;
offset[0]=(1-scale[0])/2;
offset[1]=(1-scale[1])/2;
} else
{
// Default tiling
scale[0]=scale[1]=1.0f;
offset[0]=offset[1]=1.0f;
}
//qdbg("TFACE %d; burst %d (mtlid %d), scale=%f,%f\n",
//n,burst,burstMtlID[burst],scale[0],scale[1]);
#ifdef USE_INDEXED_FACES
// Make sure the indexes of the texture vertices match
// with the indices of the vertices themselves
for(i=0;i<N_PER_TV;i++)
{
g->tvertex[g->index[n*3+0]*2+i]=
tempTVertex[v1*N_PER_V+i]*scale[i]+offset[i];
g->tvertex[g->index[n*3+1]*2+i]=
tempTVertex[v2*N_PER_V+i]*scale[i]+offset[i];
g->tvertex[g->index[n*3+2]*2+i]=
tempTVertex[v3*N_PER_V+i]*scale[i]+offset[i];
}
#ifdef OBS
qdbg("TFace %d; vertex indices=%d, %d, %d, v[]=%d,%d,%d\n",n,
index[n*3+0],index[n*3+1],index[n*3+2],v1,v2,v3);
DumpArray("tvertex",tvertex,tvertices*N_PER_TV,N_PER_TV);
#endif
#else
// We use just 2D texture maps, no Z of texture vertex is used
for(i=0;i<N_PER_TV;i++)
{
tface_v[n*N_PER_TF+0+i]=tempTVertex[v1*N_PER_V+i]*scale[i]+offset[i];
tface_v[n*N_PER_TF+2+i]=tempTVertex[v2*N_PER_V+i]*scale[i]+offset[i];
tface_v[n*N_PER_TF+4+i]=tempTVertex[v3*N_PER_V+i]*scale[i]+offset[i];
}
#endif
if((n%1000)==0)qdbg("tface %d: %d %d %d\n",n,v1,v2,v3);
} else if(cmd=="*MATERIAL_REF")
{ f->GetString(ns); n=atoi(ns);
g->materialRef=n;
}
}
// End of importing this GEOMOBJECT
#ifdef OBS
DumpArray("index",index,indices,3);
DumpArray("vertex",vertex,vertices*N_PER_V,N_PER_V);
DumpArray("face",face_v,face_v_elements,N_PER_F);
DumpArray("face_nrm",face_nrm,face_nrm_elements,N_PER_F);
DumpArray("tface",tface_v,tface_v_elements,N_PER_TV);
DumpArray("tvertex",tvertex,tvertices*N_PER_TV,N_PER_TV);
#endif
#ifdef OBS_DBG
for(i=0;i<faces*N_PER_F;i++)
{ qdbg("face_elt%d: %f\n",i,face_v[i]);
}
for(i=0;i<tfaces*N_PER_TF&&i<100;i++)
{ qdbg("tface_v[%d]: %f\n",i,tface_v[i]);
}
#endif
// Info bursts
qdbg("Bursts=%d\n",g->bursts);
for(i=0;i<g->bursts;i++)
{ qdbg("Burst @%d, count %d, mtlid %d\n",g->burstStart[i],
g->burstCount[i],g->burstMtlID[i]);
}
// Cleanup
if(tempTVertex)qfree(tempTVertex);
return TRUE;
}
/****************************
* Importing ASE file format *
****************************/
bool DGeodeImportASE(DGeode *g,cstring fname)
// A very flat importer for a subset of .ASE files
// Doesn't support multiple material depths
{
DBG_C_FLAT("DGeodeImportASE")
DBG_ARG_S(fname)
QFile *f;
qstring cmd,ns;
int i,n;
float x,y,z;
float cr,cg,cb;
int v1,v2,v3;
DMaterial *curMat=0; // Current material being edited
int curMatIndex,curSubMatIndex; // Tree-ing materials 1 deep
int curFace;
QTRACE("DGeode::ImportASE\n");
f=new QFile(fname);
QTRACE(" f=%p\n",f);
// File was found?
if(!f->IsOpen())
{
qdbg(" !open\n");
QTRACE("Can't import ASE '%s'",fname);
return FALSE;
}
// Search for vertices and information
while(1)
{
f->GetString(cmd);
if(f->IsEOF())break;
//qdbg("cmd='%s'\n",(cstring)cmd);
// Geom objects
//
if(cmd=="*GEOMOBJECT")
{
bool r;
DGeob *o;
// Create geob
if(g->geobs==DGeode::MAX_GEOB)
{ qerr("Too many geomobjects in ASE, max=%d",DGeode::MAX_GEOB);
break;
}
o=new DGeob(g);
g->geob[g->geobs]=o;
qdbg("new Geob @%p\n",o);
g->geobs++;
r=DGeobImportASE(o,f);
if(!r)goto fail;
// Materials
} else if(cmd=="*MATERIAL_COUNT")
{
f->GetString(ns); n=atoi(ns);
g->materials=n;
qdbg("%d materials\n",g->materials);
for(i=0;i<n;i++)
{ g->material[i]=new DMaterial();
}
} else if(cmd=="*MATERIAL")
{
f->GetString(ns); n=atoi(ns);
curMatIndex=n;
curMat=g->material[curMatIndex];
qdbg(" material %d (@%p)\n",n,curMat);
} else if(cmd=="*NUMSUBMTLS")
{
f->GetString(ns); n=atoi(ns);
curMat->submaterials=n;
qdbg("%d submaterials\n",n);
for(i=0;i<n;i++)
{ curMat->submaterial[i]=new DMaterial();
}
} else if(cmd=="*SUBMATERIAL")
{
f->GetString(ns); n=atoi(ns);
curSubMatIndex=n;
qdbg(" submaterial %d\n",n);
curMat=g->material[curMatIndex]->submaterial[curSubMatIndex];
//
// Material properties
//
} else if(cmd=="*BITMAP")
{
char buf[256];
f->GetString(ns);
// Go from PC full path to attempted SGI path
sprintf(buf,"images/%s",QFileBase(ns));
qdbg(" material image %s (base %s)\n",buf,(cstring)ns);
curMat->AddBitMap(buf);
} else if(cmd=="*UVW_U_TILING")
{ f->GetString(ns);
if(curMat)curMat->uvwUtiling=atof(ns);
} else if(cmd=="*UVW_V_TILING")
{ f->GetString(ns);
if(curMat)curMat->uvwVtiling=atof(ns);
} else if(cmd=="*UVW_U_OFFSET")
{ f->GetString(ns);
if(curMat)curMat->uvwUoffset=atof(ns);
} else if(cmd=="*UVW_V_OFFSET")
{ f->GetString(ns);
if(curMat)curMat->uvwVoffset=atof(ns);
} else if(cmd=="*UVW_ANGLE")
{ f->GetString(ns);
if(curMat)curMat->uvwAngle=atof(ns);
} else if(cmd=="*MATERIAL_NAME")
{ f->GetString(ns);
if(curMat)
curMat->name=ns;
} else if(cmd=="*MATERIAL_CLASS")
{ f->GetString(ns);
if(curMat)
curMat->className=ns;
} else if(cmd=="*MATERIAL_AMBIENT")
{ f->GetString(ns); cr=atof(ns);
f->GetString(ns); cg=atof(ns);
f->GetString(ns); cb=atof(ns);
if(curMat)
{
curMat->emission[0]=cr;
curMat->emission[1]=cg;
curMat->emission[2]=cb;
curMat->emission[3]=1.0;
//curMat->ambient[0]=r;
//curMat->ambient[1]=g;
//curMat->ambient[2]=b;
//curMat->ambient[3]=1.0;
}
} else if(cmd=="*MATERIAL_DIFFUSE")
{ f->GetString(ns); cr=atof(ns);
f->GetString(ns); cg=atof(ns);
f->GetString(ns); cb=atof(ns);
if(curMat)
{
curMat->diffuse[0]=cr;
curMat->diffuse[1]=cg;
curMat->diffuse[2]=cb;
curMat->diffuse[3]=1.0;
}
} else if(cmd=="*MATERIAL_SPECULAR")
{ f->GetString(ns); cr=atof(ns);
f->GetString(ns); cg=atof(ns);
f->GetString(ns); cb=atof(ns);
if(curMat)
{
curMat->specular[0]=cr;
curMat->specular[1]=cg;
curMat->specular[2]=cb;
curMat->specular[3]=1.0;
}
} else if(cmd=="*MATERIAL_SHINE")
{ // May need to use SHINESTRENGTH instead!
f->GetString(ns); x=atof(ns);
if(curMat)
curMat->shininess=x*128.0;
} else if(cmd=="*MATERIAL_TRANSPARENCY")
{ // Transparency is defined as the alpha component of DIFFUSE
f->GetString(ns); x=atof(ns);
if(curMat)
{ curMat->diffuse[3]=x;
curMat->transparency=x;
}
// From dpoly.cpp, it really uses glColor4f(1,1,1,x) to
// do transparency with GL_MODULATE texture env mode
// So this might not be the way to do it.
}
}
delete f; f=0;
qdbg("geobs=%d\n",g->geobs);
return TRUE;
fail:
if(f)delete f;
return FALSE;
}