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