-/* -----------------------------------------------------------------------------\r
-\r
-PicoModel Library\r
-\r
-Copyright (c) 2002, Randy Reddig & seaw0lf\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification,\r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list\r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the names of the copyright holders nor the names of its contributors may\r
-be used to endorse or promote products derived from this software without\r
-specific prior written permission.\r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
------------------------------------------------------------------------------ */\r
-\r
-\r
-\r
-/* marker */\r
-#define PM_3DS_C\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-\r
-/* ydnar */\r
-static picoColor_t white = { 255,255,255,255 };\r
-\r
-/* remarks:\r
- * - 3ds file version is stored in pico special field 0 on load (ydnar: removed)\r
- * todo:\r
- * - sometimes there is one unnamed surface 0 having 0 verts as\r
- * well as 0 faces. this error occurs since pm 0.6 (ydnar?)\r
- */\r
-/* uncomment when debugging this module */\r
-/* #define DEBUG_PM_3DS\r
-#define DEBUG_PM_3DS_EX */\r
-\r
-/* structure holding persistent 3ds loader specific data used */\r
-/* to store formerly static vars to keep the module reentrant */\r
-/* safe. put everything that needs to be static in here. */\r
-typedef struct S3dsLoaderPers\r
-{\r
- picoModel_t *model; /* ptr to output model */\r
- picoSurface_t *surface; /* ptr to current surface */\r
- picoShader_t *shader; /* ptr to current shader */\r
- picoByte_t *bufptr; /* ptr to raw data */\r
- char *basename; /* ptr to model base name (eg. jeep) */\r
- int cofs;\r
- int maxofs;\r
-}\r
-T3dsLoaderPers;\r
-\r
-/* 3ds chunk types that we use */\r
-enum {\r
- /* primary chunk */\r
- CHUNK_MAIN = 0x4D4D,\r
-\r
- /* main chunks */\r
- CHUNK_VERSION = 0x0002,\r
- CHUNK_EDITOR_CONFIG = 0x3D3E,\r
- CHUNK_EDITOR_DATA = 0x3D3D,\r
- CHUNK_KEYFRAME_DATA = 0xB000,\r
-\r
- /* editor data sub chunks */\r
- CHUNK_MATERIAL = 0xAFFF,\r
- CHUNK_OBJECT = 0x4000,\r
-\r
- /* material sub chunks */\r
- CHUNK_MATNAME = 0xA000,\r
- CHUNK_MATDIFFUSE = 0xA020,\r
- CHUNK_MATMAP = 0xA200,\r
- CHUNK_MATMAPFILE = 0xA300,\r
-\r
- /* lets us know we're reading a new object */\r
- CHUNK_OBJECT_MESH = 0x4100,\r
-\r
- /* object mesh sub chunks */\r
- CHUNK_OBJECT_VERTICES = 0x4110,\r
- CHUNK_OBJECT_FACES = 0x4120,\r
- CHUNK_OBJECT_MATERIAL = 0x4130,\r
- CHUNK_OBJECT_UV = 0x4140,\r
-};\r
-#ifdef DEBUG_PM_3DS\r
-static struct\r
-{\r
- int id;\r
- char *name;\r
-}\r
-debugChunkNames[] =\r
-{\r
- { CHUNK_MAIN , "CHUNK_MAIN" },\r
- { CHUNK_VERSION , "CHUNK_VERSION" },\r
- { CHUNK_EDITOR_CONFIG , "CHUNK_EDITOR_CONFIG" },\r
- { CHUNK_EDITOR_DATA , "CHUNK_EDITOR_DATA" },\r
- { CHUNK_KEYFRAME_DATA , "CHUNK_KEYFRAME_DATA" },\r
- { CHUNK_MATERIAL , "CHUNK_MATERIAL" },\r
- { CHUNK_OBJECT , "CHUNK_OBJECT" },\r
- { CHUNK_MATNAME , "CHUNK_MATNAME" },\r
- { CHUNK_MATDIFFUSE , "CHUNK_MATDIFFUSE" },\r
- { CHUNK_MATMAP , "CHUNK_MATMAP" },\r
- { CHUNK_MATMAPFILE , "CHUNK_MATMAPFILE" },\r
- { CHUNK_OBJECT_MESH , "CHUNK_OBJECT_MESH" },\r
- { CHUNK_OBJECT_VERTICES , "CHUNK_OBJECT_VERTICES" },\r
- { CHUNK_OBJECT_FACES , "CHUNK_OBJECT_FACES" },\r
- { CHUNK_OBJECT_MATERIAL , "CHUNK_OBJECT_MATERIAL" },\r
- { CHUNK_OBJECT_UV , "CHUNK_OBJECT_UV" },\r
- { 0 , NULL }\r
-};\r
-static char *DebugGetChunkName (int id)\r
-{\r
- int i,max; /* imax? ;) */\r
- max = sizeof(debugChunkNames) / sizeof(debugChunkNames[0]);\r
-\r
- for (i=0; i<max; i++)\r
- {\r
- if (debugChunkNames[i].id == id)\r
- {\r
- /* gaynux update -sea */\r
- return _pico_strlwr( debugChunkNames[i].name );\r
- }\r
- }\r
- return "chunk_unknown";\r
-}\r
-#endif /*DEBUG_PM_3DS*/\r
-\r
-/* this funky loader needs byte alignment */\r
-#pragma pack(push, 1)\r
-\r
-typedef struct S3dsIndices\r
-{\r
- unsigned short a,b,c;\r
- unsigned short visible;\r
-}\r
-T3dsIndices;\r
-\r
-typedef struct S3dsChunk\r
-{\r
- unsigned short id;\r
- unsigned int len;\r
-}\r
-T3dsChunk;\r
-\r
-/* restore previous data alignment */\r
-#pragma pack(pop)\r
-\r
-/* _3ds_canload:\r
- * validates an autodesk 3ds model file.\r
- */\r
-static int _3ds_canload( PM_PARAMS_CANLOAD )\r
-{\r
- T3dsChunk *chunk;\r
-\r
- /* to keep the compiler happy */\r
- *fileName = *fileName;\r
-\r
- /* sanity check */\r
- if (bufSize < sizeof(T3dsChunk))\r
- return PICO_PMV_ERROR_SIZE;\r
-\r
- /* get pointer to 3ds header chunk */\r
- chunk = (T3dsChunk *)buffer;\r
-\r
- /* check data length */\r
- if (bufSize < _pico_little_long(chunk->len))\r
- return PICO_PMV_ERROR_SIZE;\r
-\r
- /* check 3ds magic */\r
- if (_pico_little_short(chunk->id) != CHUNK_MAIN)\r
- return PICO_PMV_ERROR_IDENT;\r
-\r
- /* file seems to be a valid 3ds */\r
- return PICO_PMV_OK;\r
-}\r
-\r
-static T3dsChunk *GetChunk (T3dsLoaderPers *pers)\r
-{\r
- T3dsChunk *chunk;\r
-\r
- /* sanity check */\r
- if (pers->cofs > pers->maxofs) return 0;\r
-\r
-#ifdef DEBUG_PM_3DS\r
-/* printf("GetChunk: pers->cofs %x\n",pers->cofs); */\r
-#endif\r
- /* fill in pointer to chunk */\r
- chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];\r
- if (!chunk) return NULL;\r
-\r
- chunk->id = _pico_little_short(chunk->id );\r
- chunk->len = _pico_little_long (chunk->len);\r
-\r
- /* advance in buffer */\r
- pers->cofs += sizeof(T3dsChunk);\r
-\r
- /* this means yay */\r
- return chunk;\r
-}\r
-\r
-static int GetASCIIZ (T3dsLoaderPers *pers, char *dest, int max)\r
-{\r
- int pos = 0;\r
- int ch;\r
-\r
- for (;;)\r
- {\r
- ch = pers->bufptr[ pers->cofs++ ];\r
- if (ch == '\0') break;\r
- if (pers->cofs >= pers->maxofs)\r
- {\r
- dest[ pos ] = '\0';\r
- return 0;\r
- }\r
- dest[ pos++ ] = ch;\r
- if (pos >= max) break;\r
- }\r
- dest[ pos ] = '\0';\r
- return 1;\r
-}\r
-\r
-static picoByte_t GetByte (T3dsLoaderPers *pers)\r
-{\r
- picoByte_t *value;\r
-\r
- /* sanity check */\r
- if (pers->cofs > pers->maxofs) return 0;\r
-\r
- /* get and return value */\r
- value = (picoByte_t *)(pers->bufptr + pers->cofs);\r
- pers->cofs += 1;\r
- return *value;\r
-}\r
-\r
-static int GetWord (T3dsLoaderPers *pers)\r
-{\r
- unsigned short *value;\r
-\r
- /* sanity check */\r
- if (pers->cofs > pers->maxofs) return 0;\r
-\r
- /* get and return value */\r
- value = (unsigned short *)(pers->bufptr + pers->cofs);\r
- pers->cofs += 2;\r
- return _pico_little_short(*value);\r
-}\r
-\r
-static float GetFloat (T3dsLoaderPers *pers)\r
-{\r
- float *value;\r
-\r
- /* sanity check */\r
- if (pers->cofs > pers->maxofs) return 0;\r
-\r
- /* get and return value */\r
- value = (float *)(pers->bufptr + pers->cofs);\r
- pers->cofs += 4;\r
- return _pico_little_float(*value);\r
-}\r
-\r
-static int GetMeshVertices (T3dsLoaderPers *pers)\r
-{\r
- int numVerts;\r
- int i;\r
-\r
- /* get number of verts for this surface */\r
- numVerts = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
- printf("GetMeshVertices: numverts %d\n",numVerts);\r
-#endif\r
- /* read in vertices for current surface */\r
- for (i=0; i<numVerts; i++)\r
- {\r
- picoVec3_t v;\r
- v[0] = GetFloat( pers );\r
- v[1] = GetFloat( pers ); /* ydnar: unflipped */\r
- v[2] = GetFloat( pers ); /* ydnar: unflipped and negated */\r
- \r
- /* add current vertex */\r
- PicoSetSurfaceXYZ( pers->surface,i,v );\r
- PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
- printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);\r
-#endif\r
- }\r
- /* success (no errors occured) */\r
- return 1;\r
-}\r
-\r
-static int GetMeshFaces (T3dsLoaderPers *pers)\r
-{\r
- int numFaces;\r
- int i;\r
-\r
- /* get number of faces for this surface */\r
- numFaces = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
- printf("GetMeshFaces: numfaces %d\n",numFaces);\r
-#endif\r
- /* read in vertex indices for current surface */\r
- for (i=0; i<numFaces; i++)\r
- {\r
- /* remember, we only need 3 of 4 values read in for each */\r
- /* face. the 4th value is a vis flag for 3dsmax which is */\r
- /* being ignored by us here */\r
- T3dsIndices face;\r
- face.a = GetWord(pers);\r
- face.c = GetWord(pers); /* ydnar: flipped order */\r
- face.b = GetWord(pers); /* ydnar: flipped order */\r
- face.visible = GetWord(pers);\r
-\r
- /* copy indexes */\r
- PicoSetSurfaceIndex( pers->surface, (i * 3 + 0), (picoIndex_t)face.a );\r
- PicoSetSurfaceIndex( pers->surface, (i * 3 + 1), (picoIndex_t)face.b );\r
- PicoSetSurfaceIndex( pers->surface, (i * 3 + 2), (picoIndex_t)face.c );\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
- printf("Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible);\r
-#endif\r
- }\r
- /* success (no errors occured) */\r
- return 1;\r
-}\r
-\r
-static int GetMeshTexCoords (T3dsLoaderPers *pers)\r
-{\r
- int numTexCoords;\r
- int i;\r
-\r
- /* get number of uv coords for this surface */\r
- numTexCoords = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
- printf("GetMeshTexCoords: numcoords %d\n",numTexCoords);\r
-#endif\r
- /* read in uv coords for current surface */\r
- for (i=0; i<numTexCoords; i++)\r
- {\r
- picoVec2_t uv;\r
- uv[0] = GetFloat( pers );\r
- uv[1] = -GetFloat( pers ); /* ydnar: we use origin at bottom */\r
-\r
- /* to make sure we don't mess up memory */\r
- if (pers->surface == NULL)\r
- continue;\r
- \r
- /* add current uv */\r
- PicoSetSurfaceST( pers->surface,0,i,uv );\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
- printf("u: %f v: %f\n",uv[0],uv[1]);\r
-#endif\r
- }\r
- /* success (no errors occured) */\r
- return 1;\r
-}\r
-\r
-static int GetMeshShader (T3dsLoaderPers *pers)\r
-{\r
- char shaderName[255] = { 0 };\r
- picoShader_t *shader;\r
- int numSharedVerts;\r
- int setShaderName = 0;\r
- int i;\r
- \r
- /* the shader is either the color or the texture map of the */\r
- /* object. it can also hold other information like the brightness, */\r
- /* shine, etc. stuff we don't really care about. we just want the */\r
- /* color, or the texture map file name really */\r
-\r
- /* get in the shader name */\r
- if (!GetASCIIZ(pers,shaderName,sizeof(shaderName)))\r
- return 0;\r
-\r
- /* now that we have the shader name we need to go through all of */\r
- /* the shaders and check the name against each shader. when we */\r
- /* find a shader in our shader list that matches this name we */\r
- /* just read in, then we assign the shader's id of the object to */\r
- /* that shader */\r
-\r
- /* get shader id for shader name */\r
- shader = PicoFindShader( pers->model, shaderName, 1 );\r
-\r
- /* we've found a matching shader */\r
- if ((shader != NULL) && pers->surface)\r
- {\r
- char mapName[1024+1];\r
- char *mapNamePtr;\r
- memset( mapName,0,sizeof(mapName) );\r
-\r
- /* get ptr to shader's map name */\r
- mapNamePtr = PicoGetShaderMapName( shader );\r
-\r
- /* we have a valid map name ptr */\r
- if (mapNamePtr != NULL)\r
- {\r
- char temp[128];\r
- char *name;\r
-\r
- /* copy map name to local buffer */\r
- strcpy( mapName,mapNamePtr );\r
-\r
- /* extract file name */\r
- name = _pico_nopath( mapName );\r
- strncpy( temp, name, sizeof(temp) );\r
-\r
- /* remove file extension */\r
- /* name = _pico_setfext( name,"" ); */\r
-\r
- /* assign default name if no name available */\r
- if (strlen(temp) < 1)\r
- strcpy(temp,pers->basename);\r
-\r
- /* build shader name */\r
- _pico_strlwr( temp ); /* gaynux update -sea */\r
- sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );\r
-\r
- /* set shader name */\r
- /* PicoSetShaderName( shader,mapName ); */ /* ydnar: this will screw up the named shader */\r
-\r
- /* set surface's shader index */\r
- PicoSetSurfaceShader( pers->surface, shader );\r
-\r
- setShaderName = 1;\r
- }\r
- }\r
- /* we didn't set a shader name; throw out warning */\r
- if (!setShaderName)\r
- {\r
- _pico_printf( PICO_WARNING,"3DS mesh is missing shader name");\r
- }\r
- /* we don't process the list of shared vertices here; there is a */\r
- /* short int that gives the number of faces of the mesh concerned */\r
- /* by this shader, then there is the list itself of these faces. */\r
- /* 0000 means the first face of the (4120) face list */\r
-\r
- /* get number of shared verts */\r
- numSharedVerts = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
- printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts);\r
-#endif\r
- /* skip list of shared verts */\r
- for (i=0; i<numSharedVerts; i++)\r
- {\r
- GetWord(pers);\r
- }\r
- /* success (no errors occured) */\r
- return 1;\r
-}\r
-\r
-static int GetDiffuseColor (T3dsLoaderPers *pers)\r
-{\r
- /* todo: support all 3ds specific color formats; */\r
- /* that means: rgb,tru,trug,rgbg */\r
-\r
- /* get rgb color (range 0..255; 3 bytes) */\r
- picoColor_t color;\r
-\r
- color[0] = GetByte(pers);\r
- color[1] = GetByte(pers);\r
- color[2] = GetByte(pers);\r
- color[3] = 255;\r
-\r
- /* store this as the current shader's diffuse color */\r
- if( pers->shader )\r
- {\r
- PicoSetShaderDiffuseColor( pers->shader,color );\r
- }\r
-#ifdef DEBUG_PM_3DS\r
- printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]);\r
-#endif\r
- /* success (no errors occured) */\r
- return 1;\r
-}\r
-\r
-static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs)\r
-{\r
- T3dsChunk *chunk;\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
- printf("DoNextEditorDataChunk: endofs %d\n",endofs);\r
-#endif\r
- while (pers->cofs < endofs)\r
- {\r
- long nextofs = pers->cofs;\r
- if ((chunk = GetChunk(pers)) == NULL) return 0;\r
- if (!chunk->len) return 0;\r
- nextofs += chunk->len;\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
- printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);\r
-#endif\r
- /*** meshes ***/\r
- if (chunk->id == CHUNK_OBJECT)\r
- {\r
- picoSurface_t *surface;\r
- char surfaceName[ 0xff ] = { 0 };\r
-\r
- /* read in surface name */\r
- if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) )\r
- return 0; /* this is bad */\r
-\r
-//PicoGetSurfaceName\r
- /* ignore NULL name surfaces */\r
-// if( surfaceName\r
-\r
- /* allocate a pico surface */\r
- surface = PicoNewSurface( pers->model );\r
- if( surface == NULL )\r
- {\r
- pers->surface = NULL;\r
- return 0; /* this is bad too */\r
- }\r
- /* assign ptr to current surface */\r
- pers->surface = surface;\r
-\r
- /* 3ds models surfaces are all triangle meshes */\r
- PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );\r
-\r
- /* set surface name */\r
- PicoSetSurfaceName( pers->surface,surfaceName );\r
-\r
- /* continue mess with object's sub chunks */\r
- DoNextEditorDataChunk(pers,nextofs);\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_OBJECT_MESH)\r
- {\r
- /* continue mess with mesh's sub chunks */\r
- if (!DoNextEditorDataChunk(pers,nextofs)) return 0;\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_OBJECT_VERTICES)\r
- {\r
- if (!GetMeshVertices(pers)) return 0;\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_OBJECT_FACES)\r
- {\r
- if (!GetMeshFaces(pers)) return 0;\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_OBJECT_UV)\r
- {\r
- if (!GetMeshTexCoords(pers)) return 0;\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_OBJECT_MATERIAL)\r
- {\r
- if (!GetMeshShader(pers)) return 0;\r
- continue;\r
- }\r
- /*** materials ***/\r
- if (chunk->id == CHUNK_MATERIAL)\r
- {\r
- /* new shader specific things should be */\r
- /* initialized right here */\r
- picoShader_t *shader;\r
-\r
- /* allocate a pico shader */\r
- shader = PicoNewShader( pers->model ); /* ydnar */\r
- if( shader == NULL )\r
- {\r
- pers->shader = NULL;\r
- return 0; /* this is bad too */\r
- }\r
- \r
- /* assign ptr to current shader */\r
- pers->shader = shader;\r
-\r
- /* continue and process the material's sub chunks */\r
- DoNextEditorDataChunk(pers,nextofs);\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_MATNAME)\r
- {\r
- /* new material's names should be stored here. note that */\r
- /* GetMeshMaterial returns the name of the material that */\r
- /* is used by the mesh. new material names are set HERE. */\r
- /* but for now we skip the new material's name ... */\r
- if (pers->shader)\r
- {\r
- char *name = (char *)(pers->bufptr + pers->cofs);\r
- PicoSetShaderName( pers->shader,name );\r
-#ifdef DEBUG_PM_3DS\r
- printf("NewShader: '%s'\n",name);\r
-#endif\r
- }\r
- }\r
- if (chunk->id == CHUNK_MATDIFFUSE)\r
- {\r
- /* todo: color for last inserted new material should be */\r
- /* stored somewhere by GetDiffuseColor */\r
- if (!GetDiffuseColor(pers)) return 0;\r
-\r
- /* rest of chunk is skipped here */\r
- }\r
- if (chunk->id == CHUNK_MATMAP)\r
- {\r
- /* continue and process the material map sub chunks */\r
- DoNextEditorDataChunk(pers,nextofs);\r
- continue;\r
- }\r
- if (chunk->id == CHUNK_MATMAPFILE)\r
- {\r
- /* map file name for last inserted new material should */\r
- /* be stored here. but for now we skip this too ... */\r
- if( pers->shader )\r
- {\r
- char *name = (char *)(pers->bufptr + pers->cofs);\r
- PicoSetShaderMapName( pers->shader,name );\r
-#ifdef DEBUG_PM_3DS\r
- printf("NewShaderMapfile: '%s'\n",name);\r
-#endif\r
- }\r
- }\r
- /*** keyframes ***/\r
- if (chunk->id == CHUNK_KEYFRAME_DATA)\r
- {\r
- /* well umm, this is a bit too much since we don't really */\r
- /* need model animation sequences right now. we skip this */\r
-#ifdef DEBUG_PM_3DS\r
- printf("KeyframeData: len %d\n",chunk->len);\r
-#endif\r
- }\r
- /* skip unknown chunk */\r
- pers->cofs = nextofs;\r
- if (pers->cofs >= pers->maxofs) break;\r
- }\r
- return 1;\r
-}\r
-\r
-static int DoNextChunk (T3dsLoaderPers *pers, int endofs)\r
-{\r
- T3dsChunk *chunk;\r
-\r
-#ifdef DEBUG_PM_3DS\r
- printf("DoNextChunk: endofs %d\n",endofs);\r
-#endif\r
- while (pers->cofs < endofs)\r
- {\r
- long nextofs = pers->cofs;\r
- if ((chunk = GetChunk(pers)) == NULL) return 0;\r
- if (!chunk->len) return 0;\r
- nextofs += chunk->len;\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
- printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);\r
-#endif\r
- /*** version ***/\r
- if (chunk->id == CHUNK_VERSION)\r
- {\r
- /* at this point i get the 3ds file version. since there */\r
- /* might be new additions to the 3ds file format in 4.0 */\r
- /* it might be a good idea to store the version somewhere */\r
- /* for later handling or message displaying */\r
-\r
- /* get the version */\r
- int version;\r
- version = GetWord(pers);\r
- GetWord(pers);\r
-#ifdef DEBUG_PM_3DS\r
- printf("FileVersion: %d\n",version);\r
-#endif\r
-\r
- /* throw out a warning for version 4 models */\r
- if (version == 4)\r
- {\r
- _pico_printf( PICO_WARNING,\r
- "3DS version is 4. Model might load incorrectly.");\r
- }\r
- /* store the 3ds file version in pico special field 0 */\r
- /* PicoSetSurfaceSpecial(pers->surface,0,version); */ /* ydnar: this was causing a crash accessing uninitialized surface */\r
- \r
- /* rest of chunk is skipped here */\r
- }\r
- /*** editor data ***/\r
- if (chunk->id == CHUNK_EDITOR_DATA)\r
- {\r
- if (!DoNextEditorDataChunk(pers,nextofs)) return 0;\r
- continue;\r
- }\r
- /* skip unknown chunk */\r
- pers->cofs = nextofs;\r
- if (pers->cofs >= pers->maxofs) break;\r
- }\r
- return 1;\r
-}\r
-\r
-/* _3ds_load:\r
- * loads an autodesk 3ds model file.\r
-*/\r
-static picoModel_t *_3ds_load( PM_PARAMS_LOAD )\r
-{\r
- T3dsLoaderPers pers;\r
- picoModel_t *model;\r
- char basename[128];\r
-\r
- /* create a new pico model */\r
- model = PicoNewModel();\r
- if (model == NULL)\r
- {\r
- /* user must have some serious ram problems ;) */\r
- return NULL;\r
- }\r
- /* get model's base name (eg. jeep from c:\models\jeep.3ds) */\r
- memset( basename,0,sizeof(basename) );\r
- strncpy( basename,_pico_nopath(fileName),sizeof(basename) );\r
- _pico_setfext( basename,"" );\r
-\r
- /* initialize persistant vars (formerly static) */\r
- pers.model = model;\r
- pers.bufptr = (picoByte_t *)buffer;\r
- pers.basename = (char *)basename;\r
- pers.maxofs = bufSize;\r
- pers.cofs = 0L;\r
-\r
- /* do model setup */\r
- PicoSetModelFrameNum( model,frameNum );\r
- PicoSetModelName( model,fileName );\r
- PicoSetModelFileName( model,fileName );\r
-\r
- /* skip first chunk in file (magic) */\r
- GetChunk(&pers);\r
-\r
- /* process chunks */\r
- if (!DoNextChunk(&pers,pers.maxofs))\r
- {\r
- /* well, bleh i guess */\r
- PicoFreeModel(model);\r
- return NULL;\r
- }\r
- /* return allocated pico model */\r
- return model;\r
-}\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModule3DS =\r
-{\r
- "0.86-b", /* module version string */\r
- "Autodesk 3Dstudio", /* module display name */\r
- "seaw0lf", /* author's name */\r
- "2002 seaw0lf", /* module copyright */\r
- {\r
- "3ds",NULL,NULL,NULL /* default extensions to use */\r
- },\r
- _3ds_canload, /* validation routine */\r
- _3ds_load, /* load routine */\r
- NULL, /* save validation routine */\r
- NULL /* save routine */\r
-};\r
+/* -----------------------------------------------------------------------------
+
+ PicoModel Library
+
+ Copyright (c) 2002, Randy Reddig & seaw0lf
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this list
+ of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+
+ Neither the names of the copyright holders nor the names of its contributors may
+ be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------------------- */
+
+/* dependencies */
+#include "picointernal.h"
+
+/* ydnar */
+static picoColor_t white = { 255,255,255,255 };
+
+/* remarks:
+ * - 3ds file version is stored in pico special field 0 on load (ydnar: removed)
+ * todo:
+ * - sometimes there is one unnamed surface 0 having 0 verts as
+ * well as 0 faces. this error occurs since pm 0.6 (ydnar?)
+ */
+/* uncomment when debugging this module */
+/* #define DEBUG_PM_3DS
+ #define DEBUG_PM_3DS_EX */
+
+/* structure holding persistent 3ds loader specific data used */
+/* to store formerly static vars to keep the module reentrant */
+/* safe. put everything that needs to be static in here. */
+typedef struct S3dsLoaderPers
+{
+ picoModel_t *model; /* ptr to output model */
+ picoSurface_t *surface; /* ptr to current surface */
+ picoShader_t *shader; /* ptr to current shader */
+ picoByte_t *bufptr; /* ptr to raw data */
+ char *basename; /* ptr to model base name (eg. jeep) */
+ int cofs;
+ int maxofs;
+}
+T3dsLoaderPers;
+
+/* 3ds chunk types that we use */
+enum {
+ /* primary chunk */
+ CHUNK_MAIN = 0x4D4D,
+
+ /* main chunks */
+ CHUNK_VERSION = 0x0002,
+ CHUNK_EDITOR_CONFIG = 0x3D3E,
+ CHUNK_EDITOR_DATA = 0x3D3D,
+ CHUNK_KEYFRAME_DATA = 0xB000,
+
+ /* editor data sub chunks */
+ CHUNK_MATERIAL = 0xAFFF,
+ CHUNK_OBJECT = 0x4000,
+
+ /* material sub chunks */
+ CHUNK_MATNAME = 0xA000,
+ CHUNK_MATDIFFUSE = 0xA020,
+ CHUNK_MATMAP = 0xA200,
+ CHUNK_MATMAPFILE = 0xA300,
+
+ /* lets us know we're reading a new object */
+ CHUNK_OBJECT_MESH = 0x4100,
+
+ /* object mesh sub chunks */
+ CHUNK_OBJECT_VERTICES = 0x4110,
+ CHUNK_OBJECT_FACES = 0x4120,
+ CHUNK_OBJECT_MATERIAL = 0x4130,
+ CHUNK_OBJECT_UV = 0x4140,
+};
+#ifdef DEBUG_PM_3DS
+static struct
+{
+ int id;
+ char *name;
+}
+debugChunkNames[] =
+{
+ { CHUNK_MAIN, "CHUNK_MAIN" },
+ { CHUNK_VERSION, "CHUNK_VERSION" },
+ { CHUNK_EDITOR_CONFIG, "CHUNK_EDITOR_CONFIG" },
+ { CHUNK_EDITOR_DATA, "CHUNK_EDITOR_DATA" },
+ { CHUNK_KEYFRAME_DATA, "CHUNK_KEYFRAME_DATA" },
+ { CHUNK_MATERIAL, "CHUNK_MATERIAL" },
+ { CHUNK_OBJECT, "CHUNK_OBJECT" },
+ { CHUNK_MATNAME, "CHUNK_MATNAME" },
+ { CHUNK_MATDIFFUSE, "CHUNK_MATDIFFUSE" },
+ { CHUNK_MATMAP, "CHUNK_MATMAP" },
+ { CHUNK_MATMAPFILE, "CHUNK_MATMAPFILE" },
+ { CHUNK_OBJECT_MESH, "CHUNK_OBJECT_MESH" },
+ { CHUNK_OBJECT_VERTICES, "CHUNK_OBJECT_VERTICES" },
+ { CHUNK_OBJECT_FACES, "CHUNK_OBJECT_FACES" },
+ { CHUNK_OBJECT_MATERIAL, "CHUNK_OBJECT_MATERIAL" },
+ { CHUNK_OBJECT_UV, "CHUNK_OBJECT_UV" },
+ { 0, NULL }
+};
+static char *DebugGetChunkName( int id ) {
+ int i,max; /* imax? ;) */
+ max = sizeof( debugChunkNames ) / sizeof( debugChunkNames[0] );
+
+ for ( i = 0; i < max; i++ )
+ {
+ if ( debugChunkNames[i].id == id ) {
+ /* gaynux update -sea */
+ return _pico_strlwr( debugChunkNames[i].name );
+ }
+ }
+ return "chunk_unknown";
+}
+#endif /*DEBUG_PM_3DS*/
+
+/* this funky loader needs byte alignment */
+#pragma pack(push, 1)
+
+typedef struct S3dsIndices
+{
+ unsigned short a,b,c;
+ unsigned short visible;
+}
+T3dsIndices;
+
+typedef struct S3dsChunk
+{
+ unsigned short id;
+ unsigned int len;
+}
+T3dsChunk;
+
+/* restore previous data alignment */
+#pragma pack(pop)
+
+/* _3ds_canload:
+ * validates an autodesk 3ds model file.
+ */
+static int _3ds_canload( PM_PARAMS_CANLOAD ){
+ const T3dsChunk *chunk;
+
+ /* sanity check */
+ if ( bufSize < (int) sizeof( T3dsChunk ) ) {
+ return PICO_PMV_ERROR_SIZE;
+ }
+
+ /* get pointer to 3ds header chunk */
+ chunk = (const T3dsChunk *)buffer;
+
+ /* check data length */
+ if ( bufSize < (int) _pico_little_long( chunk->len ) ) {
+ return PICO_PMV_ERROR_SIZE;
+ }
+
+ /* check 3ds magic */
+ if ( _pico_little_short( chunk->id ) != CHUNK_MAIN ) {
+ return PICO_PMV_ERROR_IDENT;
+ }
+
+ /* file seems to be a valid 3ds */
+ return PICO_PMV_OK;
+}
+
+static T3dsChunk *GetChunk( T3dsLoaderPers *pers ){
+ T3dsChunk *chunk;
+
+ /* sanity check */
+ if ( pers->cofs > pers->maxofs ) {
+ return 0;
+ }
+
+#ifdef DEBUG_PM_3DS
+/* printf("GetChunk: pers->cofs %x\n",pers->cofs); */
+#endif
+ /* fill in pointer to chunk */
+ chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];
+ if ( !chunk ) {
+ return NULL;
+ }
+
+ chunk->id = _pico_little_short( chunk->id );
+ chunk->len = _pico_little_long( chunk->len );
+
+ /* advance in buffer */
+ pers->cofs += sizeof( T3dsChunk );
+
+ /* this means yay */
+ return chunk;
+}
+
+static int GetASCIIZ( T3dsLoaderPers *pers, char *dest, int max ){
+ int pos = 0;
+ int ch;
+
+ for (;; )
+ {
+ ch = pers->bufptr[ pers->cofs++ ];
+ if ( ch == '\0' ) {
+ break;
+ }
+ if ( pers->cofs >= pers->maxofs ) {
+ dest[ pos ] = '\0';
+ return 0;
+ }
+ dest[ pos++ ] = ch;
+ if ( pos >= max ) {
+ break;
+ }
+ }
+ dest[ pos ] = '\0';
+ return 1;
+}
+
+static picoByte_t GetByte( T3dsLoaderPers *pers ){
+ picoByte_t *value;
+
+ /* sanity check */
+ if ( pers->cofs > pers->maxofs ) {
+ return 0;
+ }
+
+ /* get and return value */
+ value = (picoByte_t *)( pers->bufptr + pers->cofs );
+ pers->cofs += 1;
+ return *value;
+}
+
+static int GetWord( T3dsLoaderPers *pers ){
+ unsigned short *value;
+
+ /* sanity check */
+ if ( pers->cofs > pers->maxofs ) {
+ return 0;
+ }
+
+ /* get and return value */
+ value = (unsigned short *)( pers->bufptr + pers->cofs );
+ pers->cofs += 2;
+ return _pico_little_short( *value );
+}
+
+static float GetFloat( T3dsLoaderPers *pers ){
+ float *value;
+
+ /* sanity check */
+ if ( pers->cofs > pers->maxofs ) {
+ return 0;
+ }
+
+ /* get and return value */
+ value = (float *)( pers->bufptr + pers->cofs );
+ pers->cofs += 4;
+ return _pico_little_float( *value );
+}
+
+static int GetMeshVertices( T3dsLoaderPers *pers ){
+ int numVerts;
+ int i;
+
+ /* get number of verts for this surface */
+ numVerts = GetWord( pers );
+
+#ifdef DEBUG_PM_3DS
+ printf( "GetMeshVertices: numverts %d\n",numVerts );
+#endif
+ /* read in vertices for current surface */
+ for ( i = 0; i < numVerts; i++ )
+ {
+ picoVec3_t v;
+ v[0] = GetFloat( pers );
+ v[1] = GetFloat( pers ); /* ydnar: unflipped */
+ v[2] = GetFloat( pers ); /* ydnar: unflipped and negated */
+
+ /* add current vertex */
+ PicoSetSurfaceXYZ( pers->surface,i,v );
+ PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */
+
+#ifdef DEBUG_PM_3DS_EX
+ printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
+#endif
+ }
+ /* success (no errors occured) */
+ return 1;
+}
+
+static int GetMeshFaces( T3dsLoaderPers *pers ){
+ int numFaces;
+ int i;
+
+ /* get number of faces for this surface */
+ numFaces = GetWord( pers );
+
+#ifdef DEBUG_PM_3DS
+ printf( "GetMeshFaces: numfaces %d\n",numFaces );
+#endif
+ /* read in vertex indices for current surface */
+ for ( i = 0; i < numFaces; i++ )
+ {
+ /* remember, we only need 3 of 4 values read in for each */
+ /* face. the 4th value is a vis flag for 3dsmax which is */
+ /* being ignored by us here */
+ T3dsIndices face;
+ face.a = GetWord( pers );
+ face.c = GetWord( pers ); /* ydnar: flipped order */
+ face.b = GetWord( pers ); /* ydnar: flipped order */
+ face.visible = GetWord( pers );
+
+ /* copy indexes */
+ PicoSetSurfaceIndex( pers->surface, ( i * 3 + 0 ), (picoIndex_t)face.a );
+ PicoSetSurfaceIndex( pers->surface, ( i * 3 + 1 ), (picoIndex_t)face.b );
+ PicoSetSurfaceIndex( pers->surface, ( i * 3 + 2 ), (picoIndex_t)face.c );
+
+#ifdef DEBUG_PM_3DS_EX
+ printf( "Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible );
+#endif
+ }
+ /* success (no errors occured) */
+ return 1;
+}
+
+static int GetMeshTexCoords( T3dsLoaderPers *pers ){
+ int numTexCoords;
+ int i;
+
+ /* get number of uv coords for this surface */
+ numTexCoords = GetWord( pers );
+
+#ifdef DEBUG_PM_3DS
+ printf( "GetMeshTexCoords: numcoords %d\n",numTexCoords );
+#endif
+ /* read in uv coords for current surface */
+ for ( i = 0; i < numTexCoords; i++ )
+ {
+ picoVec2_t uv;
+ uv[0] = GetFloat( pers );
+ uv[1] = -GetFloat( pers ); /* ydnar: we use origin at bottom */
+
+ /* to make sure we don't mess up memory */
+ if ( pers->surface == NULL ) {
+ continue;
+ }
+
+ /* add current uv */
+ PicoSetSurfaceST( pers->surface,0,i,uv );
+
+#ifdef DEBUG_PM_3DS_EX
+ printf( "u: %f v: %f\n",uv[0],uv[1] );
+#endif
+ }
+ /* success (no errors occured) */
+ return 1;
+}
+
+static int GetMeshShader( T3dsLoaderPers *pers ){
+ char shaderName[255] = { 0 };
+ picoShader_t *shader;
+ int numSharedVerts;
+ int setShaderName = 0;
+ int i;
+
+ /* the shader is either the color or the texture map of the */
+ /* object. it can also hold other information like the brightness, */
+ /* shine, etc. stuff we don't really care about. we just want the */
+ /* color, or the texture map file name really */
+
+ /* get in the shader name */
+ if ( !GetASCIIZ( pers,shaderName,sizeof( shaderName ) ) ) {
+ return 0;
+ }
+
+ /* ydnar: trim to first whitespace */
+ _pico_first_token( shaderName );
+
+ /* now that we have the shader name we need to go through all of */
+ /* the shaders and check the name against each shader. when we */
+ /* find a shader in our shader list that matches this name we */
+ /* just read in, then we assign the shader's id of the object to */
+ /* that shader */
+
+ /* get shader id for shader name */
+ shader = PicoFindShader( pers->model, shaderName, 1 );
+
+ /* we've found a matching shader */
+ if ( ( shader != NULL ) && pers->surface ) {
+ char mapName[1024 + 1];
+ char *mapNamePtr;
+ memset( mapName,0,sizeof( mapName ) );
+
+ /* get ptr to shader's map name */
+ mapNamePtr = PicoGetShaderMapName( shader );
+
+ /* we have a valid map name ptr */
+ if ( mapNamePtr != NULL ) {
+ char temp[128];
+ const char *name;
+
+ /* copy map name to local buffer */
+ strcpy( mapName,mapNamePtr );
+
+ /* extract file name */
+ name = _pico_nopath( mapName );
+ strncpy( temp, name, sizeof( temp ) );
+
+ /* remove file extension */
+ /* name = _pico_setfext( name,"" ); */
+
+ /* assign default name if no name available */
+ if ( strlen( temp ) < 1 ) {
+ strcpy( temp,pers->basename );
+ }
+
+ /* build shader name */
+ _pico_strlwr( temp ); /* gaynux update -sea */
+ sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );
+
+ /* set shader name */
+ /* PicoSetShaderName( shader,mapName ); */ /* ydnar: this will screw up the named shader */
+
+ /* set surface's shader index */
+ PicoSetSurfaceShader( pers->surface, shader );
+
+ setShaderName = 1;
+ }
+ }
+ /* we didn't set a shader name; throw out warning */
+ if ( !setShaderName ) {
+ _pico_printf( PICO_WARNING,"3DS mesh is missing shader name" );
+ }
+ /* we don't process the list of shared vertices here; there is a */
+ /* short int that gives the number of faces of the mesh concerned */
+ /* by this shader, then there is the list itself of these faces. */
+ /* 0000 means the first face of the (4120) face list */
+
+ /* get number of shared verts */
+ numSharedVerts = GetWord( pers );
+
+#ifdef DEBUG_PM_3DS
+ printf( "GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts );
+#endif
+ /* skip list of shared verts */
+ for ( i = 0; i < numSharedVerts; i++ )
+ {
+ GetWord( pers );
+ }
+ /* success (no errors occured) */
+ return 1;
+}
+
+static int GetDiffuseColor( T3dsLoaderPers *pers ){
+ /* todo: support all 3ds specific color formats; */
+ /* that means: rgb,tru,trug,rgbg */
+
+ /* get rgb color (range 0..255; 3 bytes) */
+ picoColor_t color;
+
+ color[0] = GetByte( pers );
+ color[1] = GetByte( pers );
+ color[2] = GetByte( pers );
+ color[3] = 255;
+
+ /* store this as the current shader's diffuse color */
+ if ( pers->shader ) {
+ PicoSetShaderDiffuseColor( pers->shader,color );
+ }
+#ifdef DEBUG_PM_3DS
+ printf( "GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2] );
+#endif
+ /* success (no errors occured) */
+ return 1;
+}
+
+static int DoNextEditorDataChunk( T3dsLoaderPers *pers, long endofs ){
+ T3dsChunk *chunk;
+
+#ifdef DEBUG_PM_3DS_EX
+ printf( "DoNextEditorDataChunk: endofs %d\n",endofs );
+#endif
+ while ( pers->cofs < endofs )
+ {
+ long nextofs = pers->cofs;
+ if ( ( chunk = GetChunk( pers ) ) == NULL ) {
+ return 0;
+ }
+ if ( !chunk->len ) {
+ return 0;
+ }
+ nextofs += chunk->len;
+
+#ifdef DEBUG_PM_3DS_EX
+ printf( "Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName( chunk->id ),chunk->len,pers->cofs );
+#endif
+ /*** meshes ***/
+ if ( chunk->id == CHUNK_OBJECT ) {
+ picoSurface_t *surface;
+ char surfaceName[ 0xff ] = { 0 };
+
+ /* read in surface name */
+ if ( !GetASCIIZ( pers,surfaceName,sizeof( surfaceName ) ) ) {
+ return 0; /* this is bad */
+ }
+//PicoGetSurfaceName
+ /* ignore NULL name surfaces */
+// if( surfaceName
+
+ /* allocate a pico surface */
+ surface = PicoNewSurface( pers->model );
+ if ( surface == NULL ) {
+ pers->surface = NULL;
+ return 0; /* this is bad too */
+ }
+ /* assign ptr to current surface */
+ pers->surface = surface;
+
+ /* 3ds models surfaces are all triangle meshes */
+ PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );
+
+ /* set surface name */
+ PicoSetSurfaceName( pers->surface,surfaceName );
+
+ /* continue mess with object's sub chunks */
+ DoNextEditorDataChunk( pers,nextofs );
+ continue;
+ }
+ if ( chunk->id == CHUNK_OBJECT_MESH ) {
+ /* continue mess with mesh's sub chunks */
+ if ( !DoNextEditorDataChunk( pers,nextofs ) ) {
+ return 0;
+ }
+ continue;
+ }
+ if ( chunk->id == CHUNK_OBJECT_VERTICES ) {
+ if ( !GetMeshVertices( pers ) ) {
+ return 0;
+ }
+ continue;
+ }
+ if ( chunk->id == CHUNK_OBJECT_FACES ) {
+ if ( !GetMeshFaces( pers ) ) {
+ return 0;
+ }
+ continue;
+ }
+ if ( chunk->id == CHUNK_OBJECT_UV ) {
+ if ( !GetMeshTexCoords( pers ) ) {
+ return 0;
+ }
+ continue;
+ }
+ if ( chunk->id == CHUNK_OBJECT_MATERIAL ) {
+ if ( !GetMeshShader( pers ) ) {
+ return 0;
+ }
+ continue;
+ }
+ /*** materials ***/
+ if ( chunk->id == CHUNK_MATERIAL ) {
+ /* new shader specific things should be */
+ /* initialized right here */
+ picoShader_t *shader;
+
+ /* allocate a pico shader */
+ shader = PicoNewShader( pers->model ); /* ydnar */
+ if ( shader == NULL ) {
+ pers->shader = NULL;
+ return 0; /* this is bad too */
+ }
+
+ /* assign ptr to current shader */
+ pers->shader = shader;
+
+ /* continue and process the material's sub chunks */
+ DoNextEditorDataChunk( pers,nextofs );
+ continue;
+ }
+ if ( chunk->id == CHUNK_MATNAME ) {
+ /* new material's names should be stored here. note that */
+ /* GetMeshMaterial returns the name of the material that */
+ /* is used by the mesh. new material names are set HERE. */
+ /* but for now we skip the new material's name ... */
+ if ( pers->shader ) {
+ char *name = (char*) ( pers->bufptr + pers->cofs );
+ char *cleanedName = _pico_clone_alloc( name );
+ _pico_first_token( cleanedName );
+ PicoSetShaderName( pers->shader, cleanedName );
+#ifdef DEBUG_PM_3DS
+ printf( "NewShader: '%s'\n", cleanedName );
+#endif
+ _pico_free( cleanedName );
+ }
+ }
+ if ( chunk->id == CHUNK_MATDIFFUSE ) {
+ /* todo: color for last inserted new material should be */
+ /* stored somewhere by GetDiffuseColor */
+ if ( !GetDiffuseColor( pers ) ) {
+ return 0;
+ }
+
+ /* rest of chunk is skipped here */
+ }
+ if ( chunk->id == CHUNK_MATMAP ) {
+ /* continue and process the material map sub chunks */
+ DoNextEditorDataChunk( pers,nextofs );
+ continue;
+ }
+ if ( chunk->id == CHUNK_MATMAPFILE ) {
+ /* map file name for last inserted new material should */
+ /* be stored here. but for now we skip this too ... */
+ if ( pers->shader ) {
+ char *name = (char *)( pers->bufptr + pers->cofs );
+ PicoSetShaderMapName( pers->shader,name );
+#ifdef DEBUG_PM_3DS
+ printf( "NewShaderMapfile: '%s'\n",name );
+#endif
+ }
+ }
+ /*** keyframes ***/
+ if ( chunk->id == CHUNK_KEYFRAME_DATA ) {
+ /* well umm, this is a bit too much since we don't really */
+ /* need model animation sequences right now. we skip this */
+#ifdef DEBUG_PM_3DS
+ printf( "KeyframeData: len %d\n",chunk->len );
+#endif
+ }
+ /* skip unknown chunk */
+ pers->cofs = nextofs;
+ if ( pers->cofs >= pers->maxofs ) {
+ break;
+ }
+ }
+ return 1;
+}
+
+static int DoNextChunk( T3dsLoaderPers *pers, int endofs ){
+ T3dsChunk *chunk;
+
+#ifdef DEBUG_PM_3DS
+ printf( "DoNextChunk: endofs %d\n",endofs );
+#endif
+ while ( pers->cofs < endofs )
+ {
+ long nextofs = pers->cofs;
+ if ( ( chunk = GetChunk( pers ) ) == NULL ) {
+ return 0;
+ }
+ if ( !chunk->len ) {
+ return 0;
+ }
+ nextofs += chunk->len;
+
+#ifdef DEBUG_PM_3DS_EX
+ printf( "Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName( chunk->id ),chunk->len,pers->cofs );
+#endif
+ /*** version ***/
+ if ( chunk->id == CHUNK_VERSION ) {
+ /* at this point i get the 3ds file version. since there */
+ /* might be new additions to the 3ds file format in 4.0 */
+ /* it might be a good idea to store the version somewhere */
+ /* for later handling or message displaying */
+
+ /* get the version */
+ int version;
+ version = GetWord( pers );
+ GetWord( pers );
+#ifdef DEBUG_PM_3DS
+ printf( "FileVersion: %d\n",version );
+#endif
+
+ /* throw out a warning for version 4 models */
+ if ( version == 4 ) {
+ _pico_printf( PICO_WARNING,
+ "3DS version is 4. Model might load incorrectly." );
+ }
+ /* store the 3ds file version in pico special field 0 */
+ /* PicoSetSurfaceSpecial(pers->surface,0,version); */ /* ydnar: this was causing a crash accessing uninitialized surface */
+
+ /* rest of chunk is skipped here */
+ }
+ /*** editor data ***/
+ if ( chunk->id == CHUNK_EDITOR_DATA ) {
+ if ( !DoNextEditorDataChunk( pers,nextofs ) ) {
+ return 0;
+ }
+ continue;
+ }
+ /* skip unknown chunk */
+ pers->cofs = nextofs;
+ if ( pers->cofs >= pers->maxofs ) {
+ break;
+ }
+ }
+ return 1;
+}
+
+/* _3ds_load:
+ * loads an autodesk 3ds model file.
+ */
+static picoModel_t *_3ds_load( PM_PARAMS_LOAD ){
+ T3dsLoaderPers pers;
+ picoModel_t *model;
+ char basename[128];
+
+ /* create a new pico model */
+ model = PicoNewModel();
+ if ( model == NULL ) {
+ /* user must have some serious ram problems ;) */
+ return NULL;
+ }
+ /* get model's base name (eg. jeep from c:\models\jeep.3ds) */
+ memset( basename,0,sizeof( basename ) );
+ strncpy( basename,_pico_nopath( fileName ),sizeof( basename ) );
+ _pico_setfext( basename,"" );
+
+ /* initialize persistant vars (formerly static) */
+ pers.model = model;
+ pers.bufptr = (picoByte_t *)_pico_alloc( bufSize );
+ memcpy( pers.bufptr, buffer, bufSize );
+ pers.basename = (char *)basename;
+ pers.maxofs = bufSize;
+ pers.cofs = 0L;
+
+ /* do model setup */
+ PicoSetModelFrameNum( model,frameNum );
+ PicoSetModelName( model,fileName );
+ PicoSetModelFileName( model,fileName );
+
+ /* skip first chunk in file (magic) */
+ GetChunk( &pers );
+
+ /* process chunks */
+ if ( !DoNextChunk( &pers,pers.maxofs ) ) {
+ /* well, bleh i guess */
+ PicoFreeModel( model );
+ return NULL;
+ }
+ /* return allocated pico model */
+ return model;
+}
+
+/* pico file format module definition */
+const picoModule_t picoModule3DS =
+{
+ "0.86-b", /* module version string */
+ "Autodesk 3Dstudio", /* module display name */
+ "seaw0lf", /* author's name */
+ "2002 seaw0lf", /* module copyright */
+ {
+ "3ds",NULL,NULL,NULL /* default extensions to use */
+ },
+ _3ds_canload, /* validation routine */
+ _3ds_load, /* load routine */
+ NULL, /* save validation routine */
+ NULL /* save routine */
+};