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