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