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