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