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