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