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