]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3data/models.c
uncrustify! now the code is only ugly on the *inside*
[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         float       *pbasevert;
563
564 /*
565     //
566     // find bounds of all the verts on the base frame
567     //
568     ClearBounds (mins, maxs);
569
570     for (i=0 ; i<numtri ; i++)
571         for (j=0 ; j<3 ; j++)
572             AddPointToBounds (ptri[i].verts[j], mins, maxs);
573
574     for (i=0 ; i<3 ; i++)
575     {
576         mins[i] = floor(mins[i]);
577         maxs[i] = ceil(maxs[i]);
578     }
579
580     width = maxs[0] - mins[0];
581     height = maxs[2] - mins[2];
582
583     if (!g_data.fixedwidth)
584     {   // old style
585         scale = 8;
586         if (width*scale >= 150)
587             scale = 150.0 / width;
588         if (height*scale >= 190)
589             scale = 190.0 / height;
590
591         s_scale = t_scale = scale;
592
593         iwidth = ceil(width*s_scale);
594         iheight = ceil(height*t_scale);
595
596         iwidth += 4;
597         iheight += 4;
598     }
599     else
600     {   // new style
601         iwidth = g_data.fixedwidth / 2;
602         iheight = g_data.fixedheight;
603
604         s_scale = (float)(iwidth-4) / width;
605         t_scale = (float)(iheight-4) / height;
606     }
607
608     // make the width a multiple of 4; some hardware requires this, and it ensures
609     // dword alignment for each scan
610     swidth = iwidth*2;
611     g_data.skinwidth = (swidth + 3) & ~3;
612     g_data.skinheight = iheight;
613  */
614
615         for ( i = 0; i < numtri ; i++, ptri++, bTri++ )
616         {
617                 int j;
618
619                 for ( j = 0 ; j < 3 ; j++ )
620                 {
621                         pbasevert = ptri->verts[j];
622
623                         VectorCopy( ptri->verts[j], bTri->v[j].xyz );
624                         VectorCopy( ptri->normals[j], bTri->v[j].normal );
625
626                         bTri->v[j].st[0] = ptri->texcoords[j][0];
627                         bTri->v[j].st[1] = ptri->texcoords[j][1];
628                 }
629         }
630 }
631
632 static void BuildBaseFrame( const char *filename, ObjectAnimationFrame_t *pOAF ){
633         baseTriangle_t  *bTri;
634         baseVertex_t    *bVert;
635         int i, j;
636
637         // calculate the base triangles
638         for ( i = 0; i < g_data.model.numSurfaces; i++ )
639         {
640                 CopyTrianglesToBaseTriangles( pOAF->surfaces[i]->triangles,
641                                                                           pOAF->surfaces[i]->numtriangles,
642                                                                           g_data.surfData[i].baseTriangles );
643
644                 strcpy( g_data.surfData[i].header.name, pOAF->surfaces[i]->name );
645
646                 g_data.surfData[i].header.numTriangles = pOAF->surfaces[i]->numtriangles;
647                 g_data.surfData[i].header.numVerts = 0;
648
649 /*
650         if ( strstr( filename, gamedir + 1 ) )
651         {
652             strcpy( shaderName, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
653         }
654         else
655         {
656             strcpy( shaderName, filename );
657         }
658
659         if ( strrchr( shaderName, '/' ) )
660    *( strrchr( shaderName, '/' ) + 1 ) = 0;
661
662
663         strcpy( shaderName, pOAF->surfaces[i]->materialname );
664  */
665                 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, pOAF->surfaces[i]->materialname );
666
667                 g_data.surfData[i].header.numShaders++;
668         }
669
670         //
671         // compute unique vertices for each polyset
672         //
673         for ( i = 0; i < g_data.model.numSurfaces; i++ )
674         {
675                 int t;
676
677                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
678                 {
679                         bTri = &g_data.surfData[i].baseTriangles[t];
680
681                         for ( j = 0 ; j < 3 ; j++ )
682                         {
683                                 int k;
684
685                                 bVert = &bTri->v[j];
686
687                                 // get the xyz index
688                                 for ( k = 0; k < g_data.surfData[i].header.numVerts; k++ )
689                                 {
690                                         if ( ( g_data.surfData[i].baseVertexes[k].st[0] == bVert->st[0] ) &&
691                                                  ( g_data.surfData[i].baseVertexes[k].st[1] == bVert->st[1] ) &&
692                                                  ( VectorCompare( bVert->xyz, g_data.surfData[i].baseVertexes[k].xyz ) ) &&
693                                                  ( VectorCompare( bVert->normal, g_data.surfData[i].baseVertexes[k].normal ) ) ) {
694                                                 break;  // this vertex is already in the base vertex list
695                                         }
696                                 }
697
698                                 if ( k == g_data.surfData[i].header.numVerts ) {    // new index
699                                         g_data.surfData[i].baseVertexes[g_data.surfData[i].header.numVerts] = *bVert;
700                                         g_data.surfData[i].header.numVerts++;
701                                 }
702
703                                 bVert->index = k;
704
705                                 g_data.surfData[i].lodTriangles[t][j] = k;
706                         }
707                 }
708         }
709
710         //
711         // find tags
712         //
713         for ( i = 0; i < g_data.model.numSurfaces; i++ )
714         {
715                 if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
716                         if ( pOAF->surfaces[i]->numtriangles != 1 ) {
717                                 Error( "tag polysets must consist of only one triangle" );
718                         }
719                         if ( strstr( filename, "_flash.md3" ) && !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) ) {
720                                 continue;
721                         }
722                         printf( "found tag '%s'\n", pOAF->surfaces[i]->name );
723                         g_data.model.numTags++;
724                 }
725         }
726
727 }
728
729 static int LoadModelFile( const char *filename, polyset_t **psets, int *numpolysets ){
730         int time1;
731         char file1[1024];
732         const char          *frameFile;
733
734         printf( "---------------------\n" );
735         if ( filename[1] != ':' ) {
736                 frameFile = filename;
737                 sprintf( file1, "%s/%s", g_cddir, frameFile );
738         }
739         else
740         {
741                 strcpy( file1, filename );
742         }
743
744         time1 = FileTime( file1 );
745         if ( time1 == -1 ) {
746                 Error( "%s doesn't exist", file1 );
747         }
748
749         //
750         // load the base triangles
751         //
752         *psets = Polyset_LoadSets( file1, numpolysets, g_data.maxSurfaceTris );
753
754         //
755         // snap polysets
756         //
757         Polyset_SnapSets( *psets, *numpolysets );
758
759         if ( strstr( file1, ".3ds" ) || strstr( file1, ".3DS" ) ) {
760                 return MD3_TYPE_BASE3DS;
761         }
762
763         Error( "Unknown model file type" );
764
765         return MD3_TYPE_UNKNOWN;
766 }
767
768 /*
769    =================
770    Cmd_Base
771    =================
772  */
773 void Cmd_Base( void ){
774         char filename[1024];
775
776         GetToken( qfalse );
777         sprintf( filename, "%s/%s", g_cddir, token );
778         LoadBase( filename );
779 }
780
781 static void LoadBase( const char *filename ){
782         int numpolysets;
783         polyset_t *psets;
784         int i;
785         ObjectAnimationFrame_t oaf;
786
787         // determine polyset splitting threshold
788         if ( TokenAvailable() ) {
789                 GetToken( qfalse );
790                 g_data.maxSurfaceTris = atoi( token );
791         }
792         else
793         {
794                 g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
795         }
796
797         g_data.type = LoadModelFile( filename, &psets, &numpolysets );
798
799         Polyset_ComputeNormals( psets, numpolysets );
800
801         g_data.model.numSurfaces = numpolysets;
802
803         memset( &oaf, 0, sizeof( oaf ) );
804
805         for ( i = 0; i < numpolysets; i++ )
806         {
807                 oaf.surfaces[i] = &psets[i];
808                 oaf.numSurfaces = numpolysets;
809         }
810
811         BuildBaseFrame( filename, &oaf );
812
813         free( psets[0].triangles );
814         free( psets );
815 }
816
817 /*
818    =================
819    Cmd_SpriteBase
820
821    $spritebase xorg yorg width height
822
823    Generate a single square for the model
824    =================
825  */
826 void Cmd_SpriteBase( void ){
827         float xl, yl, width, height;
828
829         g_data.type = MD3_TYPE_SPRITE;
830
831         GetToken( qfalse );
832         xl = atof( token );
833         GetToken( qfalse );
834         yl = atof( token );
835         GetToken( qfalse );
836         width = atof( token );
837         GetToken( qfalse );
838         height = atof( token );
839
840 //      if (g_skipmodel || g_release || g_archive)
841 //              return;
842
843         printf( "---------------------\n" );
844
845         g_data.surfData[0].verts[0] = ( float * ) calloc( 1, sizeof( float ) * 6 * 4 );
846
847         g_data.surfData[0].header.numVerts = 4;
848
849         g_data.surfData[0].verts[0][0 + 0] = 0;
850         g_data.surfData[0].verts[0][0 + 1] = -xl;
851         g_data.surfData[0].verts[0][0 + 2] = yl + height;
852
853         g_data.surfData[0].verts[0][0 + 3] = -1;
854         g_data.surfData[0].verts[0][0 + 4] = 0;
855         g_data.surfData[0].verts[0][0 + 5] = 0;
856         g_data.surfData[0].baseVertexes[0].st[0] = 0;
857         g_data.surfData[0].baseVertexes[0].st[1] = 0;
858
859
860         g_data.surfData[0].verts[0][6 + 0] = 0;
861         g_data.surfData[0].verts[0][6 + 1] = -xl - width;
862         g_data.surfData[0].verts[0][6 + 2] = yl + height;
863
864         g_data.surfData[0].verts[0][6 + 3] = -1;
865         g_data.surfData[0].verts[0][6 + 4] = 0;
866         g_data.surfData[0].verts[0][6 + 5] = 0;
867         g_data.surfData[0].baseVertexes[1].st[0] = 1;
868         g_data.surfData[0].baseVertexes[1].st[1] = 0;
869
870
871         g_data.surfData[0].verts[0][12 + 0] = 0;
872         g_data.surfData[0].verts[0][12 + 1] = -xl - width;
873         g_data.surfData[0].verts[0][12 + 2] = yl;
874
875         g_data.surfData[0].verts[0][12 + 3] = -1;
876         g_data.surfData[0].verts[0][12 + 4] = 0;
877         g_data.surfData[0].verts[0][12 + 5] = 0;
878         g_data.surfData[0].baseVertexes[2].st[0] = 1;
879         g_data.surfData[0].baseVertexes[2].st[1] = 1;
880
881
882         g_data.surfData[0].verts[0][18 + 0] = 0;
883         g_data.surfData[0].verts[0][18 + 1] = -xl;
884         g_data.surfData[0].verts[0][18 + 2] = yl;
885
886         g_data.surfData[0].verts[0][18 + 3] = -1;
887         g_data.surfData[0].verts[0][18 + 4] = 0;
888         g_data.surfData[0].verts[0][18 + 5] = 0;
889         g_data.surfData[0].baseVertexes[3].st[0] = 0;
890         g_data.surfData[0].baseVertexes[3].st[1] = 1;
891
892         g_data.surfData[0].lodTriangles[0][0] = 0;
893         g_data.surfData[0].lodTriangles[0][1] = 1;
894         g_data.surfData[0].lodTriangles[0][2] = 2;
895
896         g_data.surfData[0].lodTriangles[1][0] = 2;
897         g_data.surfData[0].lodTriangles[1][1] = 3;
898         g_data.surfData[0].lodTriangles[1][2] = 0;
899
900         g_data.model.numSurfaces = 1;
901
902         g_data.surfData[0].header.numTriangles = 2;
903         g_data.surfData[0].header.numVerts = 4;
904
905         g_data.model.numFrames = 1;
906 }
907
908 /*
909    ===========================================================================
910
911    FRAME GRABBING
912
913    ===========================================================================
914  */
915
916 /*
917    ===============
918    GrabFrame
919    ===============
920  */
921 void GrabFrame( const char *frame ){
922         int i, j, k;
923         char file1[1024];
924         md3Frame_t      *fr;
925         md3Tag_t tagParent;
926         float       *frameXyz;
927         float       *frameNormals;
928         const char  *framefile;
929         polyset_t       *psets;
930         qboolean parentTagExists = qfalse;
931         int numpolysets;
932         int numtags = 0;
933         int tagcount;
934
935         // the frame 'run1' will be looked for as either
936         // run.1 or run1.tri, so the new alias sequence save
937         // feature an be used
938         if ( frame[1] != ':' ) {
939 //              framefile = FindFrameFile (frame);
940                 framefile = frame;
941                 sprintf( file1, "%s/%s",g_cddir, framefile );
942         }
943         else
944         {
945                 strcpy( file1, frame );
946         }
947         printf( "grabbing %s\n", file1 );
948
949         if ( g_data.model.numFrames >= MD3_MAX_FRAMES ) {
950                 Error( "model.numFrames >= MD3_MAX_FRAMES" );
951         }
952         fr = &g_data.frames[g_data.model.numFrames];
953
954         strcpy( fr->name, frame );
955
956         psets = Polyset_LoadSets( file1, &numpolysets, g_data.maxSurfaceTris );
957
958         //
959         // snap polysets
960         //
961         Polyset_SnapSets( psets, numpolysets );
962
963         //
964         // compute vertex normals
965         //
966         Polyset_ComputeNormals( psets, numpolysets );
967
968         //
969         // flip everything to compensate for the alias coordinate system
970         // and perform global scale and adjust
971         //
972         for ( i = 0; i < g_data.model.numSurfaces; i++ )
973         {
974                 triangle_t *ptri = psets[i].triangles;
975                 int t;
976
977                 for ( t = 0; t < psets[i].numtriangles; t++ )
978                 {
979
980                         for ( j = 0; j < 3; j++ )
981                         {
982
983                                 // scale and adjust
984                                 for ( k = 0 ; k < 3 ; k++ ) {
985                                         ptri[t].verts[j][k] = ptri[t].verts[j][k] * g_data.scale_up +
986                                                                                   g_data.adjust[k];
987
988                                         if ( ptri[t].verts[j][k] > 1023 ||
989                                                  ptri[t].verts[j][k] < -1023 ) {
990                                                 Error( "Model extents too large" );
991                                         }
992                                 }
993                         }
994                 }
995         }
996
997         //
998         // find and count tags, locate parent tag
999         //
1000         for ( i = 0; i < numpolysets; i++ )
1001         {
1002                 if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) {
1003                         if ( strstr( psets[i].name, "tag_parent" ) == psets[i].name ) {
1004                                 if ( strstr( psets[i].name, "tag_parent" ) ) {
1005                                         float tri[3][3];
1006
1007                                         if ( parentTagExists ) {
1008                                                 Error( "Multiple parent tags not allowed" );
1009                                         }
1010
1011                                         memcpy( tri[0], psets[i].triangles[0].verts[0], sizeof( float ) * 3 );
1012                                         memcpy( tri[1], psets[i].triangles[0].verts[1], sizeof( float ) * 3 );
1013                                         memcpy( tri[2], psets[i].triangles[0].verts[2], sizeof( float ) * 3 );
1014
1015                                         MD3_ComputeTagFromTri( &tagParent, tri );
1016                                         strcpy( tagParent.name, psets[i].name );
1017                                         g_data.tags[g_data.model.numFrames][numtags] = tagParent;
1018                                         parentTagExists = qtrue;
1019
1020                                 }
1021                         }
1022                         numtags++;
1023                 }
1024
1025                 if ( strcmp( psets[i].name, g_data.surfData[i].header.name ) ) {
1026                         Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, psets[i].name, g_modelname );
1027                 }
1028         }
1029
1030         if ( numtags != g_data.model.numTags ) {
1031                 Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1032         }
1033
1034         if ( numpolysets != g_data.model.numSurfaces ) {
1035                 Error( "mismatched number of surfaces in frame(%d) vs. base(%d)", numpolysets - numtags, g_data.model.numSurfaces );
1036         }
1037
1038         //
1039         // prepare to accumulate bounds and normals
1040         //
1041         ClearBounds( fr->bounds[0], fr->bounds[1] );
1042
1043         //
1044         // store the frame's vertices in the same order as the base. This assumes the
1045         // triangles and vertices in this frame are in exactly the same order as in the
1046         // base
1047         //
1048         for ( i = 0, tagcount = 0; i < numpolysets; i++ )
1049         {
1050                 int t;
1051                 triangle_t *pTris = psets[i].triangles;
1052
1053                 strcpy( g_data.surfData[i].header.name, psets[i].name );
1054
1055                 //
1056                 // parent tag adjust
1057                 //
1058                 if ( parentTagExists ) {
1059                         for ( t = 0; t < psets[i].numtriangles; t++ )
1060                         {
1061                                 for ( j = 0; j < 3 ; j++ )
1062                                 {
1063                                         vec3_t tmp;
1064
1065                                         VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1066
1067                                         pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1068                                         pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1069                                         pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1070
1071                                         VectorCopy( pTris[t].normals[j], tmp );
1072                                         pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1073                                         pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1074                                         pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1075                                 }
1076                         }
1077                 }
1078
1079                 //
1080                 // compute tag data
1081                 //
1082                 if ( strstr( psets[i].name, "tag_" ) == psets[i].name ) {
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                         }
1100                         frameXyz = g_data.surfData[i].verts[g_data.model.numFrames] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1101                         frameNormals = frameXyz + 3;
1102
1103                         for ( t = 0; t < psets[i].numtriangles; t++ )
1104                         {
1105                                 for ( j = 0; j < 3 ; j++ )
1106                                 {
1107                                         int index;
1108
1109                                         index = g_data.surfData[i].baseTriangles[t].v[j].index;
1110                                         frameXyz[index * 6 + 0] = pTris[t].verts[j][0];
1111                                         frameXyz[index * 6 + 1] = pTris[t].verts[j][1];
1112                                         frameXyz[index * 6 + 2] = pTris[t].verts[j][2];
1113                                         frameNormals[index * 6 + 0] =  pTris[t].normals[j][0];
1114                                         frameNormals[index * 6 + 1] =  pTris[t].normals[j][1];
1115                                         frameNormals[index * 6 + 2] =  pTris[t].normals[j][2];
1116                                         AddPointToBounds( &frameXyz[index * 6], fr->bounds[0], fr->bounds[1] );
1117                                 }
1118                         }
1119                 }
1120         }
1121
1122         g_data.model.numFrames++;
1123
1124         // only free the first triangle array, all of the psets in this array share the
1125         // same triangle pool!!!
1126 //      free( psets[0].triangles );
1127 //      free( psets );
1128 }
1129
1130 //===========================================================================
1131
1132
1133
1134 /*
1135    ===============
1136    Cmd_Frame
1137    ===============
1138  */
1139 void Cmd_Frame( void ){
1140         while ( TokenAvailable() )
1141         {
1142                 GetToken( qfalse );
1143                 if ( g_skipmodel ) {
1144                         continue;
1145                 }
1146                 if ( g_release || g_archive ) {
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         polyset_t *psets;
1164         char name[1024];
1165         int numPolysets;
1166         int i;
1167
1168         _3DS_LoadPolysets( filename, &psets, &numPolysets, g_verbose );
1169
1170         for ( i = 0; i < numPolysets; i++ )
1171         {
1172 /*
1173         if ( strstr( filename, gamedir + 1 ) )
1174         {
1175             strcpy( name, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1176         }
1177         else
1178         {
1179             strcpy( name, filename );
1180         }
1181
1182         if ( strrchr( name, '/' ) )
1183    *( strrchr( name, '/' ) + 1 ) = 0;
1184  */
1185                 strcpy( name, psets[i].materialname );
1186                 strcpy( g_data.surfData[i].shaders[g_data.surfData[i].header.numShaders].name, name );
1187
1188                 g_data.surfData[i].header.numShaders++;
1189         }
1190
1191         free( psets[0].triangles );
1192         free( psets );
1193 }
1194
1195 void Cmd_Skin( void ){
1196         char skinfile[1024];
1197
1198         if ( g_data.type == MD3_TYPE_BASE3DS ) {
1199                 GetToken( qfalse );
1200
1201                 sprintf( skinfile, "%s/%s", g_cddir, token );
1202
1203                 if ( strstr( token, ".3ds" ) || strstr( token, ".3DS" ) ) {
1204                         SkinFrom3DS( skinfile );
1205                 }
1206                 else
1207                 {
1208                         Error( "Unknown file format for $skin '%s'\n", skinfile );
1209                 }
1210         }
1211         else
1212         {
1213                 Error( "invalid model type while processing $skin" );
1214         }
1215
1216         g_data.model.numSkins++;
1217 }
1218
1219 /*
1220    =================
1221    Cmd_SpriteShader
1222    =================
1223
1224    This routine is also called for $oldskin
1225
1226  */
1227 void Cmd_SpriteShader(){
1228         GetToken( qfalse );
1229         strcpy( g_data.surfData[0].shaders[g_data.surfData[0].header.numShaders].name, token );
1230         g_data.surfData[0].header.numShaders++;
1231         g_data.model.numSkins++;
1232 }
1233
1234 /*
1235    =================
1236    Cmd_Origin
1237    =================
1238  */
1239 void Cmd_Origin( void ){
1240         // rotate points into frame of reference so model points down the
1241         // positive x axis
1242         // FIXME: use alias native coordinate system
1243         GetToken( qfalse );
1244         g_data.adjust[1] = -atof( token );
1245
1246         GetToken( qfalse );
1247         g_data.adjust[0] = atof( token );
1248
1249         GetToken( qfalse );
1250         g_data.adjust[2] = -atof( token );
1251 }
1252
1253
1254 /*
1255    =================
1256    Cmd_ScaleUp
1257    =================
1258  */
1259 void Cmd_ScaleUp( void ){
1260         GetToken( qfalse );
1261         g_data.scale_up = atof( token );
1262         if ( g_skipmodel || g_release || g_archive ) {
1263                 return;
1264         }
1265
1266         printf( "Scale up: %f\n", g_data.scale_up );
1267 }
1268
1269
1270 /*
1271    =================
1272    Cmd_Skinsize
1273
1274    Set a skin size other than the default
1275    QUAKE3: not needed
1276    =================
1277  */
1278 void Cmd_Skinsize( void ){
1279         GetToken( qfalse );
1280         g_data.fixedwidth = atoi( token );
1281         GetToken( qfalse );
1282         g_data.fixedheight = atoi( token );
1283 }
1284
1285 /*
1286    =================
1287    Cmd_Modelname
1288
1289    Begin creating a model of the given name
1290    =================
1291  */
1292 void Cmd_Modelname( void ){
1293         FinishModel( TYPE_UNKNOWN );
1294         ClearModel();
1295
1296         GetToken( qfalse );
1297         strcpy( g_modelname, token );
1298         StripExtension( g_modelname );
1299         strcat( g_modelname, ".md3" );
1300         strcpy( g_data.model.name, g_modelname );
1301 }
1302
1303 /*
1304    ===============
1305    fCmd_Cd
1306    ===============
1307  */
1308 void Cmd_Cd( void ){
1309         if ( g_cddir[0] ) {
1310                 Error( "$cd command without a $modelname" );
1311         }
1312
1313         GetToken( qfalse );
1314
1315         sprintf( g_cddir, "%s%s", gamedir, token );
1316
1317         // if -only was specified and this cd doesn't match,
1318         // skip the model (you only need to match leading chars,
1319         // so you could regrab all monsters with -only models/monsters)
1320         if ( !g_only[0] ) {
1321                 return;
1322         }
1323         if ( strncmp( token, g_only, strlen( g_only ) ) ) {
1324                 g_skipmodel = qtrue;
1325                 printf( "skipping %s\n", token );
1326         }
1327 }
1328
1329 void Convert3DStoMD3( const char *file ){
1330         LoadBase( file );
1331         GrabFrame( file );
1332         SkinFrom3DS( file );
1333
1334         strcpy( g_data.model.name, g_modelname );
1335
1336         FinishModel( TYPE_UNKNOWN );
1337         ClearModel();
1338 }
1339
1340 /*
1341 ** Cmd_3DSConvert
1342 */
1343 void Cmd_3DSConvert(){
1344         char file[1024];
1345
1346         FinishModel( TYPE_UNKNOWN );
1347         ClearModel();
1348
1349         GetToken( qfalse );
1350
1351         sprintf( file, "%s%s", gamedir, token );
1352         strcpy( g_modelname, token );
1353         if ( strrchr( g_modelname, '.' ) ) {
1354                 *strrchr( g_modelname, '.' ) = 0;
1355         }
1356         strcat( g_modelname, ".md3" );
1357
1358         if ( FileTime( file ) == -1 ) {
1359                 Error( "%s doesn't exist", file );
1360         }
1361
1362         if ( TokenAvailable() ) {
1363                 GetToken( qfalse );
1364                 g_data.scale_up = atof( token );
1365         }
1366
1367         Convert3DStoMD3( file );
1368 }
1369
1370 static void ConvertASE( const char *filename, int type, qboolean grabAnims );
1371
1372 /*
1373 ** Cmd_ASEConvert
1374 */
1375 void Cmd_ASEConvert( qboolean grabAnims ){
1376         char filename[1024];
1377         int type = TYPE_ITEM;
1378
1379         FinishModel( TYPE_UNKNOWN );
1380         ClearModel();
1381
1382         GetToken( qfalse );
1383         sprintf( filename, "%s%s", gamedir, token );
1384
1385         strcpy( g_modelname, token );
1386         StripExtension( g_modelname );
1387         strcat( g_modelname, ".md3" );
1388         strcpy( g_data.model.name, g_modelname );
1389
1390         if ( !strstr( filename, ".ase" ) && !strstr( filename, ".ASE" ) ) {
1391                 strcat( filename, ".ASE" );
1392         }
1393
1394         g_data.maxSurfaceTris = MAX_SURFACE_TRIS - 1;
1395
1396         while ( TokenAvailable() )
1397         {
1398                 GetToken( qfalse );
1399                 if ( !strcmp( token, "-origin" ) ) {
1400                         if ( !TokenAvailable() ) {
1401                                 Error( "missing parameter for -origin" );
1402                         }
1403                         GetToken( qfalse );
1404                         g_data.aseAdjust[1] = -atof( token );
1405
1406                         if ( !TokenAvailable() ) {
1407                                 Error( "missing parameter for -origin" );
1408                         }
1409                         GetToken( qfalse );
1410                         g_data.aseAdjust[0] = atof( token );
1411
1412                         if ( !TokenAvailable() ) {
1413                                 Error( "missing parameter for -origin" );
1414                         }
1415                         GetToken( qfalse );
1416                         g_data.aseAdjust[2] = -atof( token );
1417                 }
1418                 else if ( !strcmp( token, "-lod" ) ) {
1419                         if ( !TokenAvailable() ) {
1420                                 Error( "No parameter for -lod" );
1421                         }
1422                         GetToken( qfalse );
1423                         g_data.currentLod = atoi( token );
1424                         if ( g_data.currentLod > MD3_MAX_LODS - 1 ) {
1425                                 Error( "-lod parameter too large! (%d)\n", g_data.currentLod );
1426                         }
1427
1428                         if ( !TokenAvailable() ) {
1429                                 Error( "No second parameter for -lod" );
1430                         }
1431                         GetToken( qfalse );
1432                         g_data.lodBias = atof( token );
1433                 }
1434                 else if ( !strcmp( token, "-maxtris" ) ) {
1435                         if ( !TokenAvailable() ) {
1436                                 Error( "No parameter for -maxtris" );
1437                         }
1438                         GetToken( qfalse );
1439                         g_data.maxSurfaceTris = atoi( token );
1440                 }
1441                 else if ( !strcmp( token, "-playerparms" ) ) {
1442                         if ( !TokenAvailable() ) {
1443                                 Error( "missing skip start parameter for -playerparms" );
1444                         }
1445                         GetToken( qfalse );
1446                         g_data.lowerSkipFrameStart = atoi( token );
1447
1448 #if 0
1449                         if ( !TokenAvailable() ) {
1450                                 Error( "missing skip end parameter for -playerparms" );
1451                         }
1452                         GetToken( qfalse );
1453                         g_data.lowerSkipFrameEnd = atoi( token );
1454 #endif
1455
1456                         if ( !TokenAvailable() ) {
1457                                 Error( "missing upper parameter for -playerparms" );
1458                         }
1459                         GetToken( qfalse );
1460                         g_data.maxUpperFrames = atoi( token );
1461
1462                         g_data.lowerSkipFrameEnd = g_data.maxUpperFrames - 1;
1463
1464 #if 0
1465                         if ( !TokenAvailable() ) {
1466                                 Error( "missing head parameter for -playerparms" );
1467                         }
1468                         GetToken( qfalse );
1469                         g_data.maxHeadFrames = atoi( token );
1470 #endif
1471                         g_data.maxHeadFrames = 1;
1472
1473                         if ( type != TYPE_ITEM ) {
1474                                 Error( "invalid argument" );
1475                         }
1476
1477                         type = TYPE_PLAYER;
1478                 }
1479                 else if ( !strcmp( token, "-weapon" ) ) {
1480                         if ( type != TYPE_ITEM ) {
1481                                 Error( "invalid argument" );
1482                         }
1483
1484                         type = TYPE_WEAPON;
1485                 }
1486         }
1487
1488         g_data.type = MD3_TYPE_ASE;
1489
1490         if ( type == TYPE_WEAPON && grabAnims ) {
1491                 Error( "can't grab anims with weapon models" );
1492         }
1493         if ( type == TYPE_PLAYER && !grabAnims ) {
1494                 Error( "player models must be converted with $aseanimconvert" );
1495         }
1496
1497         if ( type == TYPE_WEAPON ) {
1498                 ConvertASE( filename, type, qfalse );
1499                 ConvertASE( filename, TYPE_HAND, qtrue );
1500         }
1501         else
1502         {
1503                 ConvertASE( filename, type, grabAnims );
1504         }
1505 }
1506
1507 static int GetSurfaceAnimations( SurfaceAnimation_t sanims[MAX_ANIM_SURFACES],
1508                                                                  const char *part,
1509                                                                  int skipFrameStart,
1510                                                                  int skipFrameEnd,
1511                                                                  int maxFrames ){
1512         int numSurfaces;
1513         int numValidSurfaces;
1514         int i;
1515         int numFrames = -1;
1516
1517         if ( ( numSurfaces = ASE_GetNumSurfaces() ) > MAX_ANIM_SURFACES ) {
1518                 Error( "Too many surfaces in ASE" );
1519         }
1520
1521         for ( numValidSurfaces = 0, i = 0; i < numSurfaces; i++ )
1522         {
1523                 polyset_t *splitSets;
1524                 int numNewFrames;
1525                 const char *surfaceName = ASE_GetSurfaceName( i );
1526
1527                 if ( !surfaceName ) {
1528                         continue;
1529 //                      Error( "Missing animation frames in model" );
1530                 }
1531
1532                 if ( strstr( surfaceName, "tag_" ) ||
1533                          !strcmp( part, "any" ) ||
1534                          ( strstr( surfaceName, part ) == surfaceName ) ) {
1535
1536                         // skip this if it's an inappropriate tag
1537                         if ( strcmp( part, "any" ) ) {
1538                                 // ignore non-"tag_head" tags if this is the head
1539                                 if ( !strcmp( part, "h_" ) && strstr( surfaceName, "tag_" ) && strcmp( surfaceName, "tag_head" ) ) {
1540                                         continue;
1541                                 }
1542                                 // ignore "tag_head" if this is the legs
1543                                 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_head" ) ) {
1544                                         continue;
1545                                 }
1546                                 // ignore "tag_weapon" if this is the legs
1547                                 if ( !strcmp( part, "l_" ) && !strcmp( surfaceName, "tag_weapon" ) ) {
1548                                         continue;
1549                                 }
1550                         }
1551
1552                         if ( ( sanims[numValidSurfaces].frames = ASE_GetSurfaceAnimation( i, &sanims[numValidSurfaces].numFrames, skipFrameStart, skipFrameEnd, maxFrames ) ) != 0 ) {
1553                                 splitSets = Polyset_SplitSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames, &numNewFrames, g_data.maxSurfaceTris );
1554
1555                                 if ( numFrames == -1 ) {
1556                                         numFrames = sanims[numValidSurfaces].numFrames;
1557                                 }
1558                                 else if ( numFrames != sanims[numValidSurfaces].numFrames ) {
1559                                         Error( "Different number of animation frames on surfaces" );
1560                                 }
1561
1562                                 if ( sanims[numValidSurfaces].frames != splitSets ) {
1563                                         int j;
1564
1565                                         // free old data if we split the surfaces
1566                                         for ( j = 0; j < sanims[numValidSurfaces].numFrames; j++ )
1567                                         {
1568                                                 free( sanims[numValidSurfaces].frames[j].triangles );
1569                                                 free( sanims[numValidSurfaces].frames );
1570                                         }
1571
1572                                         sanims[numValidSurfaces].frames = splitSets;
1573                                         sanims[numValidSurfaces].numFrames = numNewFrames;
1574                                 }
1575                                 Polyset_SnapSets( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1576                                 Polyset_ComputeNormals( sanims[numValidSurfaces].frames, sanims[numValidSurfaces].numFrames );
1577
1578                                 numValidSurfaces++;
1579                         }
1580                 }
1581         }
1582
1583         return numValidSurfaces;
1584 }
1585
1586 static int SurfaceOrderToFrameOrder( SurfaceAnimation_t sanims[], ObjectAnimationFrame_t oanims[], int numSurfaces ){
1587         int i, s;
1588         int numFrames = -1;
1589
1590         /*
1591         ** we have the data here arranged in surface order, now we need to convert it to
1592         ** frame order
1593         */
1594         for ( i = 0, s = 0; i < numSurfaces; i++ )
1595         {
1596                 int j;
1597
1598                 if ( sanims[i].frames ) {
1599                         if ( numFrames == -1 ) {
1600                                 numFrames = sanims[i].numFrames;
1601                         }
1602                         else if ( numFrames != sanims[i].numFrames ) {
1603                                 Error( "numFrames != sanims[i].numFrames (%d != %d)\n", numFrames, sanims[i].numFrames );
1604                         }
1605
1606                         for ( j = 0; j < sanims[i].numFrames; j++ )
1607                         {
1608                                 oanims[j].surfaces[s] = &sanims[i].frames[j];
1609                                 oanims[j].numSurfaces = numSurfaces;
1610                         }
1611                         s++;
1612                 }
1613         }
1614
1615         return numFrames;
1616 }
1617
1618 static void WriteMD3( const char *_filename, ObjectAnimationFrame_t oanims[], int numFrames ){
1619         char filename[1024];
1620
1621         strcpy( filename, _filename );
1622         if ( strchr( filename, '.' ) ) {
1623                 *strchr( filename, '.' ) = 0;
1624         }
1625         strcat( filename, ".md3" );
1626 }
1627
1628 static void BuildAnimationFromOAFs( const char *filename, ObjectAnimationFrame_t oanims[], int numFrames, int type ){
1629         int f, i, j, tagcount;
1630         float *frameXyz;
1631         float *frameNormals;
1632
1633         g_data.model.numSurfaces = oanims[0].numSurfaces;
1634         g_data.model.numFrames = numFrames;
1635         if ( g_data.model.numFrames < 0 ) {
1636                 Error( "model.numFrames < 0" );
1637         }
1638         if ( g_data.model.numFrames >= MD3_MAX_FRAMES ) {
1639                 Error( "model.numFrames >= MD3_MAX_FRAMES" );
1640         }
1641
1642         // build base frame
1643         BuildBaseFrame( filename, &oanims[0] );
1644
1645         // build animation frames
1646         for ( f = 0; f < numFrames; f++ )
1647         {
1648                 ObjectAnimationFrame_t *pOAF = &oanims[f];
1649                 qboolean parentTagExists = qfalse;
1650                 md3Tag_t tagParent;
1651                 int numtags = 0;
1652                 md3Frame_t      *fr;
1653
1654                 fr = &g_data.frames[f];
1655
1656                 strcpy( fr->name, "(from ASE)" );
1657
1658                 // scale and adjust frame
1659                 for ( i = 0; i < pOAF->numSurfaces; i++ )
1660                 {
1661                         triangle_t *pTris = pOAF->surfaces[i]->triangles;
1662                         int t;
1663
1664                         for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1665                         {
1666                                 for ( j = 0; j < 3; j++ )
1667                                 {
1668                                         int k;
1669
1670                                         // scale and adjust
1671                                         for ( k = 0 ; k < 3 ; k++ ) {
1672                                                 pTris[t].verts[j][k] = pTris[t].verts[j][k] * g_data.scale_up +
1673                                                                                            g_data.aseAdjust[k];
1674
1675                                                 if ( pTris[t].verts[j][k] > 1023 ||
1676                                                          pTris[t].verts[j][k] < -1023 ) {
1677                                                         Error( "Model extents too large" );
1678                                                 }
1679                                         }
1680                                 }
1681                         }
1682                 }
1683
1684                 //
1685                 // find and count tags, locate parent tag
1686                 //
1687                 for ( i = 0; i < pOAF->numSurfaces; i++ )
1688                 {
1689                         if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
1690                                 // ignore parent tags when grabbing a weapon model and this is the flash portion
1691                                 if ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && strstr( filename, "_flash.md3" ) ) {
1692                                         continue;
1693                                 }
1694                                 else if ( !strstr( filename, "_hand.md3" ) && (
1695                                                           ( !strcmp( pOAF->surfaces[i]->name, "tag_parent" ) && !strstr( filename, "_flash.md3" ) ) ||
1696                                                           ( !strcmp( pOAF->surfaces[i]->name, "tag_torso" ) && ( strstr( filename, "upper_" ) || strstr( filename, "upper.md3" ) ) ) ||
1697                                                           ( !strcmp( pOAF->surfaces[i]->name, "tag_head" ) && ( strstr( filename, "head.md3" ) || strstr( filename, "head_" ) ) ) ||
1698                                                           ( !strcmp( pOAF->surfaces[i]->name, "tag_flash" ) && strstr( filename, "_flash.md3" ) ) ||
1699                                                           ( !strcmp( pOAF->surfaces[i]->name, "tag_weapon" ) && type == TYPE_WEAPON ) ) ) {
1700                                         float tri[3][3];
1701
1702                                         if ( parentTagExists ) {
1703                                                 Error( "Multiple parent tags not allowed" );
1704                                         }
1705
1706                                         memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1707                                         memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1708                                         memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1709
1710                                         MD3_ComputeTagFromTri( &tagParent, tri );
1711                                         strcpy( tagParent.name, "tag_parent" );
1712                                         g_data.tags[f][numtags] = tagParent;
1713                                         parentTagExists = qtrue;
1714                                 }
1715                                 else
1716                                 {
1717                                         float tri[3][3];
1718
1719                                         memcpy( tri[0], pOAF->surfaces[i]->triangles[0].verts[0], sizeof( float ) * 3 );
1720                                         memcpy( tri[1], pOAF->surfaces[i]->triangles[0].verts[1], sizeof( float ) * 3 );
1721                                         memcpy( tri[2], pOAF->surfaces[i]->triangles[0].verts[2], sizeof( float ) * 3 );
1722
1723                                         MD3_ComputeTagFromTri( &g_data.tags[f][numtags], tri );
1724                                         strcpy( g_data.tags[f][numtags].name, pOAF->surfaces[i]->name );
1725                                         if ( strstr( g_data.tags[f][numtags].name, "tag_flash" ) ) {
1726                                                 *( strstr( g_data.tags[f][numtags].name, "tag_flash" ) + strlen( "tag_flash" ) ) = 0;
1727                                         }
1728                                 }
1729
1730                                 numtags++;
1731                         }
1732
1733                         if ( strcmp( pOAF->surfaces[i]->name, g_data.surfData[i].header.name ) ) {
1734                                 Error( "Mismatched surfaces from base('%s') to frame('%s') in model '%s'\n", g_data.surfData[i].header.name, pOAF->surfaces[i]->name, filename );
1735                         }
1736                 }
1737
1738                 if ( numtags != g_data.model.numTags ) {
1739                         Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
1740                 }
1741
1742                 //
1743                 // prepare to accumulate bounds and normals
1744                 //
1745                 ClearBounds( fr->bounds[0], fr->bounds[1] );
1746
1747                 //
1748                 // store the frame's vertices in the same order as the base. This assumes the
1749                 // triangles and vertices in this frame are in exactly the same order as in the
1750                 // base
1751                 //
1752                 for ( i = 0, tagcount = 0; i < pOAF->numSurfaces; i++ )
1753                 {
1754                         int t;
1755                         triangle_t *pTris = pOAF->surfaces[i]->triangles;
1756
1757                         //
1758                         // parent tag adjust
1759                         //
1760                         if ( parentTagExists ) {
1761                                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1762                                 {
1763                                         for ( j = 0; j < 3 ; j++ )
1764                                         {
1765                                                 vec3_t tmp;
1766
1767                                                 VectorSubtract( pTris[t].verts[j], tagParent.origin, tmp );
1768
1769                                                 pTris[t].verts[j][0] = DotProduct( tmp, tagParent.axis[0] );
1770                                                 pTris[t].verts[j][1] = DotProduct( tmp, tagParent.axis[1] );
1771                                                 pTris[t].verts[j][2] = DotProduct( tmp, tagParent.axis[2] );
1772
1773                                                 VectorCopy( pTris[t].normals[j], tmp );
1774                                                 pTris[t].normals[j][0] = DotProduct( tmp, tagParent.axis[0] );
1775                                                 pTris[t].normals[j][1] = DotProduct( tmp, tagParent.axis[1] );
1776                                                 pTris[t].normals[j][2] = DotProduct( tmp, tagParent.axis[2] );
1777                                         }
1778                                 }
1779                         }
1780
1781                         //
1782                         // compute tag data
1783                         //
1784                         if ( strstr( pOAF->surfaces[i]->name, "tag_" ) == pOAF->surfaces[i]->name ) {
1785                                 md3Tag_t *pTag = &g_data.tags[f][tagcount];
1786                                 float tri[3][3];
1787
1788                                 strcpy( pTag->name, pOAF->surfaces[i]->name );
1789
1790                                 memcpy( tri[0], pTris[0].verts[0], sizeof( float ) * 3 );
1791                                 memcpy( tri[1], pTris[0].verts[1], sizeof( float ) * 3 );
1792                                 memcpy( tri[2], pTris[0].verts[2], sizeof( float ) * 3 );
1793
1794                                 MD3_ComputeTagFromTri( pTag, tri );
1795                                 tagcount++;
1796                         }
1797                         else
1798                         {
1799                                 if ( g_data.surfData[i].verts[f] ) {
1800                                         free( g_data.surfData[i].verts[f] );
1801                                 }
1802                                 frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
1803                                 frameNormals = frameXyz + 3;
1804
1805                                 for ( t = 0; t < pOAF->surfaces[i]->numtriangles; t++ )
1806                                 {
1807                                         for ( j = 0; j < 3 ; j++ )
1808                                         {
1809                                                 int index;
1810
1811                                                 index = g_data.surfData[i].baseTriangles[t].v[j].index;
1812                                                 frameXyz[index * 6 + 0] = pTris[t].verts[j][0];
1813                                                 frameXyz[index * 6 + 1] = pTris[t].verts[j][1];
1814                                                 frameXyz[index * 6 + 2] = pTris[t].verts[j][2];
1815                                                 frameNormals[index * 6 + 0] =  pTris[t].normals[j][0];
1816                                                 frameNormals[index * 6 + 1] =  pTris[t].normals[j][1];
1817                                                 frameNormals[index * 6 + 2] =  pTris[t].normals[j][2];
1818                                                 AddPointToBounds( &frameXyz[index * 6], fr->bounds[0], fr->bounds[1] );
1819                                         }
1820                                 }
1821                         }
1822                 }
1823         }
1824
1825         if ( strstr( filename, gamedir + 1 ) ) {
1826                 strcpy( g_modelname, strstr( filename, gamedir + 1 ) + strlen( gamedir ) - 1 );
1827         }
1828         else
1829         {
1830                 strcpy( g_modelname, filename );
1831         }
1832
1833         FinishModel( type );
1834         ClearModel();
1835 }
1836
1837 static void ConvertASE( const char *filename, int type, qboolean grabAnims ){
1838         int i, j;
1839         int numSurfaces;
1840         int numFrames = -1;
1841         SurfaceAnimation_t surfaceAnimations[MAX_ANIM_SURFACES];
1842         ObjectAnimationFrame_t objectAnimationFrames[MAX_ANIM_FRAMES];
1843         char outfilename[1024];
1844
1845         /*
1846         ** load ASE into memory
1847         */
1848         ASE_Load( filename, g_verbose, grabAnims );
1849
1850         /*
1851         ** process parts
1852         */
1853         if ( type == TYPE_ITEM ) {
1854                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "any", -1, -1, -1 );
1855
1856                 if ( numSurfaces <= 0 ) {
1857                         Error( "numSurfaces <= 0" );
1858                 }
1859
1860                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1861
1862                 if ( numFrames <= 0 ) {
1863                         Error( "numFrames <= 0" );
1864                 }
1865
1866                 strcpy( outfilename, filename );
1867                 if ( strrchr( outfilename, '.' ) ) {
1868                         *( strrchr( outfilename, '.' ) + 1 ) = 0;
1869                 }
1870                 strcat( outfilename, "md3" );
1871                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1872
1873                 // free memory
1874                 for ( i = 0; i < numSurfaces; i++ )
1875                 {
1876                         if ( surfaceAnimations[i].frames ) {
1877                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1878                                 {
1879                                         free( surfaceAnimations[i].frames[j].triangles );
1880                                 }
1881                                 free( surfaceAnimations[i].frames );
1882                                 surfaceAnimations[i].frames = 0;
1883                         }
1884                 }
1885         }
1886         else if ( type == TYPE_PLAYER ) {
1887                 qboolean tagTorso = qfalse;
1888                 qboolean tagHead = qfalse;
1889                 qboolean tagWeapon = qfalse;
1890
1891                 //
1892                 // verify that all necessary tags exist
1893                 //
1894                 numSurfaces = ASE_GetNumSurfaces();
1895                 for ( i = 0; i < numSurfaces; i++ )
1896                 {
1897                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_head" ) ) {
1898                                 tagHead = qtrue;
1899                         }
1900                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_torso" ) ) {
1901                                 tagTorso = qtrue;
1902                         }
1903                         if ( !strcmp( ASE_GetSurfaceName( i ), "tag_weapon" ) ) {
1904                                 tagWeapon = qtrue;
1905                         }
1906                 }
1907
1908                 if ( !tagWeapon ) {
1909                         Error( "Missing tag_weapon!" );
1910                 }
1911                 if ( !tagTorso ) {
1912                         Error( "Missing tag_torso!" );
1913                 }
1914                 if ( !tagWeapon ) {
1915                         Error( "Missing tag_weapon!" );
1916                 }
1917
1918                 // get all upper body surfaces
1919                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "u_", -1, -1, g_data.maxUpperFrames );
1920                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1921                 strcpy( outfilename, filename );
1922                 if ( strrchr( outfilename, '/' ) ) {
1923                         *( strrchr( outfilename, '/' ) + 1 ) = 0;
1924                 }
1925
1926                 if ( g_data.currentLod == 0 ) {
1927                         strcat( outfilename, "upper.md3" );
1928                 }
1929                 else
1930                 {
1931                         char temp[128];
1932
1933                         sprintf( temp, "upper_%d.md3", g_data.currentLod );
1934                         strcat( outfilename, temp );
1935                 }
1936
1937                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1938
1939                 // free memory
1940                 for ( i = 0; i < numSurfaces; i++ )
1941                 {
1942                         if ( surfaceAnimations[i].frames ) {
1943                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1944                                 {
1945                                         free( surfaceAnimations[i].frames[j].triangles );
1946                                 }
1947                                 free( surfaceAnimations[i].frames );
1948                                 surfaceAnimations[i].frames = 0;
1949                         }
1950                 }
1951
1952                 // get lower body surfaces
1953                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "l_", g_data.lowerSkipFrameStart, g_data.lowerSkipFrameEnd, -1 );
1954                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1955                 strcpy( outfilename, filename );
1956                 if ( strrchr( outfilename, '/' ) ) {
1957                         *( strrchr( outfilename, '/' ) + 1 ) = 0;
1958                 }
1959
1960                 if ( g_data.currentLod == 0 ) {
1961                         strcat( outfilename, "lower.md3" );
1962                 }
1963                 else
1964                 {
1965                         char temp[128];
1966
1967                         sprintf( temp, "lower_%d.md3", g_data.currentLod );
1968                         strcat( outfilename, temp );
1969                 }
1970                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
1971
1972                 // free memory
1973                 for ( i = 0; i < numSurfaces; i++ )
1974                 {
1975                         if ( surfaceAnimations[i].frames ) {
1976                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
1977                                 {
1978                                         free( surfaceAnimations[i].frames[j].triangles );
1979                                 }
1980                                 free( surfaceAnimations[i].frames );
1981                                 surfaceAnimations[i].frames = 0;
1982                         }
1983                 }
1984
1985                 // get head surfaces
1986                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "h_", -1, -1, g_data.maxHeadFrames );
1987                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
1988                 strcpy( outfilename, filename );
1989                 if ( strrchr( outfilename, '/' ) ) {
1990                         *( strrchr( outfilename, '/' ) + 1 ) = 0;
1991                 }
1992
1993                 if ( g_data.currentLod == 0 ) {
1994                         strcat( outfilename, "head.md3" );
1995                 }
1996                 else
1997                 {
1998                         char temp[128];
1999
2000                         sprintf( temp, "head_%d.md3", g_data.currentLod );
2001                         strcat( outfilename, temp );
2002                 }
2003                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2004
2005                 // free memory
2006                 for ( i = 0; i < numSurfaces; i++ )
2007                 {
2008                         if ( surfaceAnimations[i].frames ) {
2009                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2010                                 {
2011                                         free( surfaceAnimations[i].frames[j].triangles );
2012                                 }
2013                                 free( surfaceAnimations[i].frames );
2014                                 surfaceAnimations[i].frames = 0;
2015                         }
2016                 }
2017         }
2018         else if ( type == TYPE_WEAPON ) {
2019                 // get the weapon surfaces
2020                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "w_", -1, -1, -1 );
2021                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2022
2023                 strcpy( outfilename, filename );
2024                 if ( strrchr( outfilename, '.' ) ) {
2025                         *( strrchr( outfilename, '.' ) + 1 ) = 0;
2026                 }
2027                 strcat( outfilename, "md3" );
2028                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, type );
2029
2030                 // free memory
2031                 for ( i = 0; i < numSurfaces; i++ )
2032                 {
2033                         if ( surfaceAnimations[i].frames ) {
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                 // get the flash surfaces
2044                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "f_", -1, -1, -1 );
2045                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2046
2047                 strcpy( outfilename, filename );
2048                 if ( strrchr( outfilename, '.' ) ) {
2049                         *strrchr( outfilename, '.' ) = 0;
2050                 }
2051                 strcat( outfilename, "_flash.md3" );
2052                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_ITEM );
2053
2054                 // free memory
2055                 for ( i = 0; i < numSurfaces; i++ )
2056                 {
2057                         if ( surfaceAnimations[i].frames ) {
2058                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2059                                 {
2060                                         free( surfaceAnimations[i].frames[j].triangles );
2061                                 }
2062                                 free( surfaceAnimations[i].frames );
2063                                 surfaceAnimations[i].frames = 0;
2064                         }
2065                 }
2066         }
2067         else if ( type == TYPE_HAND ) {
2068                 // get the hand tags
2069                 numSurfaces = GetSurfaceAnimations( surfaceAnimations, "tag_", -1, -1, -1 );
2070                 numFrames = SurfaceOrderToFrameOrder( surfaceAnimations, objectAnimationFrames, numSurfaces );
2071
2072                 strcpy( outfilename, filename );
2073                 if ( strrchr( outfilename, '.' ) ) {
2074                         *strrchr( outfilename, '.' ) = 0;
2075                 }
2076                 strcat( outfilename, "_hand.md3" );
2077                 BuildAnimationFromOAFs( outfilename, objectAnimationFrames, numFrames, TYPE_HAND );
2078
2079                 // free memory
2080                 for ( i = 0; i < numSurfaces; i++ )
2081                 {
2082                         if ( surfaceAnimations[i].frames ) {
2083                                 for ( j = 0; j < surfaceAnimations[i].numFrames; j++ )
2084                                 {
2085                                         free( surfaceAnimations[i].frames[j].triangles );
2086                                 }
2087                                 free( surfaceAnimations[i].frames );
2088                                 surfaceAnimations[i].frames = 0;
2089                         }
2090                 }
2091         }
2092         else
2093         {
2094                 Error( "Unknown type passed to ConvertASE()" );
2095         }
2096
2097         g_data.currentLod = 0;
2098         g_data.lodBias = 0;
2099         g_data.maxHeadFrames = 0;
2100         g_data.maxUpperFrames = 0;
2101         g_data.lowerSkipFrameStart = 0;
2102         g_data.lowerSkipFrameEnd = 0;
2103         VectorCopy( vec3_origin, g_data.aseAdjust );
2104
2105         // unload ASE from memory
2106         ASE_Free();
2107 }