Module: dgeode_ase.cpp

Flat functions
 

DGeobImportASEbool DGeobImportASE(DGeob *g,QFile *f)

Read a GEOMOBJECT from the ASE file
Assumes the pointer is at the opening { of the object

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