]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/qdata_heretic2/models.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake2 / qdata_heretic2 / models.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 \r
23 #include "qdata.h"\r
24 #include <assert.h>\r
25 #include "jointed.h"\r
26 #include "fmodel.h"\r
27 \r
28 //=================================================================\r
29 \r
30 typedef struct \r
31 {\r
32         int             numnormals;\r
33         vec3_t  normalsum;\r
34 } vertexnormals_t;\r
35 \r
36 typedef struct\r
37 {\r
38         vec3_t          v;\r
39         int                     lightnormalindex;\r
40 } trivert_t;\r
41 \r
42 typedef struct\r
43 {\r
44         vec3_t          mins, maxs;\r
45         char            name[16];\r
46         trivert_t       v[MAX_VERTS];\r
47         QDataJoint_t    joints[NUM_CLUSTERS]; // ,this\r
48 } frame_t;\r
49 \r
50 // ,and all of this should get out of here, need to use new stuff in fmodels instead\r
51 \r
52 typedef struct IntListNode_s\r
53 {\r
54         int data;\r
55         struct IntListNode_s *next;\r
56 } IntListNode_t;  // gaak\r
57 \r
58 typedef struct\r
59 {\r
60         float           scale[3];       // multiply byte verts by this\r
61         float           translate[3];   // then add this\r
62 } PartialAliasFrame_t;\r
63 \r
64 int jointed;\r
65 int clustered;\r
66 \r
67 int *clusters[NUM_CLUSTERS];\r
68 IntListNode_t *vertLists[NUM_CLUSTERS];\r
69 int             num_verts[NUM_CLUSTERS + 1];\r
70 int             new_num_verts[NUM_CLUSTERS + 1];\r
71 \r
72 // end that\r
73 \r
74 //================================================================\r
75 \r
76 frame_t         g_frames[MAX_FRAMES];\r
77 //frame_t               *g_frames;\r
78 \r
79 static dmdl_t           model;\r
80 \r
81 \r
82 float           scale_up;                       // set by $scale\r
83 vec3_t          adjust;                         // set by $origin\r
84 int                     g_fixedwidth, g_fixedheight;    // set by $skinsize\r
85 \r
86 \r
87 //\r
88 // base frame info\r
89 //\r
90 dstvert_t       base_st[MAX_VERTS];\r
91 dtriangle_t     triangles[MAX_TRIANGLES];\r
92 \r
93 static int                      triangle_st[MAX_TRIANGLES][3][2];\r
94 \r
95 // the command list holds counts, s/t values, and xyz indexes\r
96 // that are valid for every frame\r
97 int                     commands[16384];\r
98 int                     numcommands;\r
99 int                     numglverts;\r
100 int                     used[MAX_TRIANGLES];\r
101 \r
102 char            g_skins[MAX_MD2SKINS][64];\r
103 \r
104 char            cdarchive[1024];\r
105 char            cdpartial[1024];\r
106 char            cddir[1024];\r
107 \r
108 char            modelname[64];  // empty unless $modelname issued (players)\r
109 \r
110 extern  char            *g_outputDir;\r
111 \r
112 #define NUMVERTEXNORMALS        162\r
113 \r
114 float   avertexnormals[NUMVERTEXNORMALS][3] = \r
115 {\r
116         #include "anorms.h"\r
117 };\r
118 \r
119 unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];\r
120 \r
121 FILE    *headerouthandle = NULL;\r
122 \r
123 //==============================================================\r
124 \r
125 /*\r
126 ===============\r
127 ClearModel\r
128 ===============\r
129 */\r
130 static void ClearModel (void)\r
131 {\r
132         memset (&model, 0, sizeof(model));\r
133 \r
134         modelname[0] = 0;\r
135         jointed = NOT_JOINTED;\r
136         clustered = 0;\r
137         scale_up = 1.0; \r
138         VectorCopy (vec3_origin, adjust);\r
139         g_fixedwidth = g_fixedheight = 0;\r
140         g_skipmodel = false;\r
141 }\r
142 \r
143 \r
144 void H_printf(char *fmt, ...)\r
145 {\r
146         va_list argptr;\r
147         char    name[1024];\r
148 \r
149         if (!headerouthandle)\r
150         {\r
151                 sprintf (name, "%s/tris.h", cddir);\r
152                 headerouthandle = SafeOpenWrite (name);\r
153                 fprintf(headerouthandle, "// %s\n\n", cddir);\r
154                 fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n");\r
155         }\r
156 \r
157         va_start (argptr, fmt);\r
158         vfprintf (headerouthandle, fmt, argptr);\r
159         va_end (argptr);\r
160 }\r
161 \r
162 #if 1\r
163 /*\r
164 ============\r
165 WriteModelFile\r
166 ============\r
167 */\r
168 void WriteCommonModelFile (FILE *modelouthandle, PartialAliasFrame_t *outFrames)\r
169 {\r
170         int                             i;\r
171         dmdl_t                  modeltemp;\r
172         int                             j, k;\r
173         frame_t                 *in;\r
174         daliasframe_t   *out;\r
175         byte                    buffer[MAX_VERTS*4+128];\r
176         float                   v;\r
177         int                             c_on, c_off;\r
178 \r
179         model.version = ALIAS_VERSION;\r
180         model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];\r
181         model.num_glcmds = numcommands;\r
182         model.ofs_skins = sizeof(dmdl_t);\r
183         model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;\r
184         model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);\r
185         model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);\r
186         model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;\r
187         model.ofs_end = model.ofs_glcmds + model.num_glcmds*sizeof(int);\r
188         //\r
189         // write out the model header\r
190         //\r
191         for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)\r
192                 ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);\r
193 \r
194         SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));\r
195 \r
196         //\r
197         // write out the skin names\r
198         //\r
199         SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);\r
200 \r
201         //\r
202         // write out the texture coordinates\r
203         //\r
204         c_on = c_off = 0;\r
205         for (i=0 ; i<model.num_st ; i++)\r
206         {\r
207                 base_st[i].s = LittleShort (base_st[i].s);\r
208                 base_st[i].t = LittleShort (base_st[i].t);\r
209         }\r
210 \r
211         SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));\r
212 \r
213         //\r
214         // write out the triangles\r
215         //\r
216         for (i=0 ; i<model.num_tris ; i++)\r
217         {\r
218                 int                     j;\r
219                 dtriangle_t     tri;\r
220 \r
221                 for (j=0 ; j<3 ; j++)\r
222                 {\r
223                         tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);\r
224                         tri.index_st[j] = LittleShort (triangles[i].index_st[j]);\r
225                 }\r
226 \r
227                 SafeWrite (modelouthandle, &tri, sizeof(tri));\r
228         }\r
229 \r
230         //\r
231         // write out the frames\r
232         //\r
233         for (i=0 ; i<model.num_frames ; i++)\r
234         {\r
235                 in = &g_frames[i];\r
236                 out = (daliasframe_t *)buffer;\r
237 \r
238                 strcpy (out->name, in->name);\r
239                 for (j=0 ; j<3 ; j++)\r
240                 {\r
241                         out->scale[j] = (in->maxs[j] - in->mins[j])/255;\r
242                         out->translate[j] = in->mins[j];\r
243 \r
244                         if(outFrames)\r
245                         {\r
246                                 outFrames[i].scale[j] = out->scale[j];\r
247                                 outFrames[i].translate[j] = out->translate[j];\r
248                         }\r
249                 }\r
250 \r
251                 for (j=0 ; j<model.num_xyz ; j++)\r
252                 {\r
253                 // all of these are byte values, so no need to deal with endianness\r
254                         out->verts[j].lightnormalindex = in->v[j].lightnormalindex;\r
255 \r
256                         for (k=0 ; k<3 ; k++)\r
257                         {\r
258                         // scale to byte values & min/max check\r
259                                 v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );\r
260 \r
261                         // clamp, so rounding doesn't wrap from 255.6 to 0\r
262                                 if (v > 255.0)\r
263                                         v = 255.0;\r
264                                 if (v < 0)\r
265                                         v = 0;\r
266                                 out->verts[j].v[k] = v;\r
267                         }\r
268                 }\r
269 \r
270                 for (j=0 ; j<3 ; j++)\r
271                 {\r
272                         out->scale[j] = LittleFloat (out->scale[j]);\r
273                         out->translate[j] = LittleFloat (out->translate[j]);\r
274                 }\r
275 \r
276                 SafeWrite (modelouthandle, out, model.framesize);\r
277         }\r
278 \r
279         //\r
280         // write out glcmds\r
281         //\r
282         SafeWrite (modelouthandle, commands, numcommands*4);\r
283 }\r
284 \r
285 /*\r
286 ============\r
287 WriteModelFile\r
288 ============\r
289 */\r
290 void WriteModelFile (FILE *modelouthandle)\r
291 {\r
292         model.ident = IDALIASHEADER;\r
293 \r
294         WriteCommonModelFile(modelouthandle, NULL);\r
295 }\r
296 \r
297 /*\r
298 ============\r
299 WriteJointedModelFile\r
300 ============\r
301 */\r
302 void WriteJointedModelFile (FILE *modelouthandle)\r
303 {\r
304         int                             i;\r
305         int                             j, k;\r
306         frame_t                 *in;\r
307         float                   v;\r
308         IntListNode_t   *current, *toFree;\r
309         PartialAliasFrame_t outFrames[MAX_FRAMES];\r
310 \r
311         model.ident = IDJOINTEDALIASHEADER;\r
312         \r
313         WriteCommonModelFile(modelouthandle, outFrames);\r
314 \r
315         // Skeletal Type\r
316         SafeWrite(modelouthandle, &jointed, sizeof(int));\r
317 \r
318         // number of joints\r
319         SafeWrite(modelouthandle, &numJointsForSkeleton[jointed], sizeof(int));\r
320 \r
321         // number of verts in each cluster\r
322         SafeWrite(modelouthandle, &new_num_verts[1], sizeof(int)*numJointsForSkeleton[jointed]);\r
323 \r
324         // cluster verts\r
325         for(i = 0; i < new_num_verts[0]; ++i)\r
326         {\r
327                 current = vertLists[i];\r
328                 while(current)\r
329                 {\r
330                         SafeWrite (modelouthandle, &current->data, sizeof(int));\r
331                         toFree = current;\r
332                         current = current->next;\r
333                         free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base\r
334                 }\r
335         }\r
336 \r
337         for (i=0 ; i<model.num_frames ; i++)\r
338         {\r
339                 in = &g_frames[i];\r
340 \r
341                 for (j = 0 ; j < new_num_verts[0]; ++j)\r
342                 {\r
343                         for (k=0 ; k<3 ; k++)\r
344                         {\r
345                                 // scale to byte values & min/max check\r
346                                 v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
347 \r
348                                 // clamp, so rounding doesn't wrap from 255.6 to 0\r
349                                 if (v > 255.0)\r
350                                 {\r
351                                         v = 255.0;\r
352                                 }\r
353 \r
354                                 if (v < 0)\r
355                                 {\r
356                                         v = 0;\r
357                                 }\r
358 \r
359                                 // write out origin as a float (there's only a few per model, so it's not really \r
360                                 // a size issue)\r
361                                 SafeWrite (modelouthandle, &v, sizeof(float));\r
362                         }\r
363 \r
364                         for (k=0 ; k<3 ; k++)\r
365                         {\r
366                                 v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
367 \r
368                                 // clamp, so rounding doesn't wrap from 255.6 to 0\r
369                                 if (v > 255.0)\r
370                                 {\r
371                                         v = 255.0;\r
372                                 }\r
373 \r
374                                 if (v < 0)\r
375                                 {\r
376                                         v = 0;\r
377                                 }\r
378 \r
379                                 // write out origin as a float (there's only a few per model, so it's not really \r
380                                 // a size issue)\r
381                                 SafeWrite (modelouthandle, &v, sizeof(float));\r
382                         }\r
383 \r
384                         for (k=0 ; k<3 ; k++)\r
385                         {\r
386                                 v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
387 \r
388                                 // clamp, so rounding doesn't wrap from 255.6 to 0\r
389                                 if (v > 255.0)\r
390                                 {\r
391                                         v = 255.0;\r
392                                 }\r
393 \r
394                                 if (v < 0)\r
395                                 {\r
396                                         v = 0;\r
397                                 }\r
398 \r
399                                 // write out origin as a float (there's only a few per model, so it's not really \r
400                                 // a size issue)\r
401                                 SafeWrite (modelouthandle, &v, sizeof(float));\r
402                         }\r
403                 }\r
404         }\r
405 }\r
406 #else\r
407 /*\r
408 ============\r
409 WriteModelFile\r
410 ============\r
411 */\r
412 static void WriteModelFile (FILE *modelouthandle)\r
413 {\r
414         int                             i;\r
415         dmdl_t                  modeltemp;\r
416         int                             j, k;\r
417         frame_t                 *in;\r
418         daliasframe_t   *out;\r
419         byte                    buffer[MAX_VERTS*4+128];\r
420         float                   v;\r
421         int                             c_on, c_off;\r
422 \r
423         model.ident = IDALIASHEADER;\r
424         model.version = ALIAS_VERSION;\r
425         model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];\r
426         model.num_glcmds = numcommands;\r
427         model.ofs_skins = sizeof(dmdl_t);\r
428         model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;\r
429         model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);\r
430         model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);\r
431         model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;\r
432         model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;\r
433 \r
434         //\r
435         // write out the model header\r
436         //\r
437         for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)\r
438                 ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);\r
439 \r
440         SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));\r
441 \r
442         //\r
443         // write out the skin names\r
444         //\r
445         SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);\r
446 \r
447         //\r
448         // write out the texture coordinates\r
449         //\r
450         c_on = c_off = 0;\r
451         for (i=0 ; i<model.num_st ; i++)\r
452         {\r
453                 base_st[i].s = LittleShort (base_st[i].s);\r
454                 base_st[i].t = LittleShort (base_st[i].t);\r
455         }\r
456 \r
457         SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));\r
458 \r
459         //\r
460         // write out the triangles\r
461         //\r
462         for (i=0 ; i<model.num_tris ; i++)\r
463         {\r
464                 int                     j;\r
465                 dtriangle_t     tri;\r
466 \r
467                 for (j=0 ; j<3 ; j++)\r
468                 {\r
469                         tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);\r
470                         tri.index_st[j] = LittleShort (triangles[i].index_st[j]);\r
471                 }\r
472 \r
473                 SafeWrite (modelouthandle, &tri, sizeof(tri));\r
474         }\r
475 \r
476         //\r
477         // write out the frames\r
478         //\r
479         for (i=0 ; i<model.num_frames ; i++)\r
480         {\r
481                 in = &g_frames[i];\r
482                 out = (daliasframe_t *)buffer;\r
483 \r
484                 strcpy (out->name, in->name);\r
485                 for (j=0 ; j<3 ; j++)\r
486                 {\r
487                         out->scale[j] = (in->maxs[j] - in->mins[j])/255;\r
488                         out->translate[j] = in->mins[j];\r
489                 }\r
490 \r
491                 for (j=0 ; j<model.num_xyz ; j++)\r
492                 {\r
493                 // all of these are byte values, so no need to deal with endianness\r
494                         out->verts[j].lightnormalindex = in->v[j].lightnormalindex;\r
495 \r
496                         for (k=0 ; k<3 ; k++)\r
497                         {\r
498                         // scale to byte values & min/max check\r
499                                 v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );\r
500 \r
501                         // clamp, so rounding doesn't wrap from 255.6 to 0\r
502                                 if (v > 255.0)\r
503                                         v = 255.0;\r
504                                 if (v < 0)\r
505                                         v = 0;\r
506                                 out->verts[j].v[k] = v;\r
507                         }\r
508                 }\r
509 \r
510                 for (j=0 ; j<3 ; j++)\r
511                 {\r
512                         out->scale[j] = LittleFloat (out->scale[j]);\r
513                         out->translate[j] = LittleFloat (out->translate[j]);\r
514                 }\r
515 \r
516                 SafeWrite (modelouthandle, out, model.framesize);\r
517         }\r
518 \r
519         //\r
520         // write out glcmds\r
521         //\r
522         SafeWrite (modelouthandle, commands, numcommands*4);\r
523 }\r
524 #endif\r
525 \r
526 /*\r
527 ===============\r
528 FinishModel\r
529 ===============\r
530 */\r
531 void FinishModel (void)\r
532 {\r
533         FILE            *modelouthandle;\r
534         int                     i;\r
535         char            name[1024];\r
536         \r
537         if (!model.num_frames)\r
538                 return;\r
539         \r
540 //\r
541 // copy to release directory tree if doing a release build\r
542 //\r
543         if (g_release)\r
544         {\r
545                 if (modelname[0])\r
546                         sprintf (name, "%s", modelname);\r
547                 else\r
548                         sprintf (name, "%s/tris.md2", cdpartial);\r
549                 ReleaseFile (name);\r
550 \r
551                 for (i=0 ; i<model.num_skins ; i++)\r
552                 {\r
553                         ReleaseFile (g_skins[i]);\r
554                 }\r
555                 model.num_frames = 0;\r
556                 return;\r
557         }\r
558         \r
559 //\r
560 // write the model output file\r
561 //\r
562         if (modelname[0])\r
563                 sprintf (name, "%s%s", g_outputDir, modelname);\r
564         else\r
565                 sprintf (name, "%s/tris.md2", g_outputDir);\r
566         printf ("saving to %s\n", name);\r
567         CreatePath (name);\r
568         modelouthandle = SafeOpenWrite (name);\r
569 \r
570 #if 1\r
571         if(jointed != NOT_JOINTED)\r
572                 WriteJointedModelFile(modelouthandle);\r
573         else\r
574 #endif\r
575                 WriteModelFile(modelouthandle);\r
576         \r
577         printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);\r
578         printf ("First frame boundaries:\n");\r
579         printf ("       minimum x: %3f\n", g_frames[0].mins[0]);\r
580         printf ("       maximum x: %3f\n", g_frames[0].maxs[0]);\r
581         printf ("       minimum y: %3f\n", g_frames[0].mins[1]);\r
582         printf ("       maximum y: %3f\n", g_frames[0].maxs[1]);\r
583         printf ("       minimum z: %3f\n", g_frames[0].mins[2]);\r
584         printf ("       maximum z: %3f\n", g_frames[0].maxs[2]);\r
585         printf ("%4d vertices\n", model.num_xyz);\r
586         printf ("%4d triangles\n", model.num_tris);\r
587         printf ("%4d frame\n", model.num_frames);\r
588         printf ("%4d glverts\n", numglverts);\r
589         printf ("%4d glcmd\n", model.num_glcmds);\r
590         printf ("%4d skins\n", model.num_skins);\r
591         printf ("file size: %d\n", (int)ftell (modelouthandle) );\r
592         printf ("---------------------\n");\r
593         \r
594         fclose (modelouthandle);\r
595 \r
596         // finish writing header file\r
597         H_printf("\n");\r
598 \r
599         // scale_up is usefull to allow step distances to be adjusted\r
600         H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);\r
601 \r
602         fclose (headerouthandle);\r
603         headerouthandle = NULL;\r
604 }\r
605 \r
606 \r
607 /*\r
608 =================================================================\r
609 \r
610 ALIAS MODEL DISPLAY LIST GENERATION\r
611 \r
612 =================================================================\r
613 */\r
614 \r
615 int             strip_xyz[128];\r
616 int             strip_st[128];\r
617 int             strip_tris[128];\r
618 int             stripcount;\r
619 \r
620 /*\r
621 ================\r
622 StripLength\r
623 ================\r
624 */\r
625 static int      StripLength (int starttri, int startv)\r
626 {\r
627         int                     m1, m2;\r
628         int                     st1, st2;\r
629         int                     j;\r
630         dtriangle_t     *last, *check;\r
631         int                     k;\r
632 \r
633         used[starttri] = 2;\r
634 \r
635         last = &triangles[starttri];\r
636 \r
637         strip_xyz[0] = last->index_xyz[(startv)%3];\r
638         strip_xyz[1] = last->index_xyz[(startv+1)%3];\r
639         strip_xyz[2] = last->index_xyz[(startv+2)%3];\r
640         strip_st[0] = last->index_st[(startv)%3];\r
641         strip_st[1] = last->index_st[(startv+1)%3];\r
642         strip_st[2] = last->index_st[(startv+2)%3];\r
643 \r
644         strip_tris[0] = starttri;\r
645         stripcount = 1;\r
646 \r
647         m1 = last->index_xyz[(startv+2)%3];\r
648         st1 = last->index_st[(startv+2)%3];\r
649         m2 = last->index_xyz[(startv+1)%3];\r
650         st2 = last->index_st[(startv+1)%3];\r
651 \r
652         // look for a matching triangle\r
653 nexttri:\r
654         for (j=starttri+1, check=&triangles[starttri+1]\r
655                 ; j<model.num_tris ; j++, check++)\r
656         {\r
657                 for (k=0 ; k<3 ; k++)\r
658                 {\r
659                         if (check->index_xyz[k] != m1)\r
660                                 continue;\r
661                         if (check->index_st[k] != st1)\r
662                                 continue;\r
663                         if (check->index_xyz[ (k+1)%3 ] != m2)\r
664                                 continue;\r
665                         if (check->index_st[ (k+1)%3 ] != st2)\r
666                                 continue;\r
667 \r
668                         // this is the next part of the fan\r
669 \r
670                         // if we can't use this triangle, this tristrip is done\r
671                         if (used[j])\r
672                                 goto done;\r
673 \r
674                         // the new edge\r
675                         if (stripcount & 1)\r
676                         {\r
677                                 m2 = check->index_xyz[ (k+2)%3 ];\r
678                                 st2 = check->index_st[ (k+2)%3 ];\r
679                         }\r
680                         else\r
681                         {\r
682                                 m1 = check->index_xyz[ (k+2)%3 ];\r
683                                 st1 = check->index_st[ (k+2)%3 ];\r
684                         }\r
685 \r
686                         strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];\r
687                         strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];\r
688                         strip_tris[stripcount] = j;\r
689                         stripcount++;\r
690 \r
691                         used[j] = 2;\r
692                         goto nexttri;\r
693                 }\r
694         }\r
695 done:\r
696 \r
697         // clear the temp used flags\r
698         for (j=starttri+1 ; j<model.num_tris ; j++)\r
699                 if (used[j] == 2)\r
700                         used[j] = 0;\r
701 \r
702         return stripcount;\r
703 }\r
704 \r
705 \r
706 /*\r
707 ===========\r
708 FanLength\r
709 ===========\r
710 */\r
711 static int      FanLength (int starttri, int startv)\r
712 {\r
713         int             m1, m2;\r
714         int             st1, st2;\r
715         int             j;\r
716         dtriangle_t     *last, *check;\r
717         int             k;\r
718 \r
719         used[starttri] = 2;\r
720 \r
721         last = &triangles[starttri];\r
722 \r
723         strip_xyz[0] = last->index_xyz[(startv)%3];\r
724         strip_xyz[1] = last->index_xyz[(startv+1)%3];\r
725         strip_xyz[2] = last->index_xyz[(startv+2)%3];\r
726         strip_st[0] = last->index_st[(startv)%3];\r
727         strip_st[1] = last->index_st[(startv+1)%3];\r
728         strip_st[2] = last->index_st[(startv+2)%3];\r
729 \r
730         strip_tris[0] = starttri;\r
731         stripcount = 1;\r
732 \r
733         m1 = last->index_xyz[(startv+0)%3];\r
734         st1 = last->index_st[(startv+0)%3];\r
735         m2 = last->index_xyz[(startv+2)%3];\r
736         st2 = last->index_st[(startv+2)%3];\r
737 \r
738 \r
739         // look for a matching triangle\r
740 nexttri:\r
741         for (j=starttri+1, check=&triangles[starttri+1] \r
742                 ; j<model.num_tris ; j++, check++)\r
743         {\r
744                 for (k=0 ; k<3 ; k++)\r
745                 {\r
746                         if (check->index_xyz[k] != m1)\r
747                                 continue;\r
748                         if (check->index_st[k] != st1)\r
749                                 continue;\r
750                         if (check->index_xyz[ (k+1)%3 ] != m2)\r
751                                 continue;\r
752                         if (check->index_st[ (k+1)%3 ] != st2)\r
753                                 continue;\r
754 \r
755                         // this is the next part of the fan\r
756 \r
757                         // if we can't use this triangle, this tristrip is done\r
758                         if (used[j])\r
759                                 goto done;\r
760 \r
761                         // the new edge\r
762                         m2 = check->index_xyz[ (k+2)%3 ];\r
763                         st2 = check->index_st[ (k+2)%3 ];\r
764 \r
765                         strip_xyz[stripcount+2] = m2;\r
766                         strip_st[stripcount+2] = st2;\r
767                         strip_tris[stripcount] = j;\r
768                         stripcount++;\r
769 \r
770                         used[j] = 2;\r
771                         goto nexttri;\r
772                 }\r
773         }\r
774 done:\r
775 \r
776         // clear the temp used flags\r
777         for (j=starttri+1 ; j<model.num_tris ; j++)\r
778                 if (used[j] == 2)\r
779                         used[j] = 0;\r
780 \r
781         return stripcount;\r
782 }\r
783 \r
784 \r
785 \r
786 /*\r
787 ================\r
788 BuildGlCmds\r
789 \r
790 Generate a list of trifans or strips\r
791 for the model, which holds for all frames\r
792 ================\r
793 */\r
794 static void BuildGlCmds (void)\r
795 {\r
796         int             i, j, k;\r
797         int             startv;\r
798         float   s, t;\r
799         int             len, bestlen, besttype;\r
800         int             best_xyz[1024];\r
801         int             best_st[1024];\r
802         int             best_tris[1024];\r
803         int             type;\r
804 \r
805         //\r
806         // build tristrips\r
807         //\r
808         numcommands = 0;\r
809         numglverts = 0;\r
810         memset (used, 0, sizeof(used));\r
811         for (i=0 ; i<model.num_tris ; i++)\r
812         {\r
813                 // pick an unused triangle and start the trifan\r
814                 if (used[i])\r
815                         continue;\r
816 \r
817                 bestlen = 0;\r
818                 for (type = 0 ; type < 2 ; type++)\r
819 //      type = 1;\r
820                 {\r
821                         for (startv =0 ; startv < 3 ; startv++)\r
822                         {\r
823                                 if (type == 1)\r
824                                         len = StripLength (i, startv);\r
825                                 else\r
826                                         len = FanLength (i, startv);\r
827                                 if (len > bestlen)\r
828                                 {\r
829                                         besttype = type;\r
830                                         bestlen = len;\r
831                                         for (j=0 ; j<bestlen+2 ; j++)\r
832                                         {\r
833                                                 best_st[j] = strip_st[j];\r
834                                                 best_xyz[j] = strip_xyz[j];\r
835                                         }\r
836                                         for (j=0 ; j<bestlen ; j++)\r
837                                                 best_tris[j] = strip_tris[j];\r
838                                 }\r
839                         }\r
840                 }\r
841 \r
842                 // mark the tris on the best strip/fan as used\r
843                 for (j=0 ; j<bestlen ; j++)\r
844                         used[best_tris[j]] = 1;\r
845 \r
846                 if (besttype == 1)\r
847                         commands[numcommands++] = (bestlen+2);\r
848                 else\r
849                         commands[numcommands++] = -(bestlen+2);\r
850 \r
851                 numglverts += bestlen+2;\r
852 \r
853                 for (j=0 ; j<bestlen+2 ; j++)\r
854                 {\r
855                         // emit a vertex into the reorder buffer\r
856                         k = best_st[j];\r
857 \r
858                         // emit s/t coords into the commands stream\r
859                         s = base_st[k].s;\r
860                         t = base_st[k].t;\r
861 \r
862                         s = (s + 0.5) / model.skinwidth;\r
863                         t = (t + 0.5) / model.skinheight;\r
864 \r
865                         *(float *)&commands[numcommands++] = s;\r
866                         *(float *)&commands[numcommands++] = t;\r
867                         *(int *)&commands[numcommands++] = best_xyz[j];\r
868                 }\r
869         }\r
870 \r
871         commands[numcommands++] = 0;            // end of list marker\r
872 }\r
873 \r
874 \r
875 /*\r
876 ===============================================================\r
877 \r
878 BASE FRAME SETUP\r
879 \r
880 ===============================================================\r
881 */\r
882 \r
883 /*\r
884 ============\r
885 BuildST\r
886 \r
887 Builds the triangle_st array for the base frame and\r
888 model.skinwidth / model.skinheight\r
889 \r
890   FIXME: allow this to be loaded from a file for\r
891   arbitrary mappings\r
892 ============\r
893 */\r
894 #if 0\r
895 static void OldBuildST (triangle_t *ptri, int numtri)\r
896 {\r
897         int                     i, j;\r
898         int                     width, height, iwidth, iheight, swidth;\r
899         float           basex, basey;\r
900         float           s_scale, t_scale;\r
901         float           scale;\r
902         vec3_t          mins, maxs;\r
903         float           *pbasevert;\r
904         vec3_t          vtemp1, vtemp2, normal;\r
905 \r
906         //\r
907         // find bounds of all the verts on the base frame\r
908         //\r
909         ClearBounds (mins, maxs);\r
910         \r
911         for (i=0 ; i<numtri ; i++)\r
912                 for (j=0 ; j<3 ; j++)\r
913                         AddPointToBounds (ptri[i].verts[j], mins, maxs);\r
914         \r
915         for (i=0 ; i<3 ; i++)\r
916         {\r
917                 mins[i] = floor(mins[i]);\r
918                 maxs[i] = ceil(maxs[i]);\r
919         }\r
920         \r
921         width = maxs[0] - mins[0];\r
922         height = maxs[2] - mins[2];\r
923 \r
924         if (!g_fixedwidth)\r
925         {       // old style\r
926                 scale = 8;\r
927                 if (width*scale >= 150)\r
928                         scale = 150.0 / width;  \r
929                 if (height*scale >= 190)\r
930                         scale = 190.0 / height;\r
931 \r
932                 s_scale = t_scale = scale;\r
933 \r
934                 iwidth = ceil(width*s_scale);\r
935                 iheight = ceil(height*t_scale);\r
936 \r
937                 iwidth += 4;\r
938                 iheight += 4;\r
939         }\r
940         else\r
941         {       // new style\r
942                 iwidth = g_fixedwidth / 2;\r
943                 iheight = g_fixedheight;\r
944 \r
945                 s_scale = (float)(iwidth-4) / width;\r
946                 t_scale = (float)(iheight-4) / height;\r
947         }\r
948 \r
949 //\r
950 // determine which side of each triangle to map the texture to\r
951 //\r
952         for (i=0 ; i<numtri ; i++)\r
953         {\r
954                 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
955                 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
956                 CrossProduct (vtemp1, vtemp2, normal);\r
957 \r
958                 if (normal[1] > 0)\r
959                 {\r
960                         basex = iwidth + 2;\r
961                 }\r
962                 else\r
963                 {\r
964                         basex = 2;\r
965                 }\r
966                 basey = 2;\r
967                 \r
968                 for (j=0 ; j<3 ; j++)\r
969                 {\r
970                         pbasevert = ptri[i].verts[j];\r
971 \r
972                         triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);\r
973                         triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);\r
974                 }\r
975         }\r
976 \r
977 // make the width a multiple of 4; some hardware requires this, and it ensures\r
978 // dword alignment for each scan\r
979         swidth = iwidth*2;\r
980         model.skinwidth = (swidth + 3) & ~3;\r
981         model.skinheight = iheight;\r
982 }\r
983 #endif\r
984 \r
985 //==========================================================================\r
986 //\r
987 // DrawScreen\r
988 //\r
989 //==========================================================================\r
990 \r
991 void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight)\r
992 {\r
993         int i;\r
994         byte *scrpos;\r
995         char buffer[256];\r
996 \r
997         // Divider\r
998         scrpos = &pic[(INFO_Y-2)*SKINPAGE_WIDTH];\r
999         for(i = 0; i < SKINPAGE_WIDTH; i++)\r
1000         {\r
1001                 *scrpos++ = 255;\r
1002         }\r
1003 \r
1004         sprintf(buffer, "GENSKIN:  ");\r
1005         DrawTextChar(16, INFO_Y, buffer);\r
1006         \r
1007         sprintf(buffer, "( %03d * %03d )   SCALE %f %f, SKINWIDTH %d,"\r
1008                 " SKINHEIGHT %d", (int)ScaleWidth, (int)ScaleHeight, s_scale, t_scale, (int)iwidth*2, (int)iheight);\r
1009         DrawTextChar(80, INFO_Y, buffer);\r
1010 }\r
1011 \r
1012 /*\r
1013 ============\r
1014 BuildST\r
1015 \r
1016 Builds the triangle_st array for the base frame and\r
1017 model.skinwidth / model.skinheight\r
1018 \r
1019   FIXME: allow this to be loaded from a file for\r
1020   arbitrary mappings\r
1021 ============\r
1022 */\r
1023 void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)\r
1024 {\r
1025         int                     i, j;\r
1026         int                     width, height, iwidth, iheight, swidth;\r
1027         float           basex, basey;\r
1028         float           scale;\r
1029         vec3_t          mins, maxs;\r
1030         float           *pbasevert;\r
1031         vec3_t          vtemp1, vtemp2, normal;\r
1032         float           s_scale, t_scale;\r
1033         float           scWidth;\r
1034         float           scHeight;\r
1035 \r
1036         //\r
1037         // find bounds of all the verts on the base frame\r
1038         //\r
1039         ClearBounds (mins, maxs);\r
1040         \r
1041         for (i=0 ; i<numtri ; i++)\r
1042                 for (j=0 ; j<3 ; j++)\r
1043                         AddPointToBounds (ptri[i].verts[j], mins, maxs);\r
1044         \r
1045         for (i=0 ; i<3 ; i++)\r
1046         {\r
1047                 mins[i] = floor(mins[i]);\r
1048                 maxs[i] = ceil(maxs[i]);\r
1049         }\r
1050         \r
1051         width = maxs[0] - mins[0];\r
1052         height = maxs[2] - mins[2];\r
1053 \r
1054 \r
1055         scWidth = (ScaleWidth/2)*SCALE_ADJUST_FACTOR;\r
1056         scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;\r
1057 \r
1058         scale = scWidth/width;\r
1059 \r
1060         if(height*scale >= scHeight)\r
1061         {\r
1062                 scale = scHeight/height;\r
1063         }\r
1064 \r
1065         iwidth = ceil(width*scale)+4;\r
1066         iheight = ceil(height*scale)+4;\r
1067 \r
1068         s_scale = (float)(iwidth-4) / width;\r
1069         t_scale = (float)(iheight-4) / height;\r
1070         t_scale = s_scale;\r
1071 \r
1072         if (DrawSkin)\r
1073                 DrawScreen(s_scale, t_scale, iwidth, iheight);\r
1074 \r
1075 \r
1076 /*      if (!g_fixedwidth)\r
1077         {       // old style\r
1078                 scale = 8;\r
1079                 if (width*scale >= 150)\r
1080                         scale = 150.0 / width;  \r
1081                 if (height*scale >= 190)\r
1082                         scale = 190.0 / height;\r
1083 \r
1084                 s_scale = t_scale = scale;\r
1085 \r
1086                 iwidth = ceil(width*s_scale);\r
1087                 iheight = ceil(height*t_scale);\r
1088 \r
1089                 iwidth += 4;\r
1090                 iheight += 4;\r
1091         }\r
1092         else\r
1093         {       // new style\r
1094                 iwidth = g_fixedwidth / 2;\r
1095                 iheight = g_fixedheight;\r
1096 \r
1097                 s_scale = (float)(iwidth-4) / width;\r
1098                 t_scale = (float)(iheight-4) / height;\r
1099         }*/\r
1100 \r
1101 //\r
1102 // determine which side of each triangle to map the texture to\r
1103 //\r
1104         for (i=0 ; i<numtri ; i++)\r
1105         {\r
1106                 if (ptri[i].HasUV)\r
1107                 {\r
1108                         for (j=0 ; j<3 ; j++)\r
1109                         {\r
1110                                 triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*iwidth);\r
1111                                 triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*iheight);\r
1112                         }\r
1113                 }\r
1114                 else\r
1115                 {\r
1116                         VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
1117                         VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
1118                         CrossProduct (vtemp1, vtemp2, normal);\r
1119 \r
1120                         if (normal[1] > 0)\r
1121                         {\r
1122                                 basex = iwidth + 2;\r
1123                         }\r
1124                         else\r
1125                         {\r
1126                                 basex = 2;\r
1127                         }\r
1128                         basey = 2;\r
1129                         \r
1130                         for (j=0 ; j<3 ; j++)\r
1131                         {\r
1132                                 pbasevert = ptri[i].verts[j];\r
1133 \r
1134                                 triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);\r
1135                                 triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);\r
1136                         }\r
1137                 }\r
1138 \r
1139                 DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],\r
1140                         triangle_st[i][1][0], triangle_st[i][1][1]);\r
1141                 DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],\r
1142                         triangle_st[i][2][0], triangle_st[i][2][1]);\r
1143                 DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],\r
1144                         triangle_st[i][0][0], triangle_st[i][0][1]); \r
1145         }\r
1146 \r
1147 // make the width a multiple of 4; some hardware requires this, and it ensures\r
1148 // dword alignment for each scan\r
1149 \r
1150         swidth = iwidth*2;\r
1151         model.skinwidth = (swidth + 3) & ~3;\r
1152         model.skinheight = iheight;\r
1153 }\r
1154 \r
1155 \r
1156 static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters, \r
1157         IntListNode_t **vertLists, int *num_verts, int *new_num_verts)\r
1158 {\r
1159         int i, j;\r
1160         IntListNode_t *next;\r
1161 \r
1162         for(j = 0; j < num_verts[0]; ++j)\r
1163         {\r
1164                 for(i = 0; i < num_verts[j+1]; ++i)\r
1165                 {\r
1166                         if(clusters[j][i] == oldindex)\r
1167                         {\r
1168                                 ++new_num_verts[j+1];\r
1169 \r
1170                                 next = vertLists[j];\r
1171 \r
1172                                 vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");\r
1173                                 // Currently freed in WriteJointedModelFile only\r
1174 \r
1175                                 vertLists[j]->data = newIndex;\r
1176                                 vertLists[j]->next = next;\r
1177                         }\r
1178                 }\r
1179         }\r
1180 }\r
1181 \r
1182 /*\r
1183 =================\r
1184 Cmd_Base\r
1185 =================\r
1186 */\r
1187 void Cmd_Base (void)\r
1188 {\r
1189         vec3_t          base_xyz[MAX_VERTS];\r
1190         triangle_t      *ptri;\r
1191         int                     i, j, k;\r
1192 #if 1\r
1193 #else\r
1194         int             time1;\r
1195 #endif\r
1196         char    file1[1024];\r
1197         char    file2[1024];\r
1198 \r
1199         GetScriptToken (false);\r
1200 \r
1201         if (g_skipmodel || g_release || g_archive)\r
1202                 return;\r
1203 \r
1204         printf ("---------------------\n");\r
1205 #if 1\r
1206         sprintf (file1, "%s/%s", cdpartial, token);\r
1207         printf ("%s  ", file1);\r
1208 \r
1209         ExpandPathAndArchive (file1);\r
1210 \r
1211         sprintf (file1, "%s/%s", cddir, token);\r
1212 #else\r
1213         sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);\r
1214         printf ("%s\n", file1);\r
1215 \r
1216         ExpandPathAndArchive (file1);\r
1217 \r
1218         sprintf (file1, "%s/%s.%s", cddir, token, trifileext);\r
1219 \r
1220         time1 = FileTime (file1);\r
1221         if (time1 == -1)\r
1222                 Error ("%s doesn't exist", file1);\r
1223 #endif\r
1224 //\r
1225 // load the base triangles\r
1226 //\r
1227         if (do3ds)\r
1228                 Load3DSTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);\r
1229         else\r
1230                 LoadTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);\r
1231 \r
1232 \r
1233         GetScriptToken (false);\r
1234         sprintf (file2, "%s/%s.pcx", cddir, token);\r
1235 //      sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);\r
1236 \r
1237         printf ("skin: %s\n", file2);\r
1238         Load256Image (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);\r
1239 \r
1240         if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)\r
1241         {\r
1242                 if (g_allow_newskin)\r
1243                 {\r
1244                         ScaleWidth = BaseWidth;\r
1245                         ScaleHeight = BaseHeight;\r
1246                 }\r
1247                 else\r
1248                 {\r
1249                         Error("Invalid skin page size: (%d,%d) should be (%d,%d)",\r
1250                                 BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);\r
1251                 }\r
1252         }\r
1253         else\r
1254         {\r
1255                 ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,\r
1256                         ENCODED_WIDTH_Y);\r
1257                 ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,\r
1258                         ENCODED_HEIGHT_Y);\r
1259         }\r
1260 \r
1261 //\r
1262 // get the ST values\r
1263 //\r
1264         BuildST (ptri, model.num_tris,false);\r
1265 \r
1266 //\r
1267 // run through all the base triangles, storing each unique vertex in the\r
1268 // base vertex list and setting the indirect triangles to point to the base\r
1269 // vertices\r
1270 //\r
1271         for (i=0 ; i<model.num_tris ; i++)\r
1272         {\r
1273                 for (j=0 ; j<3 ; j++)\r
1274                 {\r
1275                         // get the xyz index\r
1276                         for (k=0 ; k<model.num_xyz ; k++)\r
1277                                 if (VectorCompare (ptri[i].verts[j], base_xyz[k]))\r
1278                                         break;  // this vertex is already in the base vertex list\r
1279 \r
1280                         if (k == model.num_xyz)\r
1281                         { // new index\r
1282                                 VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);\r
1283 \r
1284                                 if(clustered)\r
1285                                         ReplaceClusterIndex(k, ptri[i].indicies[j], (int **)&clusters, (IntListNode_t **)&vertLists, (int *)&num_verts, (int *)&new_num_verts);\r
1286 \r
1287                                 model.num_xyz++;\r
1288                         }\r
1289 \r
1290                         triangles[i].index_xyz[j] = k;\r
1291 \r
1292                         // get the st index\r
1293                         for (k=0 ; k<model.num_st ; k++)\r
1294                                 if (triangle_st[i][j][0] == base_st[k].s\r
1295                                 && triangle_st[i][j][1] == base_st[k].t)\r
1296                                         break;  // this vertex is already in the base vertex list\r
1297 \r
1298                         if (k == model.num_st)\r
1299                         { // new index\r
1300                                 base_st[model.num_st].s = triangle_st[i][j][0];\r
1301                                 base_st[model.num_st].t = triangle_st[i][j][1];\r
1302                                 model.num_st++;\r
1303                         }\r
1304 \r
1305                         triangles[i].index_st[j] = k;\r
1306                 }\r
1307         }\r
1308 \r
1309         // build triangle strips / fans\r
1310         BuildGlCmds ();\r
1311 }\r
1312 \r
1313 //===============================================================\r
1314 \r
1315 char    *FindFrameFile (char *frame)\r
1316 {\r
1317         int                             time1;\r
1318         char                    file1[1024];\r
1319         static char             retname[1024];\r
1320         char                    base[32];\r
1321         char                    suffix[32];\r
1322         char                    *s;\r
1323 \r
1324         if (strstr (frame, "."))\r
1325                 return frame;           // allready in dot format\r
1326 \r
1327         // split 'run1' into 'run' and '1'\r
1328         s = frame + strlen(frame)-1;\r
1329 \r
1330         while (s != frame && *s >= '0' && *s <= '9')\r
1331                 s--;\r
1332 \r
1333         strcpy (suffix, s+1);\r
1334         strcpy (base, frame);\r
1335         base[s-frame+1] = 0;\r
1336 \r
1337         sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "hrc");\r
1338         time1 = FileTime (file1);\r
1339         if (time1 != -1)\r
1340         {\r
1341                 sprintf (retname, "%s%s.%s", base, suffix, "hrc");\r
1342                 return retname;\r
1343         }\r
1344 \r
1345         sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "asc");\r
1346         time1 = FileTime (file1);\r
1347         if (time1 != -1)\r
1348         {\r
1349                 sprintf (retname, "%s%s.%s", base, suffix, "asc");\r
1350                 return retname;\r
1351         }\r
1352 \r
1353         sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "tri");\r
1354         time1 = FileTime (file1);\r
1355         if (time1 != -1)\r
1356         {\r
1357                 sprintf (retname, "%s%s.%s", base, suffix, "tri");\r
1358                 return retname;\r
1359         }\r
1360 \r
1361         sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "3ds");\r
1362         time1 = FileTime (file1);\r
1363         if (time1 != -1)\r
1364         {\r
1365                 sprintf (retname, "%s%s.%s", base, suffix, "3ds");\r
1366                 return retname;\r
1367         }\r
1368 \r
1369         sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "htr");\r
1370         time1 = FileTime (file1);\r
1371         if (time1 != -1)\r
1372         {\r
1373                 sprintf (retname, "%s%s.%s", base, suffix, "htr");\r
1374                 return retname;\r
1375         }\r
1376 \r
1377         // check for 'run.1'\r
1378         sprintf (file1, "%s/%s.%s",cddir, base, suffix);\r
1379         time1 = FileTime (file1);\r
1380         if (time1 != -1)\r
1381         {\r
1382                 sprintf (retname, "%s.%s", base, suffix);\r
1383                 return retname;\r
1384         }\r
1385 \r
1386         Error ("frame %s could not be found",frame);\r
1387         return NULL;\r
1388 }\r
1389 \r
1390 /*\r
1391 ===============\r
1392 GrabFrame\r
1393 ===============\r
1394 */\r
1395 static void GrabFrame (char *frame)\r
1396 {\r
1397         triangle_t              *ptri;\r
1398         int                             i, j;\r
1399         trivert_t               *ptrivert;\r
1400         int                             num_tris;\r
1401         char                    file1[1024];\r
1402         frame_t                 *fr;\r
1403         vertexnormals_t vnorms[MAX_VERTS];\r
1404         int                             index_xyz;\r
1405         char                    *framefile;\r
1406 \r
1407         // the frame 'run1' will be looked for as either\r
1408         // run.1 or run1.tri, so the new alias sequence save\r
1409         // feature an be used\r
1410         framefile = FindFrameFile (frame);\r
1411 \r
1412         sprintf (file1, "%s/%s", cdarchive, framefile);\r
1413         ExpandPathAndArchive (file1);\r
1414 \r
1415         sprintf (file1, "%s/%s",cddir, framefile);\r
1416 \r
1417         printf ("grabbing %s  ", file1);\r
1418 \r
1419         if (model.num_frames >= MAX_FRAMES)\r
1420                 Error ("model.num_frames >= MAX_FRAMES");\r
1421         fr = &g_frames[model.num_frames];\r
1422         model.num_frames++;\r
1423 \r
1424         strcpy (fr->name, frame);\r
1425 \r
1426 //\r
1427 // load the frame\r
1428 //\r
1429         if (do3ds)\r
1430                 Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);\r
1431         else\r
1432                 LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);\r
1433 \r
1434         if (num_tris != model.num_tris)\r
1435                 Error ("%s: number of triangles doesn't match base frame\n", file1);\r
1436 \r
1437 //\r
1438 // allocate storage for the frame's vertices\r
1439 //\r
1440         ptrivert = fr->v;\r
1441 \r
1442         for (i=0 ; i<model.num_xyz ; i++)\r
1443         {\r
1444                 vnorms[i].numnormals = 0;\r
1445                 VectorClear (vnorms[i].normalsum);\r
1446         }\r
1447         ClearBounds (fr->mins, fr->maxs);\r
1448 \r
1449 //\r
1450 // store the frame's vertices in the same order as the base. This assumes the\r
1451 // triangles and vertices in this frame are in exactly the same order as in the\r
1452 // base\r
1453 //\r
1454         for (i=0 ; i<num_tris ; i++)\r
1455         {\r
1456                 vec3_t  vtemp1, vtemp2, normal;\r
1457                 float   ftemp;\r
1458 \r
1459                 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
1460                 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
1461                 CrossProduct (vtemp1, vtemp2, normal);\r
1462 \r
1463                 VectorNormalize (normal, normal);\r
1464 \r
1465         // rotate the normal so the model faces down the positive x axis\r
1466                 ftemp = normal[0];\r
1467                 normal[0] = -normal[1];\r
1468                 normal[1] = ftemp;\r
1469 \r
1470                 for (j=0 ; j<3 ; j++)\r
1471                 {\r
1472                         index_xyz = triangles[i].index_xyz[j];\r
1473 \r
1474                 // rotate the vertices so the model faces down the positive x axis\r
1475                 // also adjust the vertices to the desired origin\r
1476                         ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +\r
1477                                                                                 adjust[0];\r
1478                         ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +\r
1479                                                                                 adjust[1];\r
1480                         ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +\r
1481                                                                                 adjust[2];\r
1482 \r
1483                         AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);\r
1484 \r
1485                         VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);\r
1486                         vnorms[index_xyz].numnormals++;\r
1487                 }\r
1488         }\r
1489 \r
1490 //\r
1491 // calculate the vertex normals, match them to the template list, and store the\r
1492 // index of the best match\r
1493 //\r
1494         for (i=0 ; i<model.num_xyz ; i++)\r
1495         {\r
1496                 int             j;\r
1497                 vec3_t  v;\r
1498                 float   maxdot;\r
1499                 int             maxdotindex;\r
1500                 int             c;\r
1501 \r
1502                 c = vnorms[i].numnormals;\r
1503                 if (!c)\r
1504                         Error ("Vertex with no triangles attached");\r
1505 \r
1506                 VectorScale (vnorms[i].normalsum, 1.0/c, v);\r
1507                 VectorNormalize (v, v);\r
1508 \r
1509                 maxdot = -999999.0;\r
1510                 maxdotindex = -1;\r
1511 \r
1512                 for (j=0 ; j<NUMVERTEXNORMALS ; j++)\r
1513                 {\r
1514                         float   dot;\r
1515 \r
1516                         dot = DotProduct (v, avertexnormals[j]);\r
1517                         if (dot > maxdot)\r
1518                         {\r
1519                                 maxdot = dot;\r
1520                                 maxdotindex = j;\r
1521                         }\r
1522                 }\r
1523 \r
1524                 ptrivert[i].lightnormalindex = maxdotindex;\r
1525         }\r
1526 \r
1527         free (ptri);\r
1528 }\r
1529 \r
1530 /*\r
1531 ===============\r
1532 GrabJointedFrame\r
1533 ===============\r
1534 */\r
1535 void GrabJointedFrame(char *frame)\r
1536 {\r
1537         char    file1[1024];\r
1538         char    *framefile;\r
1539         frame_t         *fr;\r
1540 \r
1541         framefile = FindFrameFile (frame);\r
1542 \r
1543         sprintf (file1, "%s/%s", cdarchive, framefile);\r
1544         ExpandPathAndArchive (file1);\r
1545 \r
1546         sprintf (file1, "%s/%s",cddir, framefile);\r
1547 \r
1548         printf ("grabbing %s\n", file1);\r
1549 \r
1550         fr = &g_frames[model.num_frames - 1]; // last frame read in\r
1551 \r
1552         LoadJointList(file1, fr->joints, jointed);\r
1553 }\r
1554 \r
1555 /*\r
1556 ===============\r
1557 GrabGlobals\r
1558 ===============\r
1559 */\r
1560 void GrabGlobals(char *frame)\r
1561 {\r
1562         char    file1[1024];\r
1563         char    *framefile;\r
1564         frame_t         *fr;\r
1565 \r
1566         framefile = FindFrameFile (frame);\r
1567 \r
1568         sprintf (file1, "%s/%s", cdarchive, framefile);\r
1569         ExpandPathAndArchive (file1);\r
1570 \r
1571         sprintf (file1, "%s/%s",cddir, framefile);\r
1572 \r
1573         printf ("grabbing %s\n", file1);\r
1574 \r
1575         fr = &g_frames[model.num_frames - 1]; // last frame read in\r
1576 \r
1577         LoadGlobals(file1);\r
1578 }\r
1579 \r
1580 /*\r
1581 ===============\r
1582 Cmd_Frame       \r
1583 ===============\r
1584 */\r
1585 void Cmd_Frame (void)\r
1586 {\r
1587         while (ScriptTokenAvailable())\r
1588         {\r
1589                 GetScriptToken (false);\r
1590                 if (g_skipmodel)\r
1591                         continue;\r
1592                 if (g_release || g_archive)\r
1593                 {\r
1594                         model.num_frames = 1;   // don't skip the writeout\r
1595                         continue;\r
1596                 }\r
1597 \r
1598                 H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames);\r
1599 \r
1600                 GrabFrame (token);\r
1601         }\r
1602 }\r
1603 \r
1604 /*\r
1605 ===============\r
1606 Cmd_Skin\r
1607 \r
1608 Skins aren't actually stored in the file, only a reference\r
1609 is saved out to the header file.\r
1610 ===============\r
1611 */\r
1612 void Cmd_Skin (void)\r
1613 {\r
1614         byte    *palette;\r
1615         byte    *pixels;\r
1616         int             width, height;\r
1617         byte    *cropped;\r
1618         int             y;\r
1619         char    name[1024], savename[1024];\r
1620 \r
1621         GetScriptToken (false);\r
1622 \r
1623         if (model.num_skins == MAX_MD2SKINS)\r
1624                 Error ("model.num_skins == MAX_MD2SKINS");\r
1625 \r
1626         if (g_skipmodel)\r
1627                 return;\r
1628 \r
1629 #if 1\r
1630         sprintf (name, "%s/%s.pcx", cddir, token);\r
1631         sprintf (savename, "%s/!%s.pcx", g_outputDir, token);\r
1632         sprintf (g_skins[model.num_skins], "%s/!%s.pcx", cdpartial, token);\r
1633 #else\r
1634         sprintf (name, "%s/%s.lbm", cdarchive, token);\r
1635         strcpy (name, ExpandPathAndArchive( name ) );\r
1636 //      sprintf (name, "%s/%s.lbm", cddir, token);\r
1637 \r
1638         if (ScriptTokenAvailable())\r
1639         {\r
1640                 GetScriptToken (false);\r
1641                 sprintf (g_skins[model.num_skins], "%s.pcx", token);\r
1642                 sprintf (savename, "%s%s.pcx", g_outputDir, g_skins[model.num_skins]);\r
1643         }\r
1644         else\r
1645         {\r
1646                 sprintf (savename, "%s/%s.pcx", g_outputDir, token);\r
1647                 sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);\r
1648         }\r
1649 #endif\r
1650 \r
1651         model.num_skins++;\r
1652 \r
1653         if (g_skipmodel || g_release || g_archive)\r
1654                 return;\r
1655 \r
1656         // load the image\r
1657         printf ("loading %s\n", name);\r
1658         Load256Image (name, &pixels, &palette, &width, &height);\r
1659 //      RemapZero (pixels, palette, width, height);\r
1660 \r
1661         // crop it to the proper size\r
1662         cropped = (byte *) SafeMalloc (model.skinwidth*model.skinheight, "Cmd_Skin");\r
1663         for (y=0 ; y<model.skinheight ; y++)\r
1664         {\r
1665                 memcpy (cropped+y*model.skinwidth,\r
1666                         pixels+y*width, model.skinwidth);\r
1667         }\r
1668 \r
1669         // save off the new image\r
1670         printf ("saving %s\n", savename);\r
1671         CreatePath (savename);\r
1672         WritePCXfile (savename, cropped, model.skinwidth,\r
1673                 model.skinheight, palette);\r
1674 \r
1675         free (pixels);\r
1676         free (palette);\r
1677         free (cropped);\r
1678 }\r
1679 \r
1680 \r
1681 /*\r
1682 =================\r
1683 Cmd_Origin\r
1684 =================\r
1685 */\r
1686 void Cmd_Origin (void)\r
1687 {\r
1688         // rotate points into frame of reference so model points down the\r
1689         // positive x axis\r
1690         GetScriptToken (false);\r
1691         adjust[1] = -atof (token);\r
1692 \r
1693         GetScriptToken (false);\r
1694         adjust[0] = atof (token);\r
1695 \r
1696         GetScriptToken (false);\r
1697         adjust[2] = -atof (token);\r
1698 }\r
1699 \r
1700 \r
1701 /*\r
1702 =================\r
1703 Cmd_ScaleUp\r
1704 =================\r
1705 */\r
1706 void Cmd_ScaleUp (void)\r
1707 {\r
1708         GetScriptToken (false);\r
1709         scale_up = atof (token);\r
1710         if (g_skipmodel || g_release || g_archive)\r
1711                 return;\r
1712 \r
1713         printf ("Scale up: %f\n", scale_up);\r
1714 }\r
1715 \r
1716 \r
1717 /*\r
1718 =================\r
1719 Cmd_Skinsize\r
1720 \r
1721 Set a skin size other than the default\r
1722 =================\r
1723 */\r
1724 void Cmd_Skinsize (void)\r
1725 {\r
1726         GetScriptToken (false);\r
1727         g_fixedwidth = atoi(token);\r
1728         GetScriptToken (false);\r
1729         g_fixedheight = atoi(token);\r
1730 }\r
1731 \r
1732 /*\r
1733 =================\r
1734 Cmd_Modelname\r
1735 \r
1736 Gives a different name/location for the file, instead of the cddir\r
1737 =================\r
1738 */\r
1739 void Cmd_Modelname (void)\r
1740 {\r
1741         GetScriptToken (false);\r
1742         strcpy (modelname, token);\r
1743 }\r
1744 \r
1745 /*\r
1746 ===============\r
1747 Cmd_Cd\r
1748 ===============\r
1749 */\r
1750 void Cmd_Cd (void)\r
1751 {\r
1752         char temp[256];\r
1753 \r
1754         FinishModel ();\r
1755         ClearModel ();\r
1756 \r
1757         GetScriptToken (false);\r
1758 \r
1759         // this is a silly mess...\r
1760         sprintf (cdpartial, "models/%s", token); \r
1761         sprintf (cdarchive, "%smodels/%s", gamedir+strlen(qdir), token); \r
1762         sprintf (cddir, "%s%s", gamedir, cdpartial);\r
1763 \r
1764         // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.\r
1765         sprintf(temp, "%s%s", g_outputDir, cdpartial);\r
1766         strcpy(g_outputDir, temp);\r
1767 \r
1768         // if -only was specified and this cd doesn't match,\r
1769         // skip the model (you only need to match leading chars,\r
1770         // so you could regrab all monsters with -only monsters)\r
1771         if (!g_only[0])\r
1772                 return;\r
1773         if (strncmp(token, g_only, strlen(g_only)))\r
1774         {\r
1775                 g_skipmodel = true;\r
1776                 printf ("skipping %s\n", cdpartial);\r
1777         }\r
1778 }\r
1779 \r
1780 /*\r
1781 =================\r
1782 Cmd_Cluster\r
1783 =================\r
1784 */\r
1785 void Cmd_Cluster()\r
1786 {\r
1787         char file1[1024];\r
1788 \r
1789         GetScriptToken (false);\r
1790 \r
1791         printf ("---------------------\n");\r
1792         sprintf (file1, "%s/%s", cdpartial, token);\r
1793         printf ("%s\n", file1);\r
1794 \r
1795         ExpandPathAndArchive (file1);\r
1796 \r
1797         sprintf (file1, "%s/%s", cddir, token);\r
1798 \r
1799         LoadClusters(file1, (int **)&clusters, (int *)&num_verts, jointed);\r
1800 \r
1801         new_num_verts[0] = num_verts[0];\r
1802 \r
1803         clustered = 1;\r
1804 }\r
1805 \r
1806 // Model construction cover functions.\r
1807 void MODELCMD_Modelname (int modeltype)\r
1808 {\r
1809         if (g_forcemodel)\r
1810                 modeltype = g_forcemodel;\r
1811 \r
1812         Cmd_Modelname ();\r
1813 /*\r
1814         switch(modeltype)\r
1815         {\r
1816         case MODEL_MD2:\r
1817                 Cmd_Modelname ();               \r
1818                 break;\r
1819         case MODEL_FM:\r
1820                 Cmd_FMModelname ();\r
1821                 break;\r
1822         }\r
1823 */\r
1824 }\r
1825 \r
1826 void MODELCMD_Cd (int modeltype)\r
1827 {\r
1828         if (g_forcemodel)\r
1829                 modeltype = g_forcemodel;\r
1830 \r
1831         switch(modeltype)\r
1832         {\r
1833         case MODEL_MD2:\r
1834                 Cmd_Cd ();\r
1835                 break;\r
1836         case MODEL_FM:\r
1837                 Cmd_FMCd ();\r
1838                 break;\r
1839         }\r
1840 }\r
1841 \r
1842 void MODELCMD_Origin (int modeltype)\r
1843 {\r
1844         if (g_forcemodel)\r
1845                 modeltype = g_forcemodel;\r
1846 \r
1847         Cmd_Origin ();\r
1848 /*      switch(modeltype)\r
1849         {\r
1850         case MODEL_MD2:\r
1851                 Cmd_Origin ();\r
1852                 break;\r
1853         case MODEL_FM:\r
1854                 Cmd_FMOrigin ();\r
1855                 break;\r
1856         }\r
1857 */\r
1858 }\r
1859 \r
1860 void MODELCMD_Cluster (int modeltype)\r
1861 {\r
1862         if (g_forcemodel)\r
1863                 modeltype = g_forcemodel;\r
1864 \r
1865         switch(modeltype)\r
1866         {\r
1867         case MODEL_MD2:\r
1868                 Cmd_Cluster ();\r
1869                 break;\r
1870         case MODEL_FM:\r
1871                 Cmd_FMCluster ();\r
1872                 break;\r
1873         }\r
1874 }\r
1875 \r
1876 void MODELCMD_Base (int modeltype)\r
1877 {\r
1878         if (g_forcemodel)\r
1879                 modeltype = g_forcemodel;\r
1880 \r
1881         switch(modeltype)\r
1882         {\r
1883         case MODEL_MD2:\r
1884                 Cmd_Base ();\r
1885                 break;\r
1886         case MODEL_FM:\r
1887                 Cmd_FMBase (false);\r
1888                 break;\r
1889         }\r
1890 }\r
1891 \r
1892 void MODELCMD_BaseST (int modeltype)\r
1893 {\r
1894         if (g_forcemodel)\r
1895                 modeltype = g_forcemodel;\r
1896 \r
1897         switch(modeltype)\r
1898         {\r
1899         case MODEL_MD2:\r
1900                 Cmd_Base ();\r
1901                 break;\r
1902         case MODEL_FM:\r
1903                 Cmd_FMBase (true);\r
1904                 break;\r
1905         }\r
1906 }\r
1907 \r
1908 void MODELCMD_ScaleUp (int modeltype)\r
1909 {\r
1910         if (g_forcemodel)\r
1911                 modeltype = g_forcemodel;\r
1912 \r
1913         Cmd_ScaleUp ();\r
1914 /*      switch(modeltype)\r
1915         {\r
1916         case MODEL_MD2:\r
1917                 Cmd_ScaleUp ();\r
1918                 break;\r
1919         case MODEL_FM:\r
1920                 Cmd_FMScaleUp ();\r
1921                 break;\r
1922         }\r
1923 */\r
1924 }\r
1925 \r
1926 void MODELCMD_Frame (int modeltype)\r
1927 {\r
1928         if (g_forcemodel)\r
1929                 modeltype = g_forcemodel;\r
1930 \r
1931         switch(modeltype)\r
1932         {\r
1933         case MODEL_MD2:\r
1934                 Cmd_Frame ();\r
1935                 break;\r
1936         case MODEL_FM:\r
1937                 Cmd_FMFrame ();\r
1938                 break;\r
1939         }\r
1940 }\r
1941 \r
1942 void MODELCMD_Skin (int modeltype)\r
1943 {\r
1944         if (g_forcemodel)\r
1945                 modeltype = g_forcemodel;\r
1946 \r
1947         switch(modeltype)\r
1948         {\r
1949         case MODEL_MD2:\r
1950                 Cmd_Skin ();\r
1951                 break;\r
1952         case MODEL_FM:\r
1953                 Cmd_FMSkin ();\r
1954                 break;\r
1955         }\r
1956 }\r
1957 \r
1958 void MODELCMD_Skinsize (int modeltype)\r
1959 {\r
1960         if (g_forcemodel)\r
1961                 modeltype = g_forcemodel;\r
1962 \r
1963         Cmd_Skinsize ();\r
1964 /*\r
1965         switch(modeltype)\r
1966         {\r
1967         case MODEL_MD2:\r
1968                 Cmd_Skinsize ();\r
1969                 break;\r
1970         case MODEL_FM:\r
1971                 Cmd_FMSkinsize ();\r
1972                 break;\r
1973         }\r
1974 */\r
1975 }\r
1976 \r
1977 void MODELCMD_Skeleton (int modeltype)\r
1978 {\r
1979         if (g_forcemodel)\r
1980                 modeltype = g_forcemodel;\r
1981 \r
1982         switch(modeltype)\r
1983         {\r
1984         case MODEL_MD2:\r
1985                 break;\r
1986         case MODEL_FM:\r
1987                 Cmd_FMSkeleton ();\r
1988                 break;\r
1989         }\r
1990 }\r
1991 \r
1992 void MODELCMD_BeginGroup(int modeltype)\r
1993 {\r
1994         if (g_forcemodel)\r
1995                 modeltype = g_forcemodel;\r
1996 \r
1997         switch(modeltype)\r
1998         {\r
1999         case MODEL_MD2:\r
2000                 break;\r
2001         case MODEL_FM:\r
2002                 Cmd_FMBeginGroup();\r
2003                 break;\r
2004         }\r
2005 }\r
2006 \r
2007 void MODELCMD_EndGroup(int modeltype)\r
2008 {\r
2009         if (g_forcemodel)\r
2010                 modeltype = g_forcemodel;\r
2011 \r
2012         switch(modeltype)\r
2013         {\r
2014         case MODEL_MD2:\r
2015                 break;\r
2016         case MODEL_FM:\r
2017                 Cmd_FMEndGroup();\r
2018                 break;\r
2019         }\r
2020 }\r
2021 \r
2022 void MODELCMD_Referenced(int modeltype)\r
2023 {\r
2024         if (g_forcemodel)\r
2025                 modeltype = g_forcemodel;\r
2026 \r
2027         switch(modeltype)\r
2028         {\r
2029         case MODEL_MD2:\r
2030                 break;\r
2031         case MODEL_FM:\r
2032                 Cmd_FMReferenced();\r
2033                 break;\r
2034         }\r
2035 }\r
2036 \r
2037 void MODELCMD_NodeOrder(int modeltype)\r
2038 {\r
2039         if (g_forcemodel)\r
2040                 modeltype = g_forcemodel;\r
2041 \r
2042         switch(modeltype)\r
2043         {\r
2044         case MODEL_MD2:\r
2045                 break;\r
2046         case MODEL_FM:\r
2047                 Cmd_FMNodeOrder();\r
2048                 break;\r
2049         }\r
2050 }\r