]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/qdata_heretic2/fmodels.c
6dd16355cc6ca7e0c521dd3125e91bff06a9d2c5
[xonotic/netradiant.git] / tools / quake2 / qdata_heretic2 / fmodels.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 #include "qd_fmodel.h"\r
23 #include "animcomp.h"\r
24 #include "qd_skeletons.h"\r
25 #include "skeletons.h"\r
26 #include "qdata.h"\r
27 #include "flex.h"\r
28 #include "reference.h"\r
29 \r
30 #include <assert.h>\r
31 \r
32 /*\r
33 ========================================================================\r
34 \r
35 .FM triangle flexible model file format\r
36 \r
37 ========================================================================\r
38 */\r
39 \r
40 //=================================================================\r
41 \r
42 #define NUMVERTEXNORMALS        162\r
43 \r
44 extern float    avertexnormals[NUMVERTEXNORMALS][3];\r
45 \r
46 #define MAX_GROUPS      128\r
47 \r
48 typedef struct\r
49 {\r
50         triangle_t      triangle;\r
51         int             group;\r
52 } trigroup_t;\r
53 \r
54 #define TRIVERT_DIST    .1\r
55 \r
56 typedef struct\r
57 {\r
58         int                     start_frame;\r
59         int             num_frames;\r
60         int                     degrees;\r
61         char *mat;\r
62         char *ccomp;\r
63         char *cbase;\r
64         float *cscale;\r
65         float *coffset;\r
66         float trans[3];\r
67         float scale[3];\r
68         float bmin[3];\r
69         float bmax[3];\r
70 } fmgroup_t;\r
71 \r
72 //================================================================\r
73 \r
74 // Initial\r
75 fmheader_t      fmheader;\r
76 \r
77 // Skin\r
78 extern char                     g_skins[MAX_FM_SKINS][64];\r
79 \r
80 // ST Coord\r
81 extern fmstvert_t       base_st[MAX_FM_VERTS];\r
82 \r
83 // Triangles\r
84 extern fmtriangle_t     triangles[MAX_FM_TRIANGLES];\r
85 \r
86 // Frames\r
87 fmframe_t       g_frames[MAX_FM_FRAMES];\r
88 //fmframe_t     *g_FMframes;\r
89 \r
90 // GL Commands\r
91 extern int                      commands[16384];\r
92 extern int                      numcommands;\r
93 \r
94 \r
95 //\r
96 // varibles set by commands\r
97 //\r
98 extern float            scale_up;                                               // set by $scale\r
99 extern vec3_t           adjust;                                                 // set by $origin\r
100 extern int                      g_fixedwidth, g_fixedheight;    // set by $skinsize\r
101 extern char                     modelname[64];                                  // set by $modelname\r
102 \r
103 \r
104 extern  char            *g_outputDir;\r
105 \r
106 \r
107 // Mesh Nodes\r
108 mesh_node_t     *pmnodes = NULL;\r
109 fmmeshnode_t    mesh_nodes[MAX_FM_MESH_NODES]; \r
110 \r
111 fmgroup_t       groups[MAX_GROUPS];\r
112 int                     num_groups;\r
113 int     frame_to_group[MAX_FM_FRAMES];\r
114 \r
115 //\r
116 // variables set by command line arguments\r
117 //\r
118 qboolean        g_no_opimizations = false;\r
119 \r
120 \r
121 //\r
122 // base frame info\r
123 //\r
124 static int                      triangle_st[MAX_FM_TRIANGLES][3][2];\r
125 \r
126 \r
127 // number of gl vertices\r
128 extern int                      numglverts;\r
129 // indicates if a triangle has already been used in a glcmd\r
130 extern int                      used[MAX_FM_TRIANGLES];\r
131 // indicates if a triangle has translucency in it or not\r
132 static qboolean         translucent[MAX_FM_TRIANGLES];\r
133 \r
134 // main output file handle\r
135 extern FILE                     *headerouthandle;\r
136 // output sizes of buildst()\r
137 static int                      skin_width, skin_height;\r
138 \r
139 \r
140 // statistics\r
141 static int                      total_skin_pixels;\r
142 static int                      skin_pixels_used;\r
143 \r
144 int ShareVertex( trigroup_t trione, trigroup_t tritwo);\r
145 float DistBetween(vec3_t point1, vec3_t point2);\r
146 int GetNumTris( trigroup_t *tris, int group);\r
147 void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles);\r
148 void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts);\r
149 void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height);\r
150 \r
151 #ifndef _WIN32\r
152 \r
153 void strupr(char *string)\r
154 {\r
155         int i;\r
156 \r
157         for (i=0 ; i<strlen(string); i++)\r
158                 toupper(string[i]);\r
159 \r
160         return;\r
161 }\r
162 \r
163 #endif\r
164 //==============================================================\r
165 \r
166 /*\r
167 ===============\r
168 ClearModel\r
169 ===============\r
170 */\r
171 static void ClearModel (void)\r
172 {\r
173         memset (&fmheader, 0, sizeof(fmheader));\r
174 \r
175         modelname[0] = 0;\r
176         scale_up = 1.0; \r
177         VectorCopy (vec3_origin, adjust);\r
178         g_fixedwidth = g_fixedheight = 0;\r
179         g_skipmodel = false;\r
180         num_groups = 0;\r
181 \r
182         if (pmnodes)\r
183         {\r
184                 free(pmnodes);\r
185                 pmnodes = NULL;\r
186         }\r
187 \r
188         ClearSkeletalModel();\r
189 }\r
190 \r
191 \r
192 extern void H_printf(char *fmt, ...);\r
193 \r
194 \r
195 void WriteHeader(FILE *FH, char *Ident, int Version, int Size, void *Data)\r
196 {\r
197         header_t        header;\r
198         static long     pos = -1;\r
199         long            CurrentPos;\r
200 \r
201         if (Size == 0)\r
202         {       // Don't write out empty packets\r
203                 return;\r
204         }\r
205 \r
206         if (pos != -1)\r
207         {\r
208                 CurrentPos = ftell(FH);\r
209                 Size = CurrentPos - pos + sizeof(header_t);\r
210                 fseek(FH, pos, SEEK_SET);\r
211                 pos = -2;\r
212         }\r
213         else if (Size == -1)\r
214         {\r
215                 pos = ftell(FH);\r
216         }\r
217 \r
218         memset(&header,0,sizeof(header));\r
219         strcpy(header.ident,Ident);\r
220         header.version = Version;\r
221         header.size = Size;\r
222 \r
223         SafeWrite (FH, &header, sizeof(header));\r
224 \r
225         if (Data)\r
226         {\r
227                 SafeWrite (FH, Data, Size);\r
228         }\r
229 \r
230         if (pos == -2)\r
231         {\r
232                 pos = -1;\r
233                 fseek(FH, 0, SEEK_END);\r
234         }\r
235 }\r
236 \r
237 /*\r
238 ============\r
239 WriteModelFile\r
240 ============\r
241 */\r
242 static void WriteModelFile (FILE *modelouthandle)\r
243 {\r
244         int                             i;\r
245         int                             j, k;\r
246         fmframe_t               *in;\r
247         fmaliasframe_t  *out;\r
248         byte                    buffer[MAX_FM_VERTS*4+128];\r
249         float                   v;\r
250         int                             c_on, c_off;\r
251         IntListNode_t   *current, *toFree;\r
252         qboolean framesWritten = false;\r
253         size_t temp ,size = 0;\r
254 \r
255         // probably should do this dynamically one of these days\r
256         struct\r
257         {\r
258                 float           scale[3];       // multiply byte verts by this\r
259                 float           translate[3];   // then add this\r
260         } outFrames[MAX_FM_FRAMES];\r
261 \r
262 #define DATA_SIZE 0x60000               // 384K had better be enough, particularly for the reference points\r
263         byte data[DATA_SIZE];\r
264         byte data2[DATA_SIZE];\r
265 \r
266         fmheader.num_glcmds = numcommands;\r
267         fmheader.framesize = (int)&((fmaliasframe_t *)0)->verts[fmheader.num_xyz];\r
268 \r
269         WriteHeader(modelouthandle, FM_HEADER_NAME, FM_HEADER_VER, sizeof(fmheader), &fmheader);\r
270 \r
271         //\r
272         // write out the skin names\r
273         //\r
274 \r
275         WriteHeader(modelouthandle, FM_SKIN_NAME, FM_SKIN_VER, fmheader.num_skins * MAX_FM_SKINNAME, g_skins);\r
276 \r
277         //\r
278         // write out the texture coordinates\r
279         //\r
280         c_on = c_off = 0;\r
281         for (i=0 ; i<fmheader.num_st ; i++)\r
282         {\r
283                 base_st[i].s = LittleShort (base_st[i].s);\r
284                 base_st[i].t = LittleShort (base_st[i].t);\r
285         }\r
286 \r
287         WriteHeader(modelouthandle, FM_ST_NAME, FM_ST_VER, fmheader.num_st * sizeof(base_st[0]), base_st);\r
288 \r
289         //\r
290         // write out the triangles\r
291         //\r
292         WriteHeader(modelouthandle, FM_TRI_NAME, FM_TRI_VER, fmheader.num_tris * sizeof(fmtriangle_t), NULL);\r
293 \r
294         for (i=0 ; i<fmheader.num_tris ; i++)\r
295         {\r
296                 int                             j;\r
297                 fmtriangle_t    tri;\r
298 \r
299                 for (j=0 ; j<3 ; j++)\r
300                 {\r
301                         tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);\r
302                         tri.index_st[j] = LittleShort (triangles[i].index_st[j]);\r
303                 }\r
304 \r
305                 SafeWrite (modelouthandle, &tri, sizeof(tri));\r
306         }\r
307 \r
308         if (!num_groups)\r
309         {\r
310                 //\r
311                 // write out the frames\r
312                 //\r
313                 WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, fmheader.num_frames * fmheader.framesize, NULL);\r
314         //      WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);\r
315 \r
316                 for (i=0 ; i<fmheader.num_frames ; i++)\r
317                 {\r
318                         in = &g_frames[i];\r
319                         out = (fmaliasframe_t *)buffer;\r
320 \r
321                         strcpy (out->name, in->name);\r
322                         for (j=0 ; j<3 ; j++)\r
323                         {\r
324                                 out->scale[j] = (in->maxs[j] - in->mins[j])/255;\r
325                                 out->translate[j] = in->mins[j];\r
326 \r
327                                 outFrames[i].scale[j] = out->scale[j];\r
328                                 outFrames[i].translate[j] = out->translate[j];\r
329                         }\r
330 \r
331                         for (j=0 ; j<fmheader.num_xyz ; j++)\r
332                         {\r
333                         // all of these are byte values, so no need to deal with endianness\r
334                                 out->verts[j].lightnormalindex = in->v[j].lightnormalindex;\r
335 \r
336                                 for (k=0 ; k<3 ; k++)\r
337                                 {\r
338                                 // scale to byte values & min/max check\r
339                                         v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );\r
340 \r
341                                 // clamp, so rounding doesn't wrap from 255.6 to 0\r
342                                         if (v > 255.0)\r
343                                                 v = 255.0;\r
344                                         if (v < 0)\r
345                                                 v = 0;\r
346                                         out->verts[j].v[k] = v;\r
347                                 }\r
348                         }\r
349 \r
350                         for (j=0 ; j<3 ; j++)\r
351                         {\r
352                                 out->scale[j] = LittleFloat (out->scale[j]);\r
353                                 out->translate[j] = LittleFloat (out->translate[j]);\r
354                         }\r
355 \r
356                         SafeWrite (modelouthandle, out, fmheader.framesize);\r
357                 }\r
358 \r
359                 // Go back and finish the header\r
360         //      WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);\r
361         }\r
362         else\r
363         {\r
364                 WriteHeader(modelouthandle, FM_SHORT_FRAME_NAME, FM_SHORT_FRAME_VER,FRAME_NAME_LEN*fmheader.num_frames, NULL);\r
365                 for (i=0 ; i<fmheader.num_frames ; i++)\r
366                 {\r
367                         in = &g_frames[i];\r
368                         SafeWrite (modelouthandle,in->name,FRAME_NAME_LEN);\r
369                 }\r
370                 WriteHeader(modelouthandle, FM_NORMAL_NAME, FM_NORMAL_VER,fmheader.num_xyz, NULL);\r
371                 in = &g_frames[0];\r
372                 for (j=0 ; j<fmheader.num_xyz ; j++)\r
373                         SafeWrite (modelouthandle,&in->v[j].lightnormalindex,1);\r
374         }\r
375 \r
376         //\r
377         // write out glcmds\r
378         //\r
379         WriteHeader(modelouthandle, FM_GLCMDS_NAME, FM_GLCMDS_VER, numcommands*4, commands);\r
380 \r
381         //\r
382         // write out mesh nodes\r
383         //\r
384         for(i=0;i<fmheader.num_mesh_nodes;i++)\r
385         {\r
386                 memcpy(mesh_nodes[i].tris, pmnodes[i].tris, sizeof(mesh_nodes[i].tris));\r
387                 memcpy(mesh_nodes[i].verts, pmnodes[i].verts, sizeof(mesh_nodes[i].verts));\r
388                 mesh_nodes[i].start_glcmds = LittleShort((short)pmnodes[i].start_glcmds);\r
389                 mesh_nodes[i].num_glcmds = LittleShort((short)pmnodes[i].num_glcmds);\r
390         }\r
391 \r
392         WriteHeader(modelouthandle, FM_MESH_NAME, FM_MESH_VER, sizeof(fmmeshnode_t) * fmheader.num_mesh_nodes, mesh_nodes);\r
393 \r
394         if (num_groups)\r
395         {\r
396 \r
397 /*\r
398 typedef struct\r
399 {\r
400         int                     start_frame;\r
401         int                     num_frames;\r
402         int                     degrees;\r
403         char *mat; fmheader.num_xyz*3*g->degrees*sizeof(char)\r
404         char *ccomp; g->num_frames*g->degrees*sizeof(char)\r
405         char *cbase; fmheader.num_xyz*3*sizeof(unsigned char)\r
406         float *cscale; g->degrees*sizeof(float)\r
407         float *coffset; g->degrees*sizeof(float)\r
408         float trans[3]; 3*sizeof(float)\r
409         float scale[3]; 3*sizeof(float)\r
410 } fmgroup_t;\r
411 */\r
412                 int tmp,k;\r
413                 fmgroup_t *g;\r
414                 size=sizeof(int)+fmheader.num_frames*sizeof(int);\r
415                 for (k=0;k<num_groups;k++)\r
416                 {\r
417                         g=&groups[k];\r
418                         size+=sizeof(int)*3;\r
419                         size+=fmheader.num_xyz*3*g->degrees*sizeof(char);\r
420                         size+=g->num_frames*g->degrees*sizeof(char);\r
421                         size+=fmheader.num_xyz*3*sizeof(unsigned char);\r
422                         size+=g->degrees*sizeof(float);\r
423                         size+=g->degrees*sizeof(float);\r
424                         size+=12*sizeof(float);\r
425                 }\r
426                 WriteHeader(modelouthandle, FM_COMP_NAME, FM_COMP_VER,size, NULL);\r
427                 SafeWrite (modelouthandle,&num_groups,sizeof(int));\r
428                 SafeWrite (modelouthandle,frame_to_group,sizeof(int)*fmheader.num_frames);\r
429 \r
430                 for (k=0;k<num_groups;k++)\r
431                 {\r
432                         g=&groups[k];\r
433                         tmp=LittleLong(g->start_frame);\r
434                         SafeWrite (modelouthandle,&tmp,sizeof(int));\r
435                         tmp=LittleLong(g->num_frames);\r
436                         SafeWrite (modelouthandle,&tmp,sizeof(int));\r
437                         tmp=LittleLong(g->degrees);\r
438                         SafeWrite (modelouthandle,&tmp,sizeof(int));\r
439 \r
440                         SafeWrite (modelouthandle,g->mat,fmheader.num_xyz*3*g->degrees*sizeof(char));\r
441                         SafeWrite (modelouthandle,g->ccomp,g->num_frames*g->degrees*sizeof(char));\r
442                         SafeWrite (modelouthandle,g->cbase,fmheader.num_xyz*3*sizeof(unsigned char));\r
443                         SafeWrite (modelouthandle,g->cscale,g->degrees*sizeof(float));\r
444                         SafeWrite (modelouthandle,g->coffset,g->degrees*sizeof(float));\r
445                         SafeWrite (modelouthandle,g->trans,3*sizeof(float));\r
446                         SafeWrite (modelouthandle,g->scale,3*sizeof(float));\r
447                         SafeWrite (modelouthandle,g->bmin,3*sizeof(float));\r
448                         SafeWrite (modelouthandle,g->bmax,3*sizeof(float));\r
449                         free(g->mat);\r
450                         free(g->ccomp);\r
451                         free(g->cbase);\r
452                         free(g->cscale);\r
453                         free(g->coffset);\r
454                 }\r
455         }\r
456 \r
457         // write the skeletal info\r
458         if(g_skelModel.type != SKEL_NULL)\r
459         {\r
460                 size = 0;\r
461 \r
462                 temp = sizeof(int);             // change this to a byte\r
463                 memcpy(data + size, &g_skelModel.type, temp);\r
464                 size += temp;\r
465 \r
466                 // number of joints\r
467                 temp = sizeof(int);             // change this to a byte\r
468                 memcpy(data + size, &numJointsInSkeleton[g_skelModel.type], temp);\r
469                 size += temp;\r
470 \r
471                 // number of verts in each joint cluster\r
472                 temp = sizeof(int)*numJointsInSkeleton[g_skelModel.type]; // change this to shorts\r
473                 memcpy(data + size, &g_skelModel.new_num_verts[1], temp);\r
474                 size += temp;\r
475 \r
476                 // cluster verts\r
477                 for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)\r
478                 {\r
479                         current = g_skelModel.vertLists[i];\r
480                         while(current)\r
481                         {\r
482                                 temp = sizeof(int);     // change this to a short\r
483                                 memcpy(data + size, &current->data, temp);\r
484                                 size += temp;\r
485                                 toFree = current;\r
486                                 current = current->next;\r
487                                 free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base\r
488                         }\r
489                 }\r
490 \r
491                 if(!num_groups) // joints are stored with regular verts for compressed models\r
492                 {\r
493                         framesWritten = true;\r
494 \r
495                         temp = sizeof(int);     // change this to a byte\r
496                         memcpy(data + size, &framesWritten, temp);\r
497                         size += temp;\r
498 \r
499                         for (i = 0; i < fmheader.num_frames; ++i)\r
500                         {\r
501                                 in = &g_frames[i];\r
502 \r
503                                 for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)\r
504                                 {\r
505                                         for (k=0 ; k<3 ; k++)\r
506                                         {\r
507                                                 // scale to byte values & min/max check\r
508                                                 v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
509 \r
510                                                 // write out origin as a float since they arn't clamped\r
511                                                 temp = sizeof(float);   // change this to a short\r
512                                                 assert(size+temp < DATA_SIZE);\r
513                                                 memcpy(data + size, &v, temp);\r
514                                                 size += temp;\r
515                                         }\r
516 \r
517                                         for (k=0 ; k<3 ; k++)\r
518                                         {\r
519                                                 v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
520 \r
521                                                 // write out origin as a float since they arn't clamped\r
522                                                 temp = sizeof(float);   // change this to a short\r
523                                                 assert(size+temp < DATA_SIZE);\r
524                                                 memcpy(data + size, &v, temp);\r
525                                                 size += temp;\r
526                                         }\r
527 \r
528                                         for (k=0 ; k<3 ; k++)\r
529                                         {\r
530                                                 v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
531 \r
532                                                 // write out origin as a float since they arn't clamped\r
533                                                 temp = sizeof(float);   // change this to a short\r
534                                                 assert(size+temp < DATA_SIZE);\r
535                                                 memcpy(data + size, &v, temp);\r
536                                                 size += temp;\r
537                                         }\r
538                                 }\r
539                         }\r
540 \r
541                 }\r
542                 else\r
543                 {\r
544                         temp = sizeof(int);     // change this to a byte\r
545                         memcpy(data + size, &framesWritten, temp);\r
546                         size += temp;\r
547                 }\r
548 \r
549                 WriteHeader(modelouthandle, FM_SKELETON_NAME, FM_SKELETON_VER, size, data);\r
550         }\r
551 \r
552         if(g_skelModel.references != REF_NULL)\r
553         {\r
554                 int refnum;\r
555 \r
556                 size = 0;\r
557                 if (RefPointNum <= 0)\r
558                 {       // Hard-coded labels\r
559                         refnum = numReferences[g_skelModel.references];\r
560                 }\r
561                 else\r
562                 {       // Labels indicated in QDT\r
563                         refnum = RefPointNum;\r
564                 }\r
565 \r
566                 temp = sizeof(int);     // change this to a byte\r
567                 memcpy(data2 + size, &g_skelModel.references, temp);\r
568                 size += temp;\r
569 \r
570                 if(!num_groups)\r
571                 {\r
572                         framesWritten = true;\r
573 \r
574                         temp = sizeof(int);     // change this to a byte\r
575                         memcpy(data2 + size, &framesWritten, temp);\r
576                         size += temp;\r
577 \r
578                         for (i = 0; i < fmheader.num_frames; ++i)\r
579                         {\r
580                                 in = &g_frames[i];\r
581 \r
582                                 for (j = 0 ; j < refnum; ++j)\r
583                                 {\r
584                                         for (k=0 ; k<3 ; k++)\r
585                                         {\r
586                                                 // scale to byte values & min/max check\r
587                                                 v = Q_rint ( (in->references[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
588 \r
589                                                 // write out origin as a float since they arn't clamped\r
590                                                 temp = sizeof(float);   // change this to a short\r
591                                                 assert(size+temp < DATA_SIZE);\r
592                                                 memcpy(data2 + size, &v, temp);\r
593                                                 size += temp;\r
594                                         }\r
595 \r
596                                         for (k=0 ; k<3 ; k++)\r
597                                         {\r
598                                                 v = Q_rint ( (in->references[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
599 \r
600                                                 // write out origin as a float since they arn't clamped\r
601                                                 temp = sizeof(float);   // change this to a short\r
602                                                 assert(size+temp < DATA_SIZE);\r
603                                                 memcpy(data2 + size, &v, temp);\r
604                                                 size += temp;\r
605                                         }\r
606 \r
607                                         for (k=0 ; k<3 ; k++)\r
608                                         {\r
609                                                 v = Q_rint ( (in->references[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
610 \r
611                                                 // write out origin as a float since they arn't clamped\r
612                                                 temp = sizeof(float);   // change this to a short\r
613                                                 assert(size+temp < DATA_SIZE);\r
614                                                 memcpy(data2 + size, &v, temp);\r
615                                                 size += temp;\r
616                                         }\r
617                                 }\r
618                         }\r
619                 }\r
620                 else    // FINISH ME: references need to be stored with regular verts for compressed models\r
621                 {\r
622                         framesWritten = false;\r
623 \r
624                         temp = sizeof(int);     // change this to a byte\r
625                         memcpy(data2 + size, &framesWritten, temp);\r
626                         size += temp;\r
627                 }\r
628 \r
629                 WriteHeader(modelouthandle, FM_REFERENCES_NAME, FM_REFERENCES_VER, size, data2);\r
630         }\r
631 }\r
632 \r
633 static void CompressFrames()\r
634 {\r
635         fmgroup_t *g;\r
636         int i,j,k;\r
637         fmframe_t       *in;\r
638 \r
639         j=0;\r
640         for (i=0;i<fmheader.num_frames;i++)\r
641         {\r
642                 while (i>=groups[j].start_frame+groups[j].num_frames&&j<num_groups-1)\r
643                         j++;\r
644                 frame_to_group[i]=j;\r
645         }\r
646 \r
647         for (k=0;k<num_groups;k++)\r
648         {\r
649                 g=&groups[k];\r
650 \r
651                 printf("\nCompressing Frames for group %i...\n", k);\r
652                 AnimCompressInit(g->num_frames,fmheader.num_xyz,g->degrees);\r
653                 for (i=0;i<g->num_frames;i++)\r
654                 {\r
655                         in = &g_frames[i+g->start_frame];\r
656                         for (j=0;j<fmheader.num_xyz;j++)\r
657                                 AnimSetFrame(i,j,in->v[j].v[0],in->v[j].v[1],in->v[j].v[2]);\r
658                 }\r
659                 AnimCompressDoit();\r
660                 g->mat= (char *) SafeMalloc(fmheader.num_xyz*3*g->degrees*sizeof(char), "CompressFrames");\r
661                 g->ccomp=(char *) SafeMalloc(g->num_frames*g->degrees*sizeof(char), "CompressFrames");\r
662                 g->cbase=(char *) SafeMalloc(fmheader.num_xyz*3*sizeof(unsigned char), "CompressFrames");\r
663                 g->cscale=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");\r
664                 g->coffset=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");\r
665                 AnimCompressToBytes(g->trans,g->scale,g->mat,g->ccomp,g->cbase,g->cscale,g->coffset,g->bmin,g->bmax);\r
666                 AnimCompressEnd();\r
667         }\r
668 }\r
669 \r
670 static void OptimizeVertices(void)\r
671 {\r
672         qboolean        vert_used[MAX_FM_VERTS];\r
673         short           vert_replacement[MAX_FM_VERTS];\r
674         int                     i,j,k,l,pos,bit,set_pos,set_bit;\r
675         fmframe_t       *in;\r
676         qboolean        Found;\r
677         int                     num_unique;\r
678         static IntListNode_t *newVertLists[NUM_CLUSTERS];\r
679         static int newNum_verts[NUM_CLUSTERS];\r
680         IntListNode_t *current, *next;\r
681 \r
682         printf("Optimizing vertices...");\r
683 \r
684         memset(vert_used, 0, sizeof(vert_used));\r
685 \r
686         if(g_skelModel.clustered == true)\r
687         {\r
688                 memset(newNum_verts, 0, sizeof(newNum_verts));\r
689                 memset(newVertLists, 0, sizeof(newVertLists));\r
690         }\r
691 \r
692         num_unique = 0;\r
693 \r
694         // search for common points among all the frames\r
695         for (i=0 ; i<fmheader.num_frames ; i++)\r
696         {\r
697                 in = &g_frames[i];\r
698 \r
699                 for(j=0;j<fmheader.num_xyz;j++)\r
700                 {\r
701                         for(k=0,Found=false;k<j;k++)\r
702                         {       // starting from the beginning always ensures vert_replacement points to the first point in the array\r
703                                 if (in->v[j].v[0] == in->v[k].v[0] &&\r
704                                         in->v[j].v[1] == in->v[k].v[1] &&\r
705                                         in->v[j].v[2] == in->v[k].v[2])\r
706                                 {\r
707                                         Found = true;\r
708                                         vert_replacement[j] = k;\r
709                                         break;\r
710                                 }\r
711 \r
712                         }\r
713 \r
714                         if (!Found)\r
715                         {\r
716                                 if (!vert_used[j])\r
717                                 {\r
718                                         num_unique++;\r
719                                 }\r
720                                 vert_used[j] = true;\r
721                         }\r
722                 }\r
723         }\r
724 \r
725         // recompute the light normals\r
726         for (i=0 ; i<fmheader.num_frames ; i++)\r
727         {\r
728                 in = &g_frames[i];\r
729 \r
730                 for(j=0;j<fmheader.num_xyz;j++)\r
731                 {\r
732                         if (!vert_used[j])\r
733                         {\r
734                                 k = vert_replacement[j];\r
735 \r
736                                 VectorAdd (in->v[j].vnorm.normalsum, in->v[k].vnorm.normalsum, in->v[k].vnorm.normalsum);\r
737                                 in->v[k].vnorm.numnormals += in->v[j].vnorm.numnormals++;\r
738                         }\r
739                 }\r
740 \r
741                 for (j=0 ; j<fmheader.num_xyz ; j++)\r
742                 {\r
743                         vec3_t  v;\r
744                         float   maxdot;\r
745                         int             maxdotindex;\r
746                         int             c;\r
747 \r
748                         c = in->v[j].vnorm.numnormals;\r
749                         if (!c)\r
750                                 Error ("Vertex with no triangles attached");\r
751 \r
752                         VectorScale (in->v[j].vnorm.normalsum, 1.0/c, v);\r
753                         VectorNormalize (v, v);\r
754 \r
755                         maxdot = -999999.0;\r
756                         maxdotindex = -1;\r
757 \r
758                         for (k=0 ; k<NUMVERTEXNORMALS ; k++)\r
759                         {\r
760                                 float   dot;\r
761 \r
762                                 dot = DotProduct (v, avertexnormals[k]);\r
763                                 if (dot > maxdot)\r
764                                 {\r
765                                         maxdot = dot;\r
766                                         maxdotindex = k;\r
767                                 }\r
768                         }\r
769 \r
770                         in->v[j].lightnormalindex = maxdotindex;\r
771                 }\r
772         }\r
773 \r
774         // create substitution list\r
775         num_unique = 0;\r
776         for(i=0;i<fmheader.num_xyz;i++)\r
777         {\r
778                 if (vert_used[i])\r
779                 {\r
780                         vert_replacement[i] = num_unique;\r
781                         num_unique++;\r
782                 }\r
783                 else\r
784                 {\r
785                         vert_replacement[i] = vert_replacement[vert_replacement[i]];\r
786                 }\r
787 \r
788                 // vert_replacement[i] is the new index, i is the old index\r
789                 // need to add the new index to the cluster list if old index was in it\r
790                 if(g_skelModel.clustered == true)\r
791                 {\r
792                         for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)\r
793                         {\r
794                                 for(l = 0, current = g_skelModel.vertLists[k]; \r
795                                         l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)\r
796                                 {\r
797                                         if(current->data == i)\r
798                                         {\r
799                                                 IntListNode_t *current2;\r
800                                                 int m;\r
801                                                 qboolean added = false;\r
802 \r
803                                                 for(m = 0, current2 = newVertLists[k]; m < newNum_verts[k+1];\r
804                                                         ++m, current2 = current2->next)\r
805                                                 {\r
806                                                         if(current2->data == vert_replacement[i])\r
807                                                         {\r
808                                                                 added = true;\r
809                                                                 break;\r
810                                                         }\r
811                                                 }\r
812 \r
813                                                 if(!added)\r
814                                                 {\r
815                                                         ++newNum_verts[k+1];\r
816 \r
817                                                         next = newVertLists[k];\r
818 \r
819                                                         newVertLists[k] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "OptimizeVertices");\r
820                                                         // freed after model write out\r
821 \r
822                                                         newVertLists[k]->data = vert_replacement[i];\r
823                                                         newVertLists[k]->next = next;\r
824                                                 }\r
825                                                 break;\r
826                                         }\r
827                                 }\r
828                         }\r
829                 }\r
830         }\r
831 \r
832         // substitute\r
833         for (i=0 ; i<fmheader.num_frames ; i++)\r
834         {\r
835                 in = &g_frames[i];\r
836 \r
837                 for(j=0;j<fmheader.num_xyz;j++)\r
838                 {\r
839                         in->v[vert_replacement[j]] = in->v[j];\r
840                 }\r
841 \r
842         }\r
843 \r
844         for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)\r
845         {\r
846                 IntListNode_t *toFree;\r
847                 current = g_skelModel.vertLists[i];\r
848 \r
849                 while(current)\r
850                 {\r
851                         toFree = current;\r
852                         current = current->next;\r
853                         free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base\r
854                 }\r
855 \r
856                 g_skelModel.vertLists[i] = newVertLists[i];\r
857                 g_skelModel.new_num_verts[i+1] = newNum_verts[i+1];\r
858         }\r
859 \r
860 #ifndef NDEBUG\r
861         for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)\r
862         {\r
863                 for(l = 0, current = g_skelModel.vertLists[k]; \r
864                         l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)\r
865                 {\r
866                         IntListNode_t *current2;\r
867                         int m;\r
868 \r
869                         for(m = l+1, current2 = current->next; m < newNum_verts[k+1];\r
870                                 ++m, current2 = current2->next)\r
871                         {\r
872                                 if(current->data == current2->data)\r
873                                 {\r
874                                         printf("Warning duplicate vertex:  %d\n", current->data);\r
875                                         break;\r
876                                 }\r
877                         }\r
878                 }\r
879         }\r
880 #endif\r
881 \r
882         for(i=0;i<fmheader.num_mesh_nodes;i++)\r
883         {       // reset the vert bits\r
884                 memset(pmnodes[i].verts,0,sizeof(pmnodes[i].verts));\r
885         }\r
886 \r
887         // repleace the master triangle list vertex indexes and update the vert bits for each mesh node\r
888         for (i=0 ; i<fmheader.num_tris ; i++)\r
889         {\r
890                 pos = i >> 3;\r
891                 bit = 1 << (i & 7 );\r
892 \r
893                 for (j=0 ; j<3 ; j++)\r
894                 {       \r
895                         set_bit = set_pos = triangles[i].index_xyz[j] = vert_replacement[triangles[i].index_xyz[j]];\r
896 \r
897                         set_pos >>= 3;\r
898                         set_bit = 1 << (set_bit & 7);\r
899 \r
900                         for(k=0;k<fmheader.num_mesh_nodes;k++)\r
901                         {\r
902                                 if (!(pmnodes[k].tris[pos] & bit))\r
903                                 {\r
904                                         continue;\r
905                                 }\r
906                                 pmnodes[k].verts[set_pos] |= set_bit;\r
907                         }\r
908                 }\r
909         }\r
910 \r
911         for (i=0;i<numcommands;i++)\r
912         {\r
913                 j = commands[i];\r
914                 if (!j) continue;\r
915 \r
916                 j = abs(j);\r
917                 for(i++;j;j--,i+=3)\r
918                 {\r
919                         commands[i+2] = vert_replacement[commands[i+2]];\r
920                 }\r
921                 i--;\r
922         }\r
923 \r
924         printf("Reduced by %d\n",fmheader.num_xyz - num_unique);\r
925         \r
926         fmheader.num_xyz = num_unique;\r
927         if (num_groups)\r
928         {\r
929                 // tack on the reference verts to the regular verts\r
930                 if(g_skelModel.references != REF_NULL)\r
931                 {\r
932                         fmframe_t       *in;\r
933                         int index;\r
934                         int refnum;\r
935 \r
936                         if (RefPointNum <= 0)\r
937                         {       // Hard-coded labels\r
938                                 refnum = numReferences[g_skelModel.references];\r
939                         }\r
940                         else\r
941                         {       // Labels indicated in QDT\r
942                                 refnum = RefPointNum;\r
943                         }\r
944 \r
945 \r
946                         for (i = 0; i < fmheader.num_frames; ++i)\r
947                         {\r
948                                 in = &g_frames[i];\r
949                                 index = fmheader.num_xyz;\r
950 \r
951                                 for (j = 0 ; j < refnum; ++j)\r
952                                 {\r
953                                         VectorCopy(in->references[j].placement.origin, in->v[index].v);\r
954                                         index++;\r
955 \r
956                                         VectorCopy(in->references[j].placement.direction, in->v[index].v);\r
957                                         index++;\r
958 \r
959                                         VectorCopy(in->references[j].placement.up, in->v[index].v);\r
960                                         index++;\r
961                                 }\r
962                         }\r
963 \r
964                         fmheader.num_xyz += refnum*3;\r
965                 }\r
966 \r
967                 // tack on the skeletal joint verts to the regular verts\r
968                 if(g_skelModel.type != SKEL_NULL)\r
969                 {\r
970                         fmframe_t       *in;\r
971                         int index;\r
972 \r
973                         for (i = 0; i < fmheader.num_frames; ++i)\r
974                         {\r
975                                 in = &g_frames[i];\r
976                                 index = fmheader.num_xyz;\r
977 \r
978                                 for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)\r
979                                 {\r
980                                         VectorCopy(in->joints[j].placement.origin, in->v[index].v);\r
981                                         index++;\r
982 \r
983                                         VectorCopy(in->joints[j].placement.direction, in->v[index].v);\r
984                                         index++;\r
985 \r
986                                         VectorCopy(in->joints[j].placement.up, in->v[index].v);\r
987                                         index++;\r
988                                 }\r
989                         }\r
990 \r
991                         fmheader.num_xyz += numJointsInSkeleton[g_skelModel.type]*3;\r
992                 }\r
993 \r
994                 CompressFrames();\r
995         }\r
996 }\r
997 \r
998 \r
999 /*\r
1000 ===============\r
1001 FinishModel\r
1002 ===============\r
1003 */\r
1004 void FMFinishModel (void)\r
1005 {\r
1006         FILE            *modelouthandle;\r
1007         int                     i,j,length,tris,verts,bit,pos,total_tris,total_verts;\r
1008         char            name[1024];\r
1009         int                     trans_count;\r
1010         \r
1011         if (!fmheader.num_frames)\r
1012                 return;\r
1013         \r
1014 //\r
1015 // copy to release directory tree if doing a release build\r
1016 //\r
1017         if (g_release)\r
1018         {\r
1019                 if (modelname[0])\r
1020                         sprintf (name, "%s", modelname);\r
1021                 else\r
1022                         sprintf (name, "%s/tris.fm", cdpartial);\r
1023                 ReleaseFile (name);\r
1024 \r
1025                 for (i=0 ; i<fmheader.num_skins ; i++)\r
1026                 {\r
1027                         ReleaseFile (g_skins[i]);\r
1028                 }\r
1029                 fmheader.num_frames = 0;\r
1030                 return;\r
1031         }\r
1032 \r
1033         printf("\n");\r
1034 \r
1035         trans_count = 0;\r
1036         for(i=0;i<fmheader.num_tris;i++)\r
1037                 if (translucent[i])\r
1038                         trans_count++;\r
1039 \r
1040         if (!g_no_opimizations)\r
1041         {\r
1042                 OptimizeVertices();\r
1043         }\r
1044 \r
1045 //\r
1046 // write the model output file\r
1047 //\r
1048         if (modelname[0])\r
1049                 sprintf (name, "%s%s", g_outputDir, modelname);\r
1050         else\r
1051                 sprintf (name, "%s/tris.fm", g_outputDir);\r
1052         printf ("saving to %s\n", name);\r
1053         CreatePath (name);\r
1054         modelouthandle = SafeOpenWrite (name);\r
1055 \r
1056         WriteModelFile (modelouthandle);\r
1057         \r
1058         printf ("%3dx%3d skin\n", fmheader.skinwidth, fmheader.skinheight);\r
1059         printf ("First frame boundaries:\n");\r
1060         printf ("       minimum x: %3f\n", g_frames[0].mins[0]);\r
1061         printf ("       maximum x: %3f\n", g_frames[0].maxs[0]);\r
1062         printf ("       minimum y: %3f\n", g_frames[0].mins[1]);\r
1063         printf ("       maximum y: %3f\n", g_frames[0].maxs[1]);\r
1064         printf ("       minimum z: %3f\n", g_frames[0].mins[2]);\r
1065         printf ("       maximum z: %3f\n", g_frames[0].maxs[2]);\r
1066         printf ("%4d vertices\n", fmheader.num_xyz);\r
1067         printf ("%4d triangles, %4d of them translucent\n", fmheader.num_tris, trans_count);\r
1068         printf ("%4d frame\n", fmheader.num_frames);\r
1069         printf ("%4d glverts\n", numglverts);\r
1070         printf ("%4d glcmd\n", fmheader.num_glcmds);\r
1071         printf ("%4d skins\n", fmheader.num_skins);\r
1072         printf ("%4d mesh nodes\n", fmheader.num_mesh_nodes);\r
1073         printf ("wasted pixels: %d / %d (%5.2f Percent)\n",total_skin_pixels - skin_pixels_used, \r
1074                 total_skin_pixels, (double)(total_skin_pixels - skin_pixels_used) / (double)total_skin_pixels * 100.0);\r
1075 \r
1076         printf ("file size: %d\n", (int)ftell (modelouthandle) );\r
1077         printf ("---------------------\n");\r
1078         \r
1079         if (g_verbose)\r
1080         {\r
1081                 if (fmheader.num_mesh_nodes)\r
1082                 {\r
1083                         total_tris = total_verts = 0;\r
1084                         printf("Node Name                         Tris Verts\n");\r
1085                         printf("--------------------------------- ---- -----\n");\r
1086                         for(i=0;i<fmheader.num_mesh_nodes;i++)\r
1087                         {\r
1088                                 tris = 0;\r
1089                                 verts = 0;\r
1090                                 for(j=0;j<MAXTRIANGLES;j++)\r
1091                                 {\r
1092                                         pos = (j) >> 3;\r
1093                                         bit = 1 << ((j) & 7 );\r
1094                                         if (pmnodes[i].tris[pos] & bit)\r
1095                                         {\r
1096                                                 tris++;\r
1097                                         }\r
1098                                 }\r
1099                                 for(j=0;j<MAX_FM_VERTS;j++)\r
1100                                 {\r
1101                                         pos = (j) >> 3;\r
1102                                         bit = 1 << ((j) & 7 );\r
1103                                         if (pmnodes[i].verts[pos] & bit)\r
1104                                         {\r
1105                                                 verts++;\r
1106                                         }\r
1107                                 }\r
1108 \r
1109                                 printf("%-33s %4d %5d\n",pmnodes[i].name,tris,verts);\r
1110 \r
1111                                 total_tris += tris;\r
1112                                 total_verts += verts;\r
1113                         }\r
1114                         printf("--------------------------------- ---- -----\n");\r
1115                         printf("%-33s %4d %5d\n","TOTALS",total_tris,total_verts);\r
1116                 }\r
1117         }\r
1118         fclose (modelouthandle);\r
1119 \r
1120         // finish writing header file\r
1121         H_printf("\n");\r
1122 \r
1123         // scale_up is usefull to allow step distances to be adjusted\r
1124         H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);\r
1125 \r
1126         // mesh nodes\r
1127         if (fmheader.num_mesh_nodes)\r
1128         {\r
1129                 H_printf("\n");\r
1130                 H_printf("#define NUM_MESH_NODES\t\t%d\n\n",fmheader.num_mesh_nodes);\r
1131                 for(i=0;i<fmheader.num_mesh_nodes;i++)\r
1132                 {\r
1133                         strcpy(name, pmnodes[i].name);\r
1134                         strupr(name);\r
1135                         length = strlen(name);\r
1136                         for(j=0;j<length;j++)\r
1137                         {\r
1138                                 if (name[j] == ' ')\r
1139                                 {\r
1140                                         name[j] = '_';\r
1141                                 }\r
1142                         }\r
1143                         H_printf("#define MESH_%s\t\t%d\n", name, i);\r
1144                 }\r
1145         }\r
1146 \r
1147         fclose (headerouthandle);\r
1148         headerouthandle = NULL;\r
1149         free (pmnodes);\r
1150 }\r
1151 \r
1152 \r
1153 /*\r
1154 =================================================================\r
1155 \r
1156 ALIAS MODEL DISPLAY LIST GENERATION\r
1157 \r
1158 =================================================================\r
1159 */\r
1160 \r
1161 extern int              strip_xyz[128];\r
1162 extern int              strip_st[128];\r
1163 extern int              strip_tris[128];\r
1164 extern int              stripcount;\r
1165 \r
1166 /*\r
1167 ================\r
1168 StripLength\r
1169 ================\r
1170 */\r
1171 static int      StripLength (int starttri, int startv, int num_tris, int node)\r
1172 {\r
1173         int                             m1, m2;\r
1174         int                             st1, st2;\r
1175         int                             j;\r
1176         fmtriangle_t    *last, *check;\r
1177         int                             k;\r
1178         int                             pos, bit;\r
1179 \r
1180         used[starttri] = 2;\r
1181 \r
1182         last = &triangles[starttri];\r
1183 \r
1184         strip_xyz[0] = last->index_xyz[(startv)%3];\r
1185         strip_xyz[1] = last->index_xyz[(startv+1)%3];\r
1186         strip_xyz[2] = last->index_xyz[(startv+2)%3];\r
1187         strip_st[0] = last->index_st[(startv)%3];\r
1188         strip_st[1] = last->index_st[(startv+1)%3];\r
1189         strip_st[2] = last->index_st[(startv+2)%3];\r
1190 \r
1191         strip_tris[0] = starttri;\r
1192         stripcount = 1;\r
1193 \r
1194         m1 = last->index_xyz[(startv+2)%3];\r
1195         st1 = last->index_st[(startv+2)%3];\r
1196         m2 = last->index_xyz[(startv+1)%3];\r
1197         st2 = last->index_st[(startv+1)%3];\r
1198 \r
1199         // look for a matching triangle\r
1200 nexttri:\r
1201         for (j=starttri+1, check=&triangles[starttri+1]\r
1202                 ; j<num_tris ; j++, check++)\r
1203         {\r
1204                 pos = j >> 3;\r
1205                 bit = 1 << (j & 7 );\r
1206                 if (!(pmnodes[node].tris[pos] & bit))\r
1207                 {\r
1208                         continue;\r
1209                 }\r
1210                 for (k=0 ; k<3 ; k++)\r
1211                 {\r
1212                         if (check->index_xyz[k] != m1)\r
1213                                 continue;\r
1214                         if (check->index_st[k] != st1)\r
1215                                 continue;\r
1216                         if (check->index_xyz[ (k+1)%3 ] != m2)\r
1217                                 continue;\r
1218                         if (check->index_st[ (k+1)%3 ] != st2)\r
1219                                 continue;\r
1220 \r
1221                         // this is the next part of the fan\r
1222 \r
1223                         // if we can't use this triangle, this tristrip is done\r
1224                         if (used[j] || translucent[j] != translucent[starttri])\r
1225                                 goto done;\r
1226 \r
1227                         // the new edge\r
1228                         if (stripcount & 1)\r
1229                         {\r
1230                                 m2 = check->index_xyz[ (k+2)%3 ];\r
1231                                 st2 = check->index_st[ (k+2)%3 ];\r
1232                         }\r
1233                         else\r
1234                         {\r
1235                                 m1 = check->index_xyz[ (k+2)%3 ];\r
1236                                 st1 = check->index_st[ (k+2)%3 ];\r
1237                         }\r
1238 \r
1239                         strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];\r
1240                         strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];\r
1241                         strip_tris[stripcount] = j;\r
1242                         stripcount++;\r
1243 \r
1244                         used[j] = 2;\r
1245                         goto nexttri;\r
1246                 }\r
1247         }\r
1248 done:\r
1249 \r
1250         // clear the temp used flags\r
1251         for (j=starttri+1 ; j<num_tris ; j++)\r
1252                 if (used[j] == 2)\r
1253                         used[j] = 0;\r
1254 \r
1255         return stripcount;\r
1256 }\r
1257 \r
1258 \r
1259 /*\r
1260 ===========\r
1261 FanLength\r
1262 ===========\r
1263 */\r
1264 static int      FanLength (int starttri, int startv, int num_tris, int node)\r
1265 {\r
1266         int                             m1, m2;\r
1267         int                             st1, st2;\r
1268         int                             j;\r
1269         fmtriangle_t    *last, *check;\r
1270         int                             k;\r
1271         int                             pos, bit;\r
1272 \r
1273         used[starttri] = 2;\r
1274 \r
1275         last = &triangles[starttri];\r
1276 \r
1277         strip_xyz[0] = last->index_xyz[(startv)%3];\r
1278         strip_xyz[1] = last->index_xyz[(startv+1)%3];\r
1279         strip_xyz[2] = last->index_xyz[(startv+2)%3];\r
1280         strip_st[0] = last->index_st[(startv)%3];\r
1281         strip_st[1] = last->index_st[(startv+1)%3];\r
1282         strip_st[2] = last->index_st[(startv+2)%3];\r
1283 \r
1284         strip_tris[0] = starttri;\r
1285         stripcount = 1;\r
1286 \r
1287         m1 = last->index_xyz[(startv+0)%3];\r
1288         st1 = last->index_st[(startv+0)%3];\r
1289         m2 = last->index_xyz[(startv+2)%3];\r
1290         st2 = last->index_st[(startv+2)%3];\r
1291 \r
1292 \r
1293         // look for a matching triangle\r
1294 nexttri:\r
1295         for (j=starttri+1, check=&triangles[starttri+1] \r
1296                 ; j<num_tris ; j++, check++)\r
1297         {\r
1298                 pos = j >> 3;\r
1299                 bit = 1 << (j & 7 );\r
1300                 if (!(pmnodes[node].tris[pos] & bit))\r
1301                 {\r
1302                         continue;\r
1303                 }\r
1304                 for (k=0 ; k<3 ; k++)\r
1305                 {\r
1306                         if (check->index_xyz[k] != m1)\r
1307                                 continue;\r
1308                         if (check->index_st[k] != st1)\r
1309                                 continue;\r
1310                         if (check->index_xyz[ (k+1)%3 ] != m2)\r
1311                                 continue;\r
1312                         if (check->index_st[ (k+1)%3 ] != st2)\r
1313                                 continue;\r
1314 \r
1315                         // this is the next part of the fan\r
1316 \r
1317                         // if we can't use this triangle, this tristrip is done\r
1318                         if (used[j] || translucent[j] != translucent[starttri])\r
1319                                 goto done;\r
1320 \r
1321                         // the new edge\r
1322                         m2 = check->index_xyz[ (k+2)%3 ];\r
1323                         st2 = check->index_st[ (k+2)%3 ];\r
1324 \r
1325                         strip_xyz[stripcount+2] = m2;\r
1326                         strip_st[stripcount+2] = st2;\r
1327                         strip_tris[stripcount] = j;\r
1328                         stripcount++;\r
1329 \r
1330                         used[j] = 2;\r
1331                         goto nexttri;\r
1332                 }\r
1333         }\r
1334 done:\r
1335 \r
1336         // clear the temp used flags\r
1337         for (j=starttri+1 ; j<num_tris ; j++)\r
1338                 if (used[j] == 2)\r
1339                         used[j] = 0;\r
1340 \r
1341         return stripcount;\r
1342 }\r
1343 \r
1344 \r
1345 \r
1346 /*\r
1347 ================\r
1348 BuildGlCmds\r
1349 \r
1350 Generate a list of trifans or strips\r
1351 for the model, which holds for all frames\r
1352 ================\r
1353 */\r
1354 static void BuildGlCmds (void)\r
1355 {\r
1356         int             i, j, k, l;\r
1357         int             startv;\r
1358         float   s, t;\r
1359         int             len, bestlen, besttype;\r
1360         int             best_xyz[1024];\r
1361         int             best_st[1024];\r
1362         int             best_tris[1024];\r
1363         int             type;\r
1364         int             trans_check;\r
1365         int             bit,pos;\r
1366 \r
1367         //\r
1368         // build tristrips\r
1369         //\r
1370         numcommands = 0;\r
1371         numglverts = 0;\r
1372 \r
1373 \r
1374         for(l=0;l<fmheader.num_mesh_nodes;l++)\r
1375         {\r
1376                 memset (used, 0, sizeof(used));\r
1377 \r
1378                 pmnodes[l].start_glcmds = numcommands;\r
1379 \r
1380                 for(trans_check = 0; trans_check<2; trans_check++)\r
1381                 {\r
1382                         for (i=0 ; i < fmheader.num_tris ; i++)\r
1383                         {\r
1384                                 pos = i >> 3;\r
1385                                 bit = 1 << (i & 7 );\r
1386                                 if (!(pmnodes[l].tris[pos] & bit))\r
1387                                 {\r
1388                                         continue;\r
1389                                 }\r
1390 \r
1391                                 // pick an unused triangle and start the trifan\r
1392                                 if (used[i] || trans_check != translucent[i])\r
1393                                 {\r
1394                                         continue;\r
1395                                 }\r
1396 \r
1397                                 bestlen = 0;\r
1398                                 for (type = 0 ; type < 2 ; type++)\r
1399                 //      type = 1;\r
1400                                 {\r
1401                                         for (startv =0 ; startv < 3 ; startv++)\r
1402                                         {\r
1403                                                 if (type == 1)\r
1404                                                         len = StripLength (i, startv, fmheader.num_tris, l);\r
1405                                                 else\r
1406                                                         len = FanLength (i, startv, fmheader.num_tris, l);\r
1407                                                 if (len > bestlen)\r
1408                                                 {\r
1409                                                         besttype = type;\r
1410                                                         bestlen = len;\r
1411                                                         for (j=0 ; j<bestlen+2 ; j++)\r
1412                                                         {\r
1413                                                                 best_st[j] = strip_st[j];\r
1414                                                                 best_xyz[j] = strip_xyz[j];\r
1415                                                         }\r
1416                                                         for (j=0 ; j<bestlen ; j++)\r
1417                                                                 best_tris[j] = strip_tris[j];\r
1418                                                 }\r
1419                                         }\r
1420                                 }\r
1421 \r
1422                                 // mark the tris on the best strip/fan as used\r
1423                                 for (j=0 ; j<bestlen ; j++)\r
1424                                         used[best_tris[j]] = 1;\r
1425 \r
1426                                 if (besttype == 1)\r
1427                                         commands[numcommands++] = (bestlen+2);\r
1428                                 else\r
1429                                         commands[numcommands++] = -(bestlen+2);\r
1430 \r
1431                                 numglverts += bestlen+2;\r
1432 \r
1433                                 for (j=0 ; j<bestlen+2 ; j++)\r
1434                                 {\r
1435                                         // emit a vertex into the reorder buffer\r
1436                                         k = best_st[j];\r
1437 \r
1438                                         // emit s/t coords into the commands stream\r
1439                                         s = base_st[k].s;\r
1440                                         t = base_st[k].t;\r
1441 \r
1442                                         s = (s  ) / fmheader.skinwidth;\r
1443                                         t = (t  ) / fmheader.skinheight;\r
1444 \r
1445                                         *(float *)&commands[numcommands++] = s;\r
1446                                         *(float *)&commands[numcommands++] = t;\r
1447                                         *(int *)&commands[numcommands++] = best_xyz[j];\r
1448                                 }\r
1449                         }\r
1450                 }\r
1451                 commands[numcommands++] = 0;            // end of list marker\r
1452                 pmnodes[l].num_glcmds = numcommands - pmnodes[l].start_glcmds;\r
1453         }\r
1454 }\r
1455 \r
1456 \r
1457 /*\r
1458 ===============================================================\r
1459 \r
1460 BASE FRAME SETUP\r
1461 \r
1462 ===============================================================\r
1463 */\r
1464 \r
1465 \r
1466 #define LINE_NORMAL 1\r
1467 #define LINE_FAT 2\r
1468 #define LINE_DOTTED 3\r
1469 \r
1470 \r
1471 #define ASCII_SPACE 32\r
1472 \r
1473 int LineType = LINE_NORMAL;\r
1474 extern unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];\r
1475 unsigned char LineColor = 255;\r
1476 int ScaleWidth, ScaleHeight;\r
1477 \r
1478 \r
1479 static char *CharDefs[] =\r
1480 {\r
1481         "-------------------------",\r
1482         "-------------------------", // !\r
1483         "-------------------------", // "\r
1484         "-------------------------", // #\r
1485         "-------------------------", // $\r
1486         "-------------------------", // %\r
1487         "-------------------------", // &\r
1488         "--*----*-----------------", // '\r
1489         "-*---*----*----*-----*---", // (\r
1490         "*-----*----*----*---*----", // )\r
1491         "-----*--*--**---**--*--*-", // *\r
1492         "-------------------------", // +\r
1493         "----------------**--**---", // ,\r
1494         "-------------------------", // -\r
1495         "----------------**---**--", // .\r
1496         "-------------------------", // /\r
1497         " *** *  *** * ***  * *** ", // 0\r
1498         "   *   **    *    *    * ",\r
1499         "****     * *** *    *****",\r
1500         "****     * ***     ***** ",\r
1501         "  **  * * *  * *****   * ",\r
1502         "**** *    ****     ***** ",\r
1503         " *** *    **** *   * *** ",\r
1504         "*****    *   *   *    *  ",\r
1505         " *** *   * *** *   * *** ",\r
1506         " *** *   * ****    * *** ", // 9\r
1507         "-**---**--------**---**--", // :\r
1508         "-------------------------", // ;\r
1509         "-------------------------", // <\r
1510         "-------------------------", // =\r
1511         "-------------------------", // >\r
1512         "-------------------------", // ?\r
1513         "-------------------------", // @\r
1514         "-***-*---*******---**---*", // A\r
1515         "****-*---*****-*---*****-",\r
1516         "-*****----*----*-----****",\r
1517         "****-*---**---**---*****-",\r
1518         "******----****-*----*****",\r
1519         "******----****-*----*----",\r
1520         "-*****----*--***---*-****",\r
1521         "*---**---*******---**---*",\r
1522         "-***---*----*----*---***-",\r
1523         "----*----*----**---*-***-",\r
1524         "-*--*-*-*--**---*-*--*--*",\r
1525         "-*----*----*----*----****",\r
1526         "*---***-***-*-**---**---*",\r
1527         "*---***--**-*-**--***---*",\r
1528         "-***-*---**---**---*-***-",\r
1529         "****-*---*****-*----*----",\r
1530         "-***-*---**---*-***----**",\r
1531         "****-*---*****-*-*--*--**",\r
1532         "-*****-----***-----*****-",\r
1533         "*****--*----*----*----*--",\r
1534         "*---**---**---**---******",\r
1535         "*---**---**---*-*-*---*--",\r
1536         "*---**---**-*-***-***---*",\r
1537         "*---*-*-*---*---*-*-*---*",\r
1538         "*---**---*-*-*---*----*--",\r
1539         "*****---*---*---*---*****" // Z\r
1540 };\r
1541 \r
1542 void DrawLine(int x1, int y1, int x2, int y2)\r
1543 {\r
1544         int dx, dy;\r
1545         int adx, ady;\r
1546         int count;\r
1547         float xfrac, yfrac, xstep, ystep;\r
1548         unsigned sx, sy;\r
1549         float u, v;\r
1550 \r
1551         dx = x2 - x1;\r
1552         dy = y2 - y1;\r
1553         adx = abs(dx);\r
1554         ady = abs(dy);\r
1555 \r
1556         count = adx > ady ? adx : ady;\r
1557         count++;\r
1558 \r
1559         if(count > 300)\r
1560         {\r
1561                 printf("Bad count\n");\r
1562                 return; // don't ever hang up on bad data\r
1563         }\r
1564                 \r
1565         xfrac = x1;\r
1566         yfrac = y1;\r
1567         \r
1568         xstep = (float)dx/count;\r
1569         ystep = (float)dy/count;\r
1570 \r
1571         switch(LineType)\r
1572         {\r
1573                 case LINE_NORMAL:\r
1574                         do\r
1575                         {\r
1576                                 if(xfrac < SKINPAGE_WIDTH && yfrac < SKINPAGE_HEIGHT)\r
1577                                 {\r
1578                                         pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;\r
1579                                 }\r
1580                                 xfrac += xstep;\r
1581                                 yfrac += ystep;\r
1582                                 count--;\r
1583                         } while (count > 0);\r
1584                         break;\r
1585                 case LINE_FAT:\r
1586                         do\r
1587                         {\r
1588                                 for (u=-0.1 ; u<=0.9 ; u+=0.999)\r
1589                                 {\r
1590                                         for (v=-0.1 ; v<=0.9 ; v+=0.999)\r
1591                                         {\r
1592                                                 sx = xfrac+u;\r
1593                                                 sy = yfrac+v;\r
1594                                                 if(sx < SKINPAGE_WIDTH && sy < SKINPAGE_HEIGHT)\r
1595                                                 {\r
1596                                                         pic[sy*SKINPAGE_WIDTH+sx] = LineColor;\r
1597                                                 }\r
1598                                         }\r
1599                                 }\r
1600                                 xfrac += xstep;\r
1601                                 yfrac += ystep;\r
1602                                 count--;\r
1603                         } while (count > 0);\r
1604                         break;\r
1605                 case LINE_DOTTED:\r
1606                         do\r
1607                         {\r
1608                                 if(count&1 && xfrac < SKINPAGE_WIDTH &&\r
1609                                         yfrac < SKINPAGE_HEIGHT)\r
1610                                 {\r
1611                                         pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;\r
1612                                 }\r
1613                                 xfrac += xstep;\r
1614                                 yfrac += ystep;\r
1615                                 count--;\r
1616                         } while (count > 0);\r
1617                         break;\r
1618                 default:\r
1619                         Error("Unknown <linetype> %d.\n", LineType);\r
1620         }\r
1621 }\r
1622 \r
1623 //==========================================================================\r
1624 //\r
1625 // DrawCharacter\r
1626 //\r
1627 //==========================================================================\r
1628 \r
1629 static void DrawCharacter(int x, int y, int character)\r
1630 {\r
1631         int r, c;\r
1632         char *def;\r
1633 \r
1634         character = toupper(character);\r
1635         if(character < ASCII_SPACE || character > 'Z')\r
1636         {\r
1637                 character = ASCII_SPACE;\r
1638         }\r
1639         character -= ASCII_SPACE;\r
1640         for(def = CharDefs[character], r = 0; r < 5; r++)\r
1641         {\r
1642                 for(c = 0; c < 5; c++)\r
1643                 {\r
1644                         pic[(y+r)*SKINPAGE_WIDTH+x+c] = *def++ == '*' ? 255 : 0;\r
1645                 }\r
1646         }\r
1647 }\r
1648 \r
1649 //==========================================================================\r
1650 //\r
1651 // DrawTextChar\r
1652 //\r
1653 //==========================================================================\r
1654 \r
1655 void DrawTextChar(int x, int y, char *text)\r
1656 {\r
1657         int c;\r
1658 \r
1659         while((c = *text++) != '\0')\r
1660         {\r
1661                 DrawCharacter(x, y, c);\r
1662                 x += 6;\r
1663         }\r
1664 }\r
1665 \r
1666 \r
1667 extern void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight);\r
1668 \r
1669 //==========================================================================\r
1670 // ExtractDigit\r
1671 \r
1672 static int ExtractDigit(byte *pic, int x, int y)\r
1673 {\r
1674         int             i;\r
1675         int             r, c;\r
1676         char    digString[32];\r
1677         char    *buffer;\r
1678         byte    backColor;\r
1679         char    **DigitDefs;\r
1680 \r
1681         backColor = pic[(SKINPAGE_HEIGHT - 1) * SKINPAGE_WIDTH];\r
1682         DigitDefs = &CharDefs['0' - ASCII_SPACE];\r
1683 \r
1684         buffer = digString;\r
1685         for(r = 0; r < 5; r++)\r
1686         {\r
1687                 for(c = 0; c < 5; c++)\r
1688                 {\r
1689                         *buffer++ = (pic[(y + r) * SKINPAGE_WIDTH + x + c] == backColor) ? ' ' : '*';\r
1690                 }\r
1691         }\r
1692         *buffer = '\0';\r
1693         for(i = 0; i < 10; i++)\r
1694         {\r
1695                 if(strcmp(DigitDefs[i], digString) == 0)\r
1696                 {\r
1697                         return i;\r
1698                 }\r
1699         }\r
1700 \r
1701         Error("Unable to extract scaling info from skin PCX.");\r
1702         return 0;\r
1703 }\r
1704 \r
1705 //==========================================================================\r
1706 // ExtractNumber\r
1707 \r
1708 int ExtractNumber(byte *pic, int x, int y)\r
1709 {\r
1710         return ExtractDigit(pic, x, y) * 100 + ExtractDigit(pic, x + 6, y) * 10 + ExtractDigit(pic, x + 12, y);\r
1711 }\r
1712 \r
1713 \r
1714 \r
1715 \r
1716 \r
1717 /*\r
1718 ============\r
1719 BuildST\r
1720 \r
1721 Builds the triangle_st array for the base frame and\r
1722 fmheader.skinwidth / fmheader.skinheight\r
1723 \r
1724   FIXME: allow this to be loaded from a file for\r
1725   arbitrary mappings\r
1726 ============\r
1727 */\r
1728 static void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)\r
1729 {\r
1730         int                     backface_flag;\r
1731         int                     i, j;\r
1732         int                     width, height, iwidth, iheight, swidth;\r
1733         float           basex, basey;\r
1734         float           scale;\r
1735         vec3_t          mins, maxs;\r
1736         float           *pbasevert;\r
1737         vec3_t          vtemp1, vtemp2, normal;\r
1738         float           s_scale, t_scale;\r
1739         float           scWidth;\r
1740         float           scHeight;\r
1741         int skinwidth;\r
1742         int skinheight;\r
1743 \r
1744         //\r
1745         // find bounds of all the verts on the base frame\r
1746         //\r
1747         ClearBounds (mins, maxs);\r
1748         backface_flag = false;\r
1749         \r
1750         if (ptri[0].HasUV)      // if we have the uv already, we don't want to double up or scale\r
1751         {\r
1752                 iwidth = ScaleWidth;\r
1753                 iheight = ScaleHeight;\r
1754                 \r
1755                 t_scale = s_scale = 1.0;\r
1756         }\r
1757         else\r
1758         {\r
1759                 for (i=0 ; i<numtri ; i++)\r
1760                         for (j=0 ; j<3 ; j++)\r
1761                                 AddPointToBounds (ptri[i].verts[j], mins, maxs);\r
1762                         \r
1763                         for (i=0 ; i<3 ; i++)\r
1764                         {\r
1765                                 mins[i] = floor(mins[i]);\r
1766                                 maxs[i] = ceil(maxs[i]);\r
1767                         }\r
1768                         \r
1769                         width = maxs[0] - mins[0];\r
1770                         height = maxs[2] - mins[2];\r
1771                         \r
1772                         for (i=0 ; i<numtri ; i++)\r
1773                         {\r
1774                                 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
1775                                 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
1776                                 CrossProduct (vtemp1, vtemp2, normal);\r
1777                                 \r
1778                                 if (normal[1] > 0)\r
1779                                 {\r
1780                                         backface_flag = true;\r
1781                                         break;\r
1782                                 }\r
1783                         }\r
1784                         scWidth = ScaleWidth*SCALE_ADJUST_FACTOR;\r
1785                         if (backface_flag)      //we are doubling\r
1786                                 scWidth /= 2;\r
1787                         \r
1788                         scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;\r
1789                         \r
1790                         scale = scWidth/width;\r
1791                         \r
1792                         if(height*scale >= scHeight)\r
1793                         {\r
1794                                 scale = scHeight/height;\r
1795                         }\r
1796                         \r
1797                         iwidth = ceil(width*scale)+4;\r
1798                         iheight = ceil(height*scale)+4;\r
1799                         \r
1800                         s_scale = (float)(iwidth-4) / width;\r
1801                         t_scale = (float)(iheight-4) / height;\r
1802                         t_scale = s_scale;\r
1803         }\r
1804         if (DrawSkin)\r
1805         {\r
1806                 if(backface_flag)\r
1807                         DrawScreen(s_scale, t_scale, iwidth*2, iheight);\r
1808                 else\r
1809                         DrawScreen(s_scale, t_scale, iwidth, iheight);\r
1810         }\r
1811         if (backface_flag)\r
1812                 skinwidth=iwidth*2;\r
1813         else\r
1814                 skinwidth=iwidth;\r
1815         skinheight=iheight;\r
1816 \r
1817 \r
1818 /*      if (!g_fixedwidth)\r
1819         {       // old style\r
1820                 scale = 8;\r
1821                 if (width*scale >= 150)\r
1822                         scale = 150.0 / width;  \r
1823                 if (height*scale >= 190)\r
1824                         scale = 190.0 / height;\r
1825 \r
1826                 s_scale = t_scale = scale;\r
1827 \r
1828                 iwidth = ceil(width*s_scale);\r
1829                 iheight = ceil(height*t_scale);\r
1830 \r
1831                 iwidth += 4;\r
1832                 iheight += 4;\r
1833         }\r
1834         else\r
1835         {       // new style\r
1836                 iwidth = g_fixedwidth / 2;\r
1837                 iheight = g_fixedheight;\r
1838 \r
1839                 s_scale = (float)(iwidth-4) / width;\r
1840                 t_scale = (float)(iheight-4) / height;\r
1841         }*/\r
1842 \r
1843 //\r
1844 // determine which side of each triangle to map the texture to\r
1845 //\r
1846         basey = 2;\r
1847         for (i=0 ; i<numtri ; i++)\r
1848         {\r
1849                 if (ptri[i].HasUV)\r
1850                 {\r
1851                         for (j=0 ; j<3 ; j++)\r
1852                         {\r
1853                                 triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*skinwidth);\r
1854                                 triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*skinheight);\r
1855                         }\r
1856                 }\r
1857                 else\r
1858                 {\r
1859                         VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
1860                         VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
1861                         CrossProduct (vtemp1, vtemp2, normal);\r
1862 \r
1863                         if (normal[1] > 0)\r
1864                         {\r
1865                                 basex = iwidth + 2;\r
1866                         }\r
1867                         else\r
1868                         {\r
1869                                 basex = 2;\r
1870                         }\r
1871                         \r
1872                         for (j=0 ; j<3 ; j++)\r
1873                         {\r
1874                                 pbasevert = ptri[i].verts[j];\r
1875 \r
1876                                 triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);\r
1877                                 triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);\r
1878                         }\r
1879                 }\r
1880 \r
1881                 if (DrawSkin)\r
1882                 {\r
1883                         DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],\r
1884                                 triangle_st[i][1][0], triangle_st[i][1][1]);\r
1885                         DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],\r
1886                                 triangle_st[i][2][0], triangle_st[i][2][1]);\r
1887                         DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],\r
1888                                 triangle_st[i][0][0], triangle_st[i][0][1]);\r
1889                 }\r
1890         }\r
1891 \r
1892 // make the width a multiple of 4; some hardware requires this, and it ensures\r
1893 // dword alignment for each scan\r
1894 \r
1895         swidth = iwidth;\r
1896         if(backface_flag)\r
1897                 swidth *= 2;\r
1898         fmheader.skinwidth = (swidth + 3) & ~3;\r
1899         fmheader.skinheight = iheight;\r
1900 \r
1901         skin_width = iwidth;\r
1902         skin_height = iheight;\r
1903 }\r
1904 \r
1905 \r
1906 static void BuildNewST (triangle_t *ptri, int numtri, qboolean DrawSkin)\r
1907 {\r
1908         int                     i, j;\r
1909 \r
1910         for (i=0 ; i<numtri ; i++)\r
1911         {\r
1912                 if (ptri[i].HasUV)\r
1913                 {\r
1914                         for (j=0 ; j<3 ; j++)\r
1915                         {\r
1916                                 triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*(ScaleWidth-1));\r
1917                                 triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*(ScaleHeight-1));\r
1918                         }\r
1919                 }\r
1920 \r
1921                 if (DrawSkin)\r
1922                 {\r
1923                         DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],\r
1924                                 triangle_st[i][1][0], triangle_st[i][1][1]);\r
1925                         DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],\r
1926                                 triangle_st[i][2][0], triangle_st[i][2][1]);\r
1927                         DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],\r
1928                                 triangle_st[i][0][0], triangle_st[i][0][1]);\r
1929                 }\r
1930         }\r
1931 \r
1932 // make the width a multiple of 4; some hardware requires this, and it ensures\r
1933 // dword alignment for each scan\r
1934 \r
1935         fmheader.skinwidth = (ScaleWidth + 3) & ~3;\r
1936         fmheader.skinheight = ScaleHeight;\r
1937 \r
1938         skin_width = ScaleWidth;\r
1939         skin_height = ScaleHeight;\r
1940 }\r
1941 \r
1942 \r
1943 \r
1944 \r
1945 byte                    *BasePalette;\r
1946 byte                    *BasePixels,*TransPixels;\r
1947 int                             BaseWidth, BaseHeight, TransWidth, TransHeight;\r
1948 qboolean                BaseTrueColor;\r
1949 static qboolean SetPixel = false;\r
1950 \r
1951 int CheckTransRecursiveTri (int *lp1, int *lp2, int *lp3)\r
1952 {\r
1953         int             *temp;\r
1954         int             d;\r
1955         int             new[2];\r
1956 \r
1957         d = lp2[0] - lp1[0];\r
1958         if (d < -1 || d > 1)\r
1959                 goto split;\r
1960         d = lp2[1] - lp1[1];\r
1961         if (d < -1 || d > 1)\r
1962                 goto split;\r
1963 \r
1964         d = lp3[0] - lp2[0];\r
1965         if (d < -1 || d > 1)\r
1966                 goto split2;\r
1967         d = lp3[1] - lp2[1];\r
1968         if (d < -1 || d > 1)\r
1969                 goto split2;\r
1970 \r
1971         d = lp1[0] - lp3[0];\r
1972         if (d < -1 || d > 1)\r
1973                 goto split3;\r
1974         d = lp1[1] - lp3[1];\r
1975         if (d < -1 || d > 1)\r
1976         {\r
1977 split3:\r
1978                 temp = lp1;\r
1979                 lp1 = lp3;\r
1980                 lp3 = lp2;\r
1981                 lp2 = temp;\r
1982 \r
1983                 goto split;\r
1984         }\r
1985 \r
1986         return 0;                       // entire tri is filled\r
1987 \r
1988 split2:\r
1989         temp = lp1;\r
1990         lp1 = lp2;\r
1991         lp2 = lp3;\r
1992         lp3 = temp;\r
1993 \r
1994 split:\r
1995 // split this edge\r
1996         new[0] = (lp1[0] + lp2[0]) >> 1;\r
1997         new[1] = (lp1[1] + lp2[1]) >> 1;\r
1998 \r
1999 // draw the point if splitting a leading edge\r
2000         if (lp2[1] > lp1[1])\r
2001                 goto nodraw;\r
2002         if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0]))\r
2003                 goto nodraw;\r
2004 \r
2005         if (SetPixel)\r
2006         {\r
2007                 assert ((new[1]*BaseWidth) + new[0] < BaseWidth*BaseHeight);\r
2008 \r
2009                 if (BaseTrueColor)\r
2010                 {\r
2011                         BasePixels[((new[1]*BaseWidth) + new[0]) * 4] = 1;\r
2012                 }\r
2013                 else\r
2014                 {\r
2015                         BasePixels[(new[1]*BaseWidth) + new[0]] = 1;\r
2016                 }\r
2017         }\r
2018         else\r
2019         {\r
2020                 if (TransPixels)\r
2021                 {\r
2022                         if (TransPixels[(new[1]*TransWidth) + new[0]] != 255)\r
2023                                 return 1;\r
2024                 }\r
2025                 else if (BaseTrueColor)\r
2026                 {\r
2027                         if (BasePixels[(((new[1]*BaseWidth) + new[0]) * 4) + 3] != 255)\r
2028                                 return 1;\r
2029                 }\r
2030                 else\r
2031                 {\r
2032 //                      pixel = BasePixels[(new[1]*BaseWidth) + new[0]];\r
2033                 }\r
2034         }\r
2035 \r
2036 nodraw:\r
2037 // recursively continue\r
2038         if (CheckTransRecursiveTri(lp3, lp1, new)) \r
2039                 return 1;\r
2040 \r
2041         return CheckTransRecursiveTri(lp3, new, lp2);\r
2042 }\r
2043 \r
2044 static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters, \r
2045         IntListNode_t **vertLists, int *num_verts, int *new_num_verts)\r
2046 {\r
2047         int i, j;\r
2048         IntListNode_t *next;\r
2049 \r
2050         for(j = 0; j < numJointsInSkeleton[g_skelModel.type]; ++j)\r
2051         {\r
2052                 if(!clusters[j])\r
2053                 {\r
2054                         continue;\r
2055                 }\r
2056 \r
2057                 for(i = 0; i < num_verts[j+1]; ++i)\r
2058                 {\r
2059                         if(clusters[j][i] == oldindex)\r
2060                         {\r
2061                                 ++new_num_verts[j+1];\r
2062 \r
2063                                 next = vertLists[j];\r
2064 \r
2065                                 vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");\r
2066                                 // Currently freed in WriteJointedModelFile only\r
2067 \r
2068                                 vertLists[j]->data = newIndex;\r
2069                                 vertLists[j]->next = next;\r
2070                         }\r
2071                 }\r
2072         }\r
2073 }\r
2074 \r
2075 #define FUDGE_EPSILON   0.002\r
2076 \r
2077 qboolean VectorFudgeCompare (vec3_t v1, vec3_t v2)\r
2078 {\r
2079         int             i;\r
2080         \r
2081         for (i=0 ; i<3 ; i++)\r
2082                 if (fabs(v1[i]-v2[i]) > FUDGE_EPSILON)\r
2083                         return false;\r
2084                         \r
2085         return true;\r
2086 }\r
2087 \r
2088 /*\r
2089 =================\r
2090 Cmd_Base\r
2091 =================\r
2092 */\r
2093 void Cmd_FMBase (qboolean GetST)\r
2094 {\r
2095         triangle_t      *ptri, *st_tri;\r
2096         int                     num_st_tris;\r
2097         int                     i, j, k, l;\r
2098         int                     x,y,z;\r
2099 //      int                     time1;\r
2100         char            file1[1024],file2[1024],trans_file[1024], stfile[1024], extension[256];\r
2101         vec3_t          base_xyz[MAX_FM_VERTS];\r
2102         FILE            *FH;\r
2103         int                     pos,bit;\r
2104         qboolean        NewSkin;\r
2105 \r
2106         GetScriptToken (false);\r
2107 \r
2108         if (g_skipmodel || g_release || g_archive)\r
2109                 return;\r
2110 \r
2111         printf ("---------------------\n");\r
2112         sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);\r
2113         printf ("%s ", file1);\r
2114 \r
2115         ExpandPathAndArchive (file1);\r
2116 \r
2117         // Use the input filepath for this one.\r
2118         sprintf (file1, "%s/%s", cddir, token);\r
2119 \r
2120 //      time1 = FileTime (file1);\r
2121 //      if (time1 == -1)\r
2122 //              Error ("%s doesn't exist", file1);\r
2123 \r
2124 //\r
2125 // load the base triangles\r
2126 //\r
2127         if (do3ds)\r
2128                 Load3DSTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
2129         else\r
2130                 LoadTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
2131 \r
2132         if (g_ignoreTriUV)\r
2133         {\r
2134                 for (i=0;i<fmheader.num_tris;i++)\r
2135                 {\r
2136                         ptri[i].HasUV=0;\r
2137                 }\r
2138         }\r
2139 \r
2140         GetScriptToken (false);\r
2141         sprintf (file2, "%s/%s", cddir, token);\r
2142         sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);\r
2143 \r
2144         ExtractFileExtension (file2, extension);\r
2145         if (extension[0] == 0)\r
2146         {\r
2147                 strcat(file2, ".pcx");\r
2148         }\r
2149         printf ("skin: %s\n", file2);\r
2150 \r
2151         BaseTrueColor = LoadAnyImage (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);\r
2152 \r
2153         NewSkin = false;\r
2154         if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)\r
2155         {\r
2156                 if (g_allow_newskin)\r
2157                 {\r
2158                         ScaleWidth = BaseWidth;\r
2159                         ScaleHeight = BaseHeight;\r
2160                         NewSkin = true;\r
2161                 }\r
2162                 else\r
2163                 {\r
2164                         Error("Invalid skin page size: (%d,%d) should be (%d,%d)",\r
2165                                 BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);\r
2166                 }\r
2167         }\r
2168         else if (!BaseTrueColor)\r
2169         {\r
2170                 ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,\r
2171                         ENCODED_WIDTH_Y);\r
2172                 ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,\r
2173                         ENCODED_HEIGHT_Y);\r
2174         }\r
2175         else\r
2176         {\r
2177                 Error("Texture coordinates not supported on true color image");\r
2178         }\r
2179 \r
2180         if (GetST)\r
2181         {\r
2182                 GetScriptToken (false);\r
2183 \r
2184                 sprintf (stfile, "%s/%s.%s", cdarchive, token, trifileext);\r
2185                 printf ("ST: %s ", stfile);\r
2186 \r
2187                 sprintf (stfile, "%s/%s", cddir, token);\r
2188 \r
2189                 if (do3ds)\r
2190                         Load3DSTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);\r
2191                 else\r
2192                         LoadTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);\r
2193 \r
2194                 if (num_st_tris != fmheader.num_tris)\r
2195                 {\r
2196                         Error ("num st tris mismatch: st %d / base %d", num_st_tris, fmheader.num_tris);\r
2197                 }\r
2198 \r
2199                 printf("   matching triangles...\n");\r
2200                 for(i=0;i<fmheader.num_tris;i++)\r
2201                 {\r
2202                         k = -1;\r
2203                         for(j=0;j<num_st_tris;j++)\r
2204                         {\r
2205                                 for(x=0;x<3;x++)\r
2206                                 {\r
2207                                         for(y=0;y<3;y++)\r
2208                                         {\r
2209                                                 if (x == y)\r
2210                                                 {\r
2211                                                         continue;\r
2212                                                 }\r
2213                                                 for(z=0;z<3;z++)\r
2214                                                 {\r
2215                                                         if (z == x || z == y) \r
2216                                                         {\r
2217                                                                 continue;\r
2218                                                         }\r
2219 \r
2220                                                         if (VectorFudgeCompare (ptri[i].verts[0], st_tri[j].verts[x]) &&\r
2221                                                             VectorFudgeCompare (ptri[i].verts[1], st_tri[j].verts[y]) &&\r
2222                                                                 VectorFudgeCompare (ptri[i].verts[2], st_tri[j].verts[z]))\r
2223                                                         {\r
2224                                                                 if (k == -1)\r
2225                                                                 {\r
2226                                                                         k = j;\r
2227                                                                         ptri[i].HasUV = st_tri[k].HasUV;\r
2228                                                                         ptri[i].uv[0][0] = st_tri[k].uv[x][0];\r
2229                                                                         ptri[i].uv[0][1] = st_tri[k].uv[x][1];\r
2230                                                                         ptri[i].uv[1][0] = st_tri[k].uv[y][0];\r
2231                                                                         ptri[i].uv[1][1] = st_tri[k].uv[y][1];\r
2232                                                                         ptri[i].uv[2][0] = st_tri[k].uv[z][0];\r
2233                                                                         ptri[i].uv[2][1] = st_tri[k].uv[z][1];\r
2234                                                                         x = y = z = 999;\r
2235                                                                 }\r
2236                                                                 else if (k != j)\r
2237                                                                 {\r
2238                                                                         printf("Duplicate triangle %d found in st file: %d and %d\n",i,k,j);\r
2239                                                                         printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",\r
2240                                                                                 ptri[i].verts[0][0],ptri[i].verts[0][1],ptri[i].verts[0][2],\r
2241                                                                                 ptri[i].verts[1][0],ptri[i].verts[1][1],ptri[i].verts[1][2],\r
2242                                                                                 ptri[i].verts[2][0],ptri[i].verts[2][1],ptri[i].verts[2][2]);\r
2243                                                                         printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",\r
2244                                                                                 st_tri[k].verts[0][0],st_tri[k].verts[0][1],st_tri[k].verts[0][2],\r
2245                                                                                 st_tri[k].verts[1][0],st_tri[k].verts[1][1],st_tri[k].verts[1][2],\r
2246                                                                                 st_tri[k].verts[2][0],st_tri[k].verts[2][1],st_tri[k].verts[2][2]);\r
2247                                                                         printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",\r
2248                                                                                 st_tri[j].verts[0][0],st_tri[j].verts[0][1],st_tri[j].verts[0][2],\r
2249                                                                                 st_tri[j].verts[1][0],st_tri[j].verts[1][1],st_tri[j].verts[1][2],\r
2250                                                                                 st_tri[j].verts[2][0],st_tri[j].verts[2][1],st_tri[j].verts[2][2]);\r
2251                                                                 }\r
2252                                                         }\r
2253                                                 }\r
2254                                         }\r
2255                                 }\r
2256                         }\r
2257                         if (k == -1)\r
2258                         {\r
2259                                 printf("No matching triangle %d\n",i);\r
2260                         }\r
2261                 }\r
2262                 free (st_tri);\r
2263         }\r
2264 \r
2265 //\r
2266 // get the ST values\r
2267 //\r
2268         if (ptri && ptri[0].HasUV)\r
2269         {\r
2270                 if (!NewSkin)\r
2271                 {\r
2272                         Error("Base has UVs with old style skin page\nMaybe you want to use -ignoreUV");\r
2273                 }\r
2274                 else\r
2275                 {\r
2276                         BuildNewST (ptri, fmheader.num_tris, false);\r
2277                 }\r
2278         }\r
2279         else\r
2280         {\r
2281                 if (NewSkin)\r
2282                 {\r
2283                         Error("Base has new style skin without UVs");\r
2284                 }\r
2285                 else\r
2286                 {\r
2287                         BuildST (ptri, fmheader.num_tris, false);\r
2288                 }\r
2289         }\r
2290 \r
2291         TransPixels = NULL;\r
2292         if (!BaseTrueColor)\r
2293         {\r
2294                 FH = fopen(trans_file,"rb");\r
2295                 if (FH)\r
2296                 {\r
2297                         fclose(FH);\r
2298                         Load256Image (trans_file, &TransPixels, NULL, &TransWidth, &TransHeight);\r
2299                         if (TransWidth != fmheader.skinwidth || TransHeight != fmheader.skinheight)\r
2300                         {\r
2301                                 Error ("source image %s dimensions (%d,%d) are not the same as alpha image (%d,%d)\n",file2,fmheader.skinwidth,fmheader.skinheight,TransWidth,TransHeight);\r
2302                         }\r
2303                 }\r
2304         }\r
2305 \r
2306 //\r
2307 // run through all the base triangles, storing each unique vertex in the\r
2308 // base vertex list and setting the indirect triangles to point to the base\r
2309 // vertices\r
2310 //\r
2311         for(l=0;l<fmheader.num_mesh_nodes;l++)\r
2312         {\r
2313                 for (i=0 ; i < fmheader.num_tris ; i++)\r
2314                 {\r
2315                         pos = i >> 3;\r
2316                         bit = 1 << (i & 7 );\r
2317                         if (!(pmnodes[l].tris[pos] & bit))\r
2318                         {\r
2319                                 continue;\r
2320                         }\r
2321 \r
2322                         for (j=0 ; j<3 ; j++)\r
2323                         {\r
2324                                 // get the xyz index\r
2325                                 for (k=0 ; k<fmheader.num_xyz ; k++)\r
2326                                 {\r
2327                                         if (VectorCompare (ptri[i].verts[j], base_xyz[k]))\r
2328                                         {\r
2329                                                 break;  // this vertex is already in the base vertex list\r
2330                                         }\r
2331                                 }\r
2332 \r
2333                                 if (k == fmheader.num_xyz)\r
2334                                 { // new index\r
2335                                         VectorCopy (ptri[i].verts[j], base_xyz[fmheader.num_xyz]);\r
2336 \r
2337                                         if(pmnodes[l].clustered == true)\r
2338                                         {\r
2339                                                 ReplaceClusterIndex(k, ptri[i].indicies[j], (int **)&pmnodes[l].clusters, (IntListNode_t **)&g_skelModel.vertLists, (int *)&pmnodes[l].num_verts, (int *)&g_skelModel.new_num_verts);\r
2340                                         }\r
2341 \r
2342                                         fmheader.num_xyz++;\r
2343                                 }\r
2344 \r
2345                                 pos = k >> 3;\r
2346                                 bit = 1 << (k & 7);\r
2347                                 pmnodes[l].verts[pos] |= bit;\r
2348 \r
2349                                 triangles[i].index_xyz[j] = k;\r
2350 \r
2351                                 // get the st index\r
2352                                 for (k=0 ; k<fmheader.num_st ; k++)\r
2353                                 {\r
2354                                         if (triangle_st[i][j][0] == base_st[k].s\r
2355                                         && triangle_st[i][j][1] == base_st[k].t)\r
2356                                         {\r
2357                                                 break;  // this vertex is already in the base vertex list\r
2358                                         }\r
2359                                 }\r
2360 \r
2361                                 if (k == fmheader.num_st)\r
2362                                 { // new index\r
2363                                         base_st[fmheader.num_st].s = triangle_st[i][j][0];\r
2364                                         base_st[fmheader.num_st].t = triangle_st[i][j][1];\r
2365                                         fmheader.num_st++;\r
2366                                 }\r
2367 \r
2368                                 triangles[i].index_st[j] = k;\r
2369                         }\r
2370 \r
2371                         if (TransPixels || BaseTrueColor)\r
2372                         {\r
2373                                 translucent[i] = CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);\r
2374                         }\r
2375                         else\r
2376                         {\r
2377                                 translucent[i] = false;\r
2378                         }\r
2379                 }\r
2380         }\r
2381 \r
2382         if (!BaseTrueColor)\r
2383         {\r
2384                 SetPixel = true;\r
2385                 memset(BasePixels,0,BaseWidth*BaseHeight);\r
2386                 for (i=0 ; i < fmheader.num_tris ; i++)\r
2387                 {\r
2388                         CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);\r
2389                 }\r
2390                 SetPixel = false;\r
2391 \r
2392                 skin_pixels_used = 0;\r
2393                 for(i=0;i<fmheader.skinheight;i++)\r
2394                 {\r
2395                         for(j=0;j<fmheader.skinwidth;j++)\r
2396                         {\r
2397                                 skin_pixels_used += BasePixels[(i*BaseWidth) + j];\r
2398                         }\r
2399                 }\r
2400                 total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;\r
2401         }\r
2402         else\r
2403         {\r
2404                 SetPixel = true;\r
2405                 memset(BasePixels,0,BaseWidth*BaseHeight*4);\r
2406                 for (i=0 ; i < fmheader.num_tris ; i++)\r
2407                 {\r
2408                         CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);\r
2409                 }\r
2410                 SetPixel = false;\r
2411 \r
2412                 skin_pixels_used = 0;\r
2413                 for(i=0;i<fmheader.skinheight;i++)\r
2414                 {\r
2415                         for(j=0;j<fmheader.skinwidth;j++)\r
2416                         {\r
2417                                 skin_pixels_used += BasePixels[((i*BaseWidth) + j)*4];\r
2418                         }\r
2419                 }\r
2420                 total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;\r
2421         }\r
2422 \r
2423         // build triangle strips / fans\r
2424         BuildGlCmds ();\r
2425 \r
2426         if (TransPixels)\r
2427         {\r
2428                 free(TransPixels);\r
2429         }\r
2430         free (BasePixels);\r
2431         if (BasePalette)\r
2432         {\r
2433                 free (BasePalette);\r
2434         }\r
2435         free(ptri);\r
2436 }\r
2437 \r
2438 void Cmd_FMNodeOrder(void)\r
2439 {\r
2440         mesh_node_t     *newnodes, *pos;\r
2441         int                     i,j;\r
2442 \r
2443         if (!pmnodes)\r
2444         {\r
2445                 Error ("Base has not been established yet");\r
2446         }\r
2447 \r
2448         pos = newnodes = malloc(sizeof(mesh_node_t) * fmheader.num_mesh_nodes);\r
2449 \r
2450         for(i=0;i<fmheader.num_mesh_nodes;i++)\r
2451         {\r
2452                 GetScriptToken (false);\r
2453 \r
2454                 for(j=0;j<fmheader.num_mesh_nodes;j++)\r
2455                 {\r
2456                         if (strcmpi(pmnodes[j].name, token) == 0)\r
2457                         {\r
2458                                 *pos = pmnodes[j];\r
2459                                 pos++;\r
2460                                 break;\r
2461                         }\r
2462                 }\r
2463                 if (j >= fmheader.num_mesh_nodes)\r
2464                 {\r
2465                         Error("Node '%s' not in base list!\n", token);\r
2466                 }\r
2467         }\r
2468 \r
2469         free(pmnodes);\r
2470         pmnodes = newnodes;\r
2471 }\r
2472 \r
2473 //===============================================================\r
2474 \r
2475 extern char     *FindFrameFile (char *frame);\r
2476 \r
2477 \r
2478 /*\r
2479 ===============\r
2480 GrabFrame\r
2481 ===============\r
2482 */\r
2483 void GrabFrame (char *frame)\r
2484 {\r
2485         triangle_t              *ptri;\r
2486         int                             i, j;\r
2487         fmtrivert_t             *ptrivert;\r
2488         int                             num_tris;\r
2489         char                    file1[1024];\r
2490         fmframe_t               *fr;\r
2491         int                             index_xyz;\r
2492         char                    *framefile;\r
2493 \r
2494         // the frame 'run1' will be looked for as either\r
2495         // run.1 or run1.tri, so the new alias sequence save\r
2496         // feature an be used\r
2497         framefile = FindFrameFile (frame);\r
2498 \r
2499         sprintf (file1, "%s/%s", cdarchive, framefile);\r
2500         ExpandPathAndArchive (file1);\r
2501 \r
2502         sprintf (file1, "%s/%s",cddir, framefile);\r
2503 \r
2504         printf ("grabbing %s  ", file1);\r
2505 \r
2506         if (fmheader.num_frames >= MAX_FM_FRAMES)\r
2507                 Error ("fmheader.num_frames >= MAX_FM_FRAMES");\r
2508         fr = &g_frames[fmheader.num_frames];\r
2509         fmheader.num_frames++;\r
2510 \r
2511         strcpy (fr->name, frame);\r
2512 \r
2513 //\r
2514 // load the frame\r
2515 //\r
2516         if (do3ds)\r
2517                 Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);\r
2518         else\r
2519                 LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);\r
2520 \r
2521         if (num_tris != fmheader.num_tris)\r
2522                 Error ("%s: number of triangles (%d) doesn't match base frame (%d)\n", file1, num_tris, fmheader.num_tris);\r
2523 \r
2524 //\r
2525 // allocate storage for the frame's vertices\r
2526 //\r
2527         ptrivert = fr->v;\r
2528 \r
2529         for (i=0 ; i<fmheader.num_xyz ; i++)\r
2530         {\r
2531                 ptrivert[i].vnorm.numnormals = 0;\r
2532                 VectorClear (ptrivert[i].vnorm.normalsum);\r
2533         }\r
2534         ClearBounds (fr->mins, fr->maxs);\r
2535 \r
2536 //\r
2537 // store the frame's vertices in the same order as the base. This assumes the\r
2538 // triangles and vertices in this frame are in exactly the same order as in the\r
2539 // base\r
2540 //\r
2541         for (i=0 ; i<num_tris ; i++)\r
2542         {\r
2543                 vec3_t  vtemp1, vtemp2, normal;\r
2544                 float   ftemp;\r
2545 \r
2546                 VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
2547                 VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
2548                 CrossProduct (vtemp1, vtemp2, normal);\r
2549 \r
2550                 VectorNormalize (normal, normal);\r
2551 \r
2552         // rotate the normal so the model faces down the positive x axis\r
2553                 ftemp = normal[0];\r
2554                 normal[0] = -normal[1];\r
2555                 normal[1] = ftemp;\r
2556 \r
2557                 for (j=0 ; j<3 ; j++)\r
2558                 {\r
2559                         index_xyz = triangles[i].index_xyz[j];\r
2560 \r
2561                 // rotate the vertices so the model faces down the positive x axis\r
2562                 // also adjust the vertices to the desired origin\r
2563                         ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +\r
2564                                                                                 adjust[0];\r
2565                         ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +\r
2566                                                                                 adjust[1];\r
2567                         ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +\r
2568                                                                                 adjust[2];\r
2569 \r
2570                         AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);\r
2571 \r
2572                         VectorAdd (ptrivert[index_xyz].vnorm.normalsum, normal, ptrivert[index_xyz].vnorm.normalsum);\r
2573                         ptrivert[index_xyz].vnorm.numnormals++;\r
2574                 }\r
2575         }\r
2576 \r
2577 //\r
2578 // calculate the vertex normals, match them to the template list, and store the\r
2579 // index of the best match\r
2580 //\r
2581         for (i=0 ; i<fmheader.num_xyz ; i++)\r
2582         {\r
2583                 int             j;\r
2584                 vec3_t  v;\r
2585                 float   maxdot;\r
2586                 int             maxdotindex;\r
2587                 int             c;\r
2588 \r
2589                 c = ptrivert[i].vnorm.numnormals;\r
2590                 if (!c)\r
2591                         Error ("Vertex with no triangles attached");\r
2592 \r
2593                 VectorScale (ptrivert[i].vnorm.normalsum, 1.0/c, v);\r
2594                 VectorNormalize (v, v);\r
2595 \r
2596                 maxdot = -999999.0;\r
2597                 maxdotindex = -1;\r
2598 \r
2599                 for (j=0 ; j<NUMVERTEXNORMALS ; j++)\r
2600                 {\r
2601                         float   dot;\r
2602 \r
2603                         dot = DotProduct (v, avertexnormals[j]);\r
2604                         if (dot > maxdot)\r
2605                         {\r
2606                                 maxdot = dot;\r
2607                                 maxdotindex = j;\r
2608                         }\r
2609                 }\r
2610 \r
2611                 ptrivert[i].lightnormalindex = maxdotindex;\r
2612         }\r
2613 \r
2614         free (ptri);\r
2615 }\r
2616 \r
2617 /*\r
2618 ===============\r
2619 Cmd_Frame       \r
2620 ===============\r
2621 */\r
2622 void Cmd_FMFrame (void)\r
2623 {\r
2624         while (ScriptTokenAvailable())\r
2625         {\r
2626                 GetScriptToken (false);\r
2627                 if (g_skipmodel)\r
2628                         continue;\r
2629                 if (g_release || g_archive)\r
2630                 {\r
2631                         fmheader.num_frames = 1;        // don't skip the writeout\r
2632                         continue;\r
2633                 }\r
2634 \r
2635                 H_printf("#define FRAME_%-16s\t%i\n", token, fmheader.num_frames);\r
2636 \r
2637                 if((g_skelModel.type != SKEL_NULL) || (g_skelModel.references != REF_NULL))\r
2638                 {\r
2639                         GrabModelTransform(token);\r
2640                 }\r
2641 \r
2642                 GrabFrame (token);\r
2643 \r
2644                 if(g_skelModel.type != SKEL_NULL)\r
2645                 {\r
2646                         GrabSkeletalFrame(token);\r
2647                 }\r
2648 \r
2649                 if(g_skelModel.references != REF_NULL)\r
2650                 {\r
2651                         GrabReferencedFrame(token);\r
2652                 }\r
2653 \r
2654                 // need to add the up and dir points to the frame bounds here\r
2655                 // using AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);\r
2656                 // then remove fudge in determining scale on frame write out\r
2657         }\r
2658 }\r
2659 \r
2660 /*\r
2661 ===============\r
2662 Cmd_Skin\r
2663 \r
2664 Skins aren't actually stored in the file, only a reference\r
2665 is saved out to the header file.\r
2666 ===============\r
2667 */\r
2668 void Cmd_FMSkin (void)\r
2669 {\r
2670         byte            *palette;\r
2671         byte            *pixels;\r
2672         int                     width, height;\r
2673         byte            *cropped;\r
2674         int                     y;\r
2675         char            name[1024], savename[1024], transname[1024], extension[256];\r
2676         miptex32_t      *qtex32;\r
2677         int                     size;\r
2678         FILE            *FH;\r
2679         qboolean        TrueColor;\r
2680 \r
2681         GetScriptToken (false);\r
2682 \r
2683         if (fmheader.num_skins == MAX_FM_SKINS)\r
2684                 Error ("fmheader.num_skins == MAX_FM_SKINS");\r
2685 \r
2686         if (g_skipmodel)\r
2687                 return;\r
2688 \r
2689         sprintf (name, "%s/%s", cdarchive, token);\r
2690         strcpy (name, ExpandPathAndArchive( name ) );\r
2691 //      sprintf (name, "%s/%s.lbm", cddir, token);\r
2692 \r
2693         if (ScriptTokenAvailable())\r
2694         {\r
2695                 GetScriptToken (false);\r
2696                 sprintf (g_skins[fmheader.num_skins], "!%s", token);\r
2697                 sprintf (savename, "%s!%s", g_outputDir, token);\r
2698                 sprintf (transname, "%s!%s_a.pcx", gamedir, token);\r
2699         }\r
2700         else\r
2701         {\r
2702                 sprintf (g_skins[fmheader.num_skins], "%s/!%s", cdpartial, token);\r
2703                 sprintf (savename, "%s/!%s", g_outputDir, token);\r
2704                 sprintf (transname, "%s/!%s_a.pcx", cddir, token);\r
2705         }\r
2706 \r
2707         fmheader.num_skins++;\r
2708 \r
2709         if (g_skipmodel || g_release || g_archive)\r
2710                 return;\r
2711 \r
2712         // load the image\r
2713         printf ("loading %s\n", name);\r
2714         ExtractFileExtension (name, extension);\r
2715         if (extension[0] == 0)\r
2716         {\r
2717                 strcat(name, ".pcx");\r
2718         }\r
2719 \r
2720 \r
2721         TrueColor = LoadAnyImage (name, &pixels, &palette, &width, &height);\r
2722 //      RemapZero (pixels, palette, width, height);\r
2723 \r
2724         // crop it to the proper size\r
2725 \r
2726         if (!TrueColor)\r
2727         {\r
2728                 cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight, "Cmd_FMSkin");\r
2729                 for (y=0 ; y<fmheader.skinheight ; y++)\r
2730                 {\r
2731                         memcpy (cropped+y*fmheader.skinwidth,\r
2732                                 pixels+y*width, fmheader.skinwidth);\r
2733                 }\r
2734 \r
2735                 TransPixels = NULL;\r
2736                 FH = fopen(transname,"rb");\r
2737                 if (FH)\r
2738                 {\r
2739                         fclose(FH);\r
2740 \r
2741                         strcat(g_skins[fmheader.num_skins-1],".pcx");\r
2742                         strcat(savename,".pcx");\r
2743 \r
2744                         // save off the new image\r
2745                         printf ("saving %s\n", savename);\r
2746                         CreatePath (savename);\r
2747                         WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);\r
2748                 }\r
2749                 else\r
2750                 {\r
2751         #if 1\r
2752                 miptex_t        *qtex;\r
2753                         qtex = CreateMip(cropped, fmheader.skinwidth, fmheader.skinheight, palette, &size, true);\r
2754 \r
2755                         strcat(g_skins[fmheader.num_skins-1],".m8");\r
2756                         strcat(savename,".m8");\r
2757 \r
2758                         printf ("saving %s\n", savename);\r
2759                         CreatePath (savename);\r
2760                         SaveFile (savename, (byte *)qtex, size);\r
2761                         free(qtex);\r
2762         #else\r
2763                         strcat(g_skins[fmheader.num_skins-1],".pcx");\r
2764                         strcat(savename,".pcx");\r
2765 \r
2766                         // save off the new image\r
2767                         printf ("saving %s\n", savename);\r
2768                         CreatePath (savename);\r
2769                         WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);\r
2770         #endif\r
2771                 }\r
2772         }\r
2773         else\r
2774         {\r
2775                 cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight*4, "Cmd_FMSkin");\r
2776                 for (y=0 ; y<fmheader.skinheight ; y++)\r
2777                 {\r
2778                         memcpy (cropped+((y*fmheader.skinwidth)*4), pixels+(y*width*4), fmheader.skinwidth*4);\r
2779                 }\r
2780 \r
2781                 qtex32 = CreateMip32((unsigned *)cropped, fmheader.skinwidth, fmheader.skinheight, &size, true);\r
2782 \r
2783             StripExtension(g_skins[fmheader.num_skins-1]);\r
2784                 strcat(g_skins[fmheader.num_skins-1],".m32");\r
2785             StripExtension(savename);\r
2786                 strcat(savename,".m32");\r
2787 \r
2788                 printf ("saving %s\n", savename);\r
2789                 CreatePath (savename);\r
2790                 SaveFile (savename, (byte *)qtex32, size);\r
2791         }\r
2792 \r
2793         free (pixels);\r
2794         if (palette)\r
2795         {\r
2796                 free (palette);\r
2797         }\r
2798         free (cropped);\r
2799 }\r
2800 \r
2801 \r
2802 /*\r
2803 ===============\r
2804 Cmd_Cd\r
2805 ===============\r
2806 */\r
2807 void Cmd_FMCd (void)\r
2808 {\r
2809         char temp[256];\r
2810 \r
2811         FinishModel ();\r
2812         ClearModel ();\r
2813 \r
2814         GetScriptToken (false);\r
2815 \r
2816         // this is a silly mess...\r
2817         sprintf(cdpartial, "models/%s", token); \r
2818         sprintf(cdarchive, "%smodels/%s", gamedir+strlen(qdir), token); \r
2819         sprintf(cddir, "%s%s", gamedir, cdpartial);\r
2820 \r
2821         // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.\r
2822         sprintf(temp, "%s%s", g_outputDir, cdpartial);\r
2823         strcpy(g_outputDir, temp);\r
2824 \r
2825         // if -only was specified and this cd doesn't match,\r
2826         // skip the model (you only need to match leading chars,\r
2827         // so you could regrab all monsters with -only monsters)\r
2828         if (!g_only[0])\r
2829                 return;\r
2830         if (strncmp(token, g_only, strlen(g_only)))\r
2831         {\r
2832                 g_skipmodel = true;\r
2833                 printf ("skipping %s\n", cdpartial);\r
2834         }\r
2835 }\r
2836 \r
2837 \r
2838 /*\r
2839 \r
2840 //=======================\r
2841 //              NEW GEN\r
2842 //=======================\r
2843 \r
2844 void NewGen (char *ModelFile, char *OutputName, int width, int height)\r
2845 {\r
2846         trigroup_t  *triangles;\r
2847         triangle_t      *ptri;\r
2848         triangle_t      *grouptris;\r
2849         mesh_node_t     *pmnodes;\r
2850 \r
2851         vec3_t          *vertices;\r
2852         vec3_t          *uvs;\r
2853         vec3_t          aveNorm, crossvect;\r
2854         vec3_t          diffvect1, diffvect2;\r
2855         vec3_t          v0, v1, v2;\r
2856         vec3_t          n, u, v;\r
2857         vec3_t          base, zaxis, yaxis;\r
2858         vec3_t          uvwMin, uvwMax;\r
2859         vec3_t          groupMin, groupMax;\r
2860         vec3_t          uvw;\r
2861         \r
2862         float           *uFinal, *vFinal;\r
2863         unsigned char   *newpic;\r
2864 \r
2865         int                     finalstart = 0, finalcount = 0;\r
2866         int                     xbase = 0, xwidth = 0, ywidth = 0;\r
2867         int                     *todo, *done, finished;\r
2868         int                     i, j, k, l; //counters\r
2869         int                     groupnum, numtris, numverts, num;\r
2870         int                     count;\r
2871         FILE            *grpfile;\r
2872         long            datasize;\r
2873 \r
2874         for ( i = 0; i<3; i++)\r
2875         {\r
2876                 aveNorm[i] = 0;\r
2877                 uvwMin[i] = 1e30f;\r
2878                 uvwMax[i] = -1e30f;\r
2879         }\r
2880 \r
2881         pmnodes = NULL;\r
2882         ptri = NULL;\r
2883         triangles = NULL;\r
2884         \r
2885         zaxis[0] = 0;\r
2886         zaxis[1] = 0;\r
2887         zaxis[2] = 1;\r
2888 \r
2889         yaxis[0] = 0;\r
2890         yaxis[1] = 1;\r
2891         yaxis[2] = 0;\r
2892 \r
2893         LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
2894 \r
2895         todo = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");\r
2896         done = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");\r
2897         triangles = (trigroup_t*)SafeMalloc(fmheader.num_tris*sizeof(trigroup_t), "NewGen");\r
2898         \r
2899         for ( i=0; i < fmheader.num_tris; i++)\r
2900         {\r
2901                 todo[i] = false;\r
2902                 done[i] = false;\r
2903                 triangles[i].triangle = ptri[i];\r
2904                 triangles[i].group = 0;\r
2905         }\r
2906 \r
2907         groupnum = 0;\r
2908 \r
2909 //  transitive closure algorithm follows\r
2910 //  put all triangles who transitively share vertices into separate groups\r
2911 \r
2912         while (1)\r
2913         {\r
2914                 for ( i = 0; i < fmheader.num_tris; i++)\r
2915                 {\r
2916                         if (!done[i])\r
2917                         {\r
2918                                 break;\r
2919                         }\r
2920                 }\r
2921                 if ( i == fmheader.num_tris)\r
2922                 {\r
2923                         break;\r
2924                 }\r
2925                 finished = false;\r
2926                 todo[i] = true;\r
2927                 while (!finished)\r
2928                 {\r
2929                         finished = true;\r
2930                         for ( i = 0; i < fmheader.num_tris; i++)\r
2931                         {\r
2932                                 if (todo[i])\r
2933                                 {\r
2934                                         done[i] = true;\r
2935                                         triangles[i].group = groupnum;\r
2936                                         todo[i] = false;\r
2937                                         for ( j = 0; j < fmheader.num_tris; j++)\r
2938                                         {\r
2939                                                 if ((!done[j]) && (ShareVertex(triangles[i],triangles[j])))\r
2940                                                 {\r
2941                                                         todo[j] = true;\r
2942                                                         finished = false;\r
2943                                                 }\r
2944                                         }\r
2945                                 }\r
2946                         }\r
2947                 }\r
2948                 groupnum++;\r
2949         }\r
2950                 uFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");\r
2951                 vFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");\r
2952 \r
2953         grpfile = fopen("grpdebug.txt","w");\r
2954 \r
2955         \r
2956         for (i = 0; i < groupnum; i++)\r
2957         {\r
2958 \r
2959                 fprintf(grpfile,"Group Number: %d\n", i);\r
2960                 \r
2961                 numtris = GetNumTris(triangles, i); // number of triangles in group i\r
2962                 numverts = numtris * 3;\r
2963 \r
2964                 fprintf(grpfile,"%d triangles.\n", numtris);\r
2965 \r
2966                 vertices = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");\r
2967                 uvs = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");\r
2968                 grouptris = (triangle_t*)SafeMalloc(numtris*sizeof(triangle_t), "NewGen");\r
2969                 \r
2970                 for (count = 0; count < fmheader.num_tris; count++)\r
2971                 {\r
2972                         if (triangles[count].group == i)\r
2973                         {\r
2974                                 fprintf(grpfile,"Triangle %d\n", count);\r
2975                         }\r
2976                 }\r
2977                 fprintf(grpfile,"\n");\r
2978 \r
2979                 \r
2980                 \r
2981                 \r
2982                 GetOneGroup(triangles, i, grouptris);           \r
2983                 \r
2984                 num = 0;\r
2985                 for (j = 0; j < numtris; j++)\r
2986                 {\r
2987                         VectorCopy(grouptris[j].verts[0], v0);\r
2988                         VectorCopy(grouptris[j].verts[1], v1);\r
2989                         VectorCopy(grouptris[j].verts[2], v2);\r
2990                         VectorSubtract(v1, v0, diffvect1);\r
2991                         VectorSubtract(v2, v1, diffvect2);\r
2992                         CrossProduct( diffvect1, diffvect2, crossvect);\r
2993                         VectorAdd(aveNorm, crossvect, aveNorm); \r
2994                         VectorCopy(v0,vertices[num]);\r
2995                         num++;                                  //  FIXME\r
2996                         VectorCopy(v1,vertices[num]);\r
2997                         num++;                                  //  add routine to add only verts that\r
2998                         VectorCopy(v2,vertices[num]);\r
2999                         num++;                                  // have not already been added\r
3000                 }\r
3001 \r
3002                 assert (num >= 3);\r
3003 // figure out the best plane projections\r
3004                 DOsvdPlane ((float*)vertices, num, (float *)&n, (float *)&base);\r
3005                 \r
3006                 if (DotProduct(aveNorm,n) < 0.0f)\r
3007                 {\r
3008                         VectorScale(n, -1.0f, n);\r
3009                 }\r
3010                 VectorNormalize(n,n);\r
3011                 if (fabs(n[2]) < .57)\r
3012                 {\r
3013                         CrossProduct( zaxis, n, crossvect);\r
3014                         VectorCopy(crossvect, u);\r
3015                 }\r
3016                 else\r
3017                 {\r
3018                         CrossProduct( yaxis, n, crossvect);\r
3019                         VectorCopy(crossvect, u);\r
3020                 }\r
3021                 VectorNormalize(u,u);\r
3022                 CrossProduct( n, u, crossvect);\r
3023                 VectorCopy(crossvect, v);\r
3024                 VectorNormalize(v,v);\r
3025 \r
3026                 num = 0;\r
3027 \r
3028                 for ( j = 0; j < 3; j++)\r
3029                 {\r
3030                         groupMin[j] = 1e30f;\r
3031                         groupMax[j] = -1e30f;\r
3032                 }\r
3033                 \r
3034                 for ( j = 0; j < numtris; j++)\r
3035                 {\r
3036                         for ( k = 0; k < 3; k++)\r
3037                         {\r
3038                                 VectorCopy(grouptris[j].verts[k],v0);\r
3039                                 VectorSubtract(v0, base, v0);\r
3040                                 uvw[0] = DotProduct(v0, u);\r
3041                                 uvw[1] = DotProduct(v0, v);\r
3042                                 uvw[2] = DotProduct(v0, n);\r
3043                                 VectorCopy(uvw,uvs[num]);\r
3044                                 num++;\r
3045                                 for ( l = 0; l < 3; l++)\r
3046                                 {\r
3047                                         if (uvw[l] < groupMin[l])\r
3048                                         {\r
3049                                                 groupMin[l] = uvw[l];\r
3050                                         }\r
3051                                         if (uvw[l] > groupMax[l])\r
3052                                         {\r
3053                                                 groupMax[l] = uvw[l];\r
3054                                         }\r
3055                                 }\r
3056                         }\r
3057                 }\r
3058                 \r
3059                 xwidth = ceil(0 - groupMin[0]) + 2; // move right of origin and avoid overlap\r
3060                 ywidth = ceil(0 - groupMin[1]) + 2; // move "above" origin\r
3061                 \r
3062                 for ( j=0; j < numverts; j++)\r
3063                 {\r
3064                         uFinal[finalcount] = uvs[j][0] + xwidth + xbase;                        \r
3065                         vFinal[finalcount] = uvs[j][1] + ywidth;\r
3066                         if (uFinal[finalcount] < uvwMin[0])\r
3067                         {\r
3068                                 uvwMin[0] = uFinal[finalcount];\r
3069                         }\r
3070                         if (uFinal[finalcount] > uvwMax[0])\r
3071                         {\r
3072                                 uvwMax[0] = uFinal[finalcount];\r
3073                         }\r
3074                         if (vFinal[finalcount] < uvwMin[1])\r
3075                         {\r
3076                                 uvwMin[1] = vFinal[finalcount];\r
3077                         }\r
3078                         if (vFinal[finalcount] > uvwMax[1])\r
3079                         {\r
3080                                 uvwMax[1] = vFinal[finalcount];\r
3081                         }\r
3082                         finalcount++;\r
3083                 }\r
3084 \r
3085                 fprintf(grpfile,"svdPlaned Group min: ( %f , %f )\n",groupMin[0] + xwidth + xbase, groupMin[1] + ywidth);\r
3086                 fprintf(grpfile,"svdPlaned Group max: ( %f , %f )\n",groupMax[0] + xwidth + xbase, groupMax[1] + ywidth);\r
3087                 \r
3088                 finalcount = finalstart;\r
3089 \r
3090                 for ( count = 0; count < numverts; count++)\r
3091                 {\r
3092                         fprintf(grpfile,"Vertex %d: ( %f , %f , %f )\n",count,vertices[count][0],vertices[count][1],vertices[count][2]);\r
3093                         fprintf(grpfile,"svdPlaned: ( %f , %f )\n",uFinal[finalcount],vFinal[finalcount++]);\r
3094                 }\r
3095                 \r
3096                 finalstart = finalcount;\r
3097 \r
3098                 fprintf(grpfile,"\n");\r
3099                 \r
3100                 free(vertices);\r
3101                 free(uvs);\r
3102                 free(grouptris);\r
3103 \r
3104                 xbase += ceil(groupMax[0] - groupMin[0]) + 2;           \r
3105 \r
3106         }       \r
3107 \r
3108         fprintf(grpfile,"Global Min ( %f , %f )\n",uvwMin[0],uvwMin[1]);\r
3109         fprintf(grpfile,"Global Max ( %f , %f )\n",uvwMax[0],uvwMax[1]);\r
3110 \r
3111 \r
3112         ScaleTris(uvwMin, uvwMax, width, height, uFinal, vFinal, finalcount);\r
3113 \r
3114         for (k = 0; k < finalcount; k++)\r
3115         {\r
3116                 fprintf(grpfile, "scaled vertex %d: ( %f , %f )\n",k,uFinal[k],vFinal[k]);\r
3117         }\r
3118         \r
3119         //  i've got the array of vertices in uFinal and vFinal.  Now I need to write them and draw lines\r
3120 \r
3121         datasize = width * height*sizeof(unsigned char);\r
3122         newpic = (unsigned char*)SafeMalloc(datasize, "NewGen");\r
3123         memset(newpic,0,datasize);\r
3124         memset(pic_palette,0,sizeof(pic_palette));\r
3125         pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;\r
3126 \r
3127         k = 0;\r
3128         while (k < finalcount)\r
3129         {\r
3130                 NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);\r
3131                 k++;\r
3132                 NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);\r
3133                 k++;\r
3134                 NewDrawLine(uFinal[k], vFinal[k], uFinal[k-2], vFinal[k-2], newpic, width, height);\r
3135                 k++;\r
3136                 fprintf(grpfile, "output tri with verts %d, %d, %d", k-2, k-1, k);\r
3137         }\r
3138 \r
3139         WritePCXfile (OutputName, newpic, width, height, pic_palette);\r
3140 \r
3141         fclose(grpfile);\r
3142 \r
3143         free(todo);\r
3144         free(done);\r
3145         free(triangles);\r
3146         free(newpic);   \r
3147         return;\r
3148 }\r
3149 void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height)\r
3150 {\r
3151         long dx, dy;\r
3152         long adx, ady;\r
3153         long count;\r
3154         float xfrac, yfrac, xstep, ystep;\r
3155         unsigned long sx, sy;\r
3156         float u, v;\r
3157 \r
3158         dx = x2 - x1;\r
3159         dy = y2 - y1;\r
3160         adx = abs(dx);\r
3161         ady = abs(dy);\r
3162 \r
3163         count = adx > ady ? adx : ady;\r
3164         count++;\r
3165 \r
3166         if(count > 300)\r
3167         {\r
3168                 printf("Bad count\n");\r
3169                 return; // don't ever hang up on bad data\r
3170         }\r
3171                 \r
3172         xfrac = x1;\r
3173         yfrac = y1;\r
3174         \r
3175         xstep = (float)dx/count;\r
3176         ystep = (float)dy/count;\r
3177 \r
3178         switch(LineType)\r
3179         {\r
3180                 case LINE_NORMAL:\r
3181                         do\r
3182                         {\r
3183                                 if(xfrac < width && yfrac < height)\r
3184                                 {\r
3185                                         picture[(long)yfrac*width+(long)xfrac] = LineColor;\r
3186                                 }\r
3187                                 xfrac += xstep;\r
3188                                 yfrac += ystep;\r
3189                                 count--;\r
3190                         } while (count > 0);\r
3191                         break;\r
3192                 case LINE_FAT:\r
3193                         do\r
3194                         {\r
3195                                 for (u=-0.1 ; u<=0.9 ; u+=0.999)\r
3196                                 {\r
3197                                         for (v=-0.1 ; v<=0.9 ; v+=0.999)\r
3198                                         {\r
3199                                                 sx = xfrac+u;\r
3200                                                 sy = yfrac+v;\r
3201                                                 if(sx < width && sy < height)\r
3202                                                 {\r
3203                                                         picture[sy*width+sx] = LineColor;\r
3204                                                 }\r
3205                                         }\r
3206                                 }\r
3207                                 xfrac += xstep;\r
3208                                 yfrac += ystep;\r
3209                                 count--;\r
3210                         } while (count > 0);\r
3211                         break;\r
3212                 case LINE_DOTTED:\r
3213                         do\r
3214                         {\r
3215                                 if(count&1 && xfrac < width &&\r
3216                                         yfrac < height)\r
3217                                 {\r
3218                                         picture[(long)yfrac*width+(long)xfrac] = LineColor;\r
3219                                 }\r
3220                                 xfrac += xstep;\r
3221                                 yfrac += ystep;\r
3222                                 count--;\r
3223                         } while (count > 0);\r
3224                         break;\r
3225                 default:\r
3226                         Error("Unknown <linetype> %d.\n", LineType);\r
3227         }\r
3228 }\r
3229 */\r
3230 void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts)\r
3231 {\r
3232 \r
3233         int             i;\r
3234         float   hscale, vscale;\r
3235         float   scale;\r
3236 \r
3237         hscale = max[0];\r
3238         vscale = max[1];\r
3239 \r
3240         hscale = (Width-2) / max[0];\r
3241         vscale = (Height-2) / max[1];\r
3242 \r
3243         scale = hscale;\r
3244         if (scale > vscale)\r
3245         {\r
3246                 scale = vscale;\r
3247         }\r
3248         for ( i = 0; i<verts; i++)\r
3249         {\r
3250                 u[i] *= scale;\r
3251                 v[i] *= scale;\r
3252         }\r
3253         return;\r
3254 }\r
3255 \r
3256 \r
3257 void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles)\r
3258 {\r
3259         int     i;\r
3260         int j;\r
3261 \r
3262         j = 0;\r
3263         for (i = 0; i < fmheader.num_tris; i++)\r
3264         {\r
3265                 if (tris[i].group == grp)\r
3266                 {\r
3267                         triangles[j++] = tris[i].triangle;\r
3268                 }\r
3269         }\r
3270         return;\r
3271 }\r
3272 \r
3273 \r
3274 int GetNumTris( trigroup_t *tris, int grp)\r
3275 {\r
3276         int i;\r
3277         int verts;\r
3278 \r
3279         verts = 0;\r
3280         for (i = 0; i < fmheader.num_tris; i++)\r
3281         {\r
3282                 if (tris[i].group == grp)\r
3283                 {\r
3284                         verts++;\r
3285                 }\r
3286         }\r
3287         return verts;\r
3288 }\r
3289 \r
3290 \r
3291 int ShareVertex( trigroup_t trione, trigroup_t tritwo)\r
3292 {\r
3293         int     i;\r
3294         int     j;\r
3295 \r
3296         i = 1;\r
3297         j = 1;\r
3298         for ( i = 0; i < 3; i++)\r
3299         {\r
3300                 for ( j = 0; j < 3; j++)\r
3301                 {\r
3302                         if (DistBetween(trione.triangle.verts[i],tritwo.triangle.verts[j]) < TRIVERT_DIST)\r
3303                         {\r
3304                                 return true; \r
3305                         }\r
3306                 }\r
3307         }\r
3308         return false;\r
3309 }\r
3310 \r
3311 \r
3312 float DistBetween(vec3_t point1, vec3_t point2)\r
3313 {\r
3314         float   dist;\r
3315 \r
3316         dist = (point1[0] - point2[0]);\r
3317         dist *= dist;\r
3318         dist += (point1[1] - point2[1])*(point1[1]-point2[1]);\r
3319         dist += (point1[2] - point2[2])*(point1[2]-point2[2]);\r
3320         dist = sqrt(dist);\r
3321         return dist;\r
3322 }\r
3323 \r
3324 \r
3325 void GenSkin(char *ModelFile, char *OutputName, int Width, int Height)\r
3326 {\r
3327         triangle_t      *ptri;\r
3328         mesh_node_t *pmnodes;\r
3329         int                     i;\r
3330 \r
3331         pmnodes = NULL;\r
3332         ptri = NULL;\r
3333 \r
3334         LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
3335         if (g_ignoreTriUV)\r
3336         {\r
3337                 for (i=0;i<fmheader.num_tris;i++)\r
3338                 {\r
3339                         ptri[i].HasUV=0;\r
3340                 }\r
3341         }\r
3342 \r
3343         memset(pic,0,sizeof(pic));\r
3344         memset(pic_palette,0,sizeof(pic_palette));\r
3345         pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;\r
3346 \r
3347         ScaleWidth = Width;\r
3348         ScaleHeight = Height;\r
3349 \r
3350         BuildST (ptri, fmheader.num_tris, true);\r
3351 \r
3352         WritePCXfile (OutputName, pic, SKINPAGE_WIDTH, SKINPAGE_HEIGHT, pic_palette);\r
3353 \r
3354         printf("Gen Skin Stats:\n");\r
3355         printf("   Input Base: %s\n",ModelFile);\r
3356         printf("   Input Dimensions: %d,%d\n",Width,Height);\r
3357         printf("\n");\r
3358         printf("   Output File: %s\n",OutputName);\r
3359         printf("   Output Dimensions: %d,%d\n",ScaleWidth,ScaleHeight);\r
3360 \r
3361         if (fmheader.num_mesh_nodes)\r
3362         {\r
3363                 printf("\nNodes:\n");\r
3364                 for(i=0;i<fmheader.num_mesh_nodes;i++)\r
3365                 {\r
3366                         printf("   %s\n",pmnodes[i].name);\r
3367                 }\r
3368         }\r
3369 \r
3370         free(ptri);\r
3371         free(pmnodes);\r
3372 }\r
3373 \r
3374 \r
3375 void Cmd_FMBeginGroup (void)\r
3376 {\r
3377         GetScriptToken (false);\r
3378 \r
3379         g_no_opimizations = false;\r
3380 \r
3381         groups[num_groups].start_frame = fmheader.num_frames;\r
3382         groups[num_groups].num_frames = 0;\r
3383 \r
3384         groups[num_groups].degrees = atol(token);\r
3385         if (groups[num_groups].degrees < 1 || groups[num_groups].degrees > 32)\r
3386         {\r
3387                 Error ("Degrees of freedom out of range: %d",groups[num_groups].degrees);\r
3388         }\r
3389 }\r
3390 \r
3391 void Cmd_FMEndGroup (void)\r
3392 {\r
3393         groups[num_groups].num_frames = fmheader.num_frames - groups[num_groups].start_frame;\r
3394 \r
3395         if(num_groups < MAX_GROUPS - 1)\r
3396         {\r
3397                 num_groups++;\r
3398         }\r
3399         else\r
3400         {\r
3401                 Error("Number of compression groups exceded: %i\n", MAX_GROUPS);\r
3402         }\r
3403 }\r
3404 \r