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