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