]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_3ds.c
eol style
[xonotic/netradiant.git] / libs / picomodel / pm_3ds.c
1 /* -----------------------------------------------------------------------------
2
3 PicoModel Library
4
5 Copyright (c) 2002, Randy Reddig & seaw0lf
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
13
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
17
18 Neither the names of the copyright holders nor the names of its contributors may
19 be used to endorse or promote products derived from this software without
20 specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 ----------------------------------------------------------------------------- */
34
35
36
37 /* marker */
38 #define PM_3DS_C
39
40 /* dependencies */
41 #include "picointernal.h"
42
43 /* ydnar */
44 static picoColor_t white = { 255,255,255,255 };
45
46 /* remarks:
47  * - 3ds file version is stored in pico special field 0 on load (ydnar: removed)
48  * todo:
49  * - sometimes there is one unnamed surface 0 having 0 verts as
50  *   well as 0 faces. this error occurs since pm 0.6 (ydnar?)
51  */
52 /* uncomment when debugging this module */
53 /* #define DEBUG_PM_3DS
54 #define DEBUG_PM_3DS_EX */
55
56 /* structure holding persistent 3ds loader specific data used */
57 /* to store formerly static vars to keep the module reentrant */
58 /* safe. put everything that needs to be static in here. */
59 typedef struct S3dsLoaderPers
60 {
61         picoModel_t        *model;                      /* ptr to output model */
62         picoSurface_t  *surface;                /* ptr to current surface */
63         picoShader_t   *shader;                 /* ptr to current shader */
64         picoByte_t         *bufptr;                     /* ptr to raw data */
65         char               *basename;           /* ptr to model base name (eg. jeep) */
66         int                             cofs;
67         int                             maxofs;
68 }
69 T3dsLoaderPers;
70
71 /* 3ds chunk types that we use */
72 enum {
73     /* primary chunk */
74     CHUNK_MAIN                          = 0x4D4D,
75
76         /* main chunks */
77         CHUNK_VERSION           = 0x0002,
78         CHUNK_EDITOR_CONFIG             = 0x3D3E,
79         CHUNK_EDITOR_DATA       = 0x3D3D,
80         CHUNK_KEYFRAME_DATA             = 0xB000,
81
82         /* editor data sub chunks */
83         CHUNK_MATERIAL                  = 0xAFFF,
84         CHUNK_OBJECT            = 0x4000,
85
86         /* material sub chunks */
87         CHUNK_MATNAME                   = 0xA000,
88         CHUNK_MATDIFFUSE                = 0xA020,
89         CHUNK_MATMAP                    = 0xA200,
90         CHUNK_MATMAPFILE                = 0xA300,
91
92         /* lets us know we're reading a new object */
93         CHUNK_OBJECT_MESH               = 0x4100,
94
95         /* object mesh sub chunks */
96         CHUNK_OBJECT_VERTICES   = 0x4110,
97         CHUNK_OBJECT_FACES              = 0x4120,
98         CHUNK_OBJECT_MATERIAL   = 0x4130,
99         CHUNK_OBJECT_UV                 = 0x4140,
100 };
101 #ifdef DEBUG_PM_3DS
102 static struct
103 {
104         int     id;
105         char   *name;
106 }
107 debugChunkNames[] =
108 {
109         { CHUNK_MAIN                    , "CHUNK_MAIN"                          },
110         { CHUNK_VERSION                 , "CHUNK_VERSION"                       },
111         { CHUNK_EDITOR_CONFIG   , "CHUNK_EDITOR_CONFIG"         },
112         { CHUNK_EDITOR_DATA             , "CHUNK_EDITOR_DATA"           },
113         { CHUNK_KEYFRAME_DATA   , "CHUNK_KEYFRAME_DATA"         },
114         { CHUNK_MATERIAL                , "CHUNK_MATERIAL"                      },
115         { CHUNK_OBJECT                  , "CHUNK_OBJECT"                        },
116         { CHUNK_MATNAME                 , "CHUNK_MATNAME"                       },
117         { CHUNK_MATDIFFUSE              , "CHUNK_MATDIFFUSE"            },
118         { CHUNK_MATMAP                  , "CHUNK_MATMAP"                        },
119         { CHUNK_MATMAPFILE              , "CHUNK_MATMAPFILE"            },
120         { CHUNK_OBJECT_MESH             , "CHUNK_OBJECT_MESH"           },
121         { CHUNK_OBJECT_VERTICES , "CHUNK_OBJECT_VERTICES"       },
122         { CHUNK_OBJECT_FACES    , "CHUNK_OBJECT_FACES"          },
123         { CHUNK_OBJECT_MATERIAL , "CHUNK_OBJECT_MATERIAL"       },
124         { CHUNK_OBJECT_UV               , "CHUNK_OBJECT_UV"                     },
125         { 0                                             ,  NULL                                         }
126 };
127 static char *DebugGetChunkName (int id)
128 {
129         int i,max;      /* imax? ;) */
130         max = sizeof(debugChunkNames) / sizeof(debugChunkNames[0]);
131
132         for (i=0; i<max; i++)
133         {
134                 if (debugChunkNames[i].id == id)
135                 {
136                         /* gaynux update -sea */
137                         return _pico_strlwr( debugChunkNames[i].name );
138                 }
139         }
140         return "chunk_unknown";
141 }
142 #endif /*DEBUG_PM_3DS*/
143
144 /* this funky loader needs byte alignment */
145 #pragma pack(push, 1)
146
147 typedef struct S3dsIndices
148 {
149         unsigned short  a,b,c;
150         unsigned short  visible;
151 }
152 T3dsIndices;
153
154 typedef struct S3dsChunk
155 {
156         unsigned short  id;
157         unsigned int    len;
158 }
159 T3dsChunk;
160
161 /* restore previous data alignment */
162 #pragma pack(pop)
163
164 /* _3ds_canload:
165  *  validates an autodesk 3ds model file.
166  */
167 static int _3ds_canload( PM_PARAMS_CANLOAD )
168 {
169         T3dsChunk *chunk;
170
171         /* to keep the compiler happy */
172         *fileName = *fileName;
173
174         /* sanity check */
175         if (bufSize < sizeof(T3dsChunk))
176                 return PICO_PMV_ERROR_SIZE;
177
178         /* get pointer to 3ds header chunk */
179         chunk = (T3dsChunk *)buffer;
180
181         /* check data length */
182         if (bufSize < _pico_little_long(chunk->len))
183                 return PICO_PMV_ERROR_SIZE;
184
185         /* check 3ds magic */
186         if (_pico_little_short(chunk->id) != CHUNK_MAIN)
187                 return PICO_PMV_ERROR_IDENT;
188
189         /* file seems to be a valid 3ds */
190         return PICO_PMV_OK;
191 }
192
193 static T3dsChunk *GetChunk (T3dsLoaderPers *pers)
194 {
195         T3dsChunk *chunk;
196
197         /* sanity check */
198         if (pers->cofs > pers->maxofs) return 0;
199
200 #ifdef DEBUG_PM_3DS
201 /*      printf("GetChunk: pers->cofs %x\n",pers->cofs); */
202 #endif
203         /* fill in pointer to chunk */
204         chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];
205         if (!chunk) return NULL;
206
207         chunk->id  = _pico_little_short(chunk->id );
208         chunk->len = _pico_little_long (chunk->len);
209
210         /* advance in buffer */
211         pers->cofs += sizeof(T3dsChunk);
212
213         /* this means yay */
214         return chunk;
215 }
216
217 static int GetASCIIZ (T3dsLoaderPers *pers, char *dest, int max)
218 {
219         int pos = 0;
220         int ch;
221
222         for (;;)
223         {
224                 ch = pers->bufptr[ pers->cofs++ ];
225                 if (ch == '\0') break;
226                 if (pers->cofs >= pers->maxofs)
227                 {
228                         dest[ pos ] = '\0';
229                         return 0;
230                 }
231                 dest[ pos++ ] = ch;
232                 if (pos >= max) break;
233         }
234         dest[ pos ] = '\0';
235         return 1;
236 }
237
238 static picoByte_t GetByte (T3dsLoaderPers *pers)
239 {
240         picoByte_t *value;
241
242         /* sanity check */
243         if (pers->cofs > pers->maxofs) return 0;
244
245         /* get and return value */
246         value = (picoByte_t *)(pers->bufptr + pers->cofs);
247         pers->cofs += 1;
248         return *value;
249 }
250
251 static int GetWord (T3dsLoaderPers *pers)
252 {
253         unsigned short *value;
254
255         /* sanity check */
256         if (pers->cofs > pers->maxofs) return 0;
257
258         /* get and return value */
259         value = (unsigned short *)(pers->bufptr + pers->cofs);
260         pers->cofs += 2;
261         return _pico_little_short(*value);
262 }
263
264 static float GetFloat (T3dsLoaderPers *pers)
265 {
266         float *value;
267
268         /* sanity check */
269         if (pers->cofs > pers->maxofs) return 0;
270
271         /* get and return value */
272         value = (float *)(pers->bufptr + pers->cofs);
273         pers->cofs += 4;
274         return _pico_little_float(*value);
275 }
276
277 static int GetMeshVertices (T3dsLoaderPers *pers)
278 {
279         int numVerts;
280         int i;
281
282         /* get number of verts for this surface */
283         numVerts = GetWord(pers);
284
285 #ifdef DEBUG_PM_3DS
286         printf("GetMeshVertices: numverts %d\n",numVerts);
287 #endif
288         /* read in vertices for current surface */
289         for (i=0; i<numVerts; i++)
290         {
291                 picoVec3_t v;
292                 v[0] = GetFloat( pers );
293                 v[1] = GetFloat( pers );        /* ydnar: unflipped */
294                 v[2] = GetFloat( pers );        /* ydnar: unflipped and negated */
295                 
296                 /* add current vertex */
297                 PicoSetSurfaceXYZ( pers->surface,i,v );
298                 PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */
299
300 #ifdef DEBUG_PM_3DS_EX
301                 printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
302 #endif
303         }
304         /* success (no errors occured) */
305         return 1;
306 }
307
308 static int GetMeshFaces (T3dsLoaderPers *pers)
309 {
310         int numFaces;
311         int i;
312
313         /* get number of faces for this surface */
314         numFaces = GetWord(pers);
315
316 #ifdef DEBUG_PM_3DS
317         printf("GetMeshFaces: numfaces %d\n",numFaces);
318 #endif
319         /* read in vertex indices for current surface */
320         for (i=0; i<numFaces; i++)
321         {
322                 /* remember, we only need 3 of 4 values read in for each */
323                 /* face. the 4th value is a vis flag for 3dsmax which is */
324                 /* being ignored by us here */
325                 T3dsIndices face;
326                 face.a           = GetWord(pers);
327                 face.c           = GetWord(pers);       /* ydnar: flipped order */
328                 face.b           = GetWord(pers);       /* ydnar: flipped order */
329                 face.visible = GetWord(pers);
330
331                 /* copy indexes */
332                 PicoSetSurfaceIndex( pers->surface, (i * 3 + 0), (picoIndex_t)face.a );
333                 PicoSetSurfaceIndex( pers->surface, (i * 3 + 1), (picoIndex_t)face.b );
334                 PicoSetSurfaceIndex( pers->surface, (i * 3 + 2), (picoIndex_t)face.c );
335
336 #ifdef DEBUG_PM_3DS_EX
337                 printf("Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible);
338 #endif
339         }
340         /* success (no errors occured) */
341         return 1;
342 }
343
344 static int GetMeshTexCoords (T3dsLoaderPers *pers)
345 {
346         int numTexCoords;
347         int i;
348
349         /* get number of uv coords for this surface */
350         numTexCoords = GetWord(pers);
351
352 #ifdef DEBUG_PM_3DS
353         printf("GetMeshTexCoords: numcoords %d\n",numTexCoords);
354 #endif
355         /* read in uv coords for current surface */
356         for (i=0; i<numTexCoords; i++)
357         {
358                 picoVec2_t uv;
359                 uv[0] =  GetFloat( pers );
360                 uv[1] = -GetFloat( pers );      /* ydnar: we use origin at bottom */
361
362                 /* to make sure we don't mess up memory */
363                 if (pers->surface == NULL)
364                         continue;
365                 
366                 /* add current uv */
367                 PicoSetSurfaceST( pers->surface,0,i,uv );
368
369 #ifdef DEBUG_PM_3DS_EX
370                 printf("u: %f v: %f\n",uv[0],uv[1]);
371 #endif
372         }
373         /* success (no errors occured) */
374         return 1;
375 }
376
377 static int GetMeshShader (T3dsLoaderPers *pers)
378 {
379         char shaderName[255] = { 0 };
380         picoShader_t  *shader;
381         int  numSharedVerts;
382         int  setShaderName = 0;
383         int  i;
384         
385         /* the shader is either the color or the texture map of the */
386         /* object. it can also hold other information like the brightness, */
387         /* shine, etc. stuff we don't really care about. we just want the */
388         /* color, or the texture map file name really */
389
390         /* get in the shader name */
391         if (!GetASCIIZ(pers,shaderName,sizeof(shaderName)))
392                 return 0;
393
394         /* now that we have the shader name we need to go through all of */
395         /* the shaders and check the name against each shader. when we */
396         /* find a shader in our shader list that matches this name we */
397         /* just read in, then we assign the shader's id of the object to */
398         /* that shader */
399
400         /* get shader id for shader name */
401         shader = PicoFindShader( pers->model, shaderName, 1 );
402
403         /* we've found a matching shader */
404         if ((shader != NULL) && pers->surface)
405         {
406                 char  mapName[1024+1];
407                 char *mapNamePtr;
408                 memset( mapName,0,sizeof(mapName) );
409
410                 /* get ptr to shader's map name */
411                 mapNamePtr = PicoGetShaderMapName( shader );
412
413                 /* we have a valid map name ptr */
414                 if (mapNamePtr != NULL)
415                 {
416                         char  temp[128];
417                         char *name;
418
419                         /* copy map name to local buffer */
420                         strcpy( mapName,mapNamePtr );
421
422                         /* extract file name */
423                         name = _pico_nopath( mapName );
424                         strncpy( temp, name, sizeof(temp) );
425
426                         /* remove file extension */
427                         /* name = _pico_setfext( name,"" ); */
428
429                         /* assign default name if no name available */
430                         if (strlen(temp) < 1)
431                                 strcpy(temp,pers->basename);
432
433                         /* build shader name */
434                         _pico_strlwr( temp ); /* gaynux update -sea */
435                         sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );
436
437                         /* set shader name */
438                         /* PicoSetShaderName( shader,mapName ); */      /* ydnar: this will screw up the named shader */
439
440                         /* set surface's shader index */
441                         PicoSetSurfaceShader( pers->surface, shader );
442
443                         setShaderName = 1;
444                 }
445         }
446         /* we didn't set a shader name; throw out warning */
447         if (!setShaderName)
448         {
449                 _pico_printf( PICO_WARNING,"3DS mesh is missing shader name");
450         }
451         /* we don't process the list of shared vertices here; there is a */
452         /* short int that gives the number of faces of the mesh concerned */
453         /* by this shader, then there is the list itself of these faces. */
454         /* 0000 means the first face of the (4120) face list */
455
456         /* get number of shared verts */
457         numSharedVerts = GetWord(pers);
458
459 #ifdef DEBUG_PM_3DS
460         printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts);
461 #endif
462         /* skip list of shared verts */
463         for (i=0; i<numSharedVerts; i++)
464         {
465                 GetWord(pers);
466         }
467         /* success (no errors occured) */
468         return 1;
469 }
470
471 static int GetDiffuseColor (T3dsLoaderPers *pers)
472 {
473         /* todo: support all 3ds specific color formats; */
474         /* that means: rgb,tru,trug,rgbg */
475
476         /* get rgb color (range 0..255; 3 bytes) */
477         picoColor_t color;
478
479         color[0] = GetByte(pers);
480         color[1] = GetByte(pers);
481         color[2] = GetByte(pers);
482         color[3] = 255;
483
484         /* store this as the current shader's diffuse color */
485         if( pers->shader )
486         {
487                 PicoSetShaderDiffuseColor( pers->shader,color );
488         }
489 #ifdef DEBUG_PM_3DS
490         printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]);
491 #endif
492         /* success (no errors occured) */
493         return 1;
494 }
495
496 static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs)
497 {
498         T3dsChunk *chunk;
499
500 #ifdef DEBUG_PM_3DS_EX
501         printf("DoNextEditorDataChunk: endofs %d\n",endofs);
502 #endif
503         while (pers->cofs < endofs)
504         {
505                 long nextofs = pers->cofs;
506                 if ((chunk = GetChunk(pers)) == NULL) return 0;
507                 if (!chunk->len) return 0;
508                 nextofs += chunk->len;
509
510 #ifdef DEBUG_PM_3DS_EX
511                 printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);
512 #endif
513                 /*** meshes ***/
514                 if (chunk->id == CHUNK_OBJECT)
515                 {
516                         picoSurface_t *surface;
517                         char surfaceName[ 0xff ] = { 0 };
518
519                         /* read in surface name */
520                         if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) )
521                                 return 0; /* this is bad */
522
523 //PicoGetSurfaceName
524                         /* ignore NULL name surfaces */
525 //                      if( surfaceName
526
527                         /* allocate a pico surface */
528                         surface = PicoNewSurface( pers->model );
529                         if( surface == NULL )
530                         {
531                                 pers->surface = NULL;
532                                 return 0; /* this is bad too */
533                         }
534                         /* assign ptr to current surface */
535                         pers->surface = surface;
536
537                         /* 3ds models surfaces are all triangle meshes */
538                         PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );
539
540                         /* set surface name */
541                         PicoSetSurfaceName( pers->surface,surfaceName );
542
543                         /* continue mess with object's sub chunks */
544                         DoNextEditorDataChunk(pers,nextofs);
545                         continue;
546                 }
547                 if (chunk->id == CHUNK_OBJECT_MESH)
548                 {
549                         /* continue mess with mesh's sub chunks */
550                         if (!DoNextEditorDataChunk(pers,nextofs)) return 0;
551                         continue;
552                 }
553                 if (chunk->id == CHUNK_OBJECT_VERTICES)
554                 {
555                         if (!GetMeshVertices(pers)) return 0;
556                         continue;
557                 }
558                 if (chunk->id == CHUNK_OBJECT_FACES)
559                 {
560                         if (!GetMeshFaces(pers)) return 0;
561                         continue;
562                 }
563                 if (chunk->id == CHUNK_OBJECT_UV)
564                 {
565                         if (!GetMeshTexCoords(pers)) return 0;
566                         continue;
567                 }
568                 if (chunk->id == CHUNK_OBJECT_MATERIAL)
569                 {
570                         if (!GetMeshShader(pers)) return 0;
571                         continue;
572                 }
573                 /*** materials ***/
574                 if (chunk->id == CHUNK_MATERIAL)
575                 {
576                         /* new shader specific things should be */
577                         /* initialized right here */
578                         picoShader_t *shader;
579
580                         /* allocate a pico shader */
581                         shader = PicoNewShader( pers->model );  /* ydnar */
582                         if( shader == NULL )
583                         {
584                                 pers->shader = NULL;
585                                 return 0; /* this is bad too */
586                         }
587                         
588                         /* assign ptr to current shader */
589                         pers->shader = shader;
590
591                         /* continue and process the material's sub chunks */
592                         DoNextEditorDataChunk(pers,nextofs);
593                         continue;
594                 }
595                 if (chunk->id == CHUNK_MATNAME)
596                 {
597                         /* new material's names should be stored here. note that */
598                         /* GetMeshMaterial returns the name of the material that */
599                         /* is used by the mesh. new material names are set HERE. */
600                         /* but for now we skip the new material's name ... */
601                         if (pers->shader)
602                         {
603                                 char *name = (char *)(pers->bufptr + pers->cofs);
604                                 PicoSetShaderName( pers->shader,name );
605 #ifdef DEBUG_PM_3DS
606                                 printf("NewShader: '%s'\n",name);
607 #endif
608                         }
609                 }
610                 if (chunk->id == CHUNK_MATDIFFUSE)
611                 {
612                         /* todo: color for last inserted new material should be */
613                         /* stored somewhere by GetDiffuseColor */
614                         if (!GetDiffuseColor(pers)) return 0;
615
616                         /* rest of chunk is skipped here */
617                 }
618                 if (chunk->id == CHUNK_MATMAP)
619                 {
620                         /* continue and process the material map sub chunks */
621                         DoNextEditorDataChunk(pers,nextofs);
622                         continue;
623                 }
624                 if (chunk->id == CHUNK_MATMAPFILE)
625                 {
626                         /* map file name for last inserted new material should */
627                         /* be stored here. but for now we skip this too ... */
628                         if( pers->shader )
629                         {
630                                 char *name = (char *)(pers->bufptr + pers->cofs);
631                                 PicoSetShaderMapName( pers->shader,name );
632 #ifdef DEBUG_PM_3DS
633                                 printf("NewShaderMapfile: '%s'\n",name);
634 #endif
635                         }
636                 }
637                 /*** keyframes ***/
638                 if (chunk->id == CHUNK_KEYFRAME_DATA)
639                 {
640                         /* well umm, this is a bit too much since we don't really */
641                         /* need model animation sequences right now. we skip this */
642 #ifdef DEBUG_PM_3DS
643                         printf("KeyframeData: len %d\n",chunk->len);
644 #endif
645                 }
646                 /* skip unknown chunk */
647                 pers->cofs = nextofs;
648                 if (pers->cofs >= pers->maxofs) break;
649         }
650         return 1;
651 }
652
653 static int DoNextChunk (T3dsLoaderPers *pers, int endofs)
654 {
655         T3dsChunk *chunk;
656
657 #ifdef DEBUG_PM_3DS
658         printf("DoNextChunk: endofs %d\n",endofs);
659 #endif
660         while (pers->cofs < endofs)
661         {
662                 long nextofs = pers->cofs;
663                 if ((chunk = GetChunk(pers)) == NULL) return 0;
664                 if (!chunk->len) return 0;
665                 nextofs += chunk->len;
666
667 #ifdef DEBUG_PM_3DS_EX
668                 printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);
669 #endif
670                 /*** version ***/
671                 if (chunk->id == CHUNK_VERSION)
672                 {
673                         /* at this point i get the 3ds file version. since there */
674                         /* might be new additions to the 3ds file format in 4.0 */
675                         /* it might be a good idea to store the version somewhere */
676                         /* for later handling or message displaying */
677
678                         /* get the version */
679                         int version;
680                         version = GetWord(pers);
681                         GetWord(pers);
682 #ifdef DEBUG_PM_3DS
683                         printf("FileVersion: %d\n",version);
684 #endif
685
686                         /* throw out a warning for version 4 models */
687                         if (version == 4)
688                         {
689                                 _pico_printf( PICO_WARNING,
690                                         "3DS version is 4. Model might load incorrectly.");
691                         }
692                         /* store the 3ds file version in pico special field 0 */
693                         /* PicoSetSurfaceSpecial(pers->surface,0,version); */           /* ydnar: this was causing a crash accessing uninitialized surface */
694                         
695                         /* rest of chunk is skipped here */
696                 }
697                 /*** editor data ***/
698                 if (chunk->id == CHUNK_EDITOR_DATA)
699                 {
700                         if (!DoNextEditorDataChunk(pers,nextofs)) return 0;
701                         continue;
702                 }
703                 /* skip unknown chunk */
704                 pers->cofs = nextofs;
705                 if (pers->cofs >= pers->maxofs) break;
706         }
707         return 1;
708 }
709
710 /* _3ds_load:
711  *  loads an autodesk 3ds model file.
712 */
713 static picoModel_t *_3ds_load( PM_PARAMS_LOAD )
714 {
715         T3dsLoaderPers  pers;
716         picoModel_t        *model;
717         char                    basename[128];
718
719         /* create a new pico model */
720         model = PicoNewModel();
721         if (model == NULL)
722         {
723                 /* user must have some serious ram problems ;) */
724                 return NULL;
725         }
726         /* get model's base name (eg. jeep from c:\models\jeep.3ds) */
727         memset( basename,0,sizeof(basename) );
728         strncpy( basename,_pico_nopath(fileName),sizeof(basename) );
729         _pico_setfext( basename,"" );
730
731         /* initialize persistant vars (formerly static) */
732         pers.model    =  model;
733         pers.bufptr   = (picoByte_t *)buffer;
734         pers.basename = (char *)basename;
735         pers.maxofs   =  bufSize;
736         pers.cofs     =  0L;
737
738         /* do model setup */
739         PicoSetModelFrameNum( model,frameNum );
740         PicoSetModelName( model,fileName );
741         PicoSetModelFileName( model,fileName );
742
743         /* skip first chunk in file (magic) */
744         GetChunk(&pers);
745
746         /* process chunks */
747         if (!DoNextChunk(&pers,pers.maxofs))
748         {
749                 /* well, bleh i guess */
750                 PicoFreeModel(model);
751                 return NULL;
752         }
753         /* return allocated pico model */
754         return model;
755 }
756
757 /* pico file format module definition */
758 const picoModule_t picoModule3DS =
759 {
760         "0.86-b",                                       /* module version string */
761         "Autodesk 3Dstudio",            /* module display name */
762         "seaw0lf",                                      /* author's name */
763         "2002 seaw0lf",                         /* module copyright */
764         {
765                 "3ds",NULL,NULL,NULL    /* default extensions to use */
766         },
767         _3ds_canload,                           /* validation routine */
768         _3ds_load,                                      /* load routine */
769          NULL,                                          /* save validation routine */
770          NULL                                           /* save routine */
771 };