View Single Post
Old 18th November 2004, 08:14 PM   #4
lertulo
Junior Member
Junior member
 
Join Date: Nov 2004
Posts: 5
Default

Here's the guts of my 3DS importer:

Code:
#define CHUNKID_PRIMARY               0x4D4D

#define CHUNKID_MATERIAL              0xAFFF
#define CHUNKID_MATNAME               0xA000
#define CHUNKID_MATSHININESS          0xA040
#define CHUNKID_MATSHINSTRENGTH       0xA041
#define CHUNKID_MATDIFFUSE            0xA020
#define CHUNKID_MATRADIANCE           0xA084
#define CHUNKID_MATMAP                0xA200
#define CHUNKID_MATMAPNAME            0xA300

#define CHUNKID_OBJECTINFO            0x3D3D
#define CHUNKID_OBJECT                0x4000
#define CHUNKID_OBJECT_MESH           0x4100
#define CHUNKID_OBJECT_VERTICES       0x4110
#define CHUNKID_OBJECT_FACES          0x4120
#define CHUNKID_OBJECT_FACEMAT        0x4130
#define CHUNKID_OBJECT_UV             0x4140

void meshres::loadMesh (compiler &comp, const string &sFile)
{
   filehandle fh;
   if ((fh = file_open (sFile)) == 0)
      throwException (ERRCODE_NOT_FOUND, "unable to open sound file %s", sFile.str());

   size_t cbFile = file_getsize (fh);
   uchar *abFile = (uchar*)malloc (cbFile);
   file_read (fh, abFile, cbFile);
   file_close (fh);

   bTry {
      datastream data ((char*)abFile, cbFile);
      loadMesh (comp, data);
      free (abFile);
   } bCatch (err) {
      free (abFile);
      throwException (ERRCODE_ILLEGAL_INPUT, "%s\nerror %d processing %s", getLastException().str(), err, sFile.str());
   } bEndCatch
}

void meshres::loadMesh (compiler &comp, datastream &data)
{
   datachunk primary (data);
   if (primary.getID() != CHUNKID_PRIMARY)
      throwException (ERRCODE_ILLEGAL_INPUT, "unrecognized mesh file format (export as .3DS)");

   loadNextChunk (comp, primary);
}

void meshres::loadNextChunk (compiler &comp, datachunk &dcParent)
{
   for (datachunk dc (dcParent); !dc.isEOF(); dc.next())
   {
      if (dc.getID() == CHUNKID_OBJECTINFO)
      {
         loadNextChunk (comp, dc);
      }
      else if (dc.getID() == CHUNKID_MATERIAL)
      {
         loadMaterial (comp, dc);
      }
      else if (dc.getID() == CHUNKID_OBJECT)
      {
         loadObject (dc);
      }
   }
}

void meshres::loadMaterial (compiler &comp, datachunk &dcParent)
{
   textureres tr;
   loadMaterialChunk (comp, dcParent, tr);
   tr.idbImage = 0;

   if (!!tr.sImageName)
   {
      if ((tr.idbImage = comp.findImage (tr.sImageName)) == 0)
         printf ("warning: no image provided for texture %s; surface will be solid color\n", tr.sImageName.c_str());
      else
         comp.warnAboutTextureSize (tr.idbImage, _sName);
   }

   _aTextures.push_back (tr);
}

void meshres::loadMaterialChunk (compiler &comp, datachunk &dcParent, textureres &tr)
{
   for (datachunk dc (dcParent); !dc.isEOF(); dc.next())
   {
      if (dc.getID() == CHUNKID_MATNAME)
         tr.sTextureName = dc.readString();
      else if (dc.getID() == CHUNKID_MATDIFFUSE)
         tr.clr = dc.readColor();
      else if (dc.getID() == CHUNKID_MATSHININESS)
         tr.polish = dc.readPercentage();
      else if (dc.getID() == CHUNKID_MATSHINSTRENGTH)
         tr.reflectance = dc.readPercentage();
      else if (dc.getID() == CHUNKID_MATRADIANCE)
         tr.radiance = dc.readPercentage();
      else if (dc.getID() == CHUNKID_MATMAPNAME)
         tr.sImageName = dc.readString();
      else if (dc.getID() == CHUNKID_MATMAP)
         loadMaterialChunk (comp, dc, tr);
   }
}

void meshres::loadObject (datachunk &dcParent)
{
   string sObjName = dcParent.readString();

   for (datachunk dc (dcParent); !dc.isEOF(); dc.next())
   {
      if (dc.getID() == CHUNKID_OBJECT_MESH)
         loadObjectMesh (dc, _aVertices.size(), _aFaces.size());
   }
}

void meshres::loadObjectMesh (datachunk &dcParent, size_t iVertexBase, size_t iFaceBase)
{
   datastream &ds = dcParent.getStream();
   for (datachunk dc (dcParent); !dc.isEOF(); dc.next())
   {
      if (dc.getID() == CHUNKID_OBJECT_VERTICES)
      {
         size_t cVertices = ds.pop16();
         for (size_t ii = 0; ii < cVertices; ++ii)
         {
            float right = *(float*)(ds.read (sizeof (float)));
            float out = *(float*)(ds.read (sizeof (float)));
            float up = *(float*)(ds.read (sizeof (float)));

            vertexres vr;
            vr.vWorld.dx = right;
            vr.vWorld.dy = up;
            vr.vWorld.dz = -out;   // meshlib uses right-handed coords, z toward your nose
            vr.duS16 = 0;
            vr.dvS16 = 0;
            _aVertices.push_back (vr);
         }
      }
      else if (dc.getID() == CHUNKID_OBJECT_FACES)
      {
         if (!_aTextures.size())
            throwException (ERRCODE_ILLEGAL_INPUT, "face defined with no texture");

         size_t iTex = _aTextures.size()-1;
         size_t cFaces = ds.pop16();
         for (size_t ii = 0; ii < cFaces; ++ii)
         {
            faceres fr;
            fr.iTexture = iTex;
            fr.iVertex1 = ds.pop16() + iVertexBase;
            fr.iVertex2 = ds.pop16() + iVertexBase;
            fr.iVertex3 = ds.pop16() + iVertexBase;
            fr.flags = 0;
            size_t flags = ds.pop16();

            // 3DS writes as left-handed coords, and therefore winds its
            // vertices clockwise.  Meshlib uses right-handed coords--that's
            // why we flipped the sign on the Z axis above.  And when that
            // axis flip happened, the incoming winding suddenly became
            // counter-clockwise--so we don't need to swap vertices for
            // most models.  That's why the default winding (CCW) doesn't
            // need a vertex swap.

            if (_wind == WINDING_CW)
            {
               fr.iVertex2 ^= fr.iVertex3 ^= fr.iVertex2 ^= fr.iVertex3;
            }
            else if (_wind == WINDING_DOUBLE)
            {
               fr.flags |= MESHLIB_FACE_FLAGS_DOUBLESIDED;
            }

            if ((fr.iVertex1 >= _aVertices.size()) ||
                (fr.iVertex2 >= _aVertices.size()) ||
                (fr.iVertex3 >= _aVertices.size()))
            {
               throwException (ERRCODE_ILLEGAL_INPUT, "illegal vertex index used in mesh face definition");
            }

            _aFaces.push_back (fr);
         }

         loadObjectMesh (dc, iVertexBase, iFaceBase);
      }
      else if (dc.getID() == CHUNKID_OBJECT_FACEMAT)
      {
         string sTextureName = dc.readString();

         size_t iTex;
         for (iTex = 0; iTex < _aTextures.size(); ++iTex)
         {
            if (sTextureName == _aTextures[iTex].sTextureName)
               break;
         }
         if (iTex == _aTextures.size())
            throwException (ERRCODE_ILLEGAL_INPUT, "illegal texture name used in mesh face definition");

         size_t cFaces = ds.pop16();
         for (size_t ii = 0; ii < cFaces; ++ii)
         {
            size_t iFace = iFaceBase + ds.pop16();
            faceres &fr = _aFaces[ iFace ];
            fr.iTexture = iTex;
         }
      }
      else if (dc.getID() == CHUNKID_OBJECT_UV)
      {
         size_t cVertices = ds.pop16();
         for (size_t ii = 0; ii < cVertices; ++ii)
         {
            vertexres &vr = _aVertices[ iVertexBase + ii ];
            float uu = *(float*)(ds.read (sizeof (float)));
            float vv = *(float*)(ds.read (sizeof (float)));
            vr.duS16 = (long)(uu * 65536);
            vr.dvS16 = (long)(vv * 65536);
         }
      }
   }
}

class datachunk
{
   public:
      datachunk (datastream &ds);
      datachunk (datachunk &dcParent);
      ~datachunk (void);

      string readString (void);
      color readColor (void);
      uint8 readPercentage (void);

      size_t getID (void);
      size_t getLength (void);
      datastream & getStream (void);

      bool isEOF (void);
      bool next (void);

   protected:
      datastream &_ds;
      size_t _ibParentStops;
      size_t _idChunk;
      size_t _cbChunk;
      size_t _ibStart;
      size_t _ibAfter;
      bool _isEOF;
};

#define CHUNKHEADERSIZE 6

datachunk::datachunk (datastream &ds) : _ds(ds)
{
   _idChunk = 0;
   _cbChunk = 0;
   _ibStart = 0;
   _ibAfter = 0;
   _ibParentStops = _ds.getPos() + _ds.getRemaining();
   _isEOF = false;
   (void)next();
}

datachunk::datachunk (datachunk &dcParent) : _ds(dcParent._ds)
{
   _idChunk = 0;
   _cbChunk = 0;
   _ibStart = _ds.getPos();
   _ibParentStops = dcParent._ibAfter;
   _ibAfter = _ibStart;
   _isEOF = dcParent._isEOF;
   (void)next();
}

datachunk::~datachunk (void)
{
   if (_ds.getPos() < _ibAfter)
      _ds.setPos (_ibAfter);
}

size_t datachunk::getID (void)
{
   if (_isEOF)
      throwException (ERRCODE_ILLEGAL_INPUT, "unexpected EOF");
   return _idChunk;
}

size_t datachunk::getLength (void)
{
   if (_isEOF)
      throwException (ERRCODE_ILLEGAL_INPUT, "unexpected EOF");
   return _cbChunk;
}

datastream & datachunk::getStream (void)
{
   return _ds;
}

string datachunk::readString (void)
{
   string rc;

   char *psz = _ds.peek();
   while (*psz)
   {
      rc += *psz++;
      _ds.advance(1);
   }
   _ds.advance(1);

   return rc;
}

color datachunk::readColor (void)
{
   datachunk dc (*this);
   if (dc.getID() == 0x0011)  // 3DS COLOR_24 chunk
   {
      unsigned char *psz = (unsigned char *)_ds.read(3);
      return color (psz[0], psz[1], psz[2]);
   }
   else if (dc.getID() == 0x0010)  // 3DS COLOR_F chunk
   {
      float *pf = (float *)_ds.read(12);
      return color ((uchar)(pf[0] * 255), (uchar)(pf[1] * 255), (uchar)(pf[2] * 255));
   }

   throwException (ERRCODE_ILLEGAL_INPUT, "Unrecognized color format %X", dc.getID());
   return color (0, 0, 0);
}

uint8 datachunk::readPercentage (void)
{
   datachunk dc (*this);
   if (dc.getID() != 0x0030)  // 3DS INT_PERCENTAGE chunk
      throwException (ERRCODE_ILLEGAL_INPUT, "Unrecognized color format %X", dc.getID());

   uint16 val = _ds.pop16();
   return (val > 100) ? 255 : (uint8)((size_t)val * 255 / 100);
}

bool datachunk::isEOF (void)
{
   return _isEOF;
}

bool datachunk::next (void)
{
   if (!_isEOF)
   {
      _ds.setPos (_ibAfter);
      _ibStart = _ds.getPos();

      if (_ds.getRemaining() < CHUNKHEADERSIZE)
      {
         _isEOF = true;
      }
      else if (_ds.getPos() >= _ibParentStops)
      {
         _isEOF = true;
      }
      else
      {
         _idChunk = _ds.pop16();
         _cbChunk = _ds.pop32();
         _ibAfter = _ibStart + _cbChunk;
         if (_ds.getRemaining() < _ibAfter - _ds.getPos())
            throwException (ERRCODE_ILLEGAL_INPUT, "incomplete 3DS data file chunk");
      }
   }
   return !_isEOF;
}
lertulo is offline   Reply With Quote