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