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