* fixed warning
[xonotic/netradiant.git] / libs / picomodel / pm_ase.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 aseMaterialList 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 /* marker */
37 #define PM_ASE_C
38
39 /* uncomment when debugging this module */
40 //#define DEBUG_PM_ASE
41 //#define DEBUG_PM_ASE_EX
42
43
44 /* dependencies */
45 #include "picointernal.h"
46
47 #ifdef DEBUG_PM_ASE
48 #include "time.h"
49 #endif
50
51 /* plain white */
52 static picoColor_t white = { 255, 255, 255, 255 };
53
54 /* jhefty - multi-subobject material support */
55
56 /* Material/SubMaterial management */
57 /* A material should have 1..n submaterials assigned to it */
58
59 typedef struct aseSubMaterial_s
60 {
61         struct aseSubMaterial_s* next;
62         int subMtlId;
63         picoShader_t* shader;
64
65 } aseSubMaterial_t;
66
67 typedef struct aseMaterial_s
68 {
69         struct aseMaterial_s* next;
70         struct aseSubMaterial_s* subMtls;
71         int mtlId;
72 } aseMaterial_t;
73
74 /* Material/SubMaterial management functions */
75 static aseMaterial_t* _ase_get_material ( aseMaterial_t* list , int mtlIdParent )
76 {
77         aseMaterial_t* mtl = list;
78
79         while ( mtl )
80         {
81                 if ( mtlIdParent == mtl->mtlId )
82                 {
83                         break;
84                 }
85                 mtl = mtl->next;
86         }
87         return mtl;
88 }
89
90 static aseSubMaterial_t* _ase_get_submaterial ( aseMaterial_t* list, int  mtlIdParent , int subMtlId )
91 {
92         aseMaterial_t* parent = _ase_get_material ( list , mtlIdParent );
93         aseSubMaterial_t* subMtl = NULL;
94
95         if ( !parent )
96         {
97                 _pico_printf ( PICO_ERROR , "No ASE material exists with id %i\n" , mtlIdParent );
98                 return NULL;
99         }
100
101         subMtl = parent->subMtls;
102         while ( subMtl )
103         {
104                 if ( subMtlId == subMtl->subMtlId )
105                 {
106                         break;
107                 }
108                 subMtl = subMtl->next;
109         }
110         return subMtl;
111 }
112
113 static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent )
114 {
115         aseMaterial_t *mtl = _pico_calloc( 1, sizeof( aseMaterial_t ) );
116         mtl->mtlId = mtlIdParent;
117         mtl->subMtls = NULL;
118         mtl->next = *list;
119         *list = mtl;
120
121         return mtl;
122 }
123
124 static aseSubMaterial_t* _ase_add_submaterial( aseMaterial_t **list, int mtlIdParent, int subMtlId, picoShader_t* shader )
125 {
126         aseMaterial_t *parent = _ase_get_material( *list,  mtlIdParent );
127         aseSubMaterial_t *subMtl = _pico_calloc( 1, sizeof ( aseSubMaterial_t ) );
128
129         if ( !parent )
130         {
131                 parent = _ase_add_material ( list , mtlIdParent );
132         }
133
134         subMtl->shader = shader;
135         subMtl->subMtlId = subMtlId;
136         subMtl->next = parent->subMtls;
137         parent->subMtls = subMtl;
138
139         return subMtl;
140 }
141
142 static void _ase_free_materials( aseMaterial_t **list )
143 {
144         aseMaterial_t* mtl = *list;
145         aseSubMaterial_t* subMtl = NULL;
146
147         aseMaterial_t* mtlTemp = NULL;
148         aseSubMaterial_t* subMtlTemp = NULL;
149
150         while ( mtl )
151         {
152                 subMtl = mtl->subMtls;
153                 while ( subMtl )
154                 {
155                         subMtlTemp = subMtl->next;
156                         _pico_free ( subMtl );
157                         subMtl = subMtlTemp;
158                 }
159                 mtlTemp = mtl->next;
160                 _pico_free ( mtl );
161                 mtl = mtlTemp;
162         }
163         (*list) = NULL;
164 }
165
166 #ifdef DEBUG_PM_ASE
167 static void _ase_print_materials( aseMaterial_t *list )
168 {
169         aseMaterial_t* mtl = list;
170         aseSubMaterial_t* subMtl = NULL;
171
172         while ( mtl )
173         {
174                 _pico_printf ( PICO_NORMAL ,  "ASE Material %i" , mtl->mtlId );
175                 subMtl = mtl->subMtls;
176                 while ( subMtl )
177                 {
178                         _pico_printf ( PICO_NORMAL ,  " -- ASE SubMaterial %i - %s\n" , subMtl->subMtlId , subMtl->shader->name );
179                         subMtl = subMtl->next;
180                 }
181                 mtl = mtl->next;
182         }
183 }
184 #endif //DEBUG_PM_ASE
185
186 /* ASE Face management */
187 /* These are used to keep an association between a submaterial and a face definition */
188 /* They are kept in parallel with the current picoSurface, */
189 /* and are used by _ase_submit_triangles to lookup the proper material/submaterial IDs */
190 typedef struct aseFace_s
191 {
192         struct aseFace_s* next;
193         int mtlId;
194         int subMtlId;
195         int index[9];
196 } aseFace_t;
197
198 /* ASE Face management functions */
199 void _ase_add_face( aseFace_t **list, aseFace_t **tail, aseFace_t *newFace )
200 {
201         /* insert as head of list */
202         if ( !(*list) )
203         {
204                 *list = newFace;
205         }
206         else
207         {
208                 (*tail)->next = newFace;
209         }
210
211         *tail = newFace;
212         newFace->next = NULL;
213
214         //tag the color indices so we can detect them and apply the default color to them
215         newFace->index[6] = -1;
216         newFace->index[7] = -1;
217         newFace->index[8] = -1;
218 }
219
220 aseFace_t* _ase_get_face_for_index( aseFace_t *list, int index )
221 {
222         int counter = 0;
223         aseFace_t* face = list;
224
225         while ( counter < index )
226         {
227                 face = face->next;
228                 counter++;
229         }
230         return face;
231 }
232 static void _ase_free_faces (aseFace_t** list, aseFace_t** tail )
233 {
234         aseFace_t* face = *list;
235         aseFace_t* tempFace = NULL;
236
237         while ( face )
238         {
239                 tempFace = face->next;
240                 _pico_free ( face );
241                 face = tempFace;
242         }
243
244         (*list) = NULL;
245         (*tail) = NULL;
246 }
247
248 /* todo:
249  * - apply material specific uv offsets to uv coordinates
250  */
251
252 /* _ase_canload:
253  *  validates a 3dsmax ase model file.
254  */
255 static int _ase_canload( PM_PARAMS_CANLOAD )
256 {
257         picoParser_t *p;
258
259
260         /* quick data length validation */
261         if( bufSize < 80 )
262                 return PICO_PMV_ERROR_SIZE;
263
264         /* keep the friggin compiler happy */
265         *fileName = *fileName;
266
267         /* create pico parser */
268         p = _pico_new_parser( (picoByte_t*) buffer, bufSize );
269         if( p == NULL )
270                 return PICO_PMV_ERROR_MEMORY;
271
272         /* get first token */
273         if( _pico_parse_first( p ) == NULL)
274         {
275                 return PICO_PMV_ERROR_IDENT;
276         }
277
278         /* check first token */
279         if( _pico_stricmp( p->token, "*3dsmax_asciiexport" ) )
280         {
281                 _pico_free_parser( p );
282                 return PICO_PMV_ERROR_IDENT;
283         }
284
285         /* free the pico parser object */
286         _pico_free_parser( p );
287
288         /* file seems to be a valid ase file */
289         return PICO_PMV_OK;
290 }
291
292
293
294 /* _ase_submit_triangles - jhefty
295  use the surface and the current face list to look up material/submaterial IDs
296  and submit them to the model for proper processing
297
298 The following still holds from ydnar's _ase_make_surface:
299  indexes 0 1 2 = vert indexes
300  indexes 3 4 5 = st indexes
301  indexes 6 7 8 = color indexes (new)
302 */
303
304 static void _ase_submit_triangles ( picoSurface_t* surface , picoModel_t* model , aseMaterial_t* materials , aseFace_t* faces )
305 {
306         aseFace_t* face;
307         aseSubMaterial_t* subMtl;
308         picoVec3_t* xyz[3];
309         picoVec3_t* normal[3];
310         picoVec2_t* st[3];
311         picoColor_t* color[3];
312         int i;
313
314         face = faces;
315         while ( face != NULL )
316         {
317                 /* look up the shader for the material/submaterial pair */
318                 subMtl = _ase_get_submaterial( materials, face->mtlId, face->subMtlId );
319                 if( subMtl == NULL )
320                 {
321                         /* ydnar: trying default submaterial */
322                         subMtl = _ase_get_submaterial( materials, face->mtlId, 0 );
323                         if( subMtl == NULL )
324                         {
325                                 _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", face->mtlId, face->subMtlId );
326                                 return;
327                         }
328                 }
329
330                 /* we pull the data from the surface using the facelist data */
331                 for ( i = 0 ; i < 3 ; i ++ )
332                 {
333                         xyz[i]    = (picoVec3_t*) PicoGetSurfaceXYZ   ( surface, face->index[ i ] );
334                         normal[i] = (picoVec3_t*) PicoGetSurfaceNormal( surface, face->index[ i ] );
335                         st[i]     = (picoVec2_t*) PicoGetSurfaceST    ( surface, 0, face->index[ i + 3 ] );
336
337                         if ( face->index [ i + 6] >= 0 )
338                         {
339                                 color[i]  = (picoColor_t*)PicoGetSurfaceColor ( surface, 0, face->index[ i + 6 ] );
340                         }
341                         else
342                         {
343                                 color[i] = &white;
344                         }
345
346                 }
347
348                 /* submit the triangle to the model */
349                 PicoAddTriangleToModel ( model , xyz , normal , 1 , st , 1 , color , subMtl->shader );
350
351                 /* advance to the next face */
352                 face = face->next;
353         }
354 }
355
356 /* _ase_load:
357  *  loads a 3dsmax ase model file.
358 */
359 static picoModel_t *_ase_load( PM_PARAMS_LOAD )
360 {
361         picoModel_t    *model;
362         picoSurface_t  *surface = NULL;
363         picoParser_t   *p;
364         char                    lastNodeName[ 1024 ];
365
366         aseFace_t* faces = NULL;
367         aseFace_t* facesTail = NULL;
368         aseMaterial_t* materials = NULL;
369
370 #ifdef DEBUG_PM_ASE
371         clock_t start, finish;
372         double elapsed;
373         start = clock();
374 #endif
375
376         /* helper */
377         #define _ase_error_return(m) \
378         { \
379                 _pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine); \
380                 _pico_free_parser( p ); \
381                 PicoFreeModel( model ); \
382                 return NULL; \
383         }
384         /* create a new pico parser */
385         p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
386         if (p == NULL) return NULL;
387
388         /* create a new pico model */
389         model = PicoNewModel();
390         if (model == NULL)
391         {
392                 _pico_free_parser( p );
393                 return NULL;
394         }
395         /* do model setup */
396         PicoSetModelFrameNum( model, frameNum );
397         PicoSetModelName( model, fileName );
398         PicoSetModelFileName( model, fileName );
399
400         /* initialize some stuff */
401         memset( lastNodeName,0,sizeof(lastNodeName) );
402
403         /* parse ase model file */
404         while( 1 )
405         {
406                 /* get first token on line */
407                 if (_pico_parse_first( p ) == NULL)
408                         break;
409
410                 /* we just skip empty lines */
411                 if (p->token == NULL || !strlen( p->token ))
412                         continue;
413
414                 /* we skip invalid ase statements */
415                 if (p->token[0] != '*' && p->token[0] != '{' && p->token[0] != '}')
416                 {
417                         _pico_parse_skip_rest( p );
418                         continue;
419                 }
420                 /* remember node name */
421                 if (!_pico_stricmp(p->token,"*node_name"))
422                 {
423                         /* read node name */
424                         char *ptr = _pico_parse( p,0 );
425                         if (ptr == NULL)
426                                 _ase_error_return("Node name parse error");
427
428                         /* remember node name */
429                         strncpy( lastNodeName,ptr,sizeof(lastNodeName) );
430                 }
431                 /* model mesh (originally contained within geomobject) */
432                 else if (!_pico_stricmp(p->token,"*mesh"))
433                 {
434                         /* finish existing surface */
435                         //_ase_make_surface( model, &surface );
436                         _ase_submit_triangles (surface, model ,materials,faces);
437                         _ase_free_faces (&faces,&facesTail);
438
439                         /* allocate new pico surface */
440                         surface = PicoNewSurface( NULL );
441                         if (surface == NULL)
442                         {
443                                 PicoFreeModel( model );
444                                 return NULL;
445                         }
446                 }
447                 /* mesh material reference. this usually comes at the end of */
448                 /* geomobjects after the mesh blocks. we must assume that the */
449                 /* new mesh was already created so all we can do here is assign */
450                 /* the material reference id (shader index) now. */
451                 else if (!_pico_stricmp(p->token,"*material_ref"))
452                 {
453                         int mtlId;
454                         aseFace_t* face;
455
456                         /* we must have a valid surface */
457                         if( surface == NULL )
458                                 _ase_error_return("Missing mesh for material reference");
459
460                         /* get the material ref (0..n) */
461                         if (!_pico_parse_int( p,&mtlId) )
462                                 _ase_error_return("Missing material reference ID");
463
464                         /* fix up all of the aseFaceList in the surface to point to the parent material */
465                         /* we've already saved off their subMtl */
466                         face = faces;
467                         while ( face != NULL )
468                         {
469                                 face->mtlId = mtlId;
470                                 face = face->next;
471                         }
472                 }
473                 /* model mesh vertex */
474                 else if (!_pico_stricmp(p->token,"*mesh_vertex"))
475                 {
476                         picoVec3_t      v;
477                         int                     index;
478
479                         /* we must have a valid surface */
480                         if( surface == NULL )
481                                 continue;
482
483                         /* get vertex data (orig: index +y -x +z) */
484                         if (!_pico_parse_int( p,&index ))
485                                 _ase_error_return("Vertex parse error");
486                         if (!_pico_parse_vec( p,v ))
487                                 _ase_error_return("Vertex parse error");
488
489                         /* set vertex */
490                         PicoSetSurfaceXYZ( surface,index,v );
491                 }
492                 /* model mesh vertex normal */
493                 else if (!_pico_stricmp(p->token,"*mesh_vertexnormal"))
494                 {
495                         picoVec3_t      v;
496                         int                     index;
497
498                         /* we must have a valid surface */
499                         if( surface == NULL )
500                                 continue;
501
502                         /* get vertex data (orig: index +y -x +z) */
503                         if (!_pico_parse_int( p,&index ))
504                                 _ase_error_return("Vertex parse error");
505                         if (!_pico_parse_vec( p,v ))
506                                 _ase_error_return("Vertex parse error");
507
508                         /* set vertex */
509                         PicoSetSurfaceNormal( surface,index,v );
510                 }
511                 /* model mesh face */
512                 else if (!_pico_stricmp(p->token,"*mesh_face"))
513                 {
514                         picoIndex_t indexes[3];
515                         int                     index;
516
517                         /* we must have a valid surface */
518                         if( surface == NULL )
519                                 continue;
520
521                         /* get face index */
522                         if (!_pico_parse_int( p,&index ))
523                                 _ase_error_return("Face parse error");
524
525                         /* get 1st vertex index */
526                         _pico_parse( p,0 );
527                         if (!_pico_parse_int( p,&indexes[0] ))
528                                 _ase_error_return("Face parse error");
529
530                         /* get 2nd vertex index */
531                         _pico_parse( p,0 );
532                         if (!_pico_parse_int( p,&indexes[1] ))
533                                 _ase_error_return("Face parse error");
534
535                         /* get 3rd vertex index */
536                         _pico_parse( p,0 );
537                         if (!_pico_parse_int( p,&indexes[2] ))
538                                 _ase_error_return("Face parse error");
539
540                         /* set face indexes (note interleaved offset!) */
541                         PicoSetSurfaceIndex( surface, (index * 9 + 0), indexes[2] );
542                         PicoSetSurfaceIndex( surface, (index * 9 + 1), indexes[1] );
543                         PicoSetSurfaceIndex( surface, (index * 9 + 2), indexes[0] );
544
545                         /* parse to the subMaterial ID */
546                         while ( 1 )
547                         {
548                                 _pico_parse (p,0);
549                                 if (!_pico_stricmp (p->token,"*MESH_MTLID" ))
550                                 {
551                                         aseFace_t* newFace;
552                                         int subMtlId;
553
554                                         _pico_parse_int ( p , &subMtlId );
555                                         newFace = _pico_calloc ( 1 , sizeof ( aseFace_t ));
556
557                                         /* we fix up the mtlId later when we parse the material_ref */
558                                         newFace->mtlId = 0;
559                                         newFace->subMtlId = subMtlId;
560                                         newFace->index[0] = indexes[2];
561                                         newFace->index[1] = indexes[1];
562                                         newFace->index[2] = indexes[0];
563
564                                         _ase_add_face ( &faces,&facesTail,newFace );
565                                         break;
566                                 }
567                         }
568
569                 }
570                 /* model texture vertex */
571                 else if (!_pico_stricmp(p->token,"*mesh_tvert"))
572                 {
573                         picoVec2_t      uv;
574                         int                     index;
575
576                         /* we must have a valid surface */
577                         if( surface == NULL )
578                                 continue;
579
580                         /* get uv vertex index */
581                         if (!_pico_parse_int( p,&index ))
582                                 _ase_error_return("UV vertex parse error");
583
584                         /* get uv vertex s */
585                         if (!_pico_parse_float( p,&uv[0] ))
586                                 _ase_error_return("UV vertex parse error");
587
588                         /* get uv vertex t */
589                         if (!_pico_parse_float( p,&uv[1] ))
590                                 _ase_error_return("UV vertex parse error");
591
592                         /* ydnar: invert t */
593                         uv[ 1 ] = 1.0f - uv[ 1 ];
594
595                         /* set texture vertex */
596                         PicoSetSurfaceST( surface,0,index,uv );
597                 }
598                 /* ydnar: model mesh texture face */
599                 else if( !_pico_stricmp( p->token, "*mesh_tface" ) )
600                 {
601                         picoIndex_t indexes[3];
602                         int                     index;
603                         aseFace_t* face;
604
605                         /* we must have a valid surface */
606                         if( surface == NULL )
607                                 continue;
608
609                         /* get face index */
610                         if (!_pico_parse_int( p,&index ))
611                                 _ase_error_return("Texture face parse error");
612
613                         /* get 1st vertex index */
614                         if (!_pico_parse_int( p,&indexes[0] ))
615                                 _ase_error_return("Texture face parse error");
616
617                         /* get 2nd vertex index */
618                         if (!_pico_parse_int( p,&indexes[1] ))
619                                 _ase_error_return("Texture face parse error");
620
621                         /* get 3rd vertex index */
622                         if (!_pico_parse_int( p,&indexes[2] ))
623                                 _ase_error_return("Texture face parse error");
624
625                         /* set face indexes (note interleaved offset!) */
626                         PicoSetSurfaceIndex( surface, (index * 9 + 3), indexes[2] );
627                         PicoSetSurfaceIndex( surface, (index * 9 + 4), indexes[1] );
628                         PicoSetSurfaceIndex( surface, (index * 9 + 5), indexes[0] );
629
630                         face = _ase_get_face_for_index(faces,index);
631                         face->index[3] = indexes[2];
632                         face->index[4] = indexes[1];
633                         face->index[5] = indexes[0];
634                 }
635                 /* model color vertex */
636                 else if (!_pico_stricmp(p->token,"*mesh_vertcol"))
637                 {
638                         picoColor_t     color;
639                         int                     index;
640                         float           colorInput;
641
642                         /* we must have a valid surface */
643                         if( surface == NULL )
644                                 continue;
645
646                         /* get color vertex index */
647                         if (!_pico_parse_int( p,&index ))
648                                 _ase_error_return("UV vertex parse error");
649
650                         /* get R component */
651                         if (!_pico_parse_float( p,&colorInput ))
652                                 _ase_error_return("color vertex parse error");
653                         color[0] = (picoByte_t)(colorInput * 255);
654
655                         /* get G component */
656                         if (!_pico_parse_float( p,&colorInput ))
657                                 _ase_error_return("color vertex parse error");
658                         color[1] = (picoByte_t)(colorInput * 255);
659
660                         /* get B component */
661                         if (!_pico_parse_float( p,&colorInput ))
662                                 _ase_error_return("color vertex parse error");
663                         color[2] = (picoByte_t)(colorInput * 255);
664
665                         /* leave alpha alone since we don't get any data from the ASE format */
666                         color[3] = 255;
667
668                         /* set texture vertex */
669                         PicoSetSurfaceColor( surface,0,index,color );
670                 }
671                 /* model color face */
672                 else if (!_pico_stricmp(p->token,"*mesh_cface"))
673                 {
674                         picoIndex_t indexes[3];
675                         int                     index;
676                         aseFace_t*  face;
677
678                         /* we must have a valid surface */
679                         if( surface == NULL )
680                                 continue;
681
682                         /* get face index */
683                         if (!_pico_parse_int( p,&index ))
684                                 _ase_error_return("Face parse error");
685
686                         /* get 1st cvertex index */
687                         //                      _pico_parse( p,0 );
688                         if (!_pico_parse_int( p,&indexes[0] ))
689                                 _ase_error_return("Face parse error");
690
691                         /* get 2nd cvertex index */
692                         //                      _pico_parse( p,0 );
693                         if (!_pico_parse_int( p,&indexes[1] ))
694                                 _ase_error_return("Face parse error");
695
696                         /* get 3rd cvertex index */
697                         //                      _pico_parse( p,0 );
698                         if (!_pico_parse_int( p,&indexes[2] ))
699                                 _ase_error_return("Face parse error");
700
701                         /* set face indexes (note interleaved offset!) */
702                         PicoSetSurfaceIndex( surface, (index * 9 + 6), indexes[2] );
703                         PicoSetSurfaceIndex( surface, (index * 9 + 7), indexes[1] );
704                         PicoSetSurfaceIndex( surface, (index * 9 + 8), indexes[0] );
705
706                         face = _ase_get_face_for_index(faces,index);
707                         face->index[6] = indexes[2];
708                         face->index[7] = indexes[1];
709                         face->index[8] = indexes[0];
710                 }
711                 /* model material */
712                 else if( !_pico_stricmp( p->token, "*material" ) )
713                 {
714                         aseSubMaterial_t*       subMaterial = NULL;
715                         picoShader_t            *shader = NULL;
716                         int                                     level = 1, index;
717                         char                            materialName[ 1024 ];
718                         float                           transValue = 0.0f, shineValue = 1.0f;
719                         picoColor_t                     ambientColor, diffuseColor, specularColor;
720                         char                            *mapname = NULL;
721                         int                                     subMtlId, subMaterialLevel = -1;
722
723
724                         /* get material index */
725                         _pico_parse_int( p,&index );
726
727                         /* check brace */
728                         if (!_pico_parse_check(p,1,"{"))
729                                 _ase_error_return("Material missing opening brace");
730
731                         /* parse material block */
732                         while( 1 )
733                         {
734                                 /* get next token */
735                                 if (_pico_parse(p,1) == NULL) break;
736                                 if (!strlen(p->token)) continue;
737
738                                 /* handle levels */
739                                 if (p->token[0] == '{') level++;
740                                 if (p->token[0] == '}') level--;
741                                 if (!level) break;
742
743                                 if( level == subMaterialLevel )
744                                 {
745                                         /* set material name */
746                                         PicoSetShaderName( shader, materialName);
747
748                                         /* set shader's transparency */
749                                         PicoSetShaderTransparency( shader,transValue );
750
751                                         /* set shader's ambient color */
752                                         PicoSetShaderAmbientColor( shader,ambientColor );
753
754                                         /* set diffuse alpha to transparency */
755                                         diffuseColor[3] = (picoByte_t)( transValue * 255.0 );
756
757                                         /* set shader's diffuse color */
758                                         PicoSetShaderDiffuseColor( shader,diffuseColor );
759
760                                         /* set shader's specular color */
761                                         PicoSetShaderSpecularColor( shader,specularColor );
762
763                                         /* set shader's shininess */
764                                         PicoSetShaderShininess( shader,shineValue );
765
766                                         /* set material map name */
767                                         PicoSetShaderMapName( shader, mapname );
768
769                                         subMaterial = _ase_add_submaterial( &materials, index, subMtlId, shader );
770                                         subMaterialLevel = -1;
771                                 }
772
773                                 /* parse submaterial index */
774                                 if (!_pico_stricmp(p->token,"*submaterial"))
775                                 {
776                                         /* allocate new pico shader */
777                                         _pico_parse_int( p , &subMtlId );
778
779                                         shader = PicoNewShader( model );
780                                         if (shader == NULL)
781                                         {
782                                                 PicoFreeModel( model );
783                                                 return NULL;
784                                         }
785                                         subMaterialLevel = level;
786                                 }
787                                 /* parse material name */
788                                 else if (!_pico_stricmp(p->token,"*material_name"))
789                                 {
790                                         char* name = _pico_parse(p,0);
791                                         if ( name == NULL)
792                                                 _ase_error_return("Missing material name");
793
794                                         strcpy ( materialName , name );
795                                         /* skip rest and continue with next token */
796                                         _pico_parse_skip_rest( p );
797                                         continue;
798                                 }
799                                 /* parse material transparency */
800                                 else if (!_pico_stricmp(p->token,"*material_transparency"))
801                                 {
802                                         /* get transparency value from ase */
803                                         if (!_pico_parse_float( p,&transValue ))
804                                                 _ase_error_return("Material transparency parse error");
805
806                                         /* skip rest and continue with next token */
807                                         _pico_parse_skip_rest( p );
808                                         continue;
809                                 }
810                                 /* parse material shininess */
811                                 else if (!_pico_stricmp(p->token,"*material_shine"))
812                                 {
813                                         /* remark:
814                                          * - not sure but instead of '*material_shine' i might
815                                          *   need to use '*material_shinestrength' */
816
817                                         /* get shine value from ase */
818                                         if (!_pico_parse_float( p,&shineValue ))
819                                                 _ase_error_return("Material shine parse error");
820
821                                         /* scale ase shine range 0..1 to pico range 0..127 */
822                                         shineValue *= 128.0;
823
824                                         /* skip rest and continue with next token */
825                                         _pico_parse_skip_rest( p );
826                                         continue;
827                                 }
828                                 /* parse ambient material color */
829                                 else if (!_pico_stricmp(p->token,"*material_ambient"))
830                                 {
831                                         picoVec3_t  vec;
832                                         /* get r,g,b float values from ase */
833                                         if (!_pico_parse_vec( p,vec ))
834                                                 _ase_error_return("Material color parse error");
835
836                                         /* setup 0..255 range color values */
837                                         ambientColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );
838                                         ambientColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );
839                                         ambientColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );
840                                         ambientColor[ 3 ] = (int)( 255 );
841
842                                         /* skip rest and continue with next token */
843                                         _pico_parse_skip_rest( p );
844                                         continue;
845                                 }
846                                 /* parse diffuse material color */
847                                 else if (!_pico_stricmp(p->token,"*material_diffuse"))
848                                 {
849                                         picoVec3_t  vec;
850
851                                         /* get r,g,b float values from ase */
852                                         if (!_pico_parse_vec( p,vec ))
853                                                 _ase_error_return("Material color parse error");
854
855                                         /* setup 0..255 range color */
856                                         diffuseColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );
857                                         diffuseColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );
858                                         diffuseColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );
859                                         diffuseColor[ 3 ] = (int)( 255 );
860
861                                         /* skip rest and continue with next token */
862                                         _pico_parse_skip_rest( p );
863                                         continue;
864                                 }
865                                 /* parse specular material color */
866                                 else if (!_pico_stricmp(p->token,"*material_specular"))
867                                 {
868                                         picoVec3_t  vec;
869
870                                         /* get r,g,b float values from ase */
871                                         if (!_pico_parse_vec( p,vec ))
872                                                 _ase_error_return("Material color parse error");
873
874                                         /* setup 0..255 range color */
875                                         specularColor[ 0 ] = (int)( vec[ 0 ] * 255 );
876                                         specularColor[ 1 ] = (int)( vec[ 1 ] * 255 );
877                                         specularColor[ 2 ] = (int)( vec[ 2 ] * 255 );
878                                         specularColor[ 3 ] = (int)( 255 );
879
880                                         /* skip rest and continue with next token */
881                                         _pico_parse_skip_rest( p );
882                                         continue;
883                                 }
884                                 /* material diffuse map */
885                                 else if (!_pico_stricmp(p->token,"*map_diffuse") )
886                                 {
887                                         int sublevel = 0;
888
889                                         /* parse material block */
890                                         while( 1 )
891                                         {
892                                                 /* get next token */
893                                                 if (_pico_parse(p,1) == NULL) break;
894                                                 if (!strlen(p->token)) continue;
895
896                                                 /* handle levels */
897                                                 if (p->token[0] == '{') sublevel++;
898                                                 if (p->token[0] == '}') sublevel--;
899                                                 if (!sublevel) break;
900
901                                                 /* parse diffuse map bitmap */
902                                                 if (!_pico_stricmp(p->token,"*bitmap"))
903                                                 {
904                                                         char* name = _pico_parse(p,0);
905                                                         if (name == NULL)
906                                                                 _ase_error_return("Missing material map bitmap name");
907                                                         mapname = _pico_alloc ( strlen ( name ) + 1 );
908                                                         strcpy ( mapname, name );
909                                                         /* skip rest and continue with next token */
910                                                         _pico_parse_skip_rest( p );
911                                                         continue;
912                                                 }
913                                         }
914                                 }
915                                 /* end map_diffuse block */
916                         }
917                         /* end material block */
918
919                         if( subMaterial == NULL )
920                         {
921                                 /* allocate new pico shader */
922                                 shader = PicoNewShader( model );
923                                 if (shader == NULL)
924                                 {
925                                         PicoFreeModel( model );
926                                         return NULL;
927                                 }
928
929                                 /* set material name */
930                                 PicoSetShaderName( shader,materialName );
931
932                                 /* set shader's transparency */
933                                 PicoSetShaderTransparency( shader,transValue );
934
935                                 /* set shader's ambient color */
936                                 PicoSetShaderAmbientColor( shader,ambientColor );
937
938                                 /* set diffuse alpha to transparency */
939                                 diffuseColor[3] = (picoByte_t)( transValue * 255.0 );
940
941                                 /* set shader's diffuse color */
942                                 PicoSetShaderDiffuseColor( shader,diffuseColor );
943
944                                 /* set shader's specular color */
945                                 PicoSetShaderSpecularColor( shader,specularColor );
946
947                                 /* set shader's shininess */
948                                 PicoSetShaderShininess( shader,shineValue );
949
950                                 /* set material map name */
951                                 PicoSetShaderMapName( shader, mapname );
952
953                                 /* this is just a material with 1 submaterial */
954                                 subMaterial = _ase_add_submaterial( &materials, index, 0, shader );
955                         }
956
957                         /* ydnar: free mapname */
958                         if( mapname != NULL )
959                                 _pico_free( mapname );
960                 }       // !_pico_stricmp ( "*material" )
961
962                 /* skip unparsed rest of line and continue */
963                 _pico_parse_skip_rest( p );
964         }
965
966         /* ydnar: finish existing surface */
967 //      _ase_make_surface( model, &surface );
968         _ase_submit_triangles (surface, model ,materials,faces);
969         _ase_free_faces (&faces,&facesTail);
970
971 #ifdef DEBUG_PM_ASE
972         _ase_print_materials(materials);
973         finish = clock();
974         elapsed = (double)(finish - start) / CLOCKS_PER_SEC;
975         _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s)\n", elapsed );
976 #endif //DEBUG_PM_ASE
977
978         _ase_free_materials(&materials);
979
980         /* return allocated pico model */
981         return model;
982 }
983
984 /* pico file format module definition */
985 const picoModule_t picoModuleASE =
986 {
987         "1.0",                                  /* module version string */
988         "Autodesk 3DSMAX ASCII",        /* module display name */
989         "Jared Hefty, seaw0lf",                                 /* author's name */
990         "2003 Jared Hefty, 2002 seaw0lf",                               /* module copyright */
991         {
992                 "ase",NULL,NULL,NULL    /* default extensions to use */
993         },
994         _ase_canload,                           /* validation routine */
995         _ase_load,                                      /* load routine */
996          NULL,                                          /* save validation routine */
997          NULL                                           /* save routine */
998 };