]> icculus.org git repositories - divverent/netradiant.git/blob - libs/picomodel/pm_3ds.c
initial
[divverent/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         /* ydnar: trim to first whitespace */
395         _pico_first_token( shaderName );
396         
397         /* now that we have the shader name we need to go through all of */
398         /* the shaders and check the name against each shader. when we */
399         /* find a shader in our shader list that matches this name we */
400         /* just read in, then we assign the shader's id of the object to */
401         /* that shader */
402
403         /* get shader id for shader name */
404         shader = PicoFindShader( pers->model, shaderName, 1 );
405
406         /* we've found a matching shader */
407         if ((shader != NULL) && pers->surface)
408         {
409                 char  mapName[1024+1];
410                 char *mapNamePtr;
411                 memset( mapName,0,sizeof(mapName) );
412
413                 /* get ptr to shader's map name */
414                 mapNamePtr = PicoGetShaderMapName( shader );
415
416                 /* we have a valid map name ptr */
417                 if (mapNamePtr != NULL)
418                 {
419                         char  temp[128];
420                         const char *name;
421
422                         /* copy map name to local buffer */
423                         strcpy( mapName,mapNamePtr );
424
425                         /* extract file name */
426                         name = _pico_nopath( mapName );
427                         strncpy( temp, name, sizeof(temp) );
428
429                         /* remove file extension */
430                         /* name = _pico_setfext( name,"" ); */
431
432                         /* assign default name if no name available */
433                         if (strlen(temp) < 1)
434                                 strcpy(temp,pers->basename);
435
436                         /* build shader name */
437                         _pico_strlwr( temp ); /* gaynux update -sea */
438                         sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );
439
440                         /* set shader name */
441                         /* PicoSetShaderName( shader,mapName ); */      /* ydnar: this will screw up the named shader */
442
443                         /* set surface's shader index */
444                         PicoSetSurfaceShader( pers->surface, shader );
445
446                         setShaderName = 1;
447                 }
448         }
449         /* we didn't set a shader name; throw out warning */
450         if (!setShaderName)
451         {
452                 _pico_printf( PICO_WARNING,"3DS mesh is missing shader name");
453         }
454         /* we don't process the list of shared vertices here; there is a */
455         /* short int that gives the number of faces of the mesh concerned */
456         /* by this shader, then there is the list itself of these faces. */
457         /* 0000 means the first face of the (4120) face list */
458
459         /* get number of shared verts */
460         numSharedVerts = GetWord(pers);
461
462 #ifdef DEBUG_PM_3DS
463         printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts);
464 #endif
465         /* skip list of shared verts */
466         for (i=0; i<numSharedVerts; i++)
467         {
468                 GetWord(pers);
469         }
470         /* success (no errors occured) */
471         return 1;
472 }
473
474 static int GetDiffuseColor (T3dsLoaderPers *pers)
475 {
476         /* todo: support all 3ds specific color formats; */
477         /* that means: rgb,tru,trug,rgbg */
478
479         /* get rgb color (range 0..255; 3 bytes) */
480         picoColor_t color;
481
482         color[0] = GetByte(pers);
483         color[1] = GetByte(pers);
484         color[2] = GetByte(pers);
485         color[3] = 255;
486
487         /* store this as the current shader's diffuse color */
488         if( pers->shader )
489         {
490                 PicoSetShaderDiffuseColor( pers->shader,color );
491         }
492 #ifdef DEBUG_PM_3DS
493         printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]);
494 #endif
495         /* success (no errors occured) */
496         return 1;
497 }
498
499 static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs)
500 {
501         T3dsChunk *chunk;
502
503 #ifdef DEBUG_PM_3DS_EX
504         printf("DoNextEditorDataChunk: endofs %d\n",endofs);
505 #endif
506         while (pers->cofs < endofs)
507         {
508                 long nextofs = pers->cofs;
509                 if ((chunk = GetChunk(pers)) == NULL) return 0;
510                 if (!chunk->len) return 0;
511                 nextofs += chunk->len;
512
513 #ifdef DEBUG_PM_3DS_EX
514                 printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);
515 #endif
516                 /*** meshes ***/
517                 if (chunk->id == CHUNK_OBJECT)
518                 {
519                         picoSurface_t *surface;
520                         char surfaceName[ 0xff ] = { 0 };
521
522                         /* read in surface name */
523                         if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) )
524                                 return 0; /* this is bad */
525
526 //PicoGetSurfaceName
527                         /* ignore NULL name surfaces */
528 //                      if( surfaceName
529
530                         /* allocate a pico surface */
531                         surface = PicoNewSurface( pers->model );
532                         if( surface == NULL )
533                         {
534                                 pers->surface = NULL;
535                                 return 0; /* this is bad too */
536                         }
537                         /* assign ptr to current surface */
538                         pers->surface = surface;
539
540                         /* 3ds models surfaces are all triangle meshes */
541                         PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );
542
543                         /* set surface name */
544                         PicoSetSurfaceName( pers->surface,surfaceName );
545
546                         /* continue mess with object's sub chunks */
547                         DoNextEditorDataChunk(pers,nextofs);
548                         continue;
549                 }
550                 if (chunk->id == CHUNK_OBJECT_MESH)
551                 {
552                         /* continue mess with mesh's sub chunks */
553                         if (!DoNextEditorDataChunk(pers,nextofs)) return 0;
554                         continue;
555                 }
556                 if (chunk->id == CHUNK_OBJECT_VERTICES)
557                 {
558                         if (!GetMeshVertices(pers)) return 0;
559                         continue;
560                 }
561                 if (chunk->id == CHUNK_OBJECT_FACES)
562                 {
563                         if (!GetMeshFaces(pers)) return 0;
564                         continue;
565                 }
566                 if (chunk->id == CHUNK_OBJECT_UV)
567                 {
568                         if (!GetMeshTexCoords(pers)) return 0;
569                         continue;
570                 }
571                 if (chunk->id == CHUNK_OBJECT_MATERIAL)
572                 {
573                         if (!GetMeshShader(pers)) return 0;
574                         continue;
575                 }
576                 /*** materials ***/
577                 if (chunk->id == CHUNK_MATERIAL)
578                 {
579                         /* new shader specific things should be */
580                         /* initialized right here */
581                         picoShader_t *shader;
582
583                         /* allocate a pico shader */
584                         shader = PicoNewShader( pers->model );  /* ydnar */
585                         if( shader == NULL )
586                         {
587                                 pers->shader = NULL;
588                                 return 0; /* this is bad too */
589                         }
590                         
591                         /* assign ptr to current shader */
592                         pers->shader = shader;
593
594                         /* continue and process the material's sub chunks */
595                         DoNextEditorDataChunk(pers,nextofs);
596                         continue;
597                 }
598                 if (chunk->id == CHUNK_MATNAME)
599                 {
600                         /* new material's names should be stored here. note that */
601                         /* GetMeshMaterial returns the name of the material that */
602                         /* is used by the mesh. new material names are set HERE. */
603                         /* but for now we skip the new material's name ... */
604                         if (pers->shader)
605                         {
606                                 char *name = (char*) (pers->bufptr + pers->cofs);
607                                 char *cleanedName = _pico_clone_alloc( name );
608                                 _pico_first_token( cleanedName );
609                                 PicoSetShaderName( pers->shader, cleanedName );
610 #ifdef DEBUG_PM_3DS
611                                 printf( "NewShader: '%s'\n", cleanedName );
612 #endif
613                                 _pico_free( cleanedName );
614                         }
615                 }
616                 if (chunk->id == CHUNK_MATDIFFUSE)
617                 {
618                         /* todo: color for last inserted new material should be */
619                         /* stored somewhere by GetDiffuseColor */
620                         if (!GetDiffuseColor(pers)) return 0;
621
622                         /* rest of chunk is skipped here */
623                 }
624                 if (chunk->id == CHUNK_MATMAP)
625                 {
626                         /* continue and process the material map sub chunks */
627                         DoNextEditorDataChunk(pers,nextofs);
628                         continue;
629                 }
630                 if (chunk->id == CHUNK_MATMAPFILE)
631                 {
632                         /* map file name for last inserted new material should */
633                         /* be stored here. but for now we skip this too ... */
634                         if( pers->shader )
635                         {
636                                 char *name = (char *)(pers->bufptr + pers->cofs);
637                                 PicoSetShaderMapName( pers->shader,name );
638 #ifdef DEBUG_PM_3DS
639                                 printf("NewShaderMapfile: '%s'\n",name);
640 #endif
641                         }
642                 }
643                 /*** keyframes ***/
644                 if (chunk->id == CHUNK_KEYFRAME_DATA)
645                 {
646                         /* well umm, this is a bit too much since we don't really */
647                         /* need model animation sequences right now. we skip this */
648 #ifdef DEBUG_PM_3DS
649                         printf("KeyframeData: len %d\n",chunk->len);
650 #endif
651                 }
652                 /* skip unknown chunk */
653                 pers->cofs = nextofs;
654                 if (pers->cofs >= pers->maxofs) break;
655         }
656         return 1;
657 }
658
659 static int DoNextChunk (T3dsLoaderPers *pers, int endofs)
660 {
661         T3dsChunk *chunk;
662
663 #ifdef DEBUG_PM_3DS
664         printf("DoNextChunk: endofs %d\n",endofs);
665 #endif
666         while (pers->cofs < endofs)
667         {
668                 long nextofs = pers->cofs;
669                 if ((chunk = GetChunk(pers)) == NULL) return 0;
670                 if (!chunk->len) return 0;
671                 nextofs += chunk->len;
672
673 #ifdef DEBUG_PM_3DS_EX
674                 printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);
675 #endif
676                 /*** version ***/
677                 if (chunk->id == CHUNK_VERSION)
678                 {
679                         /* at this point i get the 3ds file version. since there */
680                         /* might be new additions to the 3ds file format in 4.0 */
681                         /* it might be a good idea to store the version somewhere */
682                         /* for later handling or message displaying */
683
684                         /* get the version */
685                         int version;
686                         version = GetWord(pers);
687                         GetWord(pers);
688 #ifdef DEBUG_PM_3DS
689                         printf("FileVersion: %d\n",version);
690 #endif
691
692                         /* throw out a warning for version 4 models */
693                         if (version == 4)
694                         {
695                                 _pico_printf( PICO_WARNING,
696                                         "3DS version is 4. Model might load incorrectly.");
697                         }
698                         /* store the 3ds file version in pico special field 0 */
699                         /* PicoSetSurfaceSpecial(pers->surface,0,version); */           /* ydnar: this was causing a crash accessing uninitialized surface */
700                         
701                         /* rest of chunk is skipped here */
702                 }
703                 /*** editor data ***/
704                 if (chunk->id == CHUNK_EDITOR_DATA)
705                 {
706                         if (!DoNextEditorDataChunk(pers,nextofs)) return 0;
707                         continue;
708                 }
709                 /* skip unknown chunk */
710                 pers->cofs = nextofs;
711                 if (pers->cofs >= pers->maxofs) break;
712         }
713         return 1;
714 }
715
716 /* _3ds_load:
717  *  loads an autodesk 3ds model file.
718 */
719 static picoModel_t *_3ds_load( PM_PARAMS_LOAD )
720 {
721         T3dsLoaderPers  pers;
722         picoModel_t        *model;
723         char                    basename[128];
724
725         /* create a new pico model */
726         model = PicoNewModel();
727         if (model == NULL)
728         {
729                 /* user must have some serious ram problems ;) */
730                 return NULL;
731         }
732         /* get model's base name (eg. jeep from c:\models\jeep.3ds) */
733         memset( basename,0,sizeof(basename) );
734         strncpy( basename,_pico_nopath(fileName),sizeof(basename) );
735         _pico_setfext( basename,"" );
736
737         /* initialize persistant vars (formerly static) */
738         pers.model    =  model;
739         pers.bufptr   = (picoByte_t *)buffer;
740         pers.basename = (char *)basename;
741         pers.maxofs   =  bufSize;
742         pers.cofs     =  0L;
743
744         /* do model setup */
745         PicoSetModelFrameNum( model,frameNum );
746         PicoSetModelName( model,fileName );
747         PicoSetModelFileName( model,fileName );
748
749         /* skip first chunk in file (magic) */
750         GetChunk(&pers);
751
752         /* process chunks */
753         if (!DoNextChunk(&pers,pers.maxofs))
754         {
755                 /* well, bleh i guess */
756                 PicoFreeModel(model);
757                 return NULL;
758         }
759         /* return allocated pico model */
760         return model;
761 }
762
763 /* pico file format module definition */
764 const picoModule_t picoModule3DS =
765 {
766         "0.86-b",                                       /* module version string */
767         "Autodesk 3Dstudio",            /* module display name */
768         "seaw0lf",                                      /* author's name */
769         "2002 seaw0lf",                         /* module copyright */
770         {
771                 "3ds",NULL,NULL,NULL    /* default extensions to use */
772         },
773         _3ds_canload,                           /* validation routine */
774         _3ds_load,                                      /* load routine */
775          NULL,                                          /* save validation routine */
776          NULL                                           /* save routine */
777 };