]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3data/models.c
* moved zeroradiant (1.6) into trunk
[xonotic/netradiant.git] / tools / quake3 / q3data / models.c
1 #include <assert.h>
2 #include "q3data.h"
3
4 //=================================================================
5
6 static void OrderSurfaces( void );
7 static void LoadBase( const char *filename );
8 static int      LoadModelFile( const char *filename, polyset_t **ppsets, int *pnumpolysets );
9
10 #define MAX_SURFACE_TRIS        (SHADER_MAX_INDEXES / 3)
11 #define MAX_SURFACE_VERTS       SHADER_MAX_VERTEXES
12
13 #define MD3_TYPE_UNKNOWN 0
14 #define MD3_TYPE_BASE3DS 1
15 #define MD3_TYPE_SPRITE  2
16 #define MD3_TYPE_ASE     3
17
18 #define MAX_ANIM_FRAMES         512
19 #define MAX_ANIM_SURFACES       32
20
21 typedef struct
22 {
23         polyset_t *frames;
24         int numFrames;
25 } SurfaceAnimation_t;
26
27 typedef struct
28 {
29         polyset_t *surfaces[MAX_ANIM_SURFACES];
30         int numSurfaces;
31 } ObjectAnimationFrame_t;
32
33 typedef struct {
34         vec3_t          xyz;
35         vec3_t          normal;
36         vec3_t          color;
37         float           st[2];
38         int                     index;
39 } baseVertex_t;
40         
41 typedef struct {
42         baseVertex_t    v[3];
43 } baseTriangle_t;
44
45 //================================================================
46
47 typedef struct
48 {
49         md3Surface_t    header;
50         md3Shader_t             shaders[MD3_MAX_SHADERS];
51         // all verts (xyz_normal)
52         float   *verts[MD3_MAX_FRAMES];
53
54         baseTriangle_t  baseTriangles[MD3_MAX_TRIANGLES];
55
56         // the triangles will be sorted so that they form long generalized tristrips
57         int                             orderedTriangles[MD3_MAX_TRIANGLES][3];
58         int                             lodTriangles[MD3_MAX_TRIANGLES][3];
59         baseVertex_t    baseVertexes[MD3_MAX_VERTS];
60
61 } md3SurfaceData_t;
62
63 typedef struct
64 {
65         int                     skinwidth, skinheight;
66         
67         md3SurfaceData_t surfData[MD3_MAX_SURFACES];
68
69         md3Tag_t                tags[MD3_MAX_FRAMES][MD3_MAX_TAGS];
70         md3Frame_t              frames[MD3_MAX_FRAMES];
71
72         md3Header_t     model;
73         float           scale_up;                       // set by $scale
74         vec3_t          adjust;                         // set by $origin
75         vec3_t          aseAdjust;
76         int                     fixedwidth, fixedheight;        // set by $skinsize
77
78         int                     maxSurfaceTris;
79
80         int                     lowerSkipFrameStart, lowerSkipFrameEnd;
81         int                     maxUpperFrames;
82         int                     maxHeadFrames;
83         int                     currentLod;
84         float           lodBias;
85
86         int                     type;           // MD3_TYPE_BASE, MD3_TYPE_OLDBASE, MD3_TYPE_ASE, or MD3_TYPE_SPRITE
87
88 } q3data;
89
90 q3data g_data;
91
92 // the command list holds counts, the count * 3 xyz, st, normal indexes
93 // that are valid for every frame
94 char            g_cddir[1024];
95 char            g_modelname[1024];
96
97 //==============================================================
98
99 /*
100 ===============
101 ClearModel
102 ===============
103 */
104 void ClearModel (void)
105 {
106         int i;
107
108         g_data.type = MD3_TYPE_UNKNOWN;
109
110         for ( i = 0; i < MD3_MAX_SURFACES; i++ )
111         {
112                 memset( &g_data.surfData[i].header, 0, sizeof( g_data.surfData[i].header ) );
113                 memset( &g_data.surfData[i].shaders, 0, sizeof( g_data.surfData[i].shaders ) );
114                 memset( &g_data.surfData[i].verts, 0, sizeof( g_data.surfData[i].verts ) );
115         }
116
117         memset( g_data.tags, 0, sizeof( g_data.tags ) );
118
119         for ( i = 0; i < g_data.model.numSurfaces; i++ )
120         {
121                 int j;
122
123                 for ( j = 0; j < g_data.surfData[i].header.numShaders; j++ )
124                 {
125                         memset( &g_data.surfData[i].shaders[j], 0, sizeof( g_data.surfData[i].shaders[j] ) );
126                 }
127         }
128         memset (&g_data.model, 0, sizeof(g_data.model));
129         memset (g_cddir, 0, sizeof(g_cddir));
130
131         g_modelname[0] = 0;
132         g_data.scale_up = 1.0;  
133         memset( &g_data.model, 0, sizeof( g_data.model ) );
134         VectorCopy (vec3_origin, g_data.adjust);
135         g_data.fixedwidth = g_data.fixedheight = 0;
136         g_skipmodel = qfalse;
137 }
138
139 /*
140 ** void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
141 **
142 ** This routine assumes that the file position has been adjusted
143 ** properly prior to entry to point at the beginning of the surface.
144 **
145 ** Since surface header information is completely relative, we can't
146 ** just randomly seek to an arbitrary surface location right now.  Is
147 ** this something we should add?
148 */
149 void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
150 {
151         md3Surface_t    *pSurf = &pSurfData->header;
152         md3Shader_t             *pShader = pSurfData->shaders;
153         baseVertex_t    *pBaseVertex = pSurfData->baseVertexes;
154         float                   **verts = pSurfData->verts;
155
156         short xyznormals[MD3_MAX_VERTS][4];
157
158         float base_st[MD3_MAX_VERTS][2];
159         md3Surface_t surftemp;
160
161         int f, i, j, k;
162
163         if ( strstr( pSurf->name, "tag_" ) == pSurf->name )
164                 return;
165
166         //
167         // write out the header
168         //
169         surftemp = *pSurf;
170         surftemp.ident = LittleLong( MD3_IDENT );
171         surftemp.flags = LittleLong( pSurf->flags );
172         surftemp.numFrames = LittleLong( pSurf->numFrames );
173         surftemp.numShaders = LittleLong( pSurf->numShaders );
174
175         surftemp.ofsShaders = LittleLong( pSurf->ofsShaders );
176
177         surftemp.ofsTriangles = LittleLong( pSurf->ofsTriangles );
178         surftemp.numTriangles = LittleLong( pSurf->numTriangles );
179
180         surftemp.ofsSt = LittleLong( pSurf->ofsSt );
181         surftemp.ofsXyzNormals = LittleLong( pSurf->ofsXyzNormals );
182         surftemp.ofsEnd = LittleLong( pSurf->ofsEnd );
183
184         SafeWrite( modelouthandle, &surftemp, sizeof( surftemp ) );
185
186         if ( g_verbose )
187         {
188                 printf( "surface '%s'\n", pSurf->name );
189                 printf( "...num shaders: %d\n", pSurf->numShaders );
190         }
191
192         //
193         // write out shaders
194         //
195         for ( i = 0; i < pSurf->numShaders; i++ )
196         {
197                 md3Shader_t shadertemp;
198
199                 if ( g_verbose )
200                         printf( "......'%s'\n", pShader[i].name );
201
202                 shadertemp = pShader[i];
203                 shadertemp.shaderIndex = LittleLong( shadertemp.shaderIndex );
204                 SafeWrite( modelouthandle, &shadertemp, sizeof( shadertemp ) );
205         }
206
207         //
208         // write out the triangles
209         //
210         for ( i = 0 ; i < pSurf->numTriangles ; i++ ) 
211         {
212                 for (j = 0 ; j < 3 ; j++) 
213                 {
214                         int ivalue = LittleLong( pSurfData->orderedTriangles[i][j] );
215                         pSurfData->orderedTriangles[i][j] = ivalue;
216                 }
217         }
218
219         SafeWrite( modelouthandle, pSurfData->orderedTriangles, pSurf->numTriangles * sizeof( g_data.surfData[0].orderedTriangles[0] ) );
220
221         if ( g_verbose )
222         {
223                 printf( "\n...num verts: %d\n", pSurf->numVerts );
224                 printf( "...TEX COORDINATES\n" );
225         }
226
227         //
228         // write out the texture coordinates
229         //
230         for ( i = 0; i < pSurf->numVerts ; i++) {
231                 base_st[i][0] = LittleFloat( pBaseVertex[i].st[0] );
232                 base_st[i][1] = LittleFloat( pBaseVertex[i].st[1] );
233                 if ( g_verbose )
234                         printf( "......%d: %f,%f\n", i, base_st[i][0], base_st[i][1] );
235         }
236         SafeWrite( modelouthandle, base_st, pSurf->numVerts * sizeof(base_st[0]));
237
238         //
239         // write the xyz_normal
240         //
241         if ( g_verbose )
242                 printf( "...XYZNORMALS\n" );
243         for ( f = 0; f < g_data.model.numFrames; f++ )
244         {
245                 for (j=0 ; j< pSurf->numVerts; j++) 
246                 {
247                         short value;
248
249                         for (k=0 ; k < 3 ; k++) 
250                         {
251                                 value = ( short ) ( verts[f][j*6+k] / MD3_XYZ_SCALE );
252                                 xyznormals[j][k] = LittleShort( value );
253                         }
254                         NormalToLatLong( &verts[f][j*6+3], (byte *)&xyznormals[j][3] );
255                 }
256                 SafeWrite( modelouthandle, xyznormals, pSurf->numVerts * sizeof( short ) * 4 );
257         }
258 }
259
260 /*
261 ** void WriteModelFile( FILE *modelouthandle )
262 **
263 ** CHUNK                        SIZE
264 ** header                       sizeof( md3Header_t )
265 ** frames                       sizeof( md3Frame_t ) * numFrames
266 ** tags                         sizeof( md3Tag_t ) * numFrames * numTags
267 ** surfaces                     surfaceSum
268 */
269 void WriteModelFile( FILE *modelouthandle )
270 {
271         int                             f;
272         int                             i, j;
273         md3Header_t             modeltemp;
274         long                    surfaceSum = 0;
275         int                             numRealSurfaces = 0;
276         int                             numFrames = g_data.model.numFrames;
277
278         // compute offsets for all surfaces, sum their total size
279         for ( i = 0; i < g_data.model.numSurfaces; i++ )
280         {
281                 if ( strstr( g_data.surfData[i].header.name, "tag_" ) != g_data.surfData[i].header.name )
282                 {
283                         md3Surface_t *psurf = &g_data.surfData[i].header;
284
285                         if ( psurf->numTriangles == 0 || psurf->numVerts == 0 )
286                                 continue;
287
288                         //
289                         // the triangle and vertex split threshold is controlled by a parameter
290                         // to $base, a la $base blah.3ds 1900, where "1900" determines the number
291                         // of triangles to split on
292                         //
293                         else if ( psurf->numVerts > MAX_SURFACE_VERTS )
294                         {
295                                 Error( "too many vertices\n" );
296                         }
297
298                         psurf->numFrames = numFrames;
299
300                         psurf->ofsShaders = sizeof( md3Surface_t );
301
302                         if ( psurf->numTriangles > MAX_SURFACE_TRIS  ) 
303                         {
304                                 Error( "too many faces\n" );
305                         }
306
307                         psurf->ofsTriangles = psurf->ofsShaders + psurf->numShaders * sizeof( md3Shader_t );
308
309                         psurf->ofsSt = psurf->ofsTriangles + psurf->numTriangles * sizeof( md3Triangle_t );
310                         psurf->ofsXyzNormals = psurf->ofsSt + psurf->numVerts * sizeof( md3St_t );
311                         psurf->ofsEnd = psurf->ofsXyzNormals + psurf->numFrames * psurf->numVerts * ( sizeof( short ) * 4 );
312
313                         surfaceSum += psurf->ofsEnd;
314
315                         numRealSurfaces++;
316                 }
317         }
318
319         g_data.model.ident = MD3_IDENT;
320         g_data.model.version = MD3_VERSION;
321
322         g_data.model.ofsFrames = sizeof(md3Header_t);
323         g_data.model.ofsTags = g_data.model.ofsFrames + numFrames*sizeof(md3Frame_t);
324         g_data.model.ofsSurfaces = g_data.model.ofsTags + numFrames*g_data.model.numTags*sizeof(md3Tag_t);
325         g_data.model.ofsEnd = g_data.model.ofsSurfaces + surfaceSum;
326
327         //
328         // write out the model header
329         //
330         modeltemp = g_data.model;
331         modeltemp.ident = LittleLong( modeltemp.ident );
332         modeltemp.version = LittleLong( modeltemp.version );
333         modeltemp.numFrames = LittleLong( modeltemp.numFrames );
334         modeltemp.numTags = LittleLong( modeltemp.numTags );
335         modeltemp.numSurfaces = LittleLong( numRealSurfaces );
336         modeltemp.ofsFrames = LittleLong( modeltemp.ofsFrames );
337         modeltemp.ofsTags = LittleLong( modeltemp.ofsTags );
338         modeltemp.ofsSurfaces = LittleLong( modeltemp.ofsSurfaces );
339         modeltemp.ofsEnd = LittleLong( modeltemp.ofsEnd );
340
341         SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
342
343         //
344         // write out the frames
345         //
346         for (i=0 ; i < numFrames ; i++) 
347         {
348                 vec3_t tmpVec;
349                 float maxRadius = 0;
350
351                 //
352                 // compute localOrigin and radius
353                 //
354                 g_data.frames[i].localOrigin[0] =
355                 g_data.frames[i].localOrigin[1] =
356                 g_data.frames[i].localOrigin[2] = 0;
357
358                 for ( j = 0; j < 8; j++ )
359                 {
360                         tmpVec[0] = g_data.frames[i].bounds[(j&1)!=0][0];
361                         tmpVec[1] = g_data.frames[i].bounds[(j&2)!=0][1];
362                         tmpVec[2] = g_data.frames[i].bounds[(j&4)!=0][2];
363
364                         if ( VectorLength( tmpVec ) > maxRadius )
365                                 maxRadius = VectorLength( tmpVec );
366                 }
367
368                 g_data.frames[i].radius = LittleFloat( maxRadius );
369
370                 // swap
371                 for (j=0 ; j<3 ; j++) {
372                         g_data.frames[i].bounds[0][j] = LittleFloat( g_data.frames[i].bounds[0][j] );
373                         g_data.frames[i].bounds[1][j] = LittleFloat( g_data.frames[i].bounds[1][j] );
374                         g_data.frames[i].localOrigin[j] = LittleFloat( g_data.frames[i].localOrigin[j] );
375                 }
376         }
377         fseek (modelouthandle, g_data.model.ofsFrames, SEEK_SET);
378         SafeWrite( modelouthandle, g_data.frames, numFrames * sizeof(g_data.frames[0]) );
379
380         //
381         // write out the tags
382         //
383         fseek( modelouthandle, g_data.model.ofsTags, SEEK_SET );
384         for (f=0 ; f<g_data.model.numFrames; f++) 
385         {
386                 int t;
387
388                 for ( t = 0; t < g_data.model.numTags; t++ )
389                 {
390                         g_data.tags[f][t].origin[0] = LittleFloat(g_data.tags[f][t].origin[0]);
391                         g_data.tags[f][t].origin[1] = LittleFloat(g_data.tags[f][t].origin[1]);
392                         g_data.tags[f][t].origin[2] = LittleFloat(g_data.tags[f][t].origin[2]);
393
394                         for (j=0 ; j<3 ; j++) 
395                         {
396                                 g_data.tags[f][t].axis[0][j] = LittleFloat(g_data.tags[f][t].axis[0][j]);
397                                 g_data.tags[f][t].axis[1][j] = LittleFloat(g_data.tags[f][t].axis[1][j]);
398                                 g_data.tags[f][t].axis[2][j] = LittleFloat(g_data.tags[f][t].axis[2][j]);
399                         }
400                 }
401                 SafeWrite( modelouthandle, g_data.tags[f], g_data.model.numTags * sizeof(md3Tag_t) );
402         }
403
404         //
405         // write out the surfaces
406         //
407         fseek( modelouthandle, g_data.model.ofsSurfaces, SEEK_SET );
408         for ( i = 0; i < g_data.model.numSurfaces; i++ )
409         {
410                 WriteModelSurface( modelouthandle, &g_data.surfData[i] );
411         }
412 }
413
414
415 /*
416 ===============
417 FinishModel
418 ===============
419 */
420 void FinishModel ( int type )
421 {
422         FILE            *modelouthandle;
423         FILE            *defaultSkinHandle;
424         char            name[1024];
425         int                     i;
426
427         if (!g_data.model.numFrames)
428                 return;
429
430         //
431         // build generalized triangle strips
432         //
433         OrderSurfaces();
434
435         if ( type == TYPE_PLAYER )
436         {
437                 sprintf( name, "%s%s", writedir, g_modelname );
438                 *strrchr( name, '.' ) = 0;
439                 strcat( name, "_default.skin" );
440
441                 defaultSkinHandle = fopen( name, "wt" );
442                 for ( i = 0; i < g_data.model.numSurfaces; i++ )
443                 {
444                         fprintf( defaultSkinHandle, "%s,%s\n", g_data.surfData[i].header.name, g_data.surfData[i].shaders[0].name );
445                 }
446                 fclose( defaultSkinHandle );
447         }
448
449         sprintf (name, "%s%s", writedir, g_modelname);
450
451         //
452         // copy the model and its shaders to release directory tree 
453         // if doing a release build
454         //
455         if ( g_release ) {
456                 int                     i, j;
457                 md3SurfaceData_t *pSurf;
458
459                 ReleaseFile( g_modelname );
460
461                 for ( i = 0; i < g_data.model.numSurfaces; i++ ) {
462                         pSurf = &g_data.surfData[i];
463                         for ( j = 0; j < g_data.model.numSkins; j++ ) {
464                                 ReleaseShader( pSurf->shaders[j].name );
465                         }
466                 }               
467                 return;
468         }
469         
470         //
471         // write the model output file
472         //
473         printf ("saving to %s\n", name);
474         CreatePath (name);
475         modelouthandle = SafeOpenWrite (name);
476
477         WriteModelFile (modelouthandle);
478         
479         printf ("%4d surfaces\n", g_data.model.numSurfaces);
480         printf ("%4d frames\n", g_data.model.numFrames);
481         printf ("%4d tags\n", g_data.model.numTags);
482         printf ("file size: %d\n", (int)ftell (modelouthandle) );
483         printf ("---------------------\n");
484         
485         fclose (modelouthandle);
486 }
487
488 /*
489 ** OrderSurfaces
490 **
491 ** Reorders triangles in all the surfaces.
492 */
493 static void OrderSurfaces( void )
494 {
495         int s;
496         extern qboolean g_stripify;
497
498         // go through each surface and find best strip/fans possible
499         for ( s = 0; s < g_data.model.numSurfaces; s++ )
500         {
501                 int mesh[MD3_MAX_TRIANGLES][3];
502                 int i;
503
504                 printf( "stripifying surface %d/%d with %d tris\n", s, g_data.model.numSurfaces, g_data.surfData[s].header.numTriangles );
505
506                 for ( i = 0; i < g_data.surfData[s].header.numTriangles; i++ )
507                 {
508                         mesh[i][0] = g_data.surfData[s].lodTriangles[i][0];
509                         mesh[i][1] = g_data.surfData[s].lodTriangles[i][1];
510                         mesh[i][2] = g_data.surfData[s].lodTriangles[i][2];
511                 }
512
513                 if ( g_stripify )
514                 {
515                         OrderMesh( mesh,                                                                        // input
516                                            g_data.surfData[s].orderedTriangles,         // output
517                                            g_data.surfData[s].header.numTriangles );
518                 }
519                 else
520                 {
521                         memcpy( g_data.surfData[s].orderedTriangles, mesh, sizeof( int ) * 3 * g_data.surfData[s].header.numTriangles );
522                 }
523         }
524 }
525
526
527 /*
528 ===============================================================
529
530 BASE FRAME SETUP
531
532 ===============================================================
533 */
534 /*
535 ============
536 CopyTrianglesToBaseTriangles
537
538 ============
539 */
540 static void CopyTrianglesToBaseTriangles(triangle_t *ptri, int numtri, baseTriangle_t *bTri )
541 {
542         int                     i;
543 //      int                     width, height, iwidth, iheight, swidth;
544 //      float           s_scale, t_scale;
545 //      float           scale;
546 //      vec3_t          mins, maxs;
547         float           *pbasevert;
548
549 /*
550         //
551         // find bounds of all the verts on the base frame
552         //
553         ClearBounds (mins, maxs);
554         
555         for (i=0 ; i<numtri ; i++)
556                 for (j=0 ; j<3 ; j++)
557                         AddPointToBounds (ptri[i].verts[j], mins, maxs);
558         
559         for (i=0 ; i<3 ; i++)
560         {
561                 mins[i] = floor(mins[i]);
562                 maxs[i] = ceil(maxs[i]);
563         }
564         
565         width = maxs[0] - mins[0];
566         height = maxs[2] - mins[2];
567
568         if (!g_data.fixedwidth)
569         {       // old style
570                 scale = 8;
571                 if (width*scale >= 150)
572                         scale = 150.0 / width;  
573                 if (height*scale >= 190)
574                         scale = 190.0 / height;
575
576                 s_scale = t_scale = scale;
577
578                 iwidth = ceil(width*s_scale);
579                 iheight = ceil(height*t_scale);
580
581                 iwidth += 4;
582                 iheight += 4;
583         }
584         else
585         {       // new style
586                 iwidth = g_data.fixedwidth / 2;
587                 iheight = g_data.fixedheight;
588
589                 s_scale = (float)(iwidth-4) / width;
590                 t_scale = (float)(iheight-4) / height;
591         }
592
593         // make the width a multiple of 4; some hardware requires this, and it ensures
594         // dword alignment for each scan
595         swidth = iwidth*2;
596         g_data.skinwidth = (swidth + 3) & ~3;
597         g_data.skinheight = iheight;
598 */
599
600         for (i=0; i<numtri ; i++, ptri++, bTri++)
601         {
602                 int j;
603
604                 for (j=0 ; j<3 ; j++) 
605                 {
606                         pbasevert = ptri->verts[j];
607
608                         VectorCopy( ptri->verts[j], bTri->v[j].xyz);
609                         VectorCopy( ptri->normals[j], bTri->v[j].normal );
610
611                         bTri->v[j].st[0] = ptri->texcoords[j][0];
612                         bTri->v[j].st[1] = ptri->texcoords[j][1];
613                 }
614         }
615 }
616
617 static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF )
618 {
619         baseTriangle_t  *bTri;
620         baseVertex_t    *bVert;
621         int i, j;
622
623         // calculate the base triangles
624         for ( i = 0; i < g_data.model.numSurfaces; i++ )
625         {
626                 CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles, 
627                                                         pOAF->surfaces[i]->numtriangles,
628                                                                         g_data.surfData[i].baseTriangles );
629
630                 strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name );
631
632                 g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles;
633                 g_data.surfData[i].header.numVerts = 0;
634
635 /*
636                 if ( strstr( filename, gamedir + 1 ) )
637                 {
638                         strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
639                 }
640                 else
641                 {
642                         strcpy( shaderName, filename );
643                 }
644
645                 if ( strrchr( shaderName, '/' ) )
646                         *( strrchr( shaderName, '/' ) + 1 ) = 0;
647
648
649                 strcpy( shaderName, pOAF->surfaces[i]->materialname );
650 */
651                 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname );
652
653                 g_data.surfData[i].header.numShaders++;
654         }
655
656         //
657         // compute unique vertices for each polyset
658         //
659         for ( i = 0; i < g_data.model.numSurfaces; i++ )
660         {
661                 int t;
662
663                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
664                 {
665                         bTri = &g_data.surfData[i].baseTriangles[t];
666
667                         for (j=0 ; j<3 ; j++)
668                         {
669                                 int k;
670
671                                 bVert = &bTri->v[j];
672
673                                 // get the xyz index
674                                 for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )
675                                 {
676                                         if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&
677                                                  ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&
678                                                  ( VectorCompare (bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz) ) &&
679                                                  ( VectorCompare (bVert->normal, g_data.surfData[i].baseVertexes[k].normal) ) )
680                                         {
681                                                 break;  // this vertex is already in the base vertex list
682                                         }
683                                 }
684
685                                 if (k == g_data.surfData[i].header.numVerts)    { // new index
686                                         g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;
687                                         g_data.surfData[i].header.numVerts++;
688                                 }
689
690                                 bVert->index = k;
691
692                                 g_data.surfData[i].lodTriangles[t][j] = k;
693                         }
694                 }
695         }
696
697         //
698         // find tags
699         //
700         for ( i = 0; i < g_data.model.numSurfaces; i++ )
701         {
702                 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )
703                 {
704                         if ( pOAF->surfaces[i]->numtriangles != 1 )
705                         {
706                                 Error( "tag polysets must consist of only one triangle" );
707                         }
708                         if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) )
709                                 continue;
710                         printf( "found tag '%s'\n", pOAF->surfaces[i]->name );
711                         g_data.model.numTags++;
712                 }
713         }
714
715 }
716
717 static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets )
718 {
719         int                     time1;
720         char            file1[1024];
721         const char                      *frameFile;
722
723         printf ("---------------------\n");
724         if ( filename[1] != ':' )
725         {
726                 frameFile = filename;
727                 sprintf( file1, "%s/%s", g_cddir, frameFile );
728         }
729         else
730         {
731                 strcpy( file1, filename );
732         }
733
734         time1 = FileTime (file1);
735         if (time1 == -1)
736                 Error ("%s doesn't exist", file1);
737
738         //
739         // load the base triangles
740         //
741         *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris );
742
743         //
744         // snap polysets
745         //
746         Polyset_SnapSets( *psets, *numpolysets );
747
748         if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) )
749                 return MD3_TYPE_BASE3DS;
750
751         Error( "Unknown model file type" );
752
753         return MD3_TYPE_UNKNOWN;
754 }
755
756 /*
757 =================
758 Cmd_Base
759 =================
760 */
761 void Cmd_Base( void )
762 {
763         char filename[1024];
764
765         GetToken( qfalse );
766         sprintf( filename, "%s/%s", g_cddir, token );
767         LoadBase( filename );
768 }
769
770 static void LoadBase( const char *filename )
771 {
772         int numpolysets;
773         polyset_t *psets;
774         int                     i;
775         ObjectAnimationFrame_t oaf;
776
777         // determine polyset splitting threshold
778         if ( TokenAvailable() )
779         {
780                 GetToken( qfalse );
781                 g_data.maxSurfaceTris = atoi( token );
782         }
783         else
784         {
785                 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
786         }
787
788         g_data.type = LoadModelFile( filename, &psets, &numpolysets );
789
790         Polyset_ComputeNormals( psets, numpolysets );
791
792         g_data.model.numSurfaces = numpolysets;
793
794         memset( &oaf, 0, sizeof( oaf ) );
795
796         for ( i = 0; i < numpolysets; i++ )
797         {
798                 oaf.surfaces[i] = &psets[i];
799                 oaf.numSurfaces = numpolysets;
800         }
801
802         BuildBaseFrame( filename, &oaf );
803
804         free( psets[0].triangles );
805         free( psets );
806 }
807
808 /*
809 =================
810 Cmd_SpriteBase
811
812 $spritebase xorg yorg width height
813
814 Generate a single square for the model
815 =================
816 */
817 void Cmd_SpriteBase (void)
818 {
819         float           xl, yl, width, height;
820
821         g_data.type = MD3_TYPE_SPRITE;
822
823         GetToken (qfalse);
824         xl = atof(token);
825         GetToken (qfalse);
826         yl = atof(token);
827         GetToken (qfalse);
828         width = atof(token);
829         GetToken (qfalse);
830         height = atof(token);
831
832 //      if (g_skipmodel || g_release || g_archive)
833 //              return;
834
835         printf ("---------------------\n");
836
837         g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 );
838
839         g_data.surfData[0].header.numVerts = 4;
840
841         g_data.surfData[0].verts[0][0+0] = 0;
842         g_data.surfData[0].verts[0][0+1] = -xl;
843         g_data.surfData[0].verts[0][0+2] = yl + height;
844
845         g_data.surfData[0].verts[0][0+3] = -1;
846         g_data.surfData[0].verts[0][0+4] = 0;
847         g_data.surfData[0].verts[0][0+5] = 0;
848         g_data.surfData[0].baseVertexes[0].st[0] = 0;
849         g_data.surfData[0].baseVertexes[0].st[1] = 0;
850
851
852         g_data.surfData[0].verts[0][6+0] = 0;
853         g_data.surfData[0].verts[0][6+1] = -xl - width;
854         g_data.surfData[0].verts[0][6+2] = yl + height;
855         
856         g_data.surfData[0].verts[0][6+3] = -1;
857         g_data.surfData[0].verts[0][6+4] = 0;
858         g_data.surfData[0].verts[0][6+5] = 0;
859         g_data.surfData[0].baseVertexes[1].st[0] = 1;
860         g_data.surfData[0].baseVertexes[1].st[1] = 0;
861
862
863         g_data.surfData[0].verts[0][12+0] = 0;
864         g_data.surfData[0].verts[0][12+1] = -xl - width;
865         g_data.surfData[0].verts[0][12+2] = yl;
866
867         g_data.surfData[0].verts[0][12+3] = -1;
868         g_data.surfData[0].verts[0][12+4] = 0;
869         g_data.surfData[0].verts[0][12+5] = 0;
870         g_data.surfData[0].baseVertexes[2].st[0] = 1;
871         g_data.surfData[0].baseVertexes[2].st[1] = 1;
872
873
874         g_data.surfData[0].verts[0][18+0] = 0;
875         g_data.surfData[0].verts[0][18+1] = -xl;
876         g_data.surfData[0].verts[0][18+2] = yl;
877
878         g_data.surfData[0].verts[0][18+3] = -1;
879         g_data.surfData[0].verts[0][18+4] = 0;
880         g_data.surfData[0].verts[0][18+5] = 0;
881         g_data.surfData[0].baseVertexes[3].st[0] = 0;
882         g_data.surfData[0].baseVertexes[3].st[1] = 1;
883
884         g_data.surfData[0].lodTriangles[0][0] = 0;
885         g_data.surfData[0].lodTriangles[0][1] = 1;
886         g_data.surfData[0].lodTriangles[0][2] = 2;
887
888         g_data.surfData[0].lodTriangles[1][0] = 2;
889         g_data.surfData[0].lodTriangles[1][1] = 3;
890         g_data.surfData[0].lodTriangles[1][2] = 0;
891
892         g_data.model.numSurfaces = 1;
893
894         g_data.surfData[0].header.numTriangles = 2;
895         g_data.surfData[0].header.numVerts = 4;
896
897         g_data.model.numFrames = 1;
898 }
899
900 /*
901 ===========================================================================
902
903   FRAME GRABBING
904
905 ===========================================================================
906 */
907
908 /*
909 ===============
910 GrabFrame
911 ===============
912 */
913 void GrabFrame (const char *frame)
914 {
915         int                     i, j, k;
916         char            file1[1024];
917         md3Frame_t              *fr;
918         md3Tag_t                tagParent;
919         float           *frameXyz;
920         float           *frameNormals;
921         const char      *framefile;
922         polyset_t               *psets;
923         qboolean         parentTagExists = qfalse;
924         int                      numpolysets;
925         int                     numtags = 0;
926         int                     tagcount;
927
928         // the frame 'run1' will be looked for as either
929         // run.1 or run1.tri, so the new alias sequence save
930         // feature an be used
931         if ( frame[1] != ':' )
932         {
933 //              framefile = FindFrameFile (frame);
934                 framefile = frame;
935                 sprintf (file1, "%s/%s",g_cddir, framefile);
936         }
937         else
938         {
939                 strcpy( file1, frame );
940         }
941         printf ("grabbing %s\n", file1);
942
943         if (g_data.model.numFrames >= MD3_MAX_FRAMES)
944                 Error ("model.numFrames >= MD3_MAX_FRAMES");
945         fr = &g_data.frames[g_data.model.numFrames];
946
947         strcpy (fr->name, frame);
948
949         psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris );
950
951         //
952         // snap polysets
953         //
954         Polyset_SnapSets( psets, numpolysets );
955
956         //
957         // compute vertex normals
958         //
959         Polyset_ComputeNormals( psets, numpolysets );
960
961         //
962         // flip everything to compensate for the alias coordinate system
963         // and perform global scale and adjust
964         //
965         for ( i = 0; i < g_data.model.numSurfaces; i++ )
966         {
967                 triangle_t *ptri = psets[i].triangles;
968                 int t;
969
970                 for ( t = 0; t < psets[i].numtriangles; t++ )
971                 {
972
973                         for ( j = 0; j < 3; j++ )
974                         {
975
976                                 // scale and adjust
977                                 for ( k = 0 ; k < 3 ; k++ ) {
978                                         ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up +
979                                                 g_data.adjust[k];
980
981                                         if ( ptri[t].verts[j][k] > 1023 ||
982                                                  ptri[t].verts[j][k] < -1023 )
983                                         {
984                                                 Error( "Model extents too large" );
985                                         }
986                                 }
987                         }
988                 }
989         }
990
991         //
992         // find and count tags, locate parent tag
993         //
994         for ( i = 0; i < numpolysets; i++ )
995         {
996                 if ( strstr( psets[i].name, "tag_" ) == psets[i].name )
997                 {
998                         if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name )
999                         {
1000                                 if ( strstr( psets[i].name, "tag_parent" ) )
1001                                 {
1002                                         float tri[3][3];
1003
1004                                         if ( parentTagExists )
1005                                                 Error( "Multiple parent tags not allowed" );
1006
1007                                         memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 );
1008                                         memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 );
1009                                         memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 );
1010
1011                                         MD3_ComputeTagFromTri( &tagParent, tri );
1012                                         strcpy( tagParent.name, psets[i].name );
1013                                         g_data.tags[g_data.model.numFrames][numtags] = tagParent;
1014                                         parentTagExists = qtrue;
1015
1016                                 }
1017                         }
1018                         numtags++;
1019                 }
1020
1021                 if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) )
1022                 {
1023                         Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, psets[i].name, g_modelname );
1024                 }
1025         }
1026
1027         if ( numtags != g_data.model.numTags )
1028         {
1029                 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1030         }
1031
1032         if ( numpolysets != g_data.model.numSurfaces )
1033         {
1034                 Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets-numtags, g_data.model.numSurfaces );
1035         }
1036         
1037         //
1038         // prepare to accumulate bounds and normals
1039         //
1040         ClearBounds( fr->bounds[0], fr->bounds[1] );
1041
1042         //
1043         // store the frame's vertices in the same order as the base. This assumes the
1044         // triangles and vertices in this frame are in exactly the same order as in the
1045         // base
1046         //
1047         for ( i = 0, tagcount = 0; i < numpolysets; i++ )
1048         {
1049                 int t;
1050                 triangle_t *pTris = psets[i].triangles;
1051
1052                 strcpy( g_data.surfData[i].header.name, psets[i].name );
1053
1054                 //
1055                 // parent tag adjust
1056                 //
1057                 if ( parentTagExists ) {
1058                         for ( t = 0; t < psets[i].numtriangles; t++ )
1059                         {
1060                                 for ( j = 0; j < 3 ; j++ )
1061                                 {
1062                                         vec3_t tmp;
1063                                         
1064                                         VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1065
1066                                         pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1067                                         pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1068                                         pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1069
1070                                         VectorCopy( pTris[t].normals[j], tmp );
1071                                         pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1072                                         pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1073                                         pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1074                                 }
1075                         }
1076                 }
1077
1078                 //
1079                 // compute tag data
1080                 //
1081                 if ( strstr( psets[i].name, "tag_" ) == psets[i].name )
1082                 {
1083                         md3Tag_t *pTag = &g_data.tags[g_data.model.numFrames][tagcount];
1084                         float tri[3][3];
1085
1086                         strcpy( pTag->name, psets[i].name );
1087
1088                         memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1089                         memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1090                         memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1091
1092                         MD3_ComputeTagFromTri( pTag, tri );
1093                         tagcount++;
1094                 }
1095                 else
1096                 {
1097                         if ( g_data.surfData[i].verts[g_data.model.numFrames] )
1098                                 free( g_data.surfData[i].verts[g_data.model.numFrames] );
1099                         frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1100                         frameNormals = frameXyz + 3;
1101
1102                         for ( t = 0; t < psets[i].numtriangles; t++ )
1103                         {
1104                                 for ( j = 0; j < 3 ; j++ )
1105                                 {
1106                                         int index;
1107
1108                                         index = g_data.surfData[i].baseTriangles[t].v[j].index;
1109                                         frameXyz[index*6+0] = pTris[t].verts[j][0];
1110                                         frameXyz[index*6+1] = pTris[t].verts[j][1];
1111                                         frameXyz[index*6+2] = pTris[t].verts[j][2];
1112                                         frameNormals[index*6+0] =  pTris[t].normals[j][0];
1113                                         frameNormals[index*6+1] =  pTris[t].normals[j][1];
1114                                         frameNormals[index*6+2] =  pTris[t].normals[j][2];
1115                                         AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] );
1116                                 }
1117                         }
1118                 }
1119         }
1120
1121         g_data.model.numFrames++;
1122
1123         // only free the first triangle array, all of the psets in this array share the
1124         // same triangle pool!!!
1125 //      free( psets[0].triangles );
1126 //      free( psets );
1127 }
1128
1129 //===========================================================================
1130
1131
1132
1133 /*
1134 ===============
1135 Cmd_Frame       
1136 ===============
1137 */
1138 void Cmd_Frame (void)
1139 {
1140         while (TokenAvailable())
1141         {
1142                 GetToken (qfalse);
1143                 if (g_skipmodel)
1144                         continue;
1145                 if (g_release || g_archive)
1146                 {
1147                         g_data.model.numFrames = 1;     // don't skip the writeout
1148                         continue;
1149                 }
1150
1151                 GrabFrame( token );
1152         }
1153 }
1154
1155
1156 /*
1157 ===============
1158 Cmd_Skin
1159
1160 ===============
1161 */
1162 void SkinFrom3DS( const char *filename )
1163 {
1164         polyset_t *psets;
1165         char name[1024];
1166         int numPolysets;
1167         int i;
1168
1169         _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose );
1170
1171         for ( i = 0; i < numPolysets; i++ )
1172         {
1173 /*
1174                 if ( strstr( filename, gamedir + 1 ) )
1175                 {
1176                         strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1177                 }
1178                 else
1179                 {
1180                         strcpy( name, filename );
1181                 }
1182
1183                 if ( strrchr( name, '/' ) )
1184                         *( strrchr( name, '/' ) + 1 ) = 0;
1185 */
1186                 strcpy( name, psets[i].materialname );
1187                 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name );
1188
1189                 g_data.surfData[i].header.numShaders++;
1190         }
1191
1192         free( psets[0].triangles );
1193         free( psets );
1194 }
1195
1196 void Cmd_Skin (void)
1197 {
1198         char skinfile[1024];
1199
1200         if ( g_data.type == MD3_TYPE_BASE3DS )
1201         {
1202                 GetToken( qfalse );
1203
1204                 sprintf( skinfile, "%s/%s", g_cddir, token );
1205
1206                 if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) )
1207                 {
1208                         SkinFrom3DS( skinfile );
1209                 }
1210                 else
1211                 {
1212                         Error( "Unknown file format for $skin '%s'\n", skinfile );
1213                 }
1214         }
1215         else
1216         {
1217                 Error( "invalid model type while processing $skin" );
1218         }
1219
1220         g_data.model.numSkins++;
1221 }
1222
1223 /*
1224 =================
1225 Cmd_SpriteShader
1226 =================
1227
1228 This routine is also called for $oldskin
1229
1230 */
1231 void Cmd_SpriteShader()
1232 {
1233         GetToken( qfalse );
1234         strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token );
1235         g_data.surfData[0].header.numShaders++;
1236         g_data.model.numSkins++;
1237 }
1238
1239 /*
1240 =================
1241 Cmd_Origin
1242 =================
1243 */
1244 void Cmd_Origin (void)
1245 {
1246         // rotate points into frame of reference so model points down the
1247         // positive x axis
1248         // FIXME: use alias native coordinate system
1249         GetToken (qfalse);
1250         g_data.adjust[1] = -atof (token);
1251
1252         GetToken (qfalse);
1253         g_data.adjust[0] = atof (token);
1254
1255         GetToken (qfalse);
1256         g_data.adjust[2] = -atof (token);
1257 }
1258
1259
1260 /*
1261 =================
1262 Cmd_ScaleUp
1263 =================
1264 */
1265 void Cmd_ScaleUp (void)
1266 {
1267         GetToken (qfalse);
1268         g_data.scale_up = atof (token);
1269         if (g_skipmodel || g_release || g_archive)
1270                 return;
1271
1272         printf ("Scale up: %f\n", g_data.scale_up);
1273 }
1274
1275
1276 /*
1277 =================
1278 Cmd_Skinsize
1279
1280 Set a skin size other than the default
1281 QUAKE3: not needed
1282 =================
1283 */
1284 void Cmd_Skinsize (void)
1285 {
1286         GetToken (qfalse);
1287         g_data.fixedwidth = atoi(token);
1288         GetToken (qfalse);
1289         g_data.fixedheight = atoi(token);
1290 }
1291
1292 /*
1293 =================
1294 Cmd_Modelname
1295
1296 Begin creating a model of the given name
1297 =================
1298 */
1299 void Cmd_Modelname (void)
1300 {
1301         FinishModel ( TYPE_UNKNOWN );
1302         ClearModel ();
1303
1304         GetToken (qfalse);
1305         strcpy (g_modelname, token);
1306         StripExtension (g_modelname);
1307         strcat (g_modelname, ".md3");
1308         strcpy (g_data.model.name, g_modelname);
1309 }
1310
1311 /*
1312 ===============
1313 fCmd_Cd
1314 ===============
1315 */
1316 void Cmd_Cd (void)
1317 {
1318         if ( g_cddir[0]) {
1319                 Error ("$cd command without a $modelname");
1320         }
1321
1322         GetToken (qfalse);
1323
1324         sprintf ( g_cddir, "%s%s", gamedir, token);
1325
1326         // if -only was specified and this cd doesn't match,
1327         // skip the model (you only need to match leading chars,
1328         // so you could regrab all monsters with -only models/monsters)
1329         if (!g_only[0])
1330                 return;
1331         if (strncmp(token, g_only, strlen(g_only)))
1332         {
1333                 g_skipmodel = qtrue;
1334                 printf ("skipping %s\n", token);
1335         }
1336 }
1337
1338 void Convert3DStoMD3( const char *file )
1339 {
1340         LoadBase( file );
1341         GrabFrame( file );
1342         SkinFrom3DS( file );
1343
1344         strcpy( g_data.model.name, g_modelname );
1345
1346         FinishModel( TYPE_UNKNOWN );
1347         ClearModel();
1348 }
1349
1350 /*
1351 ** Cmd_3DSConvert
1352 */
1353 void Cmd_3DSConvert()
1354 {
1355         char file[1024];
1356
1357         FinishModel( TYPE_UNKNOWN );
1358         ClearModel();
1359
1360         GetToken( qfalse );
1361
1362         sprintf( file, "%s%s", gamedir, token );
1363         strcpy( g_modelname, token );
1364         if ( strrchr( g_modelname, '.' ) )
1365                 *strrchr( g_modelname, '.' ) = 0;
1366         strcat( g_modelname, ".md3" );
1367
1368         if ( FileTime( file ) == -1 )
1369                 Error( "%s doesn't exist", file );
1370
1371         if ( TokenAvailable() )
1372         {
1373                 GetToken( qfalse );
1374                 g_data.scale_up = atof( token );
1375         }
1376
1377         Convert3DStoMD3( file );
1378 }
1379
1380 static void ConvertASE( const char *filename, int type, qboolean grabAnims );
1381
1382 /*
1383 ** Cmd_ASEConvert
1384 */
1385 void Cmd_ASEConvert( qboolean grabAnims )
1386 {
1387         char filename[1024];
1388         int type = TYPE_ITEM;
1389
1390         FinishModel( TYPE_UNKNOWN );
1391         ClearModel();
1392
1393         GetToken( qfalse );
1394         sprintf( filename, "%s%s", gamedir, token );
1395
1396         strcpy (g_modelname, token);
1397         StripExtension (g_modelname);
1398         strcat (g_modelname, ".md3");
1399         strcpy (g_data.model.name, g_modelname);
1400
1401         if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) )
1402                 strcat( filename, ".ASE" );
1403
1404         g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
1405
1406         while ( TokenAvailable() )
1407         {
1408                 GetToken( qfalse );
1409                 if ( !strcmp( token, "-origin" ) )
1410                 {
1411                         if ( !TokenAvailable() )
1412                                 Error( "missing parameter for -origin" );
1413                         GetToken( qfalse );
1414                         g_data.aseAdjust[1] = -atof( token );
1415
1416                         if ( !TokenAvailable() )
1417                                 Error( "missing parameter for -origin" );
1418                         GetToken( qfalse );
1419                         g_data.aseAdjust[0] = atof (token);
1420
1421                         if ( !TokenAvailable() )
1422                                 Error( "missing parameter for -origin" );
1423                         GetToken( qfalse );
1424                         g_data.aseAdjust[2] = -atof (token);
1425                 }
1426                 else if ( !strcmp( token, "-lod" ) )
1427                 {
1428                         if ( !TokenAvailable() )
1429                                 Error( "No parameter for -lod" );
1430                         GetToken( qfalse );
1431                         g_data.currentLod = atoi( token );
1432                         if ( g_data.currentLod > MD3_MAX_LODS - 1 )
1433                         {
1434                                 Error( "-lod parameter too large! (%d)\n", g_data.currentLod );
1435                         }
1436
1437                         if ( !TokenAvailable() )
1438                                 Error( "No second parameter for -lod" );
1439                         GetToken( qfalse );
1440                         g_data.lodBias = atof( token );
1441                 }
1442                 else if ( !strcmp( token, "-maxtris" ) )
1443                 {
1444                         if ( !TokenAvailable() )
1445                                 Error( "No parameter for -maxtris" );
1446                         GetToken( qfalse );
1447                         g_data.maxSurfaceTris = atoi( token );
1448                 }
1449                 else if ( !strcmp( token, "-playerparms" ) )
1450                 {
1451                         if ( !TokenAvailable() )
1452                                 Error( "missing skip start parameter for -playerparms" );
1453                         GetToken( qfalse );
1454                         g_data.lowerSkipFrameStart = atoi( token );
1455
1456 #if 0
1457                         if ( !TokenAvailable() )
1458                                 Error( "missing skip end parameter for -playerparms" );
1459                         GetToken( qfalse );
1460                         g_data.lowerSkipFrameEnd = atoi( token );
1461 #endif
1462
1463                         if ( !TokenAvailable() )
1464                                 Error( "missing upper parameter for -playerparms" );
1465                         GetToken( qfalse );
1466                         g_data.maxUpperFrames = atoi( token );
1467
1468                         g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1;
1469
1470 #if 0
1471                         if ( !TokenAvailable() )
1472                                 Error( "missing head parameter for -playerparms" );
1473                         GetToken( qfalse );
1474                         g_data.maxHeadFrames = atoi( token );
1475 #endif
1476                         g_data.maxHeadFrames = 1;
1477
1478                         if ( type != TYPE_ITEM )
1479                                 Error( "invalid argument" );
1480
1481                         type = TYPE_PLAYER;
1482                 }
1483                 else if ( !strcmp( token, "-weapon" ) )
1484                 {
1485                         if ( type != TYPE_ITEM )
1486                                 Error( "invalid argument" );
1487
1488                         type = TYPE_WEAPON;
1489                 }
1490         }
1491
1492         g_data.type = MD3_TYPE_ASE;
1493
1494         if ( type == TYPE_WEAPON && grabAnims )
1495         {
1496                 Error( "can't grab anims with weapon models" );
1497         }
1498         if ( type == TYPE_PLAYER && !grabAnims )
1499         {
1500                 Error( "player models must be converted with $aseanimconvert" );
1501         }
1502
1503         if ( type == TYPE_WEAPON )
1504         {
1505                 ConvertASE( filename, type, qfalse );
1506                 ConvertASE( filename, TYPE_HAND, qtrue );
1507         }
1508         else
1509         {
1510                 ConvertASE( filename, type, grabAnims );
1511         }
1512 }
1513
1514 static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES], 
1515                                                                   const char *part,
1516                                                                   int skipFrameStart,
1517                                                                   int skipFrameEnd,
1518                                                                   int maxFrames )
1519
1520 {
1521         int numSurfaces;
1522         int numValidSurfaces;
1523         int i;
1524         int numFrames = -1;
1525
1526         if ( ( numSurfaces = ASE_GetNumSurfaces() ) > MAX_ANIM_SURFACES )
1527         {
1528                 Error( "Too many surfaces in ASE" );
1529         }
1530
1531         for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ )
1532         {
1533                 polyset_t *splitSets;
1534                 int numNewFrames;
1535                 const char *surfaceName = ASE_GetSurfaceName( i );
1536
1537                 if ( !surfaceName )
1538                 {
1539                         continue;
1540 //                      Error( "Missing animation frames in model" );
1541                 }
1542
1543                 if ( strstr( surfaceName, "tag_" ) || 
1544                          !strcmp( part, "any" ) || 
1545                          ( strstr( surfaceName, part ) == surfaceName ) )
1546                 {
1547
1548                         // skip this if it's an inappropriate tag
1549                         if ( strcmp( part, "any" ) )
1550                         {
1551                                 // ignore non-"tag_head" tags if this is the head
1552                                 if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_head" ) )
1553                                         continue;
1554                                 // ignore "tag_head" if this is the legs
1555                                 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) )
1556                                         continue;
1557                                 // ignore "tag_weapon" if this is the legs
1558                                 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) )
1559                                         continue;
1560                         }
1561
1562                         if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 )
1563                         {
1564                                 splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris );
1565                                 
1566                                 if ( numFrames == -1 )
1567                                         numFrames = sanims[numValidSurfaces].numFrames;
1568                                 else if ( numFrames != sanims[numValidSurfaces].numFrames )
1569                                         Error( "Different number of animation frames on surfaces" );
1570                                 
1571                                 if ( sanims[numValidSurfaces].frames != splitSets )
1572                                 {
1573                                         int j;
1574
1575                                         // free old data if we split the surfaces
1576                                         for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ )
1577                                         {
1578                                                 free( sanims[numValidSurfaces].frames[j].triangles );
1579                                                 free( sanims[numValidSurfaces].frames );
1580                                         }
1581                                         
1582                                         sanims[numValidSurfaces].frames = splitSets;
1583                                         sanims[numValidSurfaces].numFrames = numNewFrames;
1584                                 }
1585                                 Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1586                                 Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1587
1588                                 numValidSurfaces++;
1589                         }
1590                 }
1591         }
1592
1593         return numValidSurfaces;
1594 }
1595
1596 static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces )
1597 {
1598         int i, s;
1599         int numFrames = -1;
1600
1601         /*
1602         ** we have the data here arranged in surface order, now we need to convert it to 
1603         ** frame order
1604         */
1605         for ( i = 0, s = 0; i < numSurfaces; i++ )
1606         {
1607                 int j;
1608                 
1609                 if ( sanims[i].frames )
1610                 {
1611                         if ( numFrames == -1 )
1612                                 numFrames = sanims[i].numFrames;
1613                         else if ( numFrames != sanims[i].numFrames )
1614                                 Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames );
1615
1616                         for ( j = 0; j < sanims[i].numFrames; j++ )
1617                         {
1618                                 oanims[j].surfaces[s] = &sanims[i].frames[j];
1619                                 oanims[j].numSurfaces = numSurfaces;
1620                         }
1621                         s++;
1622                 }
1623         }
1624
1625         return numFrames;
1626 }
1627
1628 static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames )
1629 {
1630         char filename[1024];
1631
1632         strcpy( filename, _filename );
1633         if ( strchr( filename, '.' ) )
1634                 *strchr( filename, '.' ) = 0;
1635         strcat( filename, ".md3" );
1636 }
1637
1638 static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type )
1639 {
1640         int f, i, j, tagcount;
1641         float *frameXyz;
1642         float *frameNormals;
1643
1644         g_data.model.numSurfaces = oanims[0].numSurfaces;
1645         g_data.model.numFrames = numFrames;
1646         if ( g_data.model.numFrames < 0)
1647                 Error ("model.numFrames < 0");
1648         if ( g_data.model.numFrames >= MD3_MAX_FRAMES)
1649                 Error ("model.numFrames >= MD3_MAX_FRAMES");
1650
1651         // build base frame
1652         BuildBaseFrame( filename, &oanims[0] );
1653         
1654         // build animation frames
1655         for ( f = 0; f < numFrames; f++ )
1656         {
1657                 ObjectAnimationFrame_t *pOAF = &oanims[f];
1658                 qboolean        parentTagExists = qfalse;
1659                 md3Tag_t        tagParent;
1660                 int                     numtags = 0;
1661                 md3Frame_t              *fr;
1662                 
1663                 fr = &g_data.frames[f];
1664                 
1665                 strcpy( fr->name, "(from ASE)" );
1666                 
1667                 // scale and adjust frame
1668                 for ( i = 0; i < pOAF->numSurfaces; i++ )
1669                 {
1670                         triangle_t *pTris = pOAF->surfaces[i]->triangles;
1671                         int t;
1672                         
1673                         for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1674                         {
1675                                 for ( j = 0; j < 3; j++ )
1676                                 {
1677                                         int k;
1678                                         
1679                                         // scale and adjust
1680                                         for ( k = 0 ; k < 3 ; k++ ) {
1681                                                 pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up +
1682                                                         g_data.aseAdjust[k];
1683                                                 
1684                                                 if ( pTris[t].verts[j][k] > 1023 ||
1685                                                         pTris[t].verts[j][k] < -1023 )
1686                                                 {
1687                                                         Error( "Model extents too large" );
1688                                                 }
1689                                         }
1690                                 }
1691                         }
1692                 }
1693                 
1694                 //
1695                 // find and count tags, locate parent tag
1696                 //
1697                 for ( i = 0; i < pOAF->numSurfaces; i++ )
1698                 {
1699                         if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )
1700                         {
1701                                 // ignore parent tags when grabbing a weapon model and this is the flash portion
1702                                 if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_flash.md3" ) )
1703                                 {
1704                                         continue;
1705                                 }
1706                                 else if ( !strstr( filename, "_hand.md3" ) && (
1707                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && !strstr( filename, "_flash.md3" ) ) ||
1708                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && ( strstr( filename, "upper_" ) || strstr( filename, "upper.md3" ) ) ) ||
1709                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && ( strstr( filename, "head.md3" ) || strstr( filename, "head_" ) ) ) ||
1710                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash.md3" ) )|| 
1711                                          ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) )
1712                                 {
1713                                         float tri[3][3];
1714                                         
1715                                         if ( parentTagExists )
1716                                                 Error( "Multiple parent tags not allowed" );
1717                                         
1718                                         memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1719                                         memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1720                                         memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1721                                         
1722                                         MD3_ComputeTagFromTri( &tagParent, tri );
1723                                         strcpy( tagParent.name, "tag_parent" );
1724                                         g_data.tags[f][numtags] = tagParent;
1725                                         parentTagExists = qtrue;
1726                                 }
1727                                 else
1728                                 {
1729                                         float tri[3][3];
1730                                 
1731                                         memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1732                                         memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1733                                         memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1734                                         
1735                                         MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri );
1736                                         strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name );
1737                                         if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) )
1738                                                 * ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0;
1739                                 }
1740
1741                                 numtags++;
1742                         }
1743                         
1744                         if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) )
1745                         {
1746                                 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename );
1747                         }
1748                 }
1749                 
1750                 if ( numtags != g_data.model.numTags )
1751                 {
1752                         Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1753                 }
1754                 
1755                 //
1756                 // prepare to accumulate bounds and normals
1757                 //
1758                 ClearBounds( fr->bounds[0], fr->bounds[1] );
1759                 
1760                 //
1761                 // store the frame's vertices in the same order as the base. This assumes the
1762                 // triangles and vertices in this frame are in exactly the same order as in the
1763                 // base
1764                 //
1765                 for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ )
1766                 {
1767                         int t;
1768                         triangle_t *pTris = pOAF->surfaces[i]->triangles;
1769                         
1770                         //
1771                         // parent tag adjust
1772                         //
1773                         if ( parentTagExists ) 
1774                         {
1775                                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1776                                 {
1777                                         for ( j = 0; j < 3 ; j++ )
1778                                         {
1779                                                 vec3_t tmp;
1780                                                 
1781                                                 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1782                                                 
1783                                                 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1784                                                 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1785                                                 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1786                                                 
1787                                                 VectorCopy( pTris[t].normals[j], tmp );
1788                                                 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1789                                                 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1790                                                 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1791                                         }
1792                                 }
1793                         }
1794                         
1795                         //
1796                         // compute tag data
1797                         //
1798                         if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name )
1799                         {
1800                                 md3Tag_t *pTag = &g_data.tags[f][tagcount];
1801                                 float tri[3][3];
1802                                 
1803                                 strcpy( pTag->name, pOAF->surfaces[i]->name );
1804                                 
1805                                 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1806                                 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1807                                 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1808                                 
1809                                 MD3_ComputeTagFromTri( pTag, tri );
1810                                 tagcount++;
1811                         }
1812                         else
1813                         {
1814                                 if ( g_data.surfData[i].verts[f] )
1815                                         free( g_data.surfData[i].verts[f] );
1816                                 frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1817                                 frameNormals = frameXyz + 3;
1818                                 
1819                                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1820                                 {
1821                                         for ( j = 0; j < 3 ; j++ )
1822                                         {
1823                                                 int index;
1824                                                 
1825                                                 index = g_data.surfData[i].baseTriangles[t].v[j].index;
1826                                                 frameXyz[index*6+0] = pTris[t].verts[j][0];
1827                                                 frameXyz[index*6+1] = pTris[t].verts[j][1];
1828                                                 frameXyz[index*6+2] = pTris[t].verts[j][2];
1829                                                 frameNormals[index*6+0] =  pTris[t].normals[j][0];
1830                                                 frameNormals[index*6+1] =  pTris[t].normals[j][1];
1831                                                 frameNormals[index*6+2] =  pTris[t].normals[j][2];
1832                                                 AddPointToBounds (&frameXyz[index*6], fr->bounds[0], fr->bounds[1] );
1833                                         }
1834                                 }
1835                         }
1836                 }
1837         }
1838
1839         if ( strstr( filename, gamedir + 1 ) )
1840         {
1841                 strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1842         }
1843         else
1844         {
1845                 strcpy( g_modelname, filename );
1846         }
1847
1848         FinishModel( type );
1849         ClearModel();
1850 }
1851
1852 static void ConvertASE( const char *filename, int type, qboolean grabAnims )
1853 {
1854         int i, j;
1855         int numSurfaces;
1856         int numFrames = -1;
1857         SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES];
1858         ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES];
1859         char outfilename[1024];
1860
1861         /*
1862         ** load ASE into memory
1863         */
1864         ASE_Load( filename, g_verbose, grabAnims );
1865
1866         /*
1867         ** process parts
1868         */
1869         if ( type == TYPE_ITEM )
1870         {
1871                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 );
1872
1873                 if ( numSurfaces <= 0 )
1874                         Error( "numSurfaces <= 0" );
1875
1876                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1877
1878                 if ( numFrames <= 0 )
1879                         Error( "numFrames <= 0" );
1880
1881                 strcpy( outfilename, filename );
1882                 if ( strrchr( outfilename, '.' ) )
1883                         *( strrchr( outfilename, '.' ) + 1 ) = 0;
1884                 strcat( outfilename, "md3" );
1885                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1886
1887                 // free memory
1888                 for ( i = 0; i < numSurfaces; i++ )
1889                 {
1890                         if ( surfaceAnimations[i].frames )
1891                         {
1892                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1893                                 {
1894                                         free( surfaceAnimations[i].frames[j].triangles );
1895                                 }
1896                                 free( surfaceAnimations[i].frames );
1897                                 surfaceAnimations[i].frames = 0;
1898                         }
1899                 }
1900         }
1901         else if ( type == TYPE_PLAYER )
1902         {
1903                 qboolean tagTorso = qfalse;
1904                 qboolean tagHead = qfalse;
1905                 qboolean tagWeapon = qfalse;
1906
1907                 //
1908                 // verify that all necessary tags exist
1909                 //
1910                 numSurfaces = ASE_GetNumSurfaces();
1911                 for ( i = 0; i < numSurfaces; i++ )
1912                 {
1913                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_head" ) )
1914                         {
1915                                 tagHead = qtrue;
1916                         }
1917                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_torso" ) )
1918                         {
1919                                 tagTorso = qtrue;
1920                         }
1921                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_weapon" ) )
1922                         {
1923                                 tagWeapon = qtrue;
1924                         }
1925                 }
1926
1927                 if ( !tagWeapon )
1928                 {
1929                         Error( "Missing tag_weapon!" );
1930                 }
1931                 if ( !tagTorso )
1932                 {
1933                         Error( "Missing tag_torso!" );
1934                 }
1935                 if ( !tagWeapon )
1936                 {
1937                         Error( "Missing tag_weapon!" );
1938                 }
1939
1940                 // get all upper body surfaces
1941                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames );
1942                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1943                 strcpy( outfilename, filename );
1944                 if ( strrchr( outfilename, '/' ) )
1945                         *( strrchr( outfilename, '/' ) + 1 ) = 0;
1946
1947                 if ( g_data.currentLod == 0 )
1948                 {
1949                         strcat( outfilename, "upper.md3" );
1950                 }
1951                 else
1952                 {
1953                         char temp[128];
1954
1955                         sprintf( temp, "upper_%d.md3", g_data.currentLod );
1956                         strcat( outfilename, temp );
1957                 }
1958                 
1959                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1960
1961                 // free memory
1962                 for ( i = 0; i < numSurfaces; i++ )
1963                 {
1964                         if ( surfaceAnimations[i].frames )
1965                         {
1966                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1967                                 {
1968                                         free( surfaceAnimations[i].frames[j].triangles );
1969                                 }
1970                                 free( surfaceAnimations[i].frames );
1971                                 surfaceAnimations[i].frames = 0;
1972                         }
1973                 }
1974
1975                 // get lower body surfaces
1976                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 );
1977                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1978                 strcpy( outfilename, filename );
1979                 if ( strrchr( outfilename, '/' ) )
1980                         *( strrchr( outfilename, '/' ) + 1 ) = 0;
1981
1982                 if ( g_data.currentLod == 0 )
1983                 {
1984                         strcat( outfilename, "lower.md3" );
1985                 }
1986                 else
1987                 {
1988                         char temp[128];
1989
1990                         sprintf( temp, "lower_%d.md3", g_data.currentLod );
1991                         strcat( outfilename, temp );
1992                 }
1993                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1994
1995                 // free memory
1996                 for ( i = 0; i < numSurfaces; i++ )
1997                 {
1998                         if ( surfaceAnimations[i].frames )
1999                         {
2000                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2001                                 {
2002                                         free( surfaceAnimations[i].frames[j].triangles );
2003                                 }
2004                                 free( surfaceAnimations[i].frames );
2005                                 surfaceAnimations[i].frames = 0;
2006                         }
2007                 }
2008
2009                 // get head surfaces
2010                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames );
2011                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2012                 strcpy( outfilename, filename );
2013                 if ( strrchr( outfilename, '/' ) )
2014                         *( strrchr( outfilename, '/' ) + 1 ) = 0;
2015
2016                 if ( g_data.currentLod == 0 )
2017                 {
2018                         strcat( outfilename, "head.md3" );
2019                 }
2020                 else
2021                 {
2022                         char temp[128];
2023
2024                         sprintf( temp, "head_%d.md3", g_data.currentLod );
2025                         strcat( outfilename, temp );
2026                 }
2027                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2028
2029                 // free memory
2030                 for ( i = 0; i < numSurfaces; i++ )
2031                 {
2032                         if ( surfaceAnimations[i].frames )
2033                         {
2034                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2035                                 {
2036                                         free( surfaceAnimations[i].frames[j].triangles );
2037                                 }
2038                                 free( surfaceAnimations[i].frames );
2039                                 surfaceAnimations[i].frames = 0;
2040                         }
2041                 }
2042         }
2043         else if ( type == TYPE_WEAPON )
2044         {
2045                 // get the weapon surfaces
2046                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 );
2047                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2048
2049                 strcpy( outfilename, filename );
2050                 if ( strrchr( outfilename, '.' ) )
2051                         *( strrchr( outfilename, '.' ) + 1 ) = 0;
2052                 strcat( outfilename, "md3" );
2053                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2054
2055                 // free memory
2056                 for ( i = 0; i < numSurfaces; i++ )
2057                 {
2058                         if ( surfaceAnimations[i].frames )
2059                         {
2060                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2061                                 {
2062                                         free( surfaceAnimations[i].frames[j].triangles );
2063                                 }
2064                                 free( surfaceAnimations[i].frames );
2065                                 surfaceAnimations[i].frames = 0;
2066                         }
2067                 }
2068
2069                 // get the flash surfaces
2070                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 );
2071                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2072
2073                 strcpy( outfilename, filename );
2074                 if ( strrchr( outfilename, '.' ) )
2075                         *strrchr( outfilename, '.' ) = 0;
2076                 strcat( outfilename, "_flash.md3" );
2077                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM );
2078
2079                 // free memory
2080                 for ( i = 0; i < numSurfaces; i++ )
2081                 {
2082                         if ( surfaceAnimations[i].frames )
2083                         {
2084                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2085                                 {
2086                                         free( surfaceAnimations[i].frames[j].triangles );
2087                                 }
2088                                 free( surfaceAnimations[i].frames );
2089                                 surfaceAnimations[i].frames = 0;
2090                         }
2091                 }
2092         }
2093         else if ( type == TYPE_HAND )
2094         {
2095                 // get the hand tags
2096                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 );
2097                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2098
2099                 strcpy( outfilename, filename );
2100                 if ( strrchr( outfilename, '.' ) )
2101                         *strrchr( outfilename, '.' ) = 0;
2102                 strcat( outfilename, "_hand.md3" );
2103                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND );
2104
2105                 // free memory
2106                 for ( i = 0; i < numSurfaces; i++ )
2107                 {
2108                         if ( surfaceAnimations[i].frames )
2109                         {
2110                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2111                                 {
2112                                         free( surfaceAnimations[i].frames[j].triangles );
2113                                 }
2114                                 free( surfaceAnimations[i].frames );
2115                                 surfaceAnimations[i].frames = 0;
2116                         }
2117                 }
2118         }
2119         else
2120         {
2121                 Error( "Unknown type passed to ConvertASE()" );
2122         }
2123
2124         g_data.currentLod = 0;
2125         g_data.lodBias = 0;
2126         g_data.maxHeadFrames = 0;
2127         g_data.maxUpperFrames = 0;
2128         g_data.lowerSkipFrameStart = 0;
2129         g_data.lowerSkipFrameEnd = 0;
2130         VectorCopy( vec3_origin, g_data.aseAdjust );
2131
2132         // unload ASE from memory
2133         ASE_Free();
2134 }