]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/common/aselib.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / common / aselib.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 \r
23 #include "aselib.h"\r
24 #include "inout.h"\r
25 \r
26 #include <assert.h>\r
27 #include <stdio.h>\r
28 #include <stdlib.h>\r
29 \r
30 #define MAX_ASE_MATERIALS                       32\r
31 #define MAX_ASE_OBJECTS                         64\r
32 #define MAX_ASE_ANIMATIONS                      32\r
33 #define MAX_ASE_ANIMATION_FRAMES        512\r
34 \r
35 #define VERBOSE( x ) { if ( ase.verbose ) { Sys_Printf x ; } }\r
36 \r
37 typedef struct\r
38 {\r
39         float x, y, z;\r
40         float nx, ny, nz;\r
41         float s, t;\r
42 } aseVertex_t;\r
43 \r
44 typedef struct\r
45 {\r
46         float s, t;\r
47 } aseTVertex_t;\r
48 \r
49 typedef int aseFace_t[3];\r
50 \r
51 typedef struct\r
52 {\r
53         int numFaces;\r
54         int numVertexes;\r
55         int numTVertexes;\r
56 \r
57         int timeValue;\r
58 \r
59         aseVertex_t             *vertexes;\r
60         aseTVertex_t    *tvertexes;\r
61         aseFace_t               *faces, *tfaces;\r
62 \r
63         int currentFace, currentVertex;\r
64 } aseMesh_t;\r
65 \r
66 typedef struct\r
67 {\r
68         int                     numFrames;\r
69         aseMesh_t       frames[MAX_ASE_ANIMATION_FRAMES];\r
70 \r
71         int                currentFrame;\r
72 } aseMeshAnimation_t;\r
73 \r
74 typedef struct\r
75 {\r
76         char name[128];\r
77 } aseMaterial_t;\r
78 \r
79 /*\r
80 ** contains the animate sequence of a single surface\r
81 ** using a single material\r
82 */\r
83 typedef struct\r
84 {\r
85         char name[128];\r
86 \r
87         int materialRef;\r
88         int numAnimations;\r
89 \r
90         aseMeshAnimation_t      anim;\r
91 \r
92 } aseGeomObject_t;\r
93 \r
94 typedef struct\r
95 {\r
96         int                             numMaterials;\r
97         aseMaterial_t   materials[MAX_ASE_MATERIALS];\r
98         aseGeomObject_t objects[MAX_ASE_OBJECTS];\r
99 \r
100         char    *buffer;\r
101         char    *curpos;\r
102         int              len;\r
103 \r
104         int                     currentObject;\r
105         qboolean        verbose;\r
106         qboolean        grabAnims;\r
107 \r
108 } ase_t;\r
109 \r
110 static char s_token[1024];\r
111 static ase_t ase;\r
112 static char gl_filename[1024];\r
113 \r
114 static void ASE_Process( void );\r
115 static void ASE_FreeGeomObject( int ndx );\r
116 \r
117 #if defined (__linux__) || defined (__APPLE__)\r
118 \r
119 static char* strlwr (char* string)\r
120 {\r
121   char *cp;\r
122   for (cp = string; *cp; ++cp)\r
123   {\r
124     if ('A' <= *cp && *cp <= 'Z')\r
125       *cp += 'a' - 'A';\r
126   }\r
127  \r
128   return string;\r
129 }\r
130 \r
131 #endif\r
132 \r
133 /*\r
134 ** ASE_Load\r
135 */\r
136 void ASE_Load( const char *filename, qboolean verbose, qboolean grabAnims )\r
137 {\r
138         FILE *fp = fopen( filename, "rb" );\r
139 \r
140         if ( !fp )\r
141                 Error( "File not found '%s'", filename );\r
142 \r
143         memset( &ase, 0, sizeof( ase ) );\r
144 \r
145         ase.verbose = verbose;\r
146         ase.grabAnims = grabAnims;\r
147         ase.len = Q_filelength( fp );\r
148 \r
149         ase.curpos = ase.buffer = safe_malloc( ase.len );\r
150 \r
151         Sys_Printf( "Processing '%s'\n", filename );\r
152 \r
153         if ( fread( ase.buffer, ase.len, 1, fp ) != 1 )\r
154         {\r
155                 fclose( fp );\r
156                 Error( "fread() != -1 for '%s'", filename );\r
157         }\r
158 \r
159         fclose( fp );\r
160 \r
161         strcpy(gl_filename, filename);\r
162         \r
163         ASE_Process();\r
164 }\r
165 \r
166 /*\r
167 ** ASE_Free\r
168 */\r
169 void ASE_Free( void )\r
170 {\r
171         int i;\r
172 \r
173         for ( i = 0; i < ase.currentObject; i++ )\r
174         {\r
175                 ASE_FreeGeomObject( i );\r
176         }\r
177 }\r
178 \r
179 /*\r
180 ** ASE_GetNumSurfaces\r
181 */\r
182 int ASE_GetNumSurfaces( void )\r
183 {\r
184         return ase.currentObject;\r
185 }\r
186 \r
187 /*\r
188 ** ASE_GetSurfaceName\r
189 */\r
190 const char *ASE_GetSurfaceName( int which )\r
191 {\r
192         aseGeomObject_t *pObject = &ase.objects[which];\r
193 \r
194         if ( !pObject->anim.numFrames )\r
195                 return 0;\r
196 \r
197         return pObject->name;\r
198 }\r
199 \r
200 /*\r
201 ** ASE_GetSurfaceAnimation\r
202 **\r
203 ** Returns an animation (sequence of polysets)\r
204 */\r
205 polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames )\r
206 {\r
207         aseGeomObject_t *pObject = &ase.objects[which];\r
208         polyset_t *psets;\r
209         int numFramesInAnimation;\r
210         int numFramesToKeep;\r
211         int i, f;\r
212 \r
213         if ( !pObject->anim.numFrames )\r
214                 return 0;\r
215 \r
216         if ( pObject->anim.numFrames > maxFrames && maxFrames != -1 )\r
217         {\r
218                 numFramesInAnimation = maxFrames;\r
219         }\r
220         else \r
221         {\r
222                 numFramesInAnimation = pObject->anim.numFrames;\r
223                 if ( maxFrames != -1 )\r
224                         Sys_Printf( "WARNING: ASE_GetSurfaceAnimation maxFrames > numFramesInAnimation\n" );\r
225         }\r
226 \r
227         if ( skipFrameEnd != -1 )\r
228                 numFramesToKeep = numFramesInAnimation - ( skipFrameEnd - skipFrameStart + 1 );\r
229         else\r
230                 numFramesToKeep = numFramesInAnimation;\r
231 \r
232         *pNumFrames = numFramesToKeep;\r
233 \r
234         psets = calloc( sizeof( polyset_t ) * numFramesToKeep, 1 );\r
235 \r
236         for ( f = 0, i = 0; i < numFramesInAnimation; i++ )\r
237         {\r
238                 int t;\r
239                 aseMesh_t *pMesh = &pObject->anim.frames[i];\r
240 \r
241                 if ( skipFrameStart != -1 )\r
242                 {\r
243                         if ( i >= skipFrameStart && i <= skipFrameEnd )\r
244                                 continue;\r
245                 }\r
246 \r
247                 strcpy( psets[f].name, pObject->name );\r
248                 strcpy( psets[f].materialname, ase.materials[pObject->materialRef].name );\r
249 \r
250                 psets[f].triangles = calloc( sizeof( triangle_t ) * pObject->anim.frames[i].numFaces, 1 );\r
251                 psets[f].numtriangles = pObject->anim.frames[i].numFaces;\r
252 \r
253                 for ( t = 0; t < pObject->anim.frames[i].numFaces; t++ )\r
254                 {\r
255                         int k;\r
256 \r
257                         for ( k = 0; k < 3; k++ )\r
258                         {\r
259                                 psets[f].triangles[t].verts[k][0] = pMesh->vertexes[pMesh->faces[t][k]].x;\r
260                                 psets[f].triangles[t].verts[k][1] = pMesh->vertexes[pMesh->faces[t][k]].y;\r
261                                 psets[f].triangles[t].verts[k][2] = pMesh->vertexes[pMesh->faces[t][k]].z;\r
262 \r
263                                 if ( pMesh->tvertexes && pMesh->tfaces )\r
264                                 {\r
265                                         psets[f].triangles[t].texcoords[k][0] = pMesh->tvertexes[pMesh->tfaces[t][k]].s;\r
266                                         psets[f].triangles[t].texcoords[k][1] = pMesh->tvertexes[pMesh->tfaces[t][k]].t;\r
267                                 }\r
268 \r
269                         }\r
270                 }\r
271 \r
272                 f++;\r
273         }\r
274 \r
275         return psets;\r
276 }\r
277 \r
278 static void ASE_FreeGeomObject( int ndx )\r
279 {\r
280         aseGeomObject_t *pObject;\r
281         int i;\r
282 \r
283         pObject = &ase.objects[ndx];\r
284 \r
285         for ( i = 0; i < pObject->anim.numFrames; i++ )\r
286         {\r
287                 if ( pObject->anim.frames[i].vertexes )\r
288                 {\r
289                         free( pObject->anim.frames[i].vertexes );\r
290                 }\r
291                 if ( pObject->anim.frames[i].tvertexes )\r
292                 {\r
293                         free( pObject->anim.frames[i].tvertexes );\r
294                 }\r
295                 if ( pObject->anim.frames[i].faces )\r
296                 {\r
297                         free( pObject->anim.frames[i].faces );\r
298                 }\r
299                 if ( pObject->anim.frames[i].tfaces )\r
300                 {\r
301                         free( pObject->anim.frames[i].tfaces );\r
302                 }\r
303         }\r
304 \r
305         memset( pObject, 0, sizeof( *pObject ) );\r
306 }\r
307 \r
308 static aseMesh_t *ASE_GetCurrentMesh( void )\r
309 {\r
310         aseGeomObject_t *pObject;\r
311 \r
312         if ( ase.currentObject >= MAX_ASE_OBJECTS )\r
313         {\r
314                 Error( "Too many GEOMOBJECTs" );\r
315                 return 0; // never called\r
316         }\r
317 \r
318         pObject = &ase.objects[ase.currentObject];\r
319 \r
320         if ( pObject->anim.currentFrame >= MAX_ASE_ANIMATION_FRAMES )\r
321         {\r
322                 Error( "Too many MESHes" );\r
323                 return 0;\r
324         }\r
325 \r
326         return &pObject->anim.frames[pObject->anim.currentFrame];\r
327 }\r
328 \r
329 static int CharIsTokenDelimiter( int ch )\r
330 {\r
331         if ( ch <= 32 )\r
332                 return 1;\r
333         return 0;\r
334 }\r
335 \r
336 static int ASE_GetToken( qboolean restOfLine )\r
337 {\r
338         int i = 0;\r
339 \r
340         if ( ase.buffer == 0 )\r
341                 return 0;\r
342 \r
343         if ( ( ase.curpos - ase.buffer ) == ase.len )\r
344                 return 0;\r
345 \r
346         // skip over crap\r
347         while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&\r
348                     ( *ase.curpos <= 32 ) )\r
349         {\r
350                 ase.curpos++;\r
351         }\r
352 \r
353         while ( ( ase.curpos - ase.buffer ) < ase.len )\r
354         {\r
355                 s_token[i] = *ase.curpos;\r
356 \r
357                 ase.curpos++;\r
358                 i++;\r
359 \r
360                 if ( ( CharIsTokenDelimiter( s_token[i-1] ) && !restOfLine ) ||\r
361                          ( ( s_token[i-1] == '\n' ) || ( s_token[i-1] == '\r' ) ) )\r
362                 {\r
363                         s_token[i-1] = 0;\r
364                         break;\r
365                 }\r
366         }\r
367 \r
368         s_token[i] = 0;\r
369 \r
370         return 1;\r
371 }\r
372 \r
373 static void ASE_ParseBracedBlock( void (*parser)( const char *token ) )\r
374 {\r
375         int indent = 0;\r
376 \r
377         while ( ASE_GetToken( qfalse ) )\r
378         {\r
379                 if ( !strcmp( s_token, "{" ) )\r
380                 {\r
381                         indent++;\r
382                 }\r
383                 else if ( !strcmp( s_token, "}" ) )\r
384                 {\r
385                         --indent;\r
386                         if ( indent == 0 )\r
387                                 break;\r
388                         else if ( indent < 0 )\r
389                                 Error( "Unexpected '}'" );\r
390                 }\r
391                 else\r
392                 {\r
393                         if ( parser )\r
394                                 parser( s_token );\r
395                 }\r
396         }\r
397 }\r
398 \r
399 static void ASE_SkipEnclosingBraces( void )\r
400 {\r
401         int indent = 0;\r
402 \r
403         while ( ASE_GetToken( qfalse ) )\r
404         {\r
405                 if ( !strcmp( s_token, "{" ) )\r
406                 {\r
407                         indent++;\r
408                 }\r
409                 else if ( !strcmp( s_token, "}" ) )\r
410                 {\r
411                         indent--;\r
412                         if ( indent == 0 )\r
413                                 break;\r
414                         else if ( indent < 0 )\r
415                                 Error( "Unexpected '}'" );\r
416                 }\r
417         }\r
418 }\r
419 \r
420 static void ASE_SkipRestOfLine( void )\r
421 {\r
422         ASE_GetToken( qtrue );\r
423 }\r
424 \r
425 static void ASE_KeyMAP_DIFFUSE( const char *token )\r
426 {\r
427         char fullpath[1024], bitmap[1024], modeldir[1024];\r
428         char filename[1024];\r
429         int i = 0, count;\r
430 \r
431         strcpy(filename, gl_filename);\r
432 \r
433         if ( !strcmp( token, "*BITMAP" ) )\r
434         {\r
435                 ASE_GetToken( qfalse );\r
436 \r
437                 // the purpose of this whole chunk of code below is to extract the relative path\r
438                 // from a full path in the ASE\r
439                         \r
440                 strcpy( bitmap, s_token + 1 );\r
441                 if ( strchr( bitmap, '"' ) )\r
442                         *strchr( bitmap, '"' ) = 0;\r
443 \r
444                 /* convert backslash to slash */\r
445                 while ( bitmap[i] )\r
446                 {\r
447                         if ( bitmap[i] == '\\' )\r
448                                 bitmap[i] = '/';\r
449                         i++;\r
450                 }\r
451 \r
452                 /* remove filename from path */\r
453                 for( i=strlen(filename); i>0; i--)\r
454                 {\r
455                         if(filename[i] == '/')\r
456                         {\r
457                                 filename[i] = '\0';\r
458                                 break;\r
459                         }\r
460                 }\r
461 \r
462                 /* replaces a relative path with a full path */\r
463                 if(bitmap[0] == '.' && bitmap[1] == '.' && bitmap[2] == '/')\r
464                 {\r
465                         while(bitmap[0] == '.' && bitmap[1] == '.' && bitmap[2] == '/')\r
466                         {\r
467                                 /* remove last item from path */\r
468                                 for( i=strlen(filename); i>0; i--)\r
469                                 {\r
470                                         if(filename[i] == '/')\r
471                                         {\r
472                                                 filename[i] = '\0';\r
473                                                 break;\r
474                                         }\r
475                                 }\r
476                                 strcpy(bitmap, &bitmap[3]);\r
477                         }\r
478                         strcat(filename, "/");\r
479                         strcat(filename, bitmap);\r
480                         strcpy(bitmap, filename);\r
481                 }\r
482 \r
483                 if ( strstr( bitmap, gamedir ) )\r
484                 {\r
485                         strcpy( ase.materials[ase.numMaterials].name, strstr( bitmap, gamedir ) + strlen( gamedir ) );\r
486                         Sys_Printf("material name: \'%s\'\n", strstr( bitmap, gamedir ) + strlen( gamedir ) );\r
487                 }\r
488                 else\r
489                 {\r
490                         sprintf( ase.materials[ase.numMaterials].name, "(not converted: '%s')", bitmap );\r
491                         Sys_Printf( "WARNING: illegal material name '%s'\n", bitmap );\r
492                 }\r
493         }\r
494         else\r
495         {\r
496         }\r
497 }\r
498 \r
499 static void ASE_KeyMATERIAL( const char *token )\r
500 {\r
501         if ( !strcmp( token, "*MAP_DIFFUSE" ) )\r
502         {\r
503                 ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );\r
504         }\r
505         else\r
506         {\r
507         }\r
508 }\r
509 \r
510 static void ASE_KeyMATERIAL_LIST( const char *token )\r
511 {\r
512         if ( !strcmp( token, "*MATERIAL_COUNT" ) )\r
513         {\r
514                 ASE_GetToken( qfalse );\r
515                 VERBOSE( ( "..num materials: %s\n", s_token ) );\r
516                 if ( atoi( s_token ) > MAX_ASE_MATERIALS )\r
517                 {\r
518                         Error( "Too many materials!" );\r
519                 }\r
520                 ase.numMaterials = 0;\r
521         }\r
522         else if ( !strcmp( token, "*MATERIAL" ) )\r
523         {\r
524                 VERBOSE( ( "..material %d ", ase.numMaterials ) );\r
525                 ASE_ParseBracedBlock( ASE_KeyMATERIAL );\r
526                 ase.numMaterials++;\r
527         }\r
528 }\r
529 \r
530 static void ASE_KeyMESH_VERTEX_LIST( const char *token )\r
531 {\r
532         aseMesh_t *pMesh = ASE_GetCurrentMesh();\r
533 \r
534         if ( !strcmp( token, "*MESH_VERTEX" ) )\r
535         {\r
536                 ASE_GetToken( qfalse );         // skip number\r
537 \r
538                 ASE_GetToken( qfalse );\r
539                 pMesh->vertexes[pMesh->currentVertex].y = atof( s_token );\r
540 \r
541                 ASE_GetToken( qfalse );\r
542                 pMesh->vertexes[pMesh->currentVertex].x = -atof( s_token );\r
543 \r
544                 ASE_GetToken( qfalse );\r
545                 pMesh->vertexes[pMesh->currentVertex].z = atof( s_token );\r
546 \r
547                 pMesh->currentVertex++;\r
548 \r
549                 if ( pMesh->currentVertex > pMesh->numVertexes )\r
550                 {\r
551                         Error( "pMesh->currentVertex >= pMesh->numVertexes" );\r
552                 }\r
553         }\r
554         else\r
555         {\r
556                 Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );\r
557         }\r
558 }\r
559 \r
560 static void ASE_KeyMESH_FACE_LIST( const char *token )\r
561 {\r
562         aseMesh_t *pMesh = ASE_GetCurrentMesh();\r
563 \r
564         if ( !strcmp( token, "*MESH_FACE" ) )\r
565         {\r
566                 ASE_GetToken( qfalse ); // skip face number\r
567 \r
568                 ASE_GetToken( qfalse ); // skip label\r
569                 ASE_GetToken( qfalse ); // first vertex\r
570                 pMesh->faces[pMesh->currentFace][0] = atoi( s_token );\r
571 \r
572                 ASE_GetToken( qfalse ); // skip label\r
573                 ASE_GetToken( qfalse ); // second vertex\r
574                 pMesh->faces[pMesh->currentFace][2] = atoi( s_token );\r
575 \r
576                 ASE_GetToken( qfalse ); // skip label\r
577                 ASE_GetToken( qfalse ); // third vertex\r
578                 pMesh->faces[pMesh->currentFace][1] = atoi( s_token );\r
579 \r
580                 ASE_GetToken( qtrue );\r
581 \r
582 /*\r
583                 if ( ( p = strstr( s_token, "*MESH_MTLID" ) ) != 0 )\r
584                 {\r
585                         p += strlen( "*MESH_MTLID" ) + 1;\r
586                         mtlID = atoi( p );\r
587                 }\r
588                 else\r
589                 {\r
590                         Error( "No *MESH_MTLID found for face!" );\r
591                 }\r
592 */\r
593 \r
594                 pMesh->currentFace++;\r
595         }\r
596         else\r
597         {\r
598                 Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );\r
599         }\r
600 }\r
601 \r
602 static void ASE_KeyTFACE_LIST( const char *token )\r
603 {\r
604         aseMesh_t *pMesh = ASE_GetCurrentMesh();\r
605 \r
606         if ( !strcmp( token, "*MESH_TFACE" ) )\r
607         {\r
608                 int a, b, c;\r
609 \r
610                 ASE_GetToken( qfalse );\r
611 \r
612                 ASE_GetToken( qfalse );\r
613                 a = atoi( s_token );\r
614                 ASE_GetToken( qfalse );\r
615                 c = atoi( s_token );\r
616                 ASE_GetToken( qfalse );\r
617                 b = atoi( s_token );\r
618 \r
619                 pMesh->tfaces[pMesh->currentFace][0] = a;\r
620                 pMesh->tfaces[pMesh->currentFace][1] = b;\r
621                 pMesh->tfaces[pMesh->currentFace][2] = c;\r
622 \r
623                 pMesh->currentFace++;\r
624         }\r
625         else\r
626         {\r
627                 Error( "Unknown token '%s' in MESH_TFACE", token );\r
628         }\r
629 }\r
630 \r
631 static void ASE_KeyMESH_TVERTLIST( const char *token )\r
632 {\r
633         aseMesh_t *pMesh = ASE_GetCurrentMesh();\r
634 \r
635         if ( !strcmp( token, "*MESH_TVERT" ) )\r
636         {\r
637                 char u[80], v[80], w[80];\r
638 \r
639                 ASE_GetToken( qfalse );\r
640 \r
641                 ASE_GetToken( qfalse );\r
642                 strcpy( u, s_token );\r
643 \r
644                 ASE_GetToken( qfalse );\r
645                 strcpy( v, s_token );\r
646 \r
647                 ASE_GetToken( qfalse );\r
648                 strcpy( w, s_token );\r
649 \r
650                 pMesh->tvertexes[pMesh->currentVertex].s = atof( u );\r
651                 pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof( v );\r
652 \r
653                 pMesh->currentVertex++;\r
654 \r
655                 if ( pMesh->currentVertex > pMesh->numTVertexes )\r
656                 {\r
657                         Error( "pMesh->currentVertex > pMesh->numTVertexes" );\r
658                 }\r
659         }\r
660         else\r
661         {\r
662                 Error( "Unknown token '%s' while parsing MESH_TVERTLIST" );\r
663         }\r
664 }\r
665 \r
666 static void ASE_KeyMESH( const char *token )\r
667 {\r
668         aseMesh_t *pMesh = ASE_GetCurrentMesh();\r
669 \r
670         if ( !strcmp( token, "*TIMEVALUE" ) )\r
671         {\r
672                 ASE_GetToken( qfalse );\r
673 \r
674                 pMesh->timeValue = atoi( s_token );\r
675                 VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );\r
676         }\r
677         else if ( !strcmp( token, "*MESH_NUMVERTEX" ) )\r
678         {\r
679                 ASE_GetToken( qfalse );\r
680 \r
681                 pMesh->numVertexes = atoi( s_token );\r
682                 VERBOSE( ( ".....TIMEVALUE: %d\n", pMesh->timeValue ) );\r
683                 VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );\r
684         }\r
685         else if ( !strcmp( token, "*MESH_NUMFACES" ) )\r
686         {\r
687                 ASE_GetToken( qfalse );\r
688 \r
689                 pMesh->numFaces = atoi( s_token );\r
690                 VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );\r
691         }\r
692         else if ( !strcmp( token, "*MESH_NUMTVFACES" ) )\r
693         {\r
694                 ASE_GetToken( qfalse );\r
695 \r
696                 if ( atoi( s_token ) != pMesh->numFaces )\r
697                 {\r
698                         Error( "MESH_NUMTVFACES != MESH_NUMFACES" );\r
699                 }\r
700         }\r
701         else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) )\r
702         {\r
703                 ASE_GetToken( qfalse );\r
704 \r
705                 pMesh->numTVertexes = atoi( s_token );\r
706                 VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );\r
707         }\r
708         else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) )\r
709         {\r
710                 pMesh->vertexes = calloc( sizeof( aseVertex_t ) * pMesh->numVertexes, 1 );\r
711                 pMesh->currentVertex = 0;\r
712                 VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );\r
713                 ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );\r
714         }\r
715         else if ( !strcmp( token, "*MESH_TVERTLIST" ) )\r
716         {\r
717                 pMesh->currentVertex = 0;\r
718                 pMesh->tvertexes = calloc( sizeof( aseTVertex_t ) * pMesh->numTVertexes, 1 );\r
719                 VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );\r
720                 ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );\r
721         }\r
722         else if ( !strcmp( token, "*MESH_FACE_LIST" ) )\r
723         {\r
724                 pMesh->faces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );\r
725                 pMesh->currentFace = 0;\r
726                 VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );\r
727                 ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );\r
728         }\r
729         else if ( !strcmp( token, "*MESH_TFACELIST" ) )\r
730         {\r
731                 pMesh->tfaces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );\r
732                 pMesh->currentFace = 0;\r
733                 VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );\r
734                 ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );\r
735         }\r
736         else if ( !strcmp( token, "*MESH_NORMALS" ) )\r
737         {\r
738                 ASE_ParseBracedBlock( 0 );\r
739         }\r
740 }\r
741 \r
742 static void ASE_KeyMESH_ANIMATION( const char *token )\r
743 {\r
744         aseMesh_t *pMesh = ASE_GetCurrentMesh();\r
745 \r
746         // loads a single animation frame\r
747         if ( !strcmp( token, "*MESH" ) )\r
748         {\r
749                 VERBOSE( ( "...found MESH\n" ) );\r
750                 assert( pMesh->faces == 0 );\r
751                 assert( pMesh->vertexes == 0 );\r
752                 assert( pMesh->tvertexes == 0 );\r
753                 memset( pMesh, 0, sizeof( *pMesh ) );\r
754 \r
755                 ASE_ParseBracedBlock( ASE_KeyMESH );\r
756 \r
757                 if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES )\r
758                 {\r
759                         Error( "Too many animation frames" );\r
760                 }\r
761         }\r
762         else\r
763         {\r
764                 Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );\r
765         }\r
766 }\r
767 \r
768 static void ASE_KeyGEOMOBJECT( const char *token )\r
769 {\r
770         if ( !strcmp( token, "*NODE_NAME" ) )\r
771         {\r
772                 char *name = ase.objects[ase.currentObject].name;\r
773 \r
774                 ASE_GetToken( qtrue );\r
775                 VERBOSE( ( " %s\n", s_token ) );\r
776                 strcpy( ase.objects[ase.currentObject].name, s_token + 1 );\r
777                 if ( strchr( ase.objects[ase.currentObject].name, '"' ) )\r
778                         *strchr( ase.objects[ase.currentObject].name, '"' ) = 0;\r
779 \r
780                 if ( strstr( name, "tag" ) == name )\r
781                 {\r
782                         while ( strchr( name, '_' ) != strrchr( name, '_' ) )\r
783                         {\r
784                                 *strrchr( name, '_' ) = 0;\r
785                         }\r
786                         while ( strrchr( name, ' ' ) )\r
787                         {\r
788                                 *strrchr( name, ' ' ) = 0;\r
789                         }\r
790                 }\r
791         }\r
792         else if ( !strcmp( token, "*NODE_PARENT" ) )\r
793         {\r
794                 ASE_SkipRestOfLine();\r
795         }\r
796         // ignore unused data blocks\r
797         else if ( !strcmp( token, "*NODE_TM" ) ||\r
798                       !strcmp( token, "*TM_ANIMATION" ) )\r
799         {\r
800                 ASE_ParseBracedBlock( 0 );\r
801         }\r
802         // ignore regular meshes that aren't part of animation\r
803         else if ( !strcmp( token, "*MESH" ) && !ase.grabAnims )\r
804         {\r
805 /*\r
806                 if ( strstr( ase.objects[ase.currentObject].name, "tag_" ) == ase.objects[ase.currentObject].name ) \r
807                 {\r
808                         s_forceStaticMesh = true;\r
809                         ASE_ParseBracedBlock( ASE_KeyMESH );\r
810                         s_forceStaticMesh = false;\r
811                 }\r
812 */\r
813                 ASE_ParseBracedBlock( ASE_KeyMESH );\r
814                 if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES )\r
815                 {\r
816                         Error( "Too many animation frames" );\r
817                 }\r
818                 ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;\r
819                 ase.objects[ase.currentObject].numAnimations++;\r
820 /*\r
821                 // ignore meshes that aren't part of animations if this object isn't a \r
822                 // a tag\r
823                 else\r
824                 {\r
825                         ASE_ParseBracedBlock( 0 );\r
826                 }\r
827 */\r
828         }\r
829         // according to spec these are obsolete\r
830         else if ( !strcmp( token, "*MATERIAL_REF" ) )\r
831         {\r
832                 ASE_GetToken( qfalse );\r
833 \r
834                 ase.objects[ase.currentObject].materialRef = atoi( s_token );\r
835         }\r
836         // loads a sequence of animation frames\r
837         else if ( !strcmp( token, "*MESH_ANIMATION" ) )\r
838         {\r
839                 if ( ase.grabAnims )\r
840                 {\r
841                         VERBOSE( ( "..found MESH_ANIMATION\n" ) );\r
842 \r
843                         if ( ase.objects[ase.currentObject].numAnimations )\r
844                         {\r
845                                 Error( "Multiple MESH_ANIMATIONS within a single GEOM_OBJECT" );\r
846                         }\r
847                         ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );\r
848                         ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;\r
849                         ase.objects[ase.currentObject].numAnimations++;\r
850                 }\r
851                 else\r
852                 {\r
853                         ASE_SkipEnclosingBraces();\r
854                 }\r
855         }\r
856         // skip unused info\r
857         else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||\r
858                       !strcmp( token, "*PROP_CASTSHADOW" ) ||\r
859                           !strcmp( token, "*PROP_RECVSHADOW" ) )\r
860         {\r
861                 ASE_SkipRestOfLine();\r
862         }\r
863 }\r
864 \r
865 static void ConcatenateObjects( aseGeomObject_t *pObjA, aseGeomObject_t *pObjB )\r
866 {\r
867 }\r
868 \r
869 static void CollapseObjects( void )\r
870 {\r
871         int i;\r
872         int numObjects = ase.currentObject;\r
873 \r
874         for ( i = 0; i < numObjects; i++ )\r
875         {\r
876                 int j;\r
877 \r
878                 // skip tags\r
879                 if ( strstr( ase.objects[i].name, "tag" ) == ase.objects[i].name )\r
880                 {\r
881                         continue;\r
882                 }\r
883 \r
884                 if ( !ase.objects[i].numAnimations )\r
885                 {\r
886                         continue;\r
887                 }\r
888 \r
889                 for ( j = i + 1; j < numObjects; j++ )\r
890                 {\r
891                         if ( strstr( ase.objects[j].name, "tag" ) == ase.objects[j].name )\r
892                         {\r
893                                 continue;\r
894                         }\r
895                         if ( ase.objects[i].materialRef == ase.objects[j].materialRef )\r
896                         {\r
897                                 if ( ase.objects[j].numAnimations )\r
898                                 {\r
899                                         ConcatenateObjects( &ase.objects[i], &ase.objects[j] );\r
900                                 }\r
901                         }\r
902                 }\r
903         }\r
904 }\r
905 \r
906 /*\r
907 ** ASE_Process\r
908 */\r
909 static void ASE_Process( void )\r
910 {\r
911         while ( ASE_GetToken( qfalse ) )\r
912         {\r
913                 if ( !strcmp( s_token, "*3DSMAX_ASCIIEXPORT" ) ||\r
914                          !strcmp( s_token, "*COMMENT" ) )\r
915                 {\r
916                         ASE_SkipRestOfLine();\r
917                 }\r
918                 else if ( !strcmp( s_token, "*SCENE" ) )\r
919                         ASE_SkipEnclosingBraces();\r
920                 else if ( !strcmp( s_token, "*MATERIAL_LIST" ) )\r
921                 {\r
922                         VERBOSE( ("MATERIAL_LIST\n") );\r
923 \r
924                         ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );\r
925                 }\r
926                 else if ( !strcmp( s_token, "*GEOMOBJECT" ) )\r
927                 {\r
928                         VERBOSE( ("GEOMOBJECT" ) );\r
929 \r
930                         ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );\r
931 \r
932                         if ( strstr( ase.objects[ase.currentObject].name, "Bip" ) ||\r
933                                  strstr( ase.objects[ase.currentObject].name, "ignore_" ) )\r
934                         {\r
935                                 ASE_FreeGeomObject( ase.currentObject );\r
936                                 VERBOSE( ( "(discarding BIP/ignore object)\n" ) );\r
937                         }\r
938                         else if ( ( strstr( ase.objects[ase.currentObject].name, "h_" ) != ase.objects[ase.currentObject].name ) &&\r
939                                       ( strstr( ase.objects[ase.currentObject].name, "l_" ) != ase.objects[ase.currentObject].name ) &&\r
940                                           ( strstr( ase.objects[ase.currentObject].name, "u_" ) != ase.objects[ase.currentObject].name ) &&\r
941                                           ( strstr( ase.objects[ase.currentObject].name, "tag" ) != ase.objects[ase.currentObject].name ) &&\r
942                                           ase.grabAnims )\r
943                         {\r
944                                 VERBOSE( ( "(ignoring improperly labeled object '%s')\n", ase.objects[ase.currentObject].name ) );\r
945                                 ASE_FreeGeomObject( ase.currentObject );\r
946                         }\r
947                         else\r
948                         {\r
949                                 if ( ++ase.currentObject == MAX_ASE_OBJECTS )\r
950                                 {\r
951                                         Error( "Too many GEOMOBJECTs" );\r
952                                 }\r
953                         }\r
954                 }\r
955                 else if ( s_token[0] )\r
956                 {\r
957                         Sys_Printf( "Unknown token '%s'\n", s_token );\r
958                 }\r
959         }\r
960 \r
961         if ( !ase.currentObject )\r
962                 Error( "No animation data!" );\r
963 \r
964         CollapseObjects();\r
965 }\r