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