Go Back   AC3D Forums > General > AC3D General
Register FAQ Members List Calendar Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Display Modes
Old 17th November 2004, 12:48 PM   #1
aircapsol
Junior Member
Junior member
 
Join Date: Nov 2004
Posts: 2
Default .3ds format description

Ok im a very new person to the forums and have had ac3d for 6 months or so. Im a ship designer for a small non comercial game company (http://www.unistellar.com) and was wondering where i could find a description of the .3ds format so that our coder can add an importer to their modeling program. I at the moment have to send every file to the creators to have it converted and have it come back becaue i cant export to .ase which is what they can import from at the moment. I know this will also help out in the future as we gain more users that want to design their own ships because .3ds is a very common format.

Thanks in advance for any help.
aircapsol is offline   Reply With Quote
Old 17th November 2004, 12:58 PM   #2
Andy
Administrator
Professional user
 
Andy's Avatar
 
Join Date: Jun 2003
Posts: 4,381
Default

Yes, unfortunately the 3ds file format is used everywhere :?

Rather than write your own 3ds export/import, I highly recommend looking at lib3ds http://lib3ds.sourceforge.net/

There is an ase-export plugin for AC3D which has not been fully tested - let me know if you'd like to try it.

Andy
Andy is offline   Reply With Quote
Old 17th November 2004, 01:29 PM   #3
aircapsol
Junior Member
Junior member
 
Join Date: Nov 2004
Posts: 2
Default

Id be willing to try it.
aircapsol is offline   Reply With Quote
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
Old 18th November 2004, 08:18 PM   #5
lertulo
Junior Member
Junior member
 
Join Date: Nov 2004
Posts: 5
Default

Complete with the "unable to load sound file" cut and paste error.
lertulo is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



All times are GMT -4. The time now is 09:47 PM.


AC3D Forum
(C) Inivis Limited 2020