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