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