]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/qdata/models.c
tools: reduce diff noise
[xonotic/netradiant.git] / tools / quake2 / qdata / 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 "qdata.h"
23 #include "inout.h"
24
25 //=================================================================
26
27 typedef struct
28 {
29         int numnormals;
30         vec3_t normalsum;
31 } vertexnormals_t;
32
33 typedef struct
34 {
35         vec3_t v;
36         int lightnormalindex;
37 } trivert_t;
38
39 typedef struct
40 {
41         vec3_t mins, maxs;
42         char name[16];
43         trivert_t v[MAX_VERTS];
44 } frame_t;
45
46 //================================================================
47
48 frame_t g_frames[MAX_FRAMES];
49
50 dmdl_t model;
51
52
53 float scale_up;                 // set by $scale
54 vec3_t adjust;                  // set by $origin
55 int g_fixedwidth, g_fixedheight;            // set by $skinsize
56
57
58 //
59 // base frame info
60 //
61 vec3_t base_xyz[MAX_VERTS];
62 dstvert_t base_st[MAX_VERTS];
63 dtriangle_t triangles[MAX_TRIANGLES];
64
65 int triangle_st[MAX_TRIANGLES][3][2];
66
67 // the command list holds counts, s/t values, and xyz indexes
68 // that are valid for every frame
69 int commands[16384];
70 int numcommands;
71 int numglverts;
72 int used[MAX_TRIANGLES];
73
74 char g_skins[MAX_MD2SKINS][64];
75
76 char cdarchive[1024];
77 char cdpartial[1024];
78 char cddir[1024];
79
80 char modelname[64];         // empty unless $modelname issued (players)
81
82 #define NUMVERTEXNORMALS    162
83
84 float avertexnormals[NUMVERTEXNORMALS][3] = {
85 #include "anorms.h"
86 };
87
88 FILE    *headerouthandle = NULL;
89
90 //==============================================================
91
92 /*
93    ===============
94    ClearModel
95    ===============
96  */
97 void ClearModel( void ){
98         memset( &model, 0, sizeof( model ) );
99
100         modelname[0] = 0;
101         scale_up = 1.0;
102         VectorCopy( vec3_origin, adjust );
103         g_fixedwidth = g_fixedheight = 0;
104         g_skipmodel = false;
105 }
106
107
108 void H_printf( char *fmt, ... ){
109         va_list argptr;
110         char name[1024];
111
112         if ( !headerouthandle ) {
113                 sprintf( name, "%s/tris.h", cddir );
114                 headerouthandle = SafeOpenWrite( name );
115                 fprintf( headerouthandle, "// %s\n\n", cddir );
116                 fprintf( headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n" );
117         }
118
119         va_start( argptr, fmt );
120         vfprintf( headerouthandle, fmt, argptr );
121         va_end( argptr );
122 }
123
124
125 /*
126    ============
127    WriteModelFile
128    ============
129  */
130 void WriteModelFile( FILE *modelouthandle ){
131         int i;
132         dmdl_t modeltemp;
133         int j, k;
134         frame_t         *in;
135         daliasframe_t   *out;
136         byte buffer[MAX_VERTS * 4 + 128];
137         float v;
138         int c_on, c_off;
139
140         model.ident = IDALIASHEADER;
141         model.version = ALIAS_VERSION;
142         model.framesize = (int)&( (daliasframe_t *)0 )->verts[model.num_xyz];
143         model.num_glcmds = numcommands;
144         model.ofs_skins = sizeof( dmdl_t );
145         model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
146         model.ofs_tris = model.ofs_st + model.num_st * sizeof( dstvert_t );
147         model.ofs_frames = model.ofs_tris + model.num_tris * sizeof( dtriangle_t );
148         model.ofs_glcmds = model.ofs_frames + model.num_frames * model.framesize;
149         model.ofs_end = model.ofs_glcmds + model.num_glcmds * 4;
150
151         //
152         // write out the model header
153         //
154         for ( i = 0 ; i < sizeof( dmdl_t ) / 4 ; i++ )
155                 ( (int *)&modeltemp )[i] = LittleLong( ( (int *)&model )[i] );
156
157         SafeWrite( modelouthandle, &modeltemp, sizeof( modeltemp ) );
158
159         //
160         // write out the skin names
161         //
162         SafeWrite( modelouthandle, g_skins, model.num_skins * MAX_SKINNAME );
163
164         //
165         // write out the texture coordinates
166         //
167         c_on = c_off = 0;
168         for ( i = 0 ; i < model.num_st ; i++ )
169         {
170                 base_st[i].s = LittleShort( base_st[i].s );
171                 base_st[i].t = LittleShort( base_st[i].t );
172         }
173
174         SafeWrite( modelouthandle, base_st, model.num_st * sizeof( base_st[0] ) );
175
176         //
177         // write out the triangles
178         //
179         for ( i = 0 ; i < model.num_tris ; i++ )
180         {
181                 int j;
182                 dtriangle_t tri;
183
184                 for ( j = 0 ; j < 3 ; j++ )
185                 {
186                         tri.index_xyz[j] = LittleShort( triangles[i].index_xyz[j] );
187                         tri.index_st[j] = LittleShort( triangles[i].index_st[j] );
188                 }
189
190                 SafeWrite( modelouthandle, &tri, sizeof( tri ) );
191         }
192
193         //
194         // write out the frames
195         //
196         for ( i = 0 ; i < model.num_frames ; i++ )
197         {
198                 in = &g_frames[i];
199                 out = (daliasframe_t *)buffer;
200
201                 strcpy( out->name, in->name );
202                 for ( j = 0 ; j < 3 ; j++ )
203                 {
204                         out->scale[j] = ( in->maxs[j] - in->mins[j] ) / 255;
205                         out->translate[j] = in->mins[j];
206                 }
207
208                 for ( j = 0 ; j < model.num_xyz ; j++ )
209                 {
210                         // all of these are byte values, so no need to deal with endianness
211                         out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
212
213                         for ( k = 0 ; k < 3 ; k++ )
214                         {
215                                 // scale to byte values & min/max check
216                                 v = Q_rint( ( in->v[j].v[k] - out->translate[k] ) / out->scale[k] );
217
218                                 // clamp, so rounding doesn't wrap from 255.6 to 0
219                                 if ( v > 255.0 ) {
220                                         v = 255.0;
221                                 }
222                                 if ( v < 0 ) {
223                                         v = 0;
224                                 }
225                                 out->verts[j].v[k] = v;
226                         }
227                 }
228
229                 for ( j = 0 ; j < 3 ; j++ )
230                 {
231                         out->scale[j] = LittleFloat( out->scale[j] );
232                         out->translate[j] = LittleFloat( out->translate[j] );
233                 }
234
235                 SafeWrite( modelouthandle, out, model.framesize );
236         }
237
238         //
239         // write out glcmds
240         //
241         SafeWrite( modelouthandle, commands, numcommands * 4 );
242 }
243
244
245 /*
246    ===============
247    FinishModel
248    ===============
249  */
250 void FinishModel( void ){
251         FILE        *modelouthandle;
252         int i;
253         char name[1024];
254
255         if ( !model.num_frames ) {
256                 return;
257         }
258
259 //
260 // copy to release directory tree if doing a release build
261 //
262         if ( g_release ) {
263                 if ( modelname[0] ) {
264                         sprintf( name, "%s", modelname );
265                 }
266                 else{
267                         sprintf( name, "%s/tris.md2", cdpartial );
268                 }
269                 ReleaseFile( name );
270
271                 for ( i = 0 ; i < model.num_skins ; i++ )
272                 {
273                         ReleaseFile( g_skins[i] );
274                 }
275                 model.num_frames = 0;
276                 return;
277         }
278
279 //
280 // write the model output file
281 //
282         if ( modelname[0] ) {
283                 sprintf( name, "%s%s", gamedir, modelname );
284         }
285         else{
286                 sprintf( name, "%s/tris.md2", cddir );
287         }
288         printf( "saving to %s\n", name );
289         CreatePath( name );
290         modelouthandle = SafeOpenWrite( name );
291
292         WriteModelFile( modelouthandle );
293
294         printf( "%3dx%3d skin\n", model.skinwidth, model.skinheight );
295         printf( "%4d vertexes\n", model.num_xyz );
296         printf( "%4d triangles\n", model.num_tris );
297         printf( "%4d frame\n", model.num_frames );
298         printf( "%4d glverts\n", numglverts );
299         printf( "%4d glcmd\n", model.num_glcmds );
300         printf( "%4d skins\n", model.num_skins );
301         printf( "file size: %d\n", (int)ftell( modelouthandle ) );
302         printf( "---------------------\n" );
303
304         fclose( modelouthandle );
305
306         // finish writing header file
307         H_printf( "\n" );
308
309         // scale_up is usefull to allow step distances to be adjusted
310         H_printf( "#define MODEL_SCALE\t\t%f\n", scale_up );
311
312         fclose( headerouthandle );
313         headerouthandle = NULL;
314 }
315
316
317 /*
318    =================================================================
319
320    ALIAS MODEL DISPLAY LIST GENERATION
321
322    =================================================================
323  */
324
325 int strip_xyz[128];
326 int strip_st[128];
327 int strip_tris[128];
328 int stripcount;
329
330 /*
331    ================
332    StripLength
333    ================
334  */
335 int StripLength( int starttri, int startv ){
336         int m1, m2;
337         int st1, st2;
338         int j;
339         dtriangle_t *last, *check;
340         int k;
341
342         used[starttri] = 2;
343
344         last = &triangles[starttri];
345
346         strip_xyz[0] = last->index_xyz[( startv ) % 3];
347         strip_xyz[1] = last->index_xyz[( startv + 1 ) % 3];
348         strip_xyz[2] = last->index_xyz[( startv + 2 ) % 3];
349         strip_st[0] = last->index_st[( startv ) % 3];
350         strip_st[1] = last->index_st[( startv + 1 ) % 3];
351         strip_st[2] = last->index_st[( startv + 2 ) % 3];
352
353         strip_tris[0] = starttri;
354         stripcount = 1;
355
356         m1 = last->index_xyz[( startv + 2 ) % 3];
357         st1 = last->index_st[( startv + 2 ) % 3];
358         m2 = last->index_xyz[( startv + 1 ) % 3];
359         st2 = last->index_st[( startv + 1 ) % 3];
360
361         // look for a matching triangle
362 nexttri:
363         for ( j = starttri + 1, check = &triangles[starttri + 1]
364                   ; j < model.num_tris ; j++, check++ )
365         {
366                 for ( k = 0 ; k < 3 ; k++ )
367                 {
368                         if ( check->index_xyz[k] != m1 ) {
369                                 continue;
370                         }
371                         if ( check->index_st[k] != st1 ) {
372                                 continue;
373                         }
374                         if ( check->index_xyz[ ( k + 1 ) % 3 ] != m2 ) {
375                                 continue;
376                         }
377                         if ( check->index_st[ ( k + 1 ) % 3 ] != st2 ) {
378                                 continue;
379                         }
380
381                         // this is the next part of the fan
382
383                         // if we can't use this triangle, this tristrip is done
384                         if ( used[j] ) {
385                                 goto done;
386                         }
387
388                         // the new edge
389                         if ( stripcount & 1 ) {
390                                 m2 = check->index_xyz[ ( k + 2 ) % 3 ];
391                                 st2 = check->index_st[ ( k + 2 ) % 3 ];
392                         }
393                         else
394                         {
395                                 m1 = check->index_xyz[ ( k + 2 ) % 3 ];
396                                 st1 = check->index_st[ ( k + 2 ) % 3 ];
397                         }
398
399                         strip_xyz[stripcount + 2] = check->index_xyz[ ( k + 2 ) % 3 ];
400                         strip_st[stripcount + 2] = check->index_st[ ( k + 2 ) % 3 ];
401                         strip_tris[stripcount] = j;
402                         stripcount++;
403
404                         used[j] = 2;
405                         goto nexttri;
406                 }
407         }
408 done:
409
410         // clear the temp used flags
411         for ( j = starttri + 1 ; j < model.num_tris ; j++ )
412                 if ( used[j] == 2 ) {
413                         used[j] = 0;
414                 }
415
416         return stripcount;
417 }
418
419
420 /*
421    ===========
422    FanLength
423    ===========
424  */
425 int FanLength( int starttri, int startv ){
426         int m1, m2;
427         int st1, st2;
428         int j;
429         dtriangle_t *last, *check;
430         int k;
431
432         used[starttri] = 2;
433
434         last = &triangles[starttri];
435
436         strip_xyz[0] = last->index_xyz[( startv ) % 3];
437         strip_xyz[1] = last->index_xyz[( startv + 1 ) % 3];
438         strip_xyz[2] = last->index_xyz[( startv + 2 ) % 3];
439         strip_st[0] = last->index_st[( startv ) % 3];
440         strip_st[1] = last->index_st[( startv + 1 ) % 3];
441         strip_st[2] = last->index_st[( startv + 2 ) % 3];
442
443         strip_tris[0] = starttri;
444         stripcount = 1;
445
446         m1 = last->index_xyz[( startv + 0 ) % 3];
447         st1 = last->index_st[( startv + 0 ) % 3];
448         m2 = last->index_xyz[( startv + 2 ) % 3];
449         st2 = last->index_st[( startv + 2 ) % 3];
450
451
452         // look for a matching triangle
453 nexttri:
454         for ( j = starttri + 1, check = &triangles[starttri + 1]
455                   ; j < model.num_tris ; j++, check++ )
456         {
457                 for ( k = 0 ; k < 3 ; k++ )
458                 {
459                         if ( check->index_xyz[k] != m1 ) {
460                                 continue;
461                         }
462                         if ( check->index_st[k] != st1 ) {
463                                 continue;
464                         }
465                         if ( check->index_xyz[ ( k + 1 ) % 3 ] != m2 ) {
466                                 continue;
467                         }
468                         if ( check->index_st[ ( k + 1 ) % 3 ] != st2 ) {
469                                 continue;
470                         }
471
472                         // this is the next part of the fan
473
474                         // if we can't use this triangle, this tristrip is done
475                         if ( used[j] ) {
476                                 goto done;
477                         }
478
479                         // the new edge
480                         m2 = check->index_xyz[ ( k + 2 ) % 3 ];
481                         st2 = check->index_st[ ( k + 2 ) % 3 ];
482
483                         strip_xyz[stripcount + 2] = m2;
484                         strip_st[stripcount + 2] = st2;
485                         strip_tris[stripcount] = j;
486                         stripcount++;
487
488                         used[j] = 2;
489                         goto nexttri;
490                 }
491         }
492 done:
493
494         // clear the temp used flags
495         for ( j = starttri + 1 ; j < model.num_tris ; j++ )
496                 if ( used[j] == 2 ) {
497                         used[j] = 0;
498                 }
499
500         return stripcount;
501 }
502
503
504
505 /*
506    ================
507    BuildGlCmds
508
509    Generate a list of trifans or strips
510    for the model, which holds for all frames
511    ================
512  */
513 void BuildGlCmds( void ){
514         int i, j, k;
515         int startv;
516         float s, t;
517         int len, bestlen, besttype;
518         int best_xyz[1024];
519         int best_st[1024];
520         int best_tris[1024];
521         int type;
522
523         //
524         // build tristrips
525         //
526         numcommands = 0;
527         numglverts = 0;
528         memset( used, 0, sizeof( used ) );
529         for ( i = 0 ; i < model.num_tris ; i++ )
530         {
531                 // pick an unused triangle and start the trifan
532                 if ( used[i] ) {
533                         continue;
534                 }
535
536                 bestlen = 0;
537                 for ( type = 0 ; type < 2 ; type++ )
538 //      type = 1;
539                 {
540                         for ( startv = 0 ; startv < 3 ; startv++ )
541                         {
542                                 if ( type == 1 ) {
543                                         len = StripLength( i, startv );
544                                 }
545                                 else{
546                                         len = FanLength( i, startv );
547                                 }
548                                 if ( len > bestlen ) {
549                                         besttype = type;
550                                         bestlen = len;
551                                         for ( j = 0 ; j < bestlen + 2 ; j++ )
552                                         {
553                                                 best_st[j] = strip_st[j];
554                                                 best_xyz[j] = strip_xyz[j];
555                                         }
556                                         for ( j = 0 ; j < bestlen ; j++ )
557                                                 best_tris[j] = strip_tris[j];
558                                 }
559                         }
560                 }
561
562                 // mark the tris on the best strip/fan as used
563                 for ( j = 0 ; j < bestlen ; j++ )
564                         used[best_tris[j]] = 1;
565
566                 if ( besttype == 1 ) {
567                         commands[numcommands++] = ( bestlen + 2 );
568                 }
569                 else{
570                         commands[numcommands++] = -( bestlen + 2 );
571                 }
572
573                 numglverts += bestlen + 2;
574
575                 for ( j = 0 ; j < bestlen + 2 ; j++ )
576                 {
577                         // emit a vertex into the reorder buffer
578                         k = best_st[j];
579
580                         // emit s/t coords into the commands stream
581                         s = base_st[k].s;
582                         t = base_st[k].t;
583
584                         s = ( s + 0.5 ) / model.skinwidth;
585                         t = ( t + 0.5 ) / model.skinheight;
586
587                         *(float *)&commands[numcommands++] = s;
588                         *(float *)&commands[numcommands++] = t;
589                         *(int *)&commands[numcommands++] = best_xyz[j];
590                 }
591         }
592
593         commands[numcommands++] = 0;        // end of list marker
594 }
595
596
597 /*
598    ===============================================================
599
600    BASE FRAME SETUP
601
602    ===============================================================
603  */
604
605 /*
606    ============
607    BuildST
608
609    Builds the triangle_st array for the base frame and
610    model.skinwidth / model.skinheight
611
612    FIXME: allow this to be loaded from a file for
613    arbitrary mappings
614    ============
615  */
616 void BuildST( triangle_t *ptri, int numtri ){
617         int i, j;
618         int width, height, iwidth, iheight, swidth;
619         float basex, basey;
620         float s_scale, t_scale;
621         float scale;
622         vec3_t mins, maxs;
623         float       *pbasevert;
624         vec3_t vtemp1, vtemp2, normal;
625
626         //
627         // find bounds of all the verts on the base frame
628         //
629         ClearBounds( mins, maxs );
630
631         for ( i = 0 ; i < numtri ; i++ )
632                 for ( j = 0 ; j < 3 ; j++ )
633                         AddPointToBounds( ptri[i].verts[j], mins, maxs );
634
635         for ( i = 0 ; i < 3 ; i++ )
636         {
637                 mins[i] = floor( mins[i] );
638                 maxs[i] = ceil( maxs[i] );
639         }
640
641         width = maxs[0] - mins[0];
642         height = maxs[2] - mins[2];
643
644         if ( !g_fixedwidth ) { // old style
645                 scale = 8;
646                 if ( width * scale >= 150 ) {
647                         scale = 150.0 / width;
648                 }
649                 if ( height * scale >= 190 ) {
650                         scale = 190.0 / height;
651                 }
652
653                 s_scale = t_scale = scale;
654
655                 iwidth = ceil( width * s_scale );
656                 iheight = ceil( height * t_scale );
657
658                 iwidth += 4;
659                 iheight += 4;
660         }
661         else
662         {   // new style
663                 iwidth = g_fixedwidth / 2;
664                 iheight = g_fixedheight;
665
666                 s_scale = (float)( iwidth - 4 ) / width;
667                 t_scale = (float)( iheight - 4 ) / height;
668         }
669
670 //
671 // determine which side of each triangle to map the texture to
672 //
673         for ( i = 0 ; i < numtri ; i++ )
674         {
675                 VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
676                 VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
677                 CrossProduct( vtemp1, vtemp2, normal );
678
679                 if ( normal[1] > 0 ) {
680                         basex = iwidth + 2;
681                 }
682                 else
683                 {
684                         basex = 2;
685                 }
686                 basey = 2;
687
688                 for ( j = 0 ; j < 3 ; j++ )
689                 {
690                         pbasevert = ptri[i].verts[j];
691
692                         triangle_st[i][j][0] = Q_rint( ( pbasevert[0] - mins[0] ) * s_scale + basex );
693                         triangle_st[i][j][1] = Q_rint( ( maxs[2] - pbasevert[2] ) * t_scale + basey );
694                 }
695         }
696
697 // make the width a multiple of 4; some hardware requires this, and it ensures
698 // dword alignment for each scan
699         swidth = iwidth * 2;
700         model.skinwidth = ( swidth + 3 ) & ~3;
701         model.skinheight = iheight;
702 }
703
704
705 /*
706    =================
707    Cmd_Base
708    =================
709  */
710 void Cmd_Base( void ){
711         triangle_t  *ptri;
712         int i, j, k;
713         int time1;
714         char file1[1024];
715
716         GetToken( false );
717
718         if ( g_skipmodel || g_release || g_archive ) {
719                 return;
720         }
721
722         printf( "---------------------\n" );
723         sprintf( file1, "%s/%s.%s", cdarchive, token, trifileext );
724         printf( "%s\n", file1 );
725
726         ExpandPathAndArchive( file1 );
727
728         sprintf( file1, "%s/%s.%s", cddir, token, trifileext );
729
730         time1 = FileTime( file1 );
731         if ( time1 == -1 ) {
732                 Error( "%s doesn't exist", file1 );
733         }
734
735 //
736 // load the base triangles
737 //
738         if ( do3ds ) {
739                 Load3DSTriangleList( file1, &ptri, &model.num_tris );
740         }
741         else{
742                 LoadTriangleList( file1, &ptri, &model.num_tris );
743         }
744
745 //
746 // get the ST values
747 //
748         BuildST( ptri, model.num_tris );
749
750 //
751 // run through all the base triangles, storing each unique vertex in the
752 // base vertex list and setting the indirect triangles to point to the base
753 // vertices
754 //
755         for ( i = 0 ; i < model.num_tris ; i++ )
756         {
757                 for ( j = 0 ; j < 3 ; j++ )
758                 {
759                         // get the xyz index
760                         for ( k = 0 ; k < model.num_xyz ; k++ )
761                                 if ( VectorCompare( ptri[i].verts[j], base_xyz[k] ) ) {
762                                         break;
763                                 }           // this vertex is already in the base vertex list
764
765                         if ( k == model.num_xyz ) { // new index
766                                 VectorCopy( ptri[i].verts[j], base_xyz[model.num_xyz] );
767                                 model.num_xyz++;
768                         }
769
770                         triangles[i].index_xyz[j] = k;
771
772                         // get the st index
773                         for ( k = 0 ; k < model.num_st ; k++ )
774                                 if ( triangle_st[i][j][0] == base_st[k].s
775                                          && triangle_st[i][j][1] == base_st[k].t ) {
776                                         break;
777                                 }           // this vertex is already in the base vertex list
778
779                         if ( k == model.num_st ) { // new index
780                                 base_st[model.num_st].s = triangle_st[i][j][0];
781                                 base_st[model.num_st].t = triangle_st[i][j][1];
782                                 model.num_st++;
783                         }
784
785                         triangles[i].index_st[j] = k;
786                 }
787         }
788
789         // build triangle strips / fans
790         BuildGlCmds();
791 }
792
793 //===============================================================
794
795 char    *FindFrameFile( char *frame ){
796         int time1;
797         char file1[1024];
798         static char retname[1024];
799         char base[32];
800         char suffix[32];
801         char    *s;
802
803         if ( strstr( frame, "." ) ) {
804                 return frame;       // allready in dot format
805
806         }
807         // split 'run1' into 'run' and '1'
808         s = frame + strlen( frame ) - 1;
809
810         while ( s != frame && *s >= '0' && *s <= '9' )
811                 s--;
812
813         strcpy( suffix, s + 1 );
814         strcpy( base, frame );
815         base[s - frame + 1] = 0;
816
817         // check for 'run1.tri'
818         sprintf( file1, "%s/%s%s.%s",cddir, base, suffix, trifileext );
819         time1 = FileTime( file1 );
820         if ( time1 != -1 ) {
821                 sprintf( retname, "%s%s.%s", base, suffix, trifileext );
822                 return retname;
823         }
824
825         // check for 'run.1'
826         sprintf( file1, "%s/%s.%s",cddir, base, suffix );
827         time1 = FileTime( file1 );
828         if ( time1 != -1 ) {
829                 sprintf( retname, "%s.%s", base, suffix );
830                 return retname;
831         }
832
833         Error( "frame %s could not be found",frame );
834         return NULL;
835 }
836
837 /*
838    ===============
839    GrabFrame
840    ===============
841  */
842 void GrabFrame( char *frame ){
843         triangle_t  *ptri;
844         int i, j;
845         trivert_t   *ptrivert;
846         int num_tris;
847         char file1[1024];
848         frame_t     *fr;
849         vertexnormals_t vnorms[MAX_VERTS];
850         int index_xyz;
851         char    *framefile;
852
853         // the frame 'run1' will be looked for as either
854         // run.1 or run1.tri, so the new alias sequence save
855         // feature an be used
856         framefile = FindFrameFile( frame );
857
858         sprintf( file1, "%s/%s", cdarchive, framefile );
859         ExpandPathAndArchive( file1 );
860
861         sprintf( file1, "%s/%s",cddir, framefile );
862
863         printf( "grabbing %s\n", file1 );
864
865         if ( model.num_frames >= MAX_FRAMES ) {
866                 Error( "model.num_frames >= MAX_FRAMES" );
867         }
868         fr = &g_frames[model.num_frames];
869         model.num_frames++;
870
871         strcpy( fr->name, frame );
872
873 //
874 // load the frame
875 //
876         if ( do3ds ) {
877                 Load3DSTriangleList( file1, &ptri, &num_tris );
878         }
879         else{
880                 LoadTriangleList( file1, &ptri, &num_tris );
881         }
882
883         if ( num_tris != model.num_tris ) {
884                 Error( "%s: number of triangles doesn't match base frame\n", file1 );
885         }
886
887 //
888 // allocate storage for the frame's vertices
889 //
890         ptrivert = fr->v;
891
892         for ( i = 0 ; i < model.num_xyz ; i++ )
893         {
894                 vnorms[i].numnormals = 0;
895                 VectorClear( vnorms[i].normalsum );
896         }
897         ClearBounds( fr->mins, fr->maxs );
898
899 //
900 // store the frame's vertices in the same order as the base. This assumes the
901 // triangles and vertices in this frame are in exactly the same order as in the
902 // base
903 //
904         for ( i = 0 ; i < num_tris ; i++ )
905         {
906                 vec3_t vtemp1, vtemp2, normal;
907                 float ftemp;
908
909                 VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
910                 VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
911                 CrossProduct( vtemp1, vtemp2, normal );
912
913                 VectorNormalize( normal, normal );
914
915                 // rotate the normal so the model faces down the positive x axis
916                 ftemp = normal[0];
917                 normal[0] = -normal[1];
918                 normal[1] = ftemp;
919
920                 for ( j = 0 ; j < 3 ; j++ )
921                 {
922                         index_xyz = triangles[i].index_xyz[j];
923
924                         // rotate the vertices so the model faces down the positive x axis
925                         // also adjust the vertices to the desired origin
926                         ptrivert[index_xyz].v[0] = ( ( -ptri[i].verts[j][1] ) * scale_up ) +
927                                                                            adjust[0];
928                         ptrivert[index_xyz].v[1] = ( ptri[i].verts[j][0] * scale_up ) +
929                                                                            adjust[1];
930                         ptrivert[index_xyz].v[2] = ( ptri[i].verts[j][2] * scale_up ) +
931                                                                            adjust[2];
932
933                         AddPointToBounds( ptrivert[index_xyz].v, fr->mins, fr->maxs );
934
935                         VectorAdd( vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum );
936                         vnorms[index_xyz].numnormals++;
937                 }
938         }
939
940 //
941 // calculate the vertex normals, match them to the template list, and store the
942 // index of the best match
943 //
944         for ( i = 0 ; i < model.num_xyz ; i++ )
945         {
946                 int j;
947                 vec3_t v;
948                 float maxdot;
949                 int maxdotindex;
950                 int c;
951
952                 c = vnorms[i].numnormals;
953                 if ( !c ) {
954                         Error( "Vertex with no triangles attached" );
955                 }
956
957                 VectorScale( vnorms[i].normalsum, 1.0 / c, v );
958                 VectorNormalize( v, v );
959
960                 maxdot = -999999.0;
961                 maxdotindex = -1;
962
963                 for ( j = 0 ; j < NUMVERTEXNORMALS ; j++ )
964                 {
965                         float dot;
966
967                         dot = DotProduct( v, avertexnormals[j] );
968                         if ( dot > maxdot ) {
969                                 maxdot = dot;
970                                 maxdotindex = j;
971                         }
972                 }
973
974                 ptrivert[i].lightnormalindex = maxdotindex;
975         }
976
977         free( ptri );
978 }
979
980 /*
981    ===============
982    Cmd_Frame
983    ===============
984  */
985 void Cmd_Frame( void ){
986         while ( TokenAvailable() )
987         {
988                 GetToken( false );
989                 if ( g_skipmodel ) {
990                         continue;
991                 }
992                 if ( g_release || g_archive ) {
993                         model.num_frames = 1;   // don't skip the writeout
994                         continue;
995                 }
996
997                 H_printf( "#define FRAME_%-16s\t%i\n", token, model.num_frames );
998
999                 GrabFrame( token );
1000         }
1001 }
1002
1003
1004 /*
1005    ===============
1006    Cmd_Skin
1007
1008    Skins aren't actually stored in the file, only a reference
1009    is saved out to the header file.
1010    ===============
1011  */
1012 void Cmd_Skin( void ){
1013         byte    *palette;
1014         byte    *pixels;
1015         int width, height;
1016         byte    *cropped;
1017         int y;
1018         char name[1024], savename[1024];
1019
1020         GetToken( false );
1021
1022         if ( model.num_skins == MAX_MD2SKINS ) {
1023                 Error( "model.num_skins == MAX_MD2SKINS" );
1024         }
1025
1026         if ( g_skipmodel ) {
1027                 return;
1028         }
1029
1030         sprintf( name, "%s/%s.lbm", cdarchive, token );
1031         strcpy( name, ExpandPathAndArchive( name ) );
1032 //      sprintf (name, "%s/%s.lbm", cddir, token);
1033
1034         if ( TokenAvailable() ) {
1035                 GetToken( false );
1036                 sprintf( g_skins[model.num_skins], "%s.pcx", token );
1037                 sprintf( savename, "%s%s.pcx", gamedir, g_skins[model.num_skins] );
1038         }
1039         else
1040         {
1041                 sprintf( savename, "%s/%s.pcx", cddir, token );
1042                 sprintf( g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token );
1043         }
1044
1045         model.num_skins++;
1046
1047         if ( g_skipmodel || g_release || g_archive ) {
1048                 return;
1049         }
1050
1051         // load the image
1052         printf( "loading %s\n", name );
1053         Load256Image( name, &pixels, &palette, &width, &height );
1054         RemapZero( pixels, palette, width, height );
1055
1056         // crop it to the proper size
1057         cropped = malloc( model.skinwidth * model.skinheight );
1058         for ( y = 0 ; y < model.skinheight ; y++ )
1059         {
1060                 memcpy( cropped + y * model.skinwidth,
1061                                 pixels + y * width, model.skinwidth );
1062         }
1063
1064         // save off the new image
1065         printf( "saving %s\n", savename );
1066         CreatePath( savename );
1067         WritePCXfile( savename, cropped, model.skinwidth,
1068                                   model.skinheight, palette );
1069
1070         free( pixels );
1071         free( palette );
1072         free( cropped );
1073 }
1074
1075
1076 /*
1077    =================
1078    Cmd_Origin
1079    =================
1080  */
1081 void Cmd_Origin( void ){
1082         // rotate points into frame of reference so model points down the
1083         // positive x axis
1084         GetToken( false );
1085         adjust[1] = -atof( token );
1086
1087         GetToken( false );
1088         adjust[0] = atof( token );
1089
1090         GetToken( false );
1091         adjust[2] = -atof( token );
1092 }
1093
1094
1095 /*
1096    =================
1097    Cmd_ScaleUp
1098    =================
1099  */
1100 void Cmd_ScaleUp( void ){
1101         GetToken( false );
1102         scale_up = atof( token );
1103         if ( g_skipmodel || g_release || g_archive ) {
1104                 return;
1105         }
1106
1107         printf( "Scale up: %f\n", scale_up );
1108 }
1109
1110
1111 /*
1112    =================
1113    Cmd_Skinsize
1114
1115    Set a skin size other than the default
1116    =================
1117  */
1118 void Cmd_Skinsize( void ){
1119         GetToken( false );
1120         g_fixedwidth = atoi( token );
1121         GetToken( false );
1122         g_fixedheight = atoi( token );
1123 }
1124
1125 /*
1126    =================
1127    Cmd_Modelname
1128
1129    Gives a different name/location for the file, instead of the cddir
1130    =================
1131  */
1132 void Cmd_Modelname( void ){
1133         GetToken( false );
1134         strcpy( modelname, token );
1135 }
1136
1137 /*
1138    ===============
1139    Cmd_Cd
1140    ===============
1141  */
1142 void Cmd_Cd( void ){
1143         FinishModel();
1144         ClearModel();
1145
1146         GetToken( false );
1147
1148         // this is a silly mess...
1149         sprintf( cdpartial, "models/%s", token );
1150         sprintf( cdarchive, "%smodels/%s", gamedir + strlen( qdir ), token );
1151         sprintf( cddir, "%s%s", gamedir, cdpartial );
1152
1153         // if -only was specified and this cd doesn't match,
1154         // skip the model (you only need to match leading chars,
1155         // so you could regrab all monsters with -only monsters)
1156         if ( !g_only[0] ) {
1157                 return;
1158         }
1159         if ( strncmp( token, g_only, strlen( g_only ) ) ) {
1160                 g_skipmodel = true;
1161                 printf( "skipping %s\n", cdpartial );
1162         }
1163 }