Loading 3ds files
From GPWiki
The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server. However, the GPWiki forums are still active! Come say hello.
[edit] PrefaceSample code - This example code loads and displays the points out of a file called "bull.3ds" (not included, download it from 3D Cafe in the free stuff/animals section.) The files C3dsParser.cpp C3dsParser.h and 3dschunknames.h define a basic file wrapper class that should be capable of parsing any valid .3ds file. (if you find any bugs email me.) As usual if anyone has any questions email me. [edit] .3ds formatEveryone will be relieved to know that .3ds files are not hard to load (due in no small part to the very simple and elegant chunk design, kudos to the engineer who thought it up). .3ds files are organized into sections called chunks each of which has a header that contains a 2-byte unsigned integer chunkname and a 4-byte unsigned integer of its length. The header length field includes the 6 bytes for the header itself. All fields in a .3ds are stored in little-endian byte ordering, so be sure to convert all you Mac users. The chunk name defines the format of what's contained in the chunk, which is potentially some data and/or some subchunks, in that order. If you don't know what a particular chunk is or don't care you can simply jump forward by length bytes from the beginning of the chunks header and arrive at the next one. If you're using C++ you might want to download the above example code and reuse the C3dsParser class (more on its use later). As far as game programming goes, we're probably not interested in what's selected in the views of the editor or where the editor places the lights. We're interested in geometry. Geometry is stored as one or more object chunks. The 3dschunknames.h header (borrowed with minor modification from this spec) defines these chunk names of interest:
[edit] ExampleFor example consider the following 22 bytes taken from the beginning of a .3ds file: 4D 4D 69 C5 00 00 02 00 0A 00 00 00 03 00 00 00 3D 3D 7B C4 00 00 ...
Following that header we have another header: a subchunk (the main hasn't ended according to the length and main chunks only contain subchunks, see one of the specs listed in the .3ds page for more info on what specific chunk types contain)
Following our unknown subchunk we have a sibling chunk (the unknown chunk's length is over, but its parent's isn't so its a sibling)
[edit] class C3dsParserC3dsParser is a file wrapper class I wrote to handle all the moving around in the .3ds file. It functions as follows: C3dsParser(void); - Default constructor, use LoadFile() to load a .3ds file after using this constructor. C3dsParser(char *filename); - constructor that loads a .3ds file right at the beginning. Equivalent to using the default constructor then calling LoadFile(). ~C3dsParser(void); - Destructor unsigned short GetChunkName(void); - Returns the name field from the currently selected chunk void SkipChunk(void); - Skips the currently selected chunk and selects the one directly after it void EnterChunk(long datalength = 0); - Enters the currently selected chunk so you can retrieve subchunks, if any data follows the header of the currently selected chunk, pass its size (in bytes) to this function. unsigned long GetChunkLength(void); - Returns the length field of the currently selected chunk void GetChunkData(void *buffer, unsigned long buffersize, unsigned long skip = 0); - Retrieves a number of bytes (maximum buffersize or length of the chunk - 6, whichever is smaller) after the header of the current chunk and puts them in buffer, skipping the first skip bytes void SkipStrData(void); - Skips the string data following any chunk header, such as EDIT_OBJECT, entering the chunk directly after the string data ends. unsigned long GetStrDataLength(void); - Returns the length of the string data right after a chunk header such as EDIT_OBJECT. void Rewind(void); - Rewinds the .3ds file to the beginning. BOOL LoadFile(char *filename); - Loads a .3ds file to be parsed BOOL Eof(void); - Returns true if you've recently hit the end of file marker. void CloseFile(void); - Closes the .3ds file (done automatically by the destructor) [edit] Example use of C3dsParserFrom the example code: float *GetVerts(C3dsParser &modelfile, unsigned short &numverts) { float *verts = NULL; unsigned short chunkname; unsigned long mem = 0; while(!modelfile.Eof()) { chunkname = modelfile.GetChunkName(); switch (chunkname) { case MAIN3DS: modelfile.EnterChunk(); break; case EDIT3DS: modelfile.EnterChunk(); break; case EDIT_OBJECT: modelfile.SkipStrData(); modelfile.EnterChunk(); break; case OBJ_TRIMESH: modelfile.EnterChunk(); break; case TRI_VERTEXL: modelfile.GetChunkData(&numverts, 2); // numverts vertices * 3 floats per vertex * 4 bytes per float mem = numverts * 3 * 4; verts = (float *)malloc(mem); if (verts == NULL) return NULL; modelfile.GetChunkData(verts, mem, 2); return verts; default: modelfile.SkipChunk(); break; } } } This function parses through a .3ds file and retrieves the vertices from the first object chunk it finds and returns a buffer with numverts vertices in it. The function skips all chunks except MAIN3DS, EDIT3DS, EDIT_OBJECT, OBJ_TRIMESH, and TRI_VERTEXL. In a properly formed .3ds file MAIN3DS contains EDIT3DS, which contains EDIT_OBJECT, which contains OBJ_TRIMESH, which contains TRI_VERTEXL, the vertex list. (Well, they won't always contain those chunks, an object for instance might be a camera which contains no geometry, but if we enter such a chunk we simply skip all its subchunks and end up at the chunk directly after the camera, just as if we'd skipped it.) [edit] ScreenshotsAll models in these screenshots are from 3D Cafe.
[edit] TODO
-Doug Sheets Nov 26, 2004 |


