|
![]() |
#1 |
Junior Member
Junior member
Join Date: Nov 2004
Posts: 2
|
![]()
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. |
![]() |
![]() |
![]() |
#2 |
Administrator
Professional user
Join Date: Jun 2003
Posts: 4,541
|
![]()
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 |
![]() |
![]() |
![]() |
#3 |
Junior Member
Junior member
Join Date: Nov 2004
Posts: 2
|
![]()
Id be willing to try it.
|
![]() |
![]() |
![]() |
#4 |
Junior Member
Junior member
Join Date: Nov 2004
Posts: 5
|
![]()
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; } |
![]() |
![]() |
![]() |
#5 |
Junior Member
Junior member
Join Date: Nov 2004
Posts: 5
|
![]()
Complete with the "unable to load sound file" cut and paste error.
![]() |
![]() |
![]() |
![]() |
Thread Tools | |
Display Modes | |
|
|