]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/qdata_heretic2/fmodels.c
eol style
[xonotic/netradiant.git] / tools / quake2 / qdata_heretic2 / fmodels.c
index 6dd16355cc6ca7e0c521dd3125e91bff06a9d2c5..273a482d29a3b138b1321f67aba68481a9649126 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-#include "qd_fmodel.h"\r
-#include "animcomp.h"\r
-#include "qd_skeletons.h"\r
-#include "skeletons.h"\r
-#include "qdata.h"\r
-#include "flex.h"\r
-#include "reference.h"\r
-\r
-#include <assert.h>\r
-\r
-/*\r
-========================================================================\r
-\r
-.FM triangle flexible model file format\r
-\r
-========================================================================\r
-*/\r
-\r
-//=================================================================\r
-\r
-#define NUMVERTEXNORMALS       162\r
-\r
-extern float   avertexnormals[NUMVERTEXNORMALS][3];\r
-\r
-#define MAX_GROUPS     128\r
-\r
-typedef struct\r
-{\r
-       triangle_t      triangle;\r
-       int             group;\r
-} trigroup_t;\r
-\r
-#define TRIVERT_DIST   .1\r
-\r
-typedef struct\r
-{\r
-       int                     start_frame;\r
-       int             num_frames;\r
-       int                     degrees;\r
-       char *mat;\r
-       char *ccomp;\r
-       char *cbase;\r
-       float *cscale;\r
-       float *coffset;\r
-       float trans[3];\r
-       float scale[3];\r
-       float bmin[3];\r
-       float bmax[3];\r
-} fmgroup_t;\r
-\r
-//================================================================\r
-\r
-// Initial\r
-fmheader_t     fmheader;\r
-\r
-// Skin\r
-extern char                    g_skins[MAX_FM_SKINS][64];\r
-\r
-// ST Coord\r
-extern fmstvert_t      base_st[MAX_FM_VERTS];\r
-\r
-// Triangles\r
-extern fmtriangle_t    triangles[MAX_FM_TRIANGLES];\r
-\r
-// Frames\r
-fmframe_t      g_frames[MAX_FM_FRAMES];\r
-//fmframe_t    *g_FMframes;\r
-\r
-// GL Commands\r
-extern int                     commands[16384];\r
-extern int                     numcommands;\r
-\r
-\r
-//\r
-// varibles set by commands\r
-//\r
-extern float           scale_up;                                               // set by $scale\r
-extern vec3_t          adjust;                                                 // set by $origin\r
-extern int                     g_fixedwidth, g_fixedheight;    // set by $skinsize\r
-extern char                    modelname[64];                                  // set by $modelname\r
-\r
-\r
-extern         char            *g_outputDir;\r
-\r
-\r
-// Mesh Nodes\r
-mesh_node_t    *pmnodes = NULL;\r
-fmmeshnode_t   mesh_nodes[MAX_FM_MESH_NODES]; \r
-\r
-fmgroup_t      groups[MAX_GROUPS];\r
-int                    num_groups;\r
-int    frame_to_group[MAX_FM_FRAMES];\r
-\r
-//\r
-// variables set by command line arguments\r
-//\r
-qboolean       g_no_opimizations = false;\r
-\r
-\r
-//\r
-// base frame info\r
-//\r
-static int                     triangle_st[MAX_FM_TRIANGLES][3][2];\r
-\r
-\r
-// number of gl vertices\r
-extern int                     numglverts;\r
-// indicates if a triangle has already been used in a glcmd\r
-extern int                     used[MAX_FM_TRIANGLES];\r
-// indicates if a triangle has translucency in it or not\r
-static qboolean                translucent[MAX_FM_TRIANGLES];\r
-\r
-// main output file handle\r
-extern FILE                    *headerouthandle;\r
-// output sizes of buildst()\r
-static int                     skin_width, skin_height;\r
-\r
-\r
-// statistics\r
-static int                     total_skin_pixels;\r
-static int                     skin_pixels_used;\r
-\r
-int ShareVertex( trigroup_t trione, trigroup_t tritwo);\r
-float DistBetween(vec3_t point1, vec3_t point2);\r
-int GetNumTris( trigroup_t *tris, int group);\r
-void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles);\r
-void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts);\r
-void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height);\r
-\r
-#ifndef _WIN32\r
-\r
-void strupr(char *string)\r
-{\r
-       int i;\r
-\r
-       for (i=0 ; i<strlen(string); i++)\r
-               toupper(string[i]);\r
-\r
-       return;\r
-}\r
-\r
-#endif\r
-//==============================================================\r
-\r
-/*\r
-===============\r
-ClearModel\r
-===============\r
-*/\r
-static void ClearModel (void)\r
-{\r
-       memset (&fmheader, 0, sizeof(fmheader));\r
-\r
-       modelname[0] = 0;\r
-       scale_up = 1.0; \r
-       VectorCopy (vec3_origin, adjust);\r
-       g_fixedwidth = g_fixedheight = 0;\r
-       g_skipmodel = false;\r
-       num_groups = 0;\r
-\r
-       if (pmnodes)\r
-       {\r
-               free(pmnodes);\r
-               pmnodes = NULL;\r
-       }\r
-\r
-       ClearSkeletalModel();\r
-}\r
-\r
-\r
-extern void H_printf(char *fmt, ...);\r
-\r
-\r
-void WriteHeader(FILE *FH, char *Ident, int Version, int Size, void *Data)\r
-{\r
-       header_t        header;\r
-       static long     pos = -1;\r
-       long            CurrentPos;\r
-\r
-       if (Size == 0)\r
-       {       // Don't write out empty packets\r
-               return;\r
-       }\r
-\r
-       if (pos != -1)\r
-       {\r
-               CurrentPos = ftell(FH);\r
-               Size = CurrentPos - pos + sizeof(header_t);\r
-               fseek(FH, pos, SEEK_SET);\r
-               pos = -2;\r
-       }\r
-       else if (Size == -1)\r
-       {\r
-               pos = ftell(FH);\r
-       }\r
-\r
-       memset(&header,0,sizeof(header));\r
-       strcpy(header.ident,Ident);\r
-       header.version = Version;\r
-       header.size = Size;\r
-\r
-       SafeWrite (FH, &header, sizeof(header));\r
-\r
-       if (Data)\r
-       {\r
-               SafeWrite (FH, Data, Size);\r
-       }\r
-\r
-       if (pos == -2)\r
-       {\r
-               pos = -1;\r
-               fseek(FH, 0, SEEK_END);\r
-       }\r
-}\r
-\r
-/*\r
-============\r
-WriteModelFile\r
-============\r
-*/\r
-static void WriteModelFile (FILE *modelouthandle)\r
-{\r
-       int                             i;\r
-       int                             j, k;\r
-       fmframe_t               *in;\r
-       fmaliasframe_t  *out;\r
-       byte                    buffer[MAX_FM_VERTS*4+128];\r
-       float                   v;\r
-       int                             c_on, c_off;\r
-       IntListNode_t   *current, *toFree;\r
-       qboolean framesWritten = false;\r
-       size_t temp ,size = 0;\r
-\r
-       // probably should do this dynamically one of these days\r
-       struct\r
-       {\r
-               float           scale[3];       // multiply byte verts by this\r
-               float           translate[3];   // then add this\r
-       } outFrames[MAX_FM_FRAMES];\r
-\r
-#define DATA_SIZE 0x60000              // 384K had better be enough, particularly for the reference points\r
-       byte data[DATA_SIZE];\r
-       byte data2[DATA_SIZE];\r
-\r
-       fmheader.num_glcmds = numcommands;\r
-       fmheader.framesize = (int)&((fmaliasframe_t *)0)->verts[fmheader.num_xyz];\r
-\r
-       WriteHeader(modelouthandle, FM_HEADER_NAME, FM_HEADER_VER, sizeof(fmheader), &fmheader);\r
-\r
-       //\r
-       // write out the skin names\r
-       //\r
-\r
-       WriteHeader(modelouthandle, FM_SKIN_NAME, FM_SKIN_VER, fmheader.num_skins * MAX_FM_SKINNAME, g_skins);\r
-\r
-       //\r
-       // write out the texture coordinates\r
-       //\r
-       c_on = c_off = 0;\r
-       for (i=0 ; i<fmheader.num_st ; i++)\r
-       {\r
-               base_st[i].s = LittleShort (base_st[i].s);\r
-               base_st[i].t = LittleShort (base_st[i].t);\r
-       }\r
-\r
-       WriteHeader(modelouthandle, FM_ST_NAME, FM_ST_VER, fmheader.num_st * sizeof(base_st[0]), base_st);\r
-\r
-       //\r
-       // write out the triangles\r
-       //\r
-       WriteHeader(modelouthandle, FM_TRI_NAME, FM_TRI_VER, fmheader.num_tris * sizeof(fmtriangle_t), NULL);\r
-\r
-       for (i=0 ; i<fmheader.num_tris ; i++)\r
-       {\r
-               int                             j;\r
-               fmtriangle_t    tri;\r
-\r
-               for (j=0 ; j<3 ; j++)\r
-               {\r
-                       tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);\r
-                       tri.index_st[j] = LittleShort (triangles[i].index_st[j]);\r
-               }\r
-\r
-               SafeWrite (modelouthandle, &tri, sizeof(tri));\r
-       }\r
-\r
-       if (!num_groups)\r
-       {\r
-               //\r
-               // write out the frames\r
-               //\r
-               WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, fmheader.num_frames * fmheader.framesize, NULL);\r
-       //      WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);\r
-\r
-               for (i=0 ; i<fmheader.num_frames ; i++)\r
-               {\r
-                       in = &g_frames[i];\r
-                       out = (fmaliasframe_t *)buffer;\r
-\r
-                       strcpy (out->name, in->name);\r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               out->scale[j] = (in->maxs[j] - in->mins[j])/255;\r
-                               out->translate[j] = in->mins[j];\r
-\r
-                               outFrames[i].scale[j] = out->scale[j];\r
-                               outFrames[i].translate[j] = out->translate[j];\r
-                       }\r
-\r
-                       for (j=0 ; j<fmheader.num_xyz ; j++)\r
-                       {\r
-                       // all of these are byte values, so no need to deal with endianness\r
-                               out->verts[j].lightnormalindex = in->v[j].lightnormalindex;\r
-\r
-                               for (k=0 ; k<3 ; k++)\r
-                               {\r
-                               // scale to byte values & min/max check\r
-                                       v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );\r
-\r
-                               // clamp, so rounding doesn't wrap from 255.6 to 0\r
-                                       if (v > 255.0)\r
-                                               v = 255.0;\r
-                                       if (v < 0)\r
-                                               v = 0;\r
-                                       out->verts[j].v[k] = v;\r
-                               }\r
-                       }\r
-\r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               out->scale[j] = LittleFloat (out->scale[j]);\r
-                               out->translate[j] = LittleFloat (out->translate[j]);\r
-                       }\r
-\r
-                       SafeWrite (modelouthandle, out, fmheader.framesize);\r
-               }\r
-\r
-               // Go back and finish the header\r
-       //      WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);\r
-       }\r
-       else\r
-       {\r
-               WriteHeader(modelouthandle, FM_SHORT_FRAME_NAME, FM_SHORT_FRAME_VER,FRAME_NAME_LEN*fmheader.num_frames, NULL);\r
-               for (i=0 ; i<fmheader.num_frames ; i++)\r
-               {\r
-                       in = &g_frames[i];\r
-                       SafeWrite (modelouthandle,in->name,FRAME_NAME_LEN);\r
-               }\r
-               WriteHeader(modelouthandle, FM_NORMAL_NAME, FM_NORMAL_VER,fmheader.num_xyz, NULL);\r
-               in = &g_frames[0];\r
-               for (j=0 ; j<fmheader.num_xyz ; j++)\r
-                       SafeWrite (modelouthandle,&in->v[j].lightnormalindex,1);\r
-       }\r
-\r
-       //\r
-       // write out glcmds\r
-       //\r
-       WriteHeader(modelouthandle, FM_GLCMDS_NAME, FM_GLCMDS_VER, numcommands*4, commands);\r
-\r
-       //\r
-       // write out mesh nodes\r
-       //\r
-       for(i=0;i<fmheader.num_mesh_nodes;i++)\r
-       {\r
-               memcpy(mesh_nodes[i].tris, pmnodes[i].tris, sizeof(mesh_nodes[i].tris));\r
-               memcpy(mesh_nodes[i].verts, pmnodes[i].verts, sizeof(mesh_nodes[i].verts));\r
-               mesh_nodes[i].start_glcmds = LittleShort((short)pmnodes[i].start_glcmds);\r
-               mesh_nodes[i].num_glcmds = LittleShort((short)pmnodes[i].num_glcmds);\r
-       }\r
-\r
-       WriteHeader(modelouthandle, FM_MESH_NAME, FM_MESH_VER, sizeof(fmmeshnode_t) * fmheader.num_mesh_nodes, mesh_nodes);\r
-\r
-       if (num_groups)\r
-       {\r
-\r
-/*\r
-typedef struct\r
-{\r
-       int                     start_frame;\r
-       int                     num_frames;\r
-       int                     degrees;\r
-       char *mat; fmheader.num_xyz*3*g->degrees*sizeof(char)\r
-       char *ccomp; g->num_frames*g->degrees*sizeof(char)\r
-       char *cbase; fmheader.num_xyz*3*sizeof(unsigned char)\r
-       float *cscale; g->degrees*sizeof(float)\r
-       float *coffset; g->degrees*sizeof(float)\r
-       float trans[3]; 3*sizeof(float)\r
-       float scale[3]; 3*sizeof(float)\r
-} fmgroup_t;\r
-*/\r
-               int tmp,k;\r
-               fmgroup_t *g;\r
-               size=sizeof(int)+fmheader.num_frames*sizeof(int);\r
-               for (k=0;k<num_groups;k++)\r
-               {\r
-                       g=&groups[k];\r
-                       size+=sizeof(int)*3;\r
-                       size+=fmheader.num_xyz*3*g->degrees*sizeof(char);\r
-                       size+=g->num_frames*g->degrees*sizeof(char);\r
-                       size+=fmheader.num_xyz*3*sizeof(unsigned char);\r
-                       size+=g->degrees*sizeof(float);\r
-                       size+=g->degrees*sizeof(float);\r
-                       size+=12*sizeof(float);\r
-               }\r
-               WriteHeader(modelouthandle, FM_COMP_NAME, FM_COMP_VER,size, NULL);\r
-               SafeWrite (modelouthandle,&num_groups,sizeof(int));\r
-               SafeWrite (modelouthandle,frame_to_group,sizeof(int)*fmheader.num_frames);\r
-\r
-               for (k=0;k<num_groups;k++)\r
-               {\r
-                       g=&groups[k];\r
-                       tmp=LittleLong(g->start_frame);\r
-                       SafeWrite (modelouthandle,&tmp,sizeof(int));\r
-                       tmp=LittleLong(g->num_frames);\r
-                       SafeWrite (modelouthandle,&tmp,sizeof(int));\r
-                       tmp=LittleLong(g->degrees);\r
-                       SafeWrite (modelouthandle,&tmp,sizeof(int));\r
-\r
-                       SafeWrite (modelouthandle,g->mat,fmheader.num_xyz*3*g->degrees*sizeof(char));\r
-                       SafeWrite (modelouthandle,g->ccomp,g->num_frames*g->degrees*sizeof(char));\r
-                       SafeWrite (modelouthandle,g->cbase,fmheader.num_xyz*3*sizeof(unsigned char));\r
-                       SafeWrite (modelouthandle,g->cscale,g->degrees*sizeof(float));\r
-                       SafeWrite (modelouthandle,g->coffset,g->degrees*sizeof(float));\r
-                       SafeWrite (modelouthandle,g->trans,3*sizeof(float));\r
-                       SafeWrite (modelouthandle,g->scale,3*sizeof(float));\r
-                       SafeWrite (modelouthandle,g->bmin,3*sizeof(float));\r
-                       SafeWrite (modelouthandle,g->bmax,3*sizeof(float));\r
-                       free(g->mat);\r
-                       free(g->ccomp);\r
-                       free(g->cbase);\r
-                       free(g->cscale);\r
-                       free(g->coffset);\r
-               }\r
-       }\r
-\r
-       // write the skeletal info\r
-       if(g_skelModel.type != SKEL_NULL)\r
-       {\r
-               size = 0;\r
-\r
-               temp = sizeof(int);             // change this to a byte\r
-               memcpy(data + size, &g_skelModel.type, temp);\r
-               size += temp;\r
-\r
-               // number of joints\r
-               temp = sizeof(int);             // change this to a byte\r
-               memcpy(data + size, &numJointsInSkeleton[g_skelModel.type], temp);\r
-               size += temp;\r
-\r
-               // number of verts in each joint cluster\r
-               temp = sizeof(int)*numJointsInSkeleton[g_skelModel.type]; // change this to shorts\r
-               memcpy(data + size, &g_skelModel.new_num_verts[1], temp);\r
-               size += temp;\r
-\r
-               // cluster verts\r
-               for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)\r
-               {\r
-                       current = g_skelModel.vertLists[i];\r
-                       while(current)\r
-                       {\r
-                               temp = sizeof(int);     // change this to a short\r
-                               memcpy(data + size, &current->data, temp);\r
-                               size += temp;\r
-                               toFree = current;\r
-                               current = current->next;\r
-                               free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base\r
-                       }\r
-               }\r
-\r
-               if(!num_groups) // joints are stored with regular verts for compressed models\r
-               {\r
-                       framesWritten = true;\r
-\r
-                       temp = sizeof(int);     // change this to a byte\r
-                       memcpy(data + size, &framesWritten, temp);\r
-                       size += temp;\r
-\r
-                       for (i = 0; i < fmheader.num_frames; ++i)\r
-                       {\r
-                               in = &g_frames[i];\r
-\r
-                               for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)\r
-                               {\r
-                                       for (k=0 ; k<3 ; k++)\r
-                                       {\r
-                                               // scale to byte values & min/max check\r
-                                               v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
-\r
-                                               // write out origin as a float since they arn't clamped\r
-                                               temp = sizeof(float);   // change this to a short\r
-                                               assert(size+temp < DATA_SIZE);\r
-                                               memcpy(data + size, &v, temp);\r
-                                               size += temp;\r
-                                       }\r
-\r
-                                       for (k=0 ; k<3 ; k++)\r
-                                       {\r
-                                               v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
-\r
-                                               // write out origin as a float since they arn't clamped\r
-                                               temp = sizeof(float);   // change this to a short\r
-                                               assert(size+temp < DATA_SIZE);\r
-                                               memcpy(data + size, &v, temp);\r
-                                               size += temp;\r
-                                       }\r
-\r
-                                       for (k=0 ; k<3 ; k++)\r
-                                       {\r
-                                               v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
-\r
-                                               // write out origin as a float since they arn't clamped\r
-                                               temp = sizeof(float);   // change this to a short\r
-                                               assert(size+temp < DATA_SIZE);\r
-                                               memcpy(data + size, &v, temp);\r
-                                               size += temp;\r
-                                       }\r
-                               }\r
-                       }\r
-\r
-               }\r
-               else\r
-               {\r
-                       temp = sizeof(int);     // change this to a byte\r
-                       memcpy(data + size, &framesWritten, temp);\r
-                       size += temp;\r
-               }\r
-\r
-               WriteHeader(modelouthandle, FM_SKELETON_NAME, FM_SKELETON_VER, size, data);\r
-       }\r
-\r
-       if(g_skelModel.references != REF_NULL)\r
-       {\r
-               int refnum;\r
-\r
-               size = 0;\r
-               if (RefPointNum <= 0)\r
-               {       // Hard-coded labels\r
-                       refnum = numReferences[g_skelModel.references];\r
-               }\r
-               else\r
-               {       // Labels indicated in QDT\r
-                       refnum = RefPointNum;\r
-               }\r
-\r
-               temp = sizeof(int);     // change this to a byte\r
-               memcpy(data2 + size, &g_skelModel.references, temp);\r
-               size += temp;\r
-\r
-               if(!num_groups)\r
-               {\r
-                       framesWritten = true;\r
-\r
-                       temp = sizeof(int);     // change this to a byte\r
-                       memcpy(data2 + size, &framesWritten, temp);\r
-                       size += temp;\r
-\r
-                       for (i = 0; i < fmheader.num_frames; ++i)\r
-                       {\r
-                               in = &g_frames[i];\r
-\r
-                               for (j = 0 ; j < refnum; ++j)\r
-                               {\r
-                                       for (k=0 ; k<3 ; k++)\r
-                                       {\r
-                                               // scale to byte values & min/max check\r
-                                               v = Q_rint ( (in->references[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
-\r
-                                               // write out origin as a float since they arn't clamped\r
-                                               temp = sizeof(float);   // change this to a short\r
-                                               assert(size+temp < DATA_SIZE);\r
-                                               memcpy(data2 + size, &v, temp);\r
-                                               size += temp;\r
-                                       }\r
-\r
-                                       for (k=0 ; k<3 ; k++)\r
-                                       {\r
-                                               v = Q_rint ( (in->references[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
-\r
-                                               // write out origin as a float since they arn't clamped\r
-                                               temp = sizeof(float);   // change this to a short\r
-                                               assert(size+temp < DATA_SIZE);\r
-                                               memcpy(data2 + size, &v, temp);\r
-                                               size += temp;\r
-                                       }\r
-\r
-                                       for (k=0 ; k<3 ; k++)\r
-                                       {\r
-                                               v = Q_rint ( (in->references[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
-\r
-                                               // write out origin as a float since they arn't clamped\r
-                                               temp = sizeof(float);   // change this to a short\r
-                                               assert(size+temp < DATA_SIZE);\r
-                                               memcpy(data2 + size, &v, temp);\r
-                                               size += temp;\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               else    // FINISH ME: references need to be stored with regular verts for compressed models\r
-               {\r
-                       framesWritten = false;\r
-\r
-                       temp = sizeof(int);     // change this to a byte\r
-                       memcpy(data2 + size, &framesWritten, temp);\r
-                       size += temp;\r
-               }\r
-\r
-               WriteHeader(modelouthandle, FM_REFERENCES_NAME, FM_REFERENCES_VER, size, data2);\r
-       }\r
-}\r
-\r
-static void CompressFrames()\r
-{\r
-       fmgroup_t *g;\r
-       int i,j,k;\r
-       fmframe_t       *in;\r
-\r
-       j=0;\r
-       for (i=0;i<fmheader.num_frames;i++)\r
-       {\r
-               while (i>=groups[j].start_frame+groups[j].num_frames&&j<num_groups-1)\r
-                       j++;\r
-               frame_to_group[i]=j;\r
-       }\r
-\r
-       for (k=0;k<num_groups;k++)\r
-       {\r
-               g=&groups[k];\r
-\r
-               printf("\nCompressing Frames for group %i...\n", k);\r
-               AnimCompressInit(g->num_frames,fmheader.num_xyz,g->degrees);\r
-               for (i=0;i<g->num_frames;i++)\r
-               {\r
-                       in = &g_frames[i+g->start_frame];\r
-                       for (j=0;j<fmheader.num_xyz;j++)\r
-                               AnimSetFrame(i,j,in->v[j].v[0],in->v[j].v[1],in->v[j].v[2]);\r
-               }\r
-               AnimCompressDoit();\r
-               g->mat= (char *) SafeMalloc(fmheader.num_xyz*3*g->degrees*sizeof(char), "CompressFrames");\r
-               g->ccomp=(char *) SafeMalloc(g->num_frames*g->degrees*sizeof(char), "CompressFrames");\r
-               g->cbase=(char *) SafeMalloc(fmheader.num_xyz*3*sizeof(unsigned char), "CompressFrames");\r
-               g->cscale=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");\r
-               g->coffset=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");\r
-               AnimCompressToBytes(g->trans,g->scale,g->mat,g->ccomp,g->cbase,g->cscale,g->coffset,g->bmin,g->bmax);\r
-               AnimCompressEnd();\r
-       }\r
-}\r
-\r
-static void OptimizeVertices(void)\r
-{\r
-       qboolean        vert_used[MAX_FM_VERTS];\r
-       short           vert_replacement[MAX_FM_VERTS];\r
-       int                     i,j,k,l,pos,bit,set_pos,set_bit;\r
-       fmframe_t       *in;\r
-       qboolean        Found;\r
-       int                     num_unique;\r
-       static IntListNode_t *newVertLists[NUM_CLUSTERS];\r
-       static int newNum_verts[NUM_CLUSTERS];\r
-       IntListNode_t *current, *next;\r
-\r
-       printf("Optimizing vertices...");\r
-\r
-       memset(vert_used, 0, sizeof(vert_used));\r
-\r
-       if(g_skelModel.clustered == true)\r
-       {\r
-               memset(newNum_verts, 0, sizeof(newNum_verts));\r
-               memset(newVertLists, 0, sizeof(newVertLists));\r
-       }\r
-\r
-       num_unique = 0;\r
-\r
-       // search for common points among all the frames\r
-       for (i=0 ; i<fmheader.num_frames ; i++)\r
-       {\r
-               in = &g_frames[i];\r
-\r
-               for(j=0;j<fmheader.num_xyz;j++)\r
-               {\r
-                       for(k=0,Found=false;k<j;k++)\r
-                       {       // starting from the beginning always ensures vert_replacement points to the first point in the array\r
-                               if (in->v[j].v[0] == in->v[k].v[0] &&\r
-                                       in->v[j].v[1] == in->v[k].v[1] &&\r
-                                       in->v[j].v[2] == in->v[k].v[2])\r
-                               {\r
-                                       Found = true;\r
-                                       vert_replacement[j] = k;\r
-                                       break;\r
-                               }\r
-\r
-                       }\r
-\r
-                       if (!Found)\r
-                       {\r
-                               if (!vert_used[j])\r
-                               {\r
-                                       num_unique++;\r
-                               }\r
-                               vert_used[j] = true;\r
-                       }\r
-               }\r
-       }\r
-\r
-       // recompute the light normals\r
-       for (i=0 ; i<fmheader.num_frames ; i++)\r
-       {\r
-               in = &g_frames[i];\r
-\r
-               for(j=0;j<fmheader.num_xyz;j++)\r
-               {\r
-                       if (!vert_used[j])\r
-                       {\r
-                               k = vert_replacement[j];\r
-\r
-                               VectorAdd (in->v[j].vnorm.normalsum, in->v[k].vnorm.normalsum, in->v[k].vnorm.normalsum);\r
-                               in->v[k].vnorm.numnormals += in->v[j].vnorm.numnormals++;\r
-                       }\r
-               }\r
-\r
-               for (j=0 ; j<fmheader.num_xyz ; j++)\r
-               {\r
-                       vec3_t  v;\r
-                       float   maxdot;\r
-                       int             maxdotindex;\r
-                       int             c;\r
-\r
-                       c = in->v[j].vnorm.numnormals;\r
-                       if (!c)\r
-                               Error ("Vertex with no triangles attached");\r
-\r
-                       VectorScale (in->v[j].vnorm.normalsum, 1.0/c, v);\r
-                       VectorNormalize (v, v);\r
-\r
-                       maxdot = -999999.0;\r
-                       maxdotindex = -1;\r
-\r
-                       for (k=0 ; k<NUMVERTEXNORMALS ; k++)\r
-                       {\r
-                               float   dot;\r
-\r
-                               dot = DotProduct (v, avertexnormals[k]);\r
-                               if (dot > maxdot)\r
-                               {\r
-                                       maxdot = dot;\r
-                                       maxdotindex = k;\r
-                               }\r
-                       }\r
-\r
-                       in->v[j].lightnormalindex = maxdotindex;\r
-               }\r
-       }\r
-\r
-       // create substitution list\r
-       num_unique = 0;\r
-       for(i=0;i<fmheader.num_xyz;i++)\r
-       {\r
-               if (vert_used[i])\r
-               {\r
-                       vert_replacement[i] = num_unique;\r
-                       num_unique++;\r
-               }\r
-               else\r
-               {\r
-                       vert_replacement[i] = vert_replacement[vert_replacement[i]];\r
-               }\r
-\r
-               // vert_replacement[i] is the new index, i is the old index\r
-               // need to add the new index to the cluster list if old index was in it\r
-               if(g_skelModel.clustered == true)\r
-               {\r
-                       for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)\r
-                       {\r
-                               for(l = 0, current = g_skelModel.vertLists[k]; \r
-                                       l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)\r
-                               {\r
-                                       if(current->data == i)\r
-                                       {\r
-                                               IntListNode_t *current2;\r
-                                               int m;\r
-                                               qboolean added = false;\r
-\r
-                                               for(m = 0, current2 = newVertLists[k]; m < newNum_verts[k+1];\r
-                                                       ++m, current2 = current2->next)\r
-                                               {\r
-                                                       if(current2->data == vert_replacement[i])\r
-                                                       {\r
-                                                               added = true;\r
-                                                               break;\r
-                                                       }\r
-                                               }\r
-\r
-                                               if(!added)\r
-                                               {\r
-                                                       ++newNum_verts[k+1];\r
-\r
-                                                       next = newVertLists[k];\r
-\r
-                                                       newVertLists[k] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "OptimizeVertices");\r
-                                                       // freed after model write out\r
-\r
-                                                       newVertLists[k]->data = vert_replacement[i];\r
-                                                       newVertLists[k]->next = next;\r
-                                               }\r
-                                               break;\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       // substitute\r
-       for (i=0 ; i<fmheader.num_frames ; i++)\r
-       {\r
-               in = &g_frames[i];\r
-\r
-               for(j=0;j<fmheader.num_xyz;j++)\r
-               {\r
-                       in->v[vert_replacement[j]] = in->v[j];\r
-               }\r
-\r
-       }\r
-\r
-       for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)\r
-       {\r
-               IntListNode_t *toFree;\r
-               current = g_skelModel.vertLists[i];\r
-\r
-               while(current)\r
-               {\r
-                       toFree = current;\r
-                       current = current->next;\r
-                       free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base\r
-               }\r
-\r
-               g_skelModel.vertLists[i] = newVertLists[i];\r
-               g_skelModel.new_num_verts[i+1] = newNum_verts[i+1];\r
-       }\r
-\r
-#ifndef NDEBUG\r
-       for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)\r
-       {\r
-               for(l = 0, current = g_skelModel.vertLists[k]; \r
-                       l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)\r
-               {\r
-                       IntListNode_t *current2;\r
-                       int m;\r
-\r
-                       for(m = l+1, current2 = current->next; m < newNum_verts[k+1];\r
-                               ++m, current2 = current2->next)\r
-                       {\r
-                               if(current->data == current2->data)\r
-                               {\r
-                                       printf("Warning duplicate vertex:  %d\n", current->data);\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-#endif\r
-\r
-       for(i=0;i<fmheader.num_mesh_nodes;i++)\r
-       {       // reset the vert bits\r
-               memset(pmnodes[i].verts,0,sizeof(pmnodes[i].verts));\r
-       }\r
-\r
-       // repleace the master triangle list vertex indexes and update the vert bits for each mesh node\r
-       for (i=0 ; i<fmheader.num_tris ; i++)\r
-       {\r
-               pos = i >> 3;\r
-               bit = 1 << (i & 7 );\r
-\r
-               for (j=0 ; j<3 ; j++)\r
-               {       \r
-                       set_bit = set_pos = triangles[i].index_xyz[j] = vert_replacement[triangles[i].index_xyz[j]];\r
-\r
-                       set_pos >>= 3;\r
-                       set_bit = 1 << (set_bit & 7);\r
-\r
-                       for(k=0;k<fmheader.num_mesh_nodes;k++)\r
-                       {\r
-                               if (!(pmnodes[k].tris[pos] & bit))\r
-                               {\r
-                                       continue;\r
-                               }\r
-                               pmnodes[k].verts[set_pos] |= set_bit;\r
-                       }\r
-               }\r
-       }\r
-\r
-       for (i=0;i<numcommands;i++)\r
-       {\r
-               j = commands[i];\r
-               if (!j) continue;\r
-\r
-               j = abs(j);\r
-               for(i++;j;j--,i+=3)\r
-               {\r
-                       commands[i+2] = vert_replacement[commands[i+2]];\r
-               }\r
-               i--;\r
-       }\r
-\r
-       printf("Reduced by %d\n",fmheader.num_xyz - num_unique);\r
-       \r
-       fmheader.num_xyz = num_unique;\r
-       if (num_groups)\r
-       {\r
-               // tack on the reference verts to the regular verts\r
-               if(g_skelModel.references != REF_NULL)\r
-               {\r
-                       fmframe_t       *in;\r
-                       int index;\r
-                       int refnum;\r
-\r
-                       if (RefPointNum <= 0)\r
-                       {       // Hard-coded labels\r
-                               refnum = numReferences[g_skelModel.references];\r
-                       }\r
-                       else\r
-                       {       // Labels indicated in QDT\r
-                               refnum = RefPointNum;\r
-                       }\r
-\r
-\r
-                       for (i = 0; i < fmheader.num_frames; ++i)\r
-                       {\r
-                               in = &g_frames[i];\r
-                               index = fmheader.num_xyz;\r
-\r
-                               for (j = 0 ; j < refnum; ++j)\r
-                               {\r
-                                       VectorCopy(in->references[j].placement.origin, in->v[index].v);\r
-                                       index++;\r
-\r
-                                       VectorCopy(in->references[j].placement.direction, in->v[index].v);\r
-                                       index++;\r
-\r
-                                       VectorCopy(in->references[j].placement.up, in->v[index].v);\r
-                                       index++;\r
-                               }\r
-                       }\r
-\r
-                       fmheader.num_xyz += refnum*3;\r
-               }\r
-\r
-               // tack on the skeletal joint verts to the regular verts\r
-               if(g_skelModel.type != SKEL_NULL)\r
-               {\r
-                       fmframe_t       *in;\r
-                       int index;\r
-\r
-                       for (i = 0; i < fmheader.num_frames; ++i)\r
-                       {\r
-                               in = &g_frames[i];\r
-                               index = fmheader.num_xyz;\r
-\r
-                               for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)\r
-                               {\r
-                                       VectorCopy(in->joints[j].placement.origin, in->v[index].v);\r
-                                       index++;\r
-\r
-                                       VectorCopy(in->joints[j].placement.direction, in->v[index].v);\r
-                                       index++;\r
-\r
-                                       VectorCopy(in->joints[j].placement.up, in->v[index].v);\r
-                                       index++;\r
-                               }\r
-                       }\r
-\r
-                       fmheader.num_xyz += numJointsInSkeleton[g_skelModel.type]*3;\r
-               }\r
-\r
-               CompressFrames();\r
-       }\r
-}\r
-\r
-\r
-/*\r
-===============\r
-FinishModel\r
-===============\r
-*/\r
-void FMFinishModel (void)\r
-{\r
-       FILE            *modelouthandle;\r
-       int                     i,j,length,tris,verts,bit,pos,total_tris,total_verts;\r
-       char            name[1024];\r
-       int                     trans_count;\r
-       \r
-       if (!fmheader.num_frames)\r
-               return;\r
-       \r
-//\r
-// copy to release directory tree if doing a release build\r
-//\r
-       if (g_release)\r
-       {\r
-               if (modelname[0])\r
-                       sprintf (name, "%s", modelname);\r
-               else\r
-                       sprintf (name, "%s/tris.fm", cdpartial);\r
-               ReleaseFile (name);\r
-\r
-               for (i=0 ; i<fmheader.num_skins ; i++)\r
-               {\r
-                       ReleaseFile (g_skins[i]);\r
-               }\r
-               fmheader.num_frames = 0;\r
-               return;\r
-       }\r
-\r
-       printf("\n");\r
-\r
-       trans_count = 0;\r
-       for(i=0;i<fmheader.num_tris;i++)\r
-               if (translucent[i])\r
-                       trans_count++;\r
-\r
-       if (!g_no_opimizations)\r
-       {\r
-               OptimizeVertices();\r
-       }\r
-\r
-//\r
-// write the model output file\r
-//\r
-       if (modelname[0])\r
-               sprintf (name, "%s%s", g_outputDir, modelname);\r
-       else\r
-               sprintf (name, "%s/tris.fm", g_outputDir);\r
-       printf ("saving to %s\n", name);\r
-       CreatePath (name);\r
-       modelouthandle = SafeOpenWrite (name);\r
-\r
-       WriteModelFile (modelouthandle);\r
-       \r
-       printf ("%3dx%3d skin\n", fmheader.skinwidth, fmheader.skinheight);\r
-       printf ("First frame boundaries:\n");\r
-       printf ("       minimum x: %3f\n", g_frames[0].mins[0]);\r
-       printf ("       maximum x: %3f\n", g_frames[0].maxs[0]);\r
-       printf ("       minimum y: %3f\n", g_frames[0].mins[1]);\r
-       printf ("       maximum y: %3f\n", g_frames[0].maxs[1]);\r
-       printf ("       minimum z: %3f\n", g_frames[0].mins[2]);\r
-       printf ("       maximum z: %3f\n", g_frames[0].maxs[2]);\r
-       printf ("%4d vertices\n", fmheader.num_xyz);\r
-       printf ("%4d triangles, %4d of them translucent\n", fmheader.num_tris, trans_count);\r
-       printf ("%4d frame\n", fmheader.num_frames);\r
-       printf ("%4d glverts\n", numglverts);\r
-       printf ("%4d glcmd\n", fmheader.num_glcmds);\r
-       printf ("%4d skins\n", fmheader.num_skins);\r
-       printf ("%4d mesh nodes\n", fmheader.num_mesh_nodes);\r
-       printf ("wasted pixels: %d / %d (%5.2f Percent)\n",total_skin_pixels - skin_pixels_used, \r
-               total_skin_pixels, (double)(total_skin_pixels - skin_pixels_used) / (double)total_skin_pixels * 100.0);\r
-\r
-       printf ("file size: %d\n", (int)ftell (modelouthandle) );\r
-       printf ("---------------------\n");\r
-       \r
-       if (g_verbose)\r
-       {\r
-               if (fmheader.num_mesh_nodes)\r
-               {\r
-                       total_tris = total_verts = 0;\r
-                       printf("Node Name                         Tris Verts\n");\r
-                       printf("--------------------------------- ---- -----\n");\r
-                       for(i=0;i<fmheader.num_mesh_nodes;i++)\r
-                       {\r
-                               tris = 0;\r
-                               verts = 0;\r
-                               for(j=0;j<MAXTRIANGLES;j++)\r
-                               {\r
-                                       pos = (j) >> 3;\r
-                                       bit = 1 << ((j) & 7 );\r
-                                       if (pmnodes[i].tris[pos] & bit)\r
-                                       {\r
-                                               tris++;\r
-                                       }\r
-                               }\r
-                               for(j=0;j<MAX_FM_VERTS;j++)\r
-                               {\r
-                                       pos = (j) >> 3;\r
-                                       bit = 1 << ((j) & 7 );\r
-                                       if (pmnodes[i].verts[pos] & bit)\r
-                                       {\r
-                                               verts++;\r
-                                       }\r
-                               }\r
-\r
-                               printf("%-33s %4d %5d\n",pmnodes[i].name,tris,verts);\r
-\r
-                               total_tris += tris;\r
-                               total_verts += verts;\r
-                       }\r
-                       printf("--------------------------------- ---- -----\n");\r
-                       printf("%-33s %4d %5d\n","TOTALS",total_tris,total_verts);\r
-               }\r
-       }\r
-       fclose (modelouthandle);\r
-\r
-       // finish writing header file\r
-       H_printf("\n");\r
-\r
-       // scale_up is usefull to allow step distances to be adjusted\r
-       H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);\r
-\r
-       // mesh nodes\r
-       if (fmheader.num_mesh_nodes)\r
-       {\r
-               H_printf("\n");\r
-               H_printf("#define NUM_MESH_NODES\t\t%d\n\n",fmheader.num_mesh_nodes);\r
-               for(i=0;i<fmheader.num_mesh_nodes;i++)\r
-               {\r
-                       strcpy(name, pmnodes[i].name);\r
-                       strupr(name);\r
-                       length = strlen(name);\r
-                       for(j=0;j<length;j++)\r
-                       {\r
-                               if (name[j] == ' ')\r
-                               {\r
-                                       name[j] = '_';\r
-                               }\r
-                       }\r
-                       H_printf("#define MESH_%s\t\t%d\n", name, i);\r
-               }\r
-       }\r
-\r
-       fclose (headerouthandle);\r
-       headerouthandle = NULL;\r
-       free (pmnodes);\r
-}\r
-\r
-\r
-/*\r
-=================================================================\r
-\r
-ALIAS MODEL DISPLAY LIST GENERATION\r
-\r
-=================================================================\r
-*/\r
-\r
-extern int             strip_xyz[128];\r
-extern int             strip_st[128];\r
-extern int             strip_tris[128];\r
-extern int             stripcount;\r
-\r
-/*\r
-================\r
-StripLength\r
-================\r
-*/\r
-static int     StripLength (int starttri, int startv, int num_tris, int node)\r
-{\r
-       int                             m1, m2;\r
-       int                             st1, st2;\r
-       int                             j;\r
-       fmtriangle_t    *last, *check;\r
-       int                             k;\r
-       int                             pos, bit;\r
-\r
-       used[starttri] = 2;\r
-\r
-       last = &triangles[starttri];\r
-\r
-       strip_xyz[0] = last->index_xyz[(startv)%3];\r
-       strip_xyz[1] = last->index_xyz[(startv+1)%3];\r
-       strip_xyz[2] = last->index_xyz[(startv+2)%3];\r
-       strip_st[0] = last->index_st[(startv)%3];\r
-       strip_st[1] = last->index_st[(startv+1)%3];\r
-       strip_st[2] = last->index_st[(startv+2)%3];\r
-\r
-       strip_tris[0] = starttri;\r
-       stripcount = 1;\r
-\r
-       m1 = last->index_xyz[(startv+2)%3];\r
-       st1 = last->index_st[(startv+2)%3];\r
-       m2 = last->index_xyz[(startv+1)%3];\r
-       st2 = last->index_st[(startv+1)%3];\r
-\r
-       // look for a matching triangle\r
-nexttri:\r
-       for (j=starttri+1, check=&triangles[starttri+1]\r
-               ; j<num_tris ; j++, check++)\r
-       {\r
-               pos = j >> 3;\r
-               bit = 1 << (j & 7 );\r
-               if (!(pmnodes[node].tris[pos] & bit))\r
-               {\r
-                       continue;\r
-               }\r
-               for (k=0 ; k<3 ; k++)\r
-               {\r
-                       if (check->index_xyz[k] != m1)\r
-                               continue;\r
-                       if (check->index_st[k] != st1)\r
-                               continue;\r
-                       if (check->index_xyz[ (k+1)%3 ] != m2)\r
-                               continue;\r
-                       if (check->index_st[ (k+1)%3 ] != st2)\r
-                               continue;\r
-\r
-                       // this is the next part of the fan\r
-\r
-                       // if we can't use this triangle, this tristrip is done\r
-                       if (used[j] || translucent[j] != translucent[starttri])\r
-                               goto done;\r
-\r
-                       // the new edge\r
-                       if (stripcount & 1)\r
-                       {\r
-                               m2 = check->index_xyz[ (k+2)%3 ];\r
-                               st2 = check->index_st[ (k+2)%3 ];\r
-                       }\r
-                       else\r
-                       {\r
-                               m1 = check->index_xyz[ (k+2)%3 ];\r
-                               st1 = check->index_st[ (k+2)%3 ];\r
-                       }\r
-\r
-                       strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];\r
-                       strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];\r
-                       strip_tris[stripcount] = j;\r
-                       stripcount++;\r
-\r
-                       used[j] = 2;\r
-                       goto nexttri;\r
-               }\r
-       }\r
-done:\r
-\r
-       // clear the temp used flags\r
-       for (j=starttri+1 ; j<num_tris ; j++)\r
-               if (used[j] == 2)\r
-                       used[j] = 0;\r
-\r
-       return stripcount;\r
-}\r
-\r
-\r
-/*\r
-===========\r
-FanLength\r
-===========\r
-*/\r
-static int     FanLength (int starttri, int startv, int num_tris, int node)\r
-{\r
-       int                             m1, m2;\r
-       int                             st1, st2;\r
-       int                             j;\r
-       fmtriangle_t    *last, *check;\r
-       int                             k;\r
-       int                             pos, bit;\r
-\r
-       used[starttri] = 2;\r
-\r
-       last = &triangles[starttri];\r
-\r
-       strip_xyz[0] = last->index_xyz[(startv)%3];\r
-       strip_xyz[1] = last->index_xyz[(startv+1)%3];\r
-       strip_xyz[2] = last->index_xyz[(startv+2)%3];\r
-       strip_st[0] = last->index_st[(startv)%3];\r
-       strip_st[1] = last->index_st[(startv+1)%3];\r
-       strip_st[2] = last->index_st[(startv+2)%3];\r
-\r
-       strip_tris[0] = starttri;\r
-       stripcount = 1;\r
-\r
-       m1 = last->index_xyz[(startv+0)%3];\r
-       st1 = last->index_st[(startv+0)%3];\r
-       m2 = last->index_xyz[(startv+2)%3];\r
-       st2 = last->index_st[(startv+2)%3];\r
-\r
-\r
-       // look for a matching triangle\r
-nexttri:\r
-       for (j=starttri+1, check=&triangles[starttri+1] \r
-               ; j<num_tris ; j++, check++)\r
-       {\r
-               pos = j >> 3;\r
-               bit = 1 << (j & 7 );\r
-               if (!(pmnodes[node].tris[pos] & bit))\r
-               {\r
-                       continue;\r
-               }\r
-               for (k=0 ; k<3 ; k++)\r
-               {\r
-                       if (check->index_xyz[k] != m1)\r
-                               continue;\r
-                       if (check->index_st[k] != st1)\r
-                               continue;\r
-                       if (check->index_xyz[ (k+1)%3 ] != m2)\r
-                               continue;\r
-                       if (check->index_st[ (k+1)%3 ] != st2)\r
-                               continue;\r
-\r
-                       // this is the next part of the fan\r
-\r
-                       // if we can't use this triangle, this tristrip is done\r
-                       if (used[j] || translucent[j] != translucent[starttri])\r
-                               goto done;\r
-\r
-                       // the new edge\r
-                       m2 = check->index_xyz[ (k+2)%3 ];\r
-                       st2 = check->index_st[ (k+2)%3 ];\r
-\r
-                       strip_xyz[stripcount+2] = m2;\r
-                       strip_st[stripcount+2] = st2;\r
-                       strip_tris[stripcount] = j;\r
-                       stripcount++;\r
-\r
-                       used[j] = 2;\r
-                       goto nexttri;\r
-               }\r
-       }\r
-done:\r
-\r
-       // clear the temp used flags\r
-       for (j=starttri+1 ; j<num_tris ; j++)\r
-               if (used[j] == 2)\r
-                       used[j] = 0;\r
-\r
-       return stripcount;\r
-}\r
-\r
-\r
-\r
-/*\r
-================\r
-BuildGlCmds\r
-\r
-Generate a list of trifans or strips\r
-for the model, which holds for all frames\r
-================\r
-*/\r
-static void BuildGlCmds (void)\r
-{\r
-       int             i, j, k, l;\r
-       int             startv;\r
-       float   s, t;\r
-       int             len, bestlen, besttype;\r
-       int             best_xyz[1024];\r
-       int             best_st[1024];\r
-       int             best_tris[1024];\r
-       int             type;\r
-       int             trans_check;\r
-       int             bit,pos;\r
-\r
-       //\r
-       // build tristrips\r
-       //\r
-       numcommands = 0;\r
-       numglverts = 0;\r
-\r
-\r
-       for(l=0;l<fmheader.num_mesh_nodes;l++)\r
-       {\r
-               memset (used, 0, sizeof(used));\r
-\r
-               pmnodes[l].start_glcmds = numcommands;\r
-\r
-               for(trans_check = 0; trans_check<2; trans_check++)\r
-               {\r
-                       for (i=0 ; i < fmheader.num_tris ; i++)\r
-                       {\r
-                               pos = i >> 3;\r
-                               bit = 1 << (i & 7 );\r
-                               if (!(pmnodes[l].tris[pos] & bit))\r
-                               {\r
-                                       continue;\r
-                               }\r
-\r
-                               // pick an unused triangle and start the trifan\r
-                               if (used[i] || trans_check != translucent[i])\r
-                               {\r
-                                       continue;\r
-                               }\r
-\r
-                               bestlen = 0;\r
-                               for (type = 0 ; type < 2 ; type++)\r
-               //      type = 1;\r
-                               {\r
-                                       for (startv =0 ; startv < 3 ; startv++)\r
-                                       {\r
-                                               if (type == 1)\r
-                                                       len = StripLength (i, startv, fmheader.num_tris, l);\r
-                                               else\r
-                                                       len = FanLength (i, startv, fmheader.num_tris, l);\r
-                                               if (len > bestlen)\r
-                                               {\r
-                                                       besttype = type;\r
-                                                       bestlen = len;\r
-                                                       for (j=0 ; j<bestlen+2 ; j++)\r
-                                                       {\r
-                                                               best_st[j] = strip_st[j];\r
-                                                               best_xyz[j] = strip_xyz[j];\r
-                                                       }\r
-                                                       for (j=0 ; j<bestlen ; j++)\r
-                                                               best_tris[j] = strip_tris[j];\r
-                                               }\r
-                                       }\r
-                               }\r
-\r
-                               // mark the tris on the best strip/fan as used\r
-                               for (j=0 ; j<bestlen ; j++)\r
-                                       used[best_tris[j]] = 1;\r
-\r
-                               if (besttype == 1)\r
-                                       commands[numcommands++] = (bestlen+2);\r
-                               else\r
-                                       commands[numcommands++] = -(bestlen+2);\r
-\r
-                               numglverts += bestlen+2;\r
-\r
-                               for (j=0 ; j<bestlen+2 ; j++)\r
-                               {\r
-                                       // emit a vertex into the reorder buffer\r
-                                       k = best_st[j];\r
-\r
-                                       // emit s/t coords into the commands stream\r
-                                       s = base_st[k].s;\r
-                                       t = base_st[k].t;\r
-\r
-                                       s = (s  ) / fmheader.skinwidth;\r
-                                       t = (t  ) / fmheader.skinheight;\r
-\r
-                                       *(float *)&commands[numcommands++] = s;\r
-                                       *(float *)&commands[numcommands++] = t;\r
-                                       *(int *)&commands[numcommands++] = best_xyz[j];\r
-                               }\r
-                       }\r
-               }\r
-               commands[numcommands++] = 0;            // end of list marker\r
-               pmnodes[l].num_glcmds = numcommands - pmnodes[l].start_glcmds;\r
-       }\r
-}\r
-\r
-\r
-/*\r
-===============================================================\r
-\r
-BASE FRAME SETUP\r
-\r
-===============================================================\r
-*/\r
-\r
-\r
-#define LINE_NORMAL 1\r
-#define LINE_FAT 2\r
-#define LINE_DOTTED 3\r
-\r
-\r
-#define ASCII_SPACE 32\r
-\r
-int LineType = LINE_NORMAL;\r
-extern unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];\r
-unsigned char LineColor = 255;\r
-int ScaleWidth, ScaleHeight;\r
-\r
-\r
-static char *CharDefs[] =\r
-{\r
-       "-------------------------",\r
-       "-------------------------", // !\r
-       "-------------------------", // "\r
-       "-------------------------", // #\r
-       "-------------------------", // $\r
-       "-------------------------", // %\r
-       "-------------------------", // &\r
-       "--*----*-----------------", // '\r
-       "-*---*----*----*-----*---", // (\r
-       "*-----*----*----*---*----", // )\r
-       "-----*--*--**---**--*--*-", // *\r
-       "-------------------------", // +\r
-       "----------------**--**---", // ,\r
-       "-------------------------", // -\r
-       "----------------**---**--", // .\r
-       "-------------------------", // /\r
-       " *** *  *** * ***  * *** ", // 0\r
-       "   *   **    *    *    * ",\r
-       "****     * *** *    *****",\r
-       "****     * ***     ***** ",\r
-       "  **  * * *  * *****   * ",\r
-       "**** *    ****     ***** ",\r
-       " *** *    **** *   * *** ",\r
-       "*****    *   *   *    *  ",\r
-       " *** *   * *** *   * *** ",\r
-       " *** *   * ****    * *** ", // 9\r
-       "-**---**--------**---**--", // :\r
-       "-------------------------", // ;\r
-       "-------------------------", // <\r
-       "-------------------------", // =\r
-       "-------------------------", // >\r
-       "-------------------------", // ?\r
-       "-------------------------", // @\r
-       "-***-*---*******---**---*", // A\r
-       "****-*---*****-*---*****-",\r
-       "-*****----*----*-----****",\r
-       "****-*---**---**---*****-",\r
-       "******----****-*----*****",\r
-       "******----****-*----*----",\r
-       "-*****----*--***---*-****",\r
-       "*---**---*******---**---*",\r
-       "-***---*----*----*---***-",\r
-       "----*----*----**---*-***-",\r
-       "-*--*-*-*--**---*-*--*--*",\r
-       "-*----*----*----*----****",\r
-       "*---***-***-*-**---**---*",\r
-       "*---***--**-*-**--***---*",\r
-       "-***-*---**---**---*-***-",\r
-       "****-*---*****-*----*----",\r
-       "-***-*---**---*-***----**",\r
-       "****-*---*****-*-*--*--**",\r
-       "-*****-----***-----*****-",\r
-       "*****--*----*----*----*--",\r
-       "*---**---**---**---******",\r
-       "*---**---**---*-*-*---*--",\r
-       "*---**---**-*-***-***---*",\r
-       "*---*-*-*---*---*-*-*---*",\r
-       "*---**---*-*-*---*----*--",\r
-       "*****---*---*---*---*****" // Z\r
-};\r
-\r
-void DrawLine(int x1, int y1, int x2, int y2)\r
-{\r
-       int dx, dy;\r
-       int adx, ady;\r
-       int count;\r
-       float xfrac, yfrac, xstep, ystep;\r
-       unsigned sx, sy;\r
-       float u, v;\r
-\r
-       dx = x2 - x1;\r
-       dy = y2 - y1;\r
-       adx = abs(dx);\r
-       ady = abs(dy);\r
-\r
-       count = adx > ady ? adx : ady;\r
-       count++;\r
-\r
-       if(count > 300)\r
-       {\r
-               printf("Bad count\n");\r
-               return; // don't ever hang up on bad data\r
-       }\r
-               \r
-       xfrac = x1;\r
-       yfrac = y1;\r
-       \r
-       xstep = (float)dx/count;\r
-       ystep = (float)dy/count;\r
-\r
-       switch(LineType)\r
-       {\r
-               case LINE_NORMAL:\r
-                       do\r
-                       {\r
-                               if(xfrac < SKINPAGE_WIDTH && yfrac < SKINPAGE_HEIGHT)\r
-                               {\r
-                                       pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;\r
-                               }\r
-                               xfrac += xstep;\r
-                               yfrac += ystep;\r
-                               count--;\r
-                       } while (count > 0);\r
-                       break;\r
-               case LINE_FAT:\r
-                       do\r
-                       {\r
-                               for (u=-0.1 ; u<=0.9 ; u+=0.999)\r
-                               {\r
-                                       for (v=-0.1 ; v<=0.9 ; v+=0.999)\r
-                                       {\r
-                                               sx = xfrac+u;\r
-                                               sy = yfrac+v;\r
-                                               if(sx < SKINPAGE_WIDTH && sy < SKINPAGE_HEIGHT)\r
-                                               {\r
-                                                       pic[sy*SKINPAGE_WIDTH+sx] = LineColor;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               xfrac += xstep;\r
-                               yfrac += ystep;\r
-                               count--;\r
-                       } while (count > 0);\r
-                       break;\r
-               case LINE_DOTTED:\r
-                       do\r
-                       {\r
-                               if(count&1 && xfrac < SKINPAGE_WIDTH &&\r
-                                       yfrac < SKINPAGE_HEIGHT)\r
-                               {\r
-                                       pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;\r
-                               }\r
-                               xfrac += xstep;\r
-                               yfrac += ystep;\r
-                               count--;\r
-                       } while (count > 0);\r
-                       break;\r
-               default:\r
-                       Error("Unknown <linetype> %d.\n", LineType);\r
-       }\r
-}\r
-\r
-//==========================================================================\r
-//\r
-// DrawCharacter\r
-//\r
-//==========================================================================\r
-\r
-static void DrawCharacter(int x, int y, int character)\r
-{\r
-       int r, c;\r
-       char *def;\r
-\r
-       character = toupper(character);\r
-       if(character < ASCII_SPACE || character > 'Z')\r
-       {\r
-               character = ASCII_SPACE;\r
-       }\r
-       character -= ASCII_SPACE;\r
-       for(def = CharDefs[character], r = 0; r < 5; r++)\r
-       {\r
-               for(c = 0; c < 5; c++)\r
-               {\r
-                       pic[(y+r)*SKINPAGE_WIDTH+x+c] = *def++ == '*' ? 255 : 0;\r
-               }\r
-       }\r
-}\r
-\r
-//==========================================================================\r
-//\r
-// DrawTextChar\r
-//\r
-//==========================================================================\r
-\r
-void DrawTextChar(int x, int y, char *text)\r
-{\r
-       int c;\r
-\r
-       while((c = *text++) != '\0')\r
-       {\r
-               DrawCharacter(x, y, c);\r
-               x += 6;\r
-       }\r
-}\r
-\r
-\r
-extern void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight);\r
-\r
-//==========================================================================\r
-// ExtractDigit\r
-\r
-static int ExtractDigit(byte *pic, int x, int y)\r
-{\r
-       int             i;\r
-       int             r, c;\r
-       char    digString[32];\r
-       char    *buffer;\r
-       byte    backColor;\r
-       char    **DigitDefs;\r
-\r
-       backColor = pic[(SKINPAGE_HEIGHT - 1) * SKINPAGE_WIDTH];\r
-       DigitDefs = &CharDefs['0' - ASCII_SPACE];\r
-\r
-       buffer = digString;\r
-       for(r = 0; r < 5; r++)\r
-       {\r
-               for(c = 0; c < 5; c++)\r
-               {\r
-                       *buffer++ = (pic[(y + r) * SKINPAGE_WIDTH + x + c] == backColor) ? ' ' : '*';\r
-               }\r
-       }\r
-       *buffer = '\0';\r
-       for(i = 0; i < 10; i++)\r
-       {\r
-               if(strcmp(DigitDefs[i], digString) == 0)\r
-               {\r
-                       return i;\r
-               }\r
-       }\r
-\r
-       Error("Unable to extract scaling info from skin PCX.");\r
-       return 0;\r
-}\r
-\r
-//==========================================================================\r
-// ExtractNumber\r
-\r
-int ExtractNumber(byte *pic, int x, int y)\r
-{\r
-       return ExtractDigit(pic, x, y) * 100 + ExtractDigit(pic, x + 6, y) * 10 + ExtractDigit(pic, x + 12, y);\r
-}\r
-\r
-\r
-\r
-\r
-\r
-/*\r
-============\r
-BuildST\r
-\r
-Builds the triangle_st array for the base frame and\r
-fmheader.skinwidth / fmheader.skinheight\r
-\r
-  FIXME: allow this to be loaded from a file for\r
-  arbitrary mappings\r
-============\r
-*/\r
-static void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)\r
-{\r
-       int                     backface_flag;\r
-       int                     i, j;\r
-       int                     width, height, iwidth, iheight, swidth;\r
-       float           basex, basey;\r
-       float           scale;\r
-       vec3_t          mins, maxs;\r
-       float           *pbasevert;\r
-       vec3_t          vtemp1, vtemp2, normal;\r
-       float           s_scale, t_scale;\r
-       float           scWidth;\r
-       float           scHeight;\r
-       int skinwidth;\r
-       int skinheight;\r
-\r
-       //\r
-       // find bounds of all the verts on the base frame\r
-       //\r
-       ClearBounds (mins, maxs);\r
-       backface_flag = false;\r
-       \r
-       if (ptri[0].HasUV)      // if we have the uv already, we don't want to double up or scale\r
-       {\r
-               iwidth = ScaleWidth;\r
-               iheight = ScaleHeight;\r
-               \r
-               t_scale = s_scale = 1.0;\r
-       }\r
-       else\r
-       {\r
-               for (i=0 ; i<numtri ; i++)\r
-                       for (j=0 ; j<3 ; j++)\r
-                               AddPointToBounds (ptri[i].verts[j], mins, maxs);\r
-                       \r
-                       for (i=0 ; i<3 ; i++)\r
-                       {\r
-                               mins[i] = floor(mins[i]);\r
-                               maxs[i] = ceil(maxs[i]);\r
-                       }\r
-                       \r
-                       width = maxs[0] - mins[0];\r
-                       height = maxs[2] - mins[2];\r
-                       \r
-                       for (i=0 ; i<numtri ; i++)\r
-                       {\r
-                               VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
-                               VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
-                               CrossProduct (vtemp1, vtemp2, normal);\r
-                               \r
-                               if (normal[1] > 0)\r
-                               {\r
-                                       backface_flag = true;\r
-                                       break;\r
-                               }\r
-                       }\r
-                       scWidth = ScaleWidth*SCALE_ADJUST_FACTOR;\r
-                       if (backface_flag)      //we are doubling\r
-                               scWidth /= 2;\r
-                       \r
-                       scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;\r
-                       \r
-                       scale = scWidth/width;\r
-                       \r
-                       if(height*scale >= scHeight)\r
-                       {\r
-                               scale = scHeight/height;\r
-                       }\r
-                       \r
-                       iwidth = ceil(width*scale)+4;\r
-                       iheight = ceil(height*scale)+4;\r
-                       \r
-                       s_scale = (float)(iwidth-4) / width;\r
-                       t_scale = (float)(iheight-4) / height;\r
-                       t_scale = s_scale;\r
-       }\r
-       if (DrawSkin)\r
-       {\r
-               if(backface_flag)\r
-                       DrawScreen(s_scale, t_scale, iwidth*2, iheight);\r
-               else\r
-                       DrawScreen(s_scale, t_scale, iwidth, iheight);\r
-       }\r
-       if (backface_flag)\r
-               skinwidth=iwidth*2;\r
-       else\r
-               skinwidth=iwidth;\r
-       skinheight=iheight;\r
-\r
-\r
-/*     if (!g_fixedwidth)\r
-       {       // old style\r
-               scale = 8;\r
-               if (width*scale >= 150)\r
-                       scale = 150.0 / width;  \r
-               if (height*scale >= 190)\r
-                       scale = 190.0 / height;\r
-\r
-               s_scale = t_scale = scale;\r
-\r
-               iwidth = ceil(width*s_scale);\r
-               iheight = ceil(height*t_scale);\r
-\r
-               iwidth += 4;\r
-               iheight += 4;\r
-       }\r
-       else\r
-       {       // new style\r
-               iwidth = g_fixedwidth / 2;\r
-               iheight = g_fixedheight;\r
-\r
-               s_scale = (float)(iwidth-4) / width;\r
-               t_scale = (float)(iheight-4) / height;\r
-       }*/\r
-\r
-//\r
-// determine which side of each triangle to map the texture to\r
-//\r
-       basey = 2;\r
-       for (i=0 ; i<numtri ; i++)\r
-       {\r
-               if (ptri[i].HasUV)\r
-               {\r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*skinwidth);\r
-                               triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*skinheight);\r
-                       }\r
-               }\r
-               else\r
-               {\r
-                       VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
-                       VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
-                       CrossProduct (vtemp1, vtemp2, normal);\r
-\r
-                       if (normal[1] > 0)\r
-                       {\r
-                               basex = iwidth + 2;\r
-                       }\r
-                       else\r
-                       {\r
-                               basex = 2;\r
-                       }\r
-                       \r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               pbasevert = ptri[i].verts[j];\r
-\r
-                               triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);\r
-                               triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);\r
-                       }\r
-               }\r
-\r
-               if (DrawSkin)\r
-               {\r
-                       DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],\r
-                               triangle_st[i][1][0], triangle_st[i][1][1]);\r
-                       DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],\r
-                               triangle_st[i][2][0], triangle_st[i][2][1]);\r
-                       DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],\r
-                               triangle_st[i][0][0], triangle_st[i][0][1]);\r
-               }\r
-       }\r
-\r
-// make the width a multiple of 4; some hardware requires this, and it ensures\r
-// dword alignment for each scan\r
-\r
-       swidth = iwidth;\r
-       if(backface_flag)\r
-               swidth *= 2;\r
-       fmheader.skinwidth = (swidth + 3) & ~3;\r
-       fmheader.skinheight = iheight;\r
-\r
-       skin_width = iwidth;\r
-       skin_height = iheight;\r
-}\r
-\r
-\r
-static void BuildNewST (triangle_t *ptri, int numtri, qboolean DrawSkin)\r
-{\r
-       int                     i, j;\r
-\r
-       for (i=0 ; i<numtri ; i++)\r
-       {\r
-               if (ptri[i].HasUV)\r
-               {\r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*(ScaleWidth-1));\r
-                               triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*(ScaleHeight-1));\r
-                       }\r
-               }\r
-\r
-               if (DrawSkin)\r
-               {\r
-                       DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],\r
-                               triangle_st[i][1][0], triangle_st[i][1][1]);\r
-                       DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],\r
-                               triangle_st[i][2][0], triangle_st[i][2][1]);\r
-                       DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],\r
-                               triangle_st[i][0][0], triangle_st[i][0][1]);\r
-               }\r
-       }\r
-\r
-// make the width a multiple of 4; some hardware requires this, and it ensures\r
-// dword alignment for each scan\r
-\r
-       fmheader.skinwidth = (ScaleWidth + 3) & ~3;\r
-       fmheader.skinheight = ScaleHeight;\r
-\r
-       skin_width = ScaleWidth;\r
-       skin_height = ScaleHeight;\r
-}\r
-\r
-\r
-\r
-\r
-byte                   *BasePalette;\r
-byte                   *BasePixels,*TransPixels;\r
-int                            BaseWidth, BaseHeight, TransWidth, TransHeight;\r
-qboolean               BaseTrueColor;\r
-static qboolean SetPixel = false;\r
-\r
-int CheckTransRecursiveTri (int *lp1, int *lp2, int *lp3)\r
-{\r
-       int             *temp;\r
-       int             d;\r
-       int             new[2];\r
-\r
-       d = lp2[0] - lp1[0];\r
-       if (d < -1 || d > 1)\r
-               goto split;\r
-       d = lp2[1] - lp1[1];\r
-       if (d < -1 || d > 1)\r
-               goto split;\r
-\r
-       d = lp3[0] - lp2[0];\r
-       if (d < -1 || d > 1)\r
-               goto split2;\r
-       d = lp3[1] - lp2[1];\r
-       if (d < -1 || d > 1)\r
-               goto split2;\r
-\r
-       d = lp1[0] - lp3[0];\r
-       if (d < -1 || d > 1)\r
-               goto split3;\r
-       d = lp1[1] - lp3[1];\r
-       if (d < -1 || d > 1)\r
-       {\r
-split3:\r
-               temp = lp1;\r
-               lp1 = lp3;\r
-               lp3 = lp2;\r
-               lp2 = temp;\r
-\r
-               goto split;\r
-       }\r
-\r
-       return 0;                       // entire tri is filled\r
-\r
-split2:\r
-       temp = lp1;\r
-       lp1 = lp2;\r
-       lp2 = lp3;\r
-       lp3 = temp;\r
-\r
-split:\r
-// split this edge\r
-       new[0] = (lp1[0] + lp2[0]) >> 1;\r
-       new[1] = (lp1[1] + lp2[1]) >> 1;\r
-\r
-// draw the point if splitting a leading edge\r
-       if (lp2[1] > lp1[1])\r
-               goto nodraw;\r
-       if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0]))\r
-               goto nodraw;\r
-\r
-       if (SetPixel)\r
-       {\r
-               assert ((new[1]*BaseWidth) + new[0] < BaseWidth*BaseHeight);\r
-\r
-               if (BaseTrueColor)\r
-               {\r
-                       BasePixels[((new[1]*BaseWidth) + new[0]) * 4] = 1;\r
-               }\r
-               else\r
-               {\r
-                       BasePixels[(new[1]*BaseWidth) + new[0]] = 1;\r
-               }\r
-       }\r
-       else\r
-       {\r
-               if (TransPixels)\r
-               {\r
-                       if (TransPixels[(new[1]*TransWidth) + new[0]] != 255)\r
-                               return 1;\r
-               }\r
-               else if (BaseTrueColor)\r
-               {\r
-                       if (BasePixels[(((new[1]*BaseWidth) + new[0]) * 4) + 3] != 255)\r
-                               return 1;\r
-               }\r
-               else\r
-               {\r
-//                     pixel = BasePixels[(new[1]*BaseWidth) + new[0]];\r
-               }\r
-       }\r
-\r
-nodraw:\r
-// recursively continue\r
-       if (CheckTransRecursiveTri(lp3, lp1, new)) \r
-               return 1;\r
-\r
-       return CheckTransRecursiveTri(lp3, new, lp2);\r
-}\r
-\r
-static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters, \r
-       IntListNode_t **vertLists, int *num_verts, int *new_num_verts)\r
-{\r
-       int i, j;\r
-       IntListNode_t *next;\r
-\r
-       for(j = 0; j < numJointsInSkeleton[g_skelModel.type]; ++j)\r
-       {\r
-               if(!clusters[j])\r
-               {\r
-                       continue;\r
-               }\r
-\r
-               for(i = 0; i < num_verts[j+1]; ++i)\r
-               {\r
-                       if(clusters[j][i] == oldindex)\r
-                       {\r
-                               ++new_num_verts[j+1];\r
-\r
-                               next = vertLists[j];\r
-\r
-                               vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");\r
-                               // Currently freed in WriteJointedModelFile only\r
-\r
-                               vertLists[j]->data = newIndex;\r
-                               vertLists[j]->next = next;\r
-                       }\r
-               }\r
-       }\r
-}\r
-\r
-#define        FUDGE_EPSILON   0.002\r
-\r
-qboolean VectorFudgeCompare (vec3_t v1, vec3_t v2)\r
-{\r
-       int             i;\r
-       \r
-       for (i=0 ; i<3 ; i++)\r
-               if (fabs(v1[i]-v2[i]) > FUDGE_EPSILON)\r
-                       return false;\r
-                       \r
-       return true;\r
-}\r
-\r
-/*\r
-=================\r
-Cmd_Base\r
-=================\r
-*/\r
-void Cmd_FMBase (qboolean GetST)\r
-{\r
-       triangle_t      *ptri, *st_tri;\r
-       int                     num_st_tris;\r
-       int                     i, j, k, l;\r
-       int                     x,y,z;\r
-//     int                     time1;\r
-       char            file1[1024],file2[1024],trans_file[1024], stfile[1024], extension[256];\r
-       vec3_t          base_xyz[MAX_FM_VERTS];\r
-       FILE            *FH;\r
-       int                     pos,bit;\r
-       qboolean        NewSkin;\r
-\r
-       GetScriptToken (false);\r
-\r
-       if (g_skipmodel || g_release || g_archive)\r
-               return;\r
-\r
-       printf ("---------------------\n");\r
-       sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);\r
-       printf ("%s ", file1);\r
-\r
-       ExpandPathAndArchive (file1);\r
-\r
-       // Use the input filepath for this one.\r
-       sprintf (file1, "%s/%s", cddir, token);\r
-\r
-//     time1 = FileTime (file1);\r
-//     if (time1 == -1)\r
-//             Error ("%s doesn't exist", file1);\r
-\r
-//\r
-// load the base triangles\r
-//\r
-       if (do3ds)\r
-               Load3DSTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
-       else\r
-               LoadTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
-\r
-       if (g_ignoreTriUV)\r
-       {\r
-               for (i=0;i<fmheader.num_tris;i++)\r
-               {\r
-                       ptri[i].HasUV=0;\r
-               }\r
-       }\r
-\r
-       GetScriptToken (false);\r
-       sprintf (file2, "%s/%s", cddir, token);\r
-       sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);\r
-\r
-       ExtractFileExtension (file2, extension);\r
-       if (extension[0] == 0)\r
-       {\r
-               strcat(file2, ".pcx");\r
-       }\r
-       printf ("skin: %s\n", file2);\r
-\r
-       BaseTrueColor = LoadAnyImage (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);\r
-\r
-       NewSkin = false;\r
-       if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)\r
-       {\r
-               if (g_allow_newskin)\r
-               {\r
-                       ScaleWidth = BaseWidth;\r
-                       ScaleHeight = BaseHeight;\r
-                       NewSkin = true;\r
-               }\r
-               else\r
-               {\r
-                       Error("Invalid skin page size: (%d,%d) should be (%d,%d)",\r
-                               BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);\r
-               }\r
-       }\r
-       else if (!BaseTrueColor)\r
-       {\r
-               ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,\r
-                       ENCODED_WIDTH_Y);\r
-               ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,\r
-                       ENCODED_HEIGHT_Y);\r
-       }\r
-       else\r
-       {\r
-               Error("Texture coordinates not supported on true color image");\r
-       }\r
-\r
-       if (GetST)\r
-       {\r
-               GetScriptToken (false);\r
-\r
-               sprintf (stfile, "%s/%s.%s", cdarchive, token, trifileext);\r
-               printf ("ST: %s ", stfile);\r
-\r
-               sprintf (stfile, "%s/%s", cddir, token);\r
-\r
-               if (do3ds)\r
-                       Load3DSTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);\r
-               else\r
-                       LoadTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);\r
-\r
-               if (num_st_tris != fmheader.num_tris)\r
-               {\r
-                       Error ("num st tris mismatch: st %d / base %d", num_st_tris, fmheader.num_tris);\r
-               }\r
-\r
-               printf("   matching triangles...\n");\r
-               for(i=0;i<fmheader.num_tris;i++)\r
-               {\r
-                       k = -1;\r
-                       for(j=0;j<num_st_tris;j++)\r
-                       {\r
-                               for(x=0;x<3;x++)\r
-                               {\r
-                                       for(y=0;y<3;y++)\r
-                                       {\r
-                                               if (x == y)\r
-                                               {\r
-                                                       continue;\r
-                                               }\r
-                                               for(z=0;z<3;z++)\r
-                                               {\r
-                                                       if (z == x || z == y) \r
-                                                       {\r
-                                                               continue;\r
-                                                       }\r
-\r
-                                                       if (VectorFudgeCompare (ptri[i].verts[0], st_tri[j].verts[x]) &&\r
-                                                           VectorFudgeCompare (ptri[i].verts[1], st_tri[j].verts[y]) &&\r
-                                                               VectorFudgeCompare (ptri[i].verts[2], st_tri[j].verts[z]))\r
-                                                       {\r
-                                                               if (k == -1)\r
-                                                               {\r
-                                                                       k = j;\r
-                                                                       ptri[i].HasUV = st_tri[k].HasUV;\r
-                                                                       ptri[i].uv[0][0] = st_tri[k].uv[x][0];\r
-                                                                       ptri[i].uv[0][1] = st_tri[k].uv[x][1];\r
-                                                                       ptri[i].uv[1][0] = st_tri[k].uv[y][0];\r
-                                                                       ptri[i].uv[1][1] = st_tri[k].uv[y][1];\r
-                                                                       ptri[i].uv[2][0] = st_tri[k].uv[z][0];\r
-                                                                       ptri[i].uv[2][1] = st_tri[k].uv[z][1];\r
-                                                                       x = y = z = 999;\r
-                                                               }\r
-                                                               else if (k != j)\r
-                                                               {\r
-                                                                       printf("Duplicate triangle %d found in st file: %d and %d\n",i,k,j);\r
-                                                                       printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",\r
-                                                                               ptri[i].verts[0][0],ptri[i].verts[0][1],ptri[i].verts[0][2],\r
-                                                                               ptri[i].verts[1][0],ptri[i].verts[1][1],ptri[i].verts[1][2],\r
-                                                                               ptri[i].verts[2][0],ptri[i].verts[2][1],ptri[i].verts[2][2]);\r
-                                                                       printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",\r
-                                                                               st_tri[k].verts[0][0],st_tri[k].verts[0][1],st_tri[k].verts[0][2],\r
-                                                                               st_tri[k].verts[1][0],st_tri[k].verts[1][1],st_tri[k].verts[1][2],\r
-                                                                               st_tri[k].verts[2][0],st_tri[k].verts[2][1],st_tri[k].verts[2][2]);\r
-                                                                       printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",\r
-                                                                               st_tri[j].verts[0][0],st_tri[j].verts[0][1],st_tri[j].verts[0][2],\r
-                                                                               st_tri[j].verts[1][0],st_tri[j].verts[1][1],st_tri[j].verts[1][2],\r
-                                                                               st_tri[j].verts[2][0],st_tri[j].verts[2][1],st_tri[j].verts[2][2]);\r
-                                                               }\r
-                                                       }\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-                       if (k == -1)\r
-                       {\r
-                               printf("No matching triangle %d\n",i);\r
-                       }\r
-               }\r
-               free (st_tri);\r
-       }\r
-\r
-//\r
-// get the ST values\r
-//\r
-       if (ptri && ptri[0].HasUV)\r
-       {\r
-               if (!NewSkin)\r
-               {\r
-                       Error("Base has UVs with old style skin page\nMaybe you want to use -ignoreUV");\r
-               }\r
-               else\r
-               {\r
-                       BuildNewST (ptri, fmheader.num_tris, false);\r
-               }\r
-       }\r
-       else\r
-       {\r
-               if (NewSkin)\r
-               {\r
-                       Error("Base has new style skin without UVs");\r
-               }\r
-               else\r
-               {\r
-                       BuildST (ptri, fmheader.num_tris, false);\r
-               }\r
-       }\r
-\r
-       TransPixels = NULL;\r
-       if (!BaseTrueColor)\r
-       {\r
-               FH = fopen(trans_file,"rb");\r
-               if (FH)\r
-               {\r
-                       fclose(FH);\r
-                       Load256Image (trans_file, &TransPixels, NULL, &TransWidth, &TransHeight);\r
-                       if (TransWidth != fmheader.skinwidth || TransHeight != fmheader.skinheight)\r
-                       {\r
-                               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
-                       }\r
-               }\r
-       }\r
-\r
-//\r
-// run through all the base triangles, storing each unique vertex in the\r
-// base vertex list and setting the indirect triangles to point to the base\r
-// vertices\r
-//\r
-       for(l=0;l<fmheader.num_mesh_nodes;l++)\r
-       {\r
-               for (i=0 ; i < fmheader.num_tris ; i++)\r
-               {\r
-                       pos = i >> 3;\r
-                       bit = 1 << (i & 7 );\r
-                       if (!(pmnodes[l].tris[pos] & bit))\r
-                       {\r
-                               continue;\r
-                       }\r
-\r
-                       for (j=0 ; j<3 ; j++)\r
-                       {\r
-                               // get the xyz index\r
-                               for (k=0 ; k<fmheader.num_xyz ; k++)\r
-                               {\r
-                                       if (VectorCompare (ptri[i].verts[j], base_xyz[k]))\r
-                                       {\r
-                                               break;  // this vertex is already in the base vertex list\r
-                                       }\r
-                               }\r
-\r
-                               if (k == fmheader.num_xyz)\r
-                               { // new index\r
-                                       VectorCopy (ptri[i].verts[j], base_xyz[fmheader.num_xyz]);\r
-\r
-                                       if(pmnodes[l].clustered == true)\r
-                                       {\r
-                                               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
-                                       }\r
-\r
-                                       fmheader.num_xyz++;\r
-                               }\r
-\r
-                               pos = k >> 3;\r
-                               bit = 1 << (k & 7);\r
-                               pmnodes[l].verts[pos] |= bit;\r
-\r
-                               triangles[i].index_xyz[j] = k;\r
-\r
-                               // get the st index\r
-                               for (k=0 ; k<fmheader.num_st ; k++)\r
-                               {\r
-                                       if (triangle_st[i][j][0] == base_st[k].s\r
-                                       && triangle_st[i][j][1] == base_st[k].t)\r
-                                       {\r
-                                               break;  // this vertex is already in the base vertex list\r
-                                       }\r
-                               }\r
-\r
-                               if (k == fmheader.num_st)\r
-                               { // new index\r
-                                       base_st[fmheader.num_st].s = triangle_st[i][j][0];\r
-                                       base_st[fmheader.num_st].t = triangle_st[i][j][1];\r
-                                       fmheader.num_st++;\r
-                               }\r
-\r
-                               triangles[i].index_st[j] = k;\r
-                       }\r
-\r
-                       if (TransPixels || BaseTrueColor)\r
-                       {\r
-                               translucent[i] = CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);\r
-                       }\r
-                       else\r
-                       {\r
-                               translucent[i] = false;\r
-                       }\r
-               }\r
-       }\r
-\r
-       if (!BaseTrueColor)\r
-       {\r
-               SetPixel = true;\r
-               memset(BasePixels,0,BaseWidth*BaseHeight);\r
-               for (i=0 ; i < fmheader.num_tris ; i++)\r
-               {\r
-                       CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);\r
-               }\r
-               SetPixel = false;\r
-\r
-               skin_pixels_used = 0;\r
-               for(i=0;i<fmheader.skinheight;i++)\r
-               {\r
-                       for(j=0;j<fmheader.skinwidth;j++)\r
-                       {\r
-                               skin_pixels_used += BasePixels[(i*BaseWidth) + j];\r
-                       }\r
-               }\r
-               total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;\r
-       }\r
-       else\r
-       {\r
-               SetPixel = true;\r
-               memset(BasePixels,0,BaseWidth*BaseHeight*4);\r
-               for (i=0 ; i < fmheader.num_tris ; i++)\r
-               {\r
-                       CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);\r
-               }\r
-               SetPixel = false;\r
-\r
-               skin_pixels_used = 0;\r
-               for(i=0;i<fmheader.skinheight;i++)\r
-               {\r
-                       for(j=0;j<fmheader.skinwidth;j++)\r
-                       {\r
-                               skin_pixels_used += BasePixels[((i*BaseWidth) + j)*4];\r
-                       }\r
-               }\r
-               total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;\r
-       }\r
-\r
-       // build triangle strips / fans\r
-       BuildGlCmds ();\r
-\r
-       if (TransPixels)\r
-       {\r
-               free(TransPixels);\r
-       }\r
-       free (BasePixels);\r
-       if (BasePalette)\r
-       {\r
-               free (BasePalette);\r
-       }\r
-       free(ptri);\r
-}\r
-\r
-void Cmd_FMNodeOrder(void)\r
-{\r
-       mesh_node_t     *newnodes, *pos;\r
-       int                     i,j;\r
-\r
-       if (!pmnodes)\r
-       {\r
-               Error ("Base has not been established yet");\r
-       }\r
-\r
-       pos = newnodes = malloc(sizeof(mesh_node_t) * fmheader.num_mesh_nodes);\r
-\r
-       for(i=0;i<fmheader.num_mesh_nodes;i++)\r
-       {\r
-               GetScriptToken (false);\r
-\r
-               for(j=0;j<fmheader.num_mesh_nodes;j++)\r
-               {\r
-                       if (strcmpi(pmnodes[j].name, token) == 0)\r
-                       {\r
-                               *pos = pmnodes[j];\r
-                               pos++;\r
-                               break;\r
-                       }\r
-               }\r
-               if (j >= fmheader.num_mesh_nodes)\r
-               {\r
-                       Error("Node '%s' not in base list!\n", token);\r
-               }\r
-       }\r
-\r
-       free(pmnodes);\r
-       pmnodes = newnodes;\r
-}\r
-\r
-//===============================================================\r
-\r
-extern char    *FindFrameFile (char *frame);\r
-\r
-\r
-/*\r
-===============\r
-GrabFrame\r
-===============\r
-*/\r
-void GrabFrame (char *frame)\r
-{\r
-       triangle_t              *ptri;\r
-       int                             i, j;\r
-       fmtrivert_t             *ptrivert;\r
-       int                             num_tris;\r
-       char                    file1[1024];\r
-       fmframe_t               *fr;\r
-       int                             index_xyz;\r
-       char                    *framefile;\r
-\r
-       // the frame 'run1' will be looked for as either\r
-       // run.1 or run1.tri, so the new alias sequence save\r
-       // feature an be used\r
-       framefile = FindFrameFile (frame);\r
-\r
-       sprintf (file1, "%s/%s", cdarchive, framefile);\r
-       ExpandPathAndArchive (file1);\r
-\r
-       sprintf (file1, "%s/%s",cddir, framefile);\r
-\r
-       printf ("grabbing %s  ", file1);\r
-\r
-       if (fmheader.num_frames >= MAX_FM_FRAMES)\r
-               Error ("fmheader.num_frames >= MAX_FM_FRAMES");\r
-       fr = &g_frames[fmheader.num_frames];\r
-       fmheader.num_frames++;\r
-\r
-       strcpy (fr->name, frame);\r
-\r
-//\r
-// load the frame\r
-//\r
-       if (do3ds)\r
-               Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);\r
-       else\r
-               LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);\r
-\r
-       if (num_tris != fmheader.num_tris)\r
-               Error ("%s: number of triangles (%d) doesn't match base frame (%d)\n", file1, num_tris, fmheader.num_tris);\r
-\r
-//\r
-// allocate storage for the frame's vertices\r
-//\r
-       ptrivert = fr->v;\r
-\r
-       for (i=0 ; i<fmheader.num_xyz ; i++)\r
-       {\r
-               ptrivert[i].vnorm.numnormals = 0;\r
-               VectorClear (ptrivert[i].vnorm.normalsum);\r
-       }\r
-       ClearBounds (fr->mins, fr->maxs);\r
-\r
-//\r
-// store the frame's vertices in the same order as the base. This assumes the\r
-// triangles and vertices in this frame are in exactly the same order as in the\r
-// base\r
-//\r
-       for (i=0 ; i<num_tris ; i++)\r
-       {\r
-               vec3_t  vtemp1, vtemp2, normal;\r
-               float   ftemp;\r
-\r
-               VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
-               VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
-               CrossProduct (vtemp1, vtemp2, normal);\r
-\r
-               VectorNormalize (normal, normal);\r
-\r
-       // rotate the normal so the model faces down the positive x axis\r
-               ftemp = normal[0];\r
-               normal[0] = -normal[1];\r
-               normal[1] = ftemp;\r
-\r
-               for (j=0 ; j<3 ; j++)\r
-               {\r
-                       index_xyz = triangles[i].index_xyz[j];\r
-\r
-               // rotate the vertices so the model faces down the positive x axis\r
-               // also adjust the vertices to the desired origin\r
-                       ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +\r
-                                                                               adjust[0];\r
-                       ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +\r
-                                                                               adjust[1];\r
-                       ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +\r
-                                                                               adjust[2];\r
-\r
-                       AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);\r
-\r
-                       VectorAdd (ptrivert[index_xyz].vnorm.normalsum, normal, ptrivert[index_xyz].vnorm.normalsum);\r
-                       ptrivert[index_xyz].vnorm.numnormals++;\r
-               }\r
-       }\r
-\r
-//\r
-// calculate the vertex normals, match them to the template list, and store the\r
-// index of the best match\r
-//\r
-       for (i=0 ; i<fmheader.num_xyz ; i++)\r
-       {\r
-               int             j;\r
-               vec3_t  v;\r
-               float   maxdot;\r
-               int             maxdotindex;\r
-               int             c;\r
-\r
-               c = ptrivert[i].vnorm.numnormals;\r
-               if (!c)\r
-                       Error ("Vertex with no triangles attached");\r
-\r
-               VectorScale (ptrivert[i].vnorm.normalsum, 1.0/c, v);\r
-               VectorNormalize (v, v);\r
-\r
-               maxdot = -999999.0;\r
-               maxdotindex = -1;\r
-\r
-               for (j=0 ; j<NUMVERTEXNORMALS ; j++)\r
-               {\r
-                       float   dot;\r
-\r
-                       dot = DotProduct (v, avertexnormals[j]);\r
-                       if (dot > maxdot)\r
-                       {\r
-                               maxdot = dot;\r
-                               maxdotindex = j;\r
-                       }\r
-               }\r
-\r
-               ptrivert[i].lightnormalindex = maxdotindex;\r
-       }\r
-\r
-       free (ptri);\r
-}\r
-\r
-/*\r
-===============\r
-Cmd_Frame      \r
-===============\r
-*/\r
-void Cmd_FMFrame (void)\r
-{\r
-       while (ScriptTokenAvailable())\r
-       {\r
-               GetScriptToken (false);\r
-               if (g_skipmodel)\r
-                       continue;\r
-               if (g_release || g_archive)\r
-               {\r
-                       fmheader.num_frames = 1;        // don't skip the writeout\r
-                       continue;\r
-               }\r
-\r
-               H_printf("#define FRAME_%-16s\t%i\n", token, fmheader.num_frames);\r
-\r
-               if((g_skelModel.type != SKEL_NULL) || (g_skelModel.references != REF_NULL))\r
-               {\r
-                       GrabModelTransform(token);\r
-               }\r
-\r
-               GrabFrame (token);\r
-\r
-               if(g_skelModel.type != SKEL_NULL)\r
-               {\r
-                       GrabSkeletalFrame(token);\r
-               }\r
-\r
-               if(g_skelModel.references != REF_NULL)\r
-               {\r
-                       GrabReferencedFrame(token);\r
-               }\r
-\r
-               // need to add the up and dir points to the frame bounds here\r
-               // using AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);\r
-               // then remove fudge in determining scale on frame write out\r
-       }\r
-}\r
-\r
-/*\r
-===============\r
-Cmd_Skin\r
-\r
-Skins aren't actually stored in the file, only a reference\r
-is saved out to the header file.\r
-===============\r
-*/\r
-void Cmd_FMSkin (void)\r
-{\r
-       byte            *palette;\r
-       byte            *pixels;\r
-       int                     width, height;\r
-       byte            *cropped;\r
-       int                     y;\r
-       char            name[1024], savename[1024], transname[1024], extension[256];\r
-       miptex32_t      *qtex32;\r
-       int                     size;\r
-       FILE            *FH;\r
-       qboolean        TrueColor;\r
-\r
-       GetScriptToken (false);\r
-\r
-       if (fmheader.num_skins == MAX_FM_SKINS)\r
-               Error ("fmheader.num_skins == MAX_FM_SKINS");\r
-\r
-       if (g_skipmodel)\r
-               return;\r
-\r
-       sprintf (name, "%s/%s", cdarchive, token);\r
-       strcpy (name, ExpandPathAndArchive( name ) );\r
-//     sprintf (name, "%s/%s.lbm", cddir, token);\r
-\r
-       if (ScriptTokenAvailable())\r
-       {\r
-               GetScriptToken (false);\r
-               sprintf (g_skins[fmheader.num_skins], "!%s", token);\r
-               sprintf (savename, "%s!%s", g_outputDir, token);\r
-               sprintf (transname, "%s!%s_a.pcx", gamedir, token);\r
-       }\r
-       else\r
-       {\r
-               sprintf (g_skins[fmheader.num_skins], "%s/!%s", cdpartial, token);\r
-               sprintf (savename, "%s/!%s", g_outputDir, token);\r
-               sprintf (transname, "%s/!%s_a.pcx", cddir, token);\r
-       }\r
-\r
-       fmheader.num_skins++;\r
-\r
-       if (g_skipmodel || g_release || g_archive)\r
-               return;\r
-\r
-       // load the image\r
-       printf ("loading %s\n", name);\r
-       ExtractFileExtension (name, extension);\r
-       if (extension[0] == 0)\r
-       {\r
-               strcat(name, ".pcx");\r
-       }\r
-\r
-\r
-       TrueColor = LoadAnyImage (name, &pixels, &palette, &width, &height);\r
-//     RemapZero (pixels, palette, width, height);\r
-\r
-       // crop it to the proper size\r
-\r
-       if (!TrueColor)\r
-       {\r
-               cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight, "Cmd_FMSkin");\r
-               for (y=0 ; y<fmheader.skinheight ; y++)\r
-               {\r
-                       memcpy (cropped+y*fmheader.skinwidth,\r
-                               pixels+y*width, fmheader.skinwidth);\r
-               }\r
-\r
-               TransPixels = NULL;\r
-               FH = fopen(transname,"rb");\r
-               if (FH)\r
-               {\r
-                       fclose(FH);\r
-\r
-                       strcat(g_skins[fmheader.num_skins-1],".pcx");\r
-                       strcat(savename,".pcx");\r
-\r
-                       // save off the new image\r
-                       printf ("saving %s\n", savename);\r
-                       CreatePath (savename);\r
-                       WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);\r
-               }\r
-               else\r
-               {\r
-       #if 1\r
-               miptex_t        *qtex;\r
-                       qtex = CreateMip(cropped, fmheader.skinwidth, fmheader.skinheight, palette, &size, true);\r
-\r
-                       strcat(g_skins[fmheader.num_skins-1],".m8");\r
-                       strcat(savename,".m8");\r
-\r
-                       printf ("saving %s\n", savename);\r
-                       CreatePath (savename);\r
-                       SaveFile (savename, (byte *)qtex, size);\r
-                       free(qtex);\r
-       #else\r
-                       strcat(g_skins[fmheader.num_skins-1],".pcx");\r
-                       strcat(savename,".pcx");\r
-\r
-                       // save off the new image\r
-                       printf ("saving %s\n", savename);\r
-                       CreatePath (savename);\r
-                       WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);\r
-       #endif\r
-               }\r
-       }\r
-       else\r
-       {\r
-               cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight*4, "Cmd_FMSkin");\r
-               for (y=0 ; y<fmheader.skinheight ; y++)\r
-               {\r
-                       memcpy (cropped+((y*fmheader.skinwidth)*4), pixels+(y*width*4), fmheader.skinwidth*4);\r
-               }\r
-\r
-               qtex32 = CreateMip32((unsigned *)cropped, fmheader.skinwidth, fmheader.skinheight, &size, true);\r
-\r
-           StripExtension(g_skins[fmheader.num_skins-1]);\r
-               strcat(g_skins[fmheader.num_skins-1],".m32");\r
-           StripExtension(savename);\r
-               strcat(savename,".m32");\r
-\r
-               printf ("saving %s\n", savename);\r
-               CreatePath (savename);\r
-               SaveFile (savename, (byte *)qtex32, size);\r
-       }\r
-\r
-       free (pixels);\r
-       if (palette)\r
-       {\r
-               free (palette);\r
-       }\r
-       free (cropped);\r
-}\r
-\r
-\r
-/*\r
-===============\r
-Cmd_Cd\r
-===============\r
-*/\r
-void Cmd_FMCd (void)\r
-{\r
-       char temp[256];\r
-\r
-       FinishModel ();\r
-       ClearModel ();\r
-\r
-       GetScriptToken (false);\r
-\r
-       // this is a silly mess...\r
-       sprintf(cdpartial, "models/%s", token); \r
-       sprintf(cdarchive, "%smodels/%s", gamedir+strlen(qdir), token); \r
-       sprintf(cddir, "%s%s", gamedir, cdpartial);\r
-\r
-       // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.\r
-       sprintf(temp, "%s%s", g_outputDir, cdpartial);\r
-       strcpy(g_outputDir, temp);\r
-\r
-       // if -only was specified and this cd doesn't match,\r
-       // skip the model (you only need to match leading chars,\r
-       // so you could regrab all monsters with -only monsters)\r
-       if (!g_only[0])\r
-               return;\r
-       if (strncmp(token, g_only, strlen(g_only)))\r
-       {\r
-               g_skipmodel = true;\r
-               printf ("skipping %s\n", cdpartial);\r
-       }\r
-}\r
-\r
-\r
-/*\r
-\r
-//=======================\r
-//             NEW GEN\r
-//=======================\r
-\r
-void NewGen (char *ModelFile, char *OutputName, int width, int height)\r
-{\r
-       trigroup_t  *triangles;\r
-       triangle_t      *ptri;\r
-       triangle_t      *grouptris;\r
-       mesh_node_t     *pmnodes;\r
-\r
-       vec3_t          *vertices;\r
-       vec3_t          *uvs;\r
-       vec3_t          aveNorm, crossvect;\r
-       vec3_t          diffvect1, diffvect2;\r
-       vec3_t          v0, v1, v2;\r
-       vec3_t          n, u, v;\r
-       vec3_t          base, zaxis, yaxis;\r
-       vec3_t          uvwMin, uvwMax;\r
-       vec3_t          groupMin, groupMax;\r
-       vec3_t          uvw;\r
-       \r
-       float           *uFinal, *vFinal;\r
-       unsigned char   *newpic;\r
-\r
-       int                     finalstart = 0, finalcount = 0;\r
-       int                     xbase = 0, xwidth = 0, ywidth = 0;\r
-       int                     *todo, *done, finished;\r
-       int                     i, j, k, l; //counters\r
-       int                     groupnum, numtris, numverts, num;\r
-       int                     count;\r
-       FILE            *grpfile;\r
-       long            datasize;\r
-\r
-       for ( i = 0; i<3; i++)\r
-       {\r
-               aveNorm[i] = 0;\r
-               uvwMin[i] = 1e30f;\r
-               uvwMax[i] = -1e30f;\r
-       }\r
-\r
-       pmnodes = NULL;\r
-       ptri = NULL;\r
-       triangles = NULL;\r
-       \r
-       zaxis[0] = 0;\r
-       zaxis[1] = 0;\r
-       zaxis[2] = 1;\r
-\r
-       yaxis[0] = 0;\r
-       yaxis[1] = 1;\r
-       yaxis[2] = 0;\r
-\r
-       LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
-\r
-       todo = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");\r
-       done = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");\r
-       triangles = (trigroup_t*)SafeMalloc(fmheader.num_tris*sizeof(trigroup_t), "NewGen");\r
-       \r
-       for ( i=0; i < fmheader.num_tris; i++)\r
-       {\r
-               todo[i] = false;\r
-               done[i] = false;\r
-               triangles[i].triangle = ptri[i];\r
-               triangles[i].group = 0;\r
-       }\r
-\r
-       groupnum = 0;\r
-\r
-//  transitive closure algorithm follows\r
-//  put all triangles who transitively share vertices into separate groups\r
-\r
-       while (1)\r
-       {\r
-               for ( i = 0; i < fmheader.num_tris; i++)\r
-               {\r
-                       if (!done[i])\r
-                       {\r
-                               break;\r
-                       }\r
-               }\r
-               if ( i == fmheader.num_tris)\r
-               {\r
-                       break;\r
-               }\r
-               finished = false;\r
-               todo[i] = true;\r
-               while (!finished)\r
-               {\r
-                       finished = true;\r
-                       for ( i = 0; i < fmheader.num_tris; i++)\r
-                       {\r
-                               if (todo[i])\r
-                               {\r
-                                       done[i] = true;\r
-                                       triangles[i].group = groupnum;\r
-                                       todo[i] = false;\r
-                                       for ( j = 0; j < fmheader.num_tris; j++)\r
-                                       {\r
-                                               if ((!done[j]) && (ShareVertex(triangles[i],triangles[j])))\r
-                                               {\r
-                                                       todo[j] = true;\r
-                                                       finished = false;\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               groupnum++;\r
-       }\r
-               uFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");\r
-               vFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");\r
-\r
-       grpfile = fopen("grpdebug.txt","w");\r
-\r
-       \r
-       for (i = 0; i < groupnum; i++)\r
-       {\r
-\r
-               fprintf(grpfile,"Group Number: %d\n", i);\r
-               \r
-               numtris = GetNumTris(triangles, i); // number of triangles in group i\r
-               numverts = numtris * 3;\r
-\r
-               fprintf(grpfile,"%d triangles.\n", numtris);\r
-\r
-               vertices = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");\r
-               uvs = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");\r
-               grouptris = (triangle_t*)SafeMalloc(numtris*sizeof(triangle_t), "NewGen");\r
-               \r
-               for (count = 0; count < fmheader.num_tris; count++)\r
-               {\r
-                       if (triangles[count].group == i)\r
-                       {\r
-                               fprintf(grpfile,"Triangle %d\n", count);\r
-                       }\r
-               }\r
-               fprintf(grpfile,"\n");\r
-\r
-               \r
-               \r
-               \r
-               GetOneGroup(triangles, i, grouptris);           \r
-               \r
-               num = 0;\r
-               for (j = 0; j < numtris; j++)\r
-               {\r
-                       VectorCopy(grouptris[j].verts[0], v0);\r
-                       VectorCopy(grouptris[j].verts[1], v1);\r
-                       VectorCopy(grouptris[j].verts[2], v2);\r
-                       VectorSubtract(v1, v0, diffvect1);\r
-                       VectorSubtract(v2, v1, diffvect2);\r
-                       CrossProduct( diffvect1, diffvect2, crossvect);\r
-                       VectorAdd(aveNorm, crossvect, aveNorm); \r
-                       VectorCopy(v0,vertices[num]);\r
-                       num++;                                  //  FIXME\r
-                       VectorCopy(v1,vertices[num]);\r
-                       num++;                                  //  add routine to add only verts that\r
-                       VectorCopy(v2,vertices[num]);\r
-                       num++;                                  // have not already been added\r
-               }\r
-\r
-               assert (num >= 3);\r
-// figure out the best plane projections\r
-               DOsvdPlane ((float*)vertices, num, (float *)&n, (float *)&base);\r
-               \r
-               if (DotProduct(aveNorm,n) < 0.0f)\r
-               {\r
-                       VectorScale(n, -1.0f, n);\r
-               }\r
-               VectorNormalize(n,n);\r
-               if (fabs(n[2]) < .57)\r
-               {\r
-                       CrossProduct( zaxis, n, crossvect);\r
-                       VectorCopy(crossvect, u);\r
-               }\r
-               else\r
-               {\r
-                       CrossProduct( yaxis, n, crossvect);\r
-                       VectorCopy(crossvect, u);\r
-               }\r
-               VectorNormalize(u,u);\r
-               CrossProduct( n, u, crossvect);\r
-               VectorCopy(crossvect, v);\r
-               VectorNormalize(v,v);\r
-\r
-               num = 0;\r
-\r
-               for ( j = 0; j < 3; j++)\r
-               {\r
-                       groupMin[j] = 1e30f;\r
-                       groupMax[j] = -1e30f;\r
-               }\r
-               \r
-               for ( j = 0; j < numtris; j++)\r
-               {\r
-                       for ( k = 0; k < 3; k++)\r
-                       {\r
-                               VectorCopy(grouptris[j].verts[k],v0);\r
-                               VectorSubtract(v0, base, v0);\r
-                               uvw[0] = DotProduct(v0, u);\r
-                               uvw[1] = DotProduct(v0, v);\r
-                               uvw[2] = DotProduct(v0, n);\r
-                               VectorCopy(uvw,uvs[num]);\r
-                               num++;\r
-                               for ( l = 0; l < 3; l++)\r
-                               {\r
-                                       if (uvw[l] < groupMin[l])\r
-                                       {\r
-                                               groupMin[l] = uvw[l];\r
-                                       }\r
-                                       if (uvw[l] > groupMax[l])\r
-                                       {\r
-                                               groupMax[l] = uvw[l];\r
-                                       }\r
-                               }\r
-                       }\r
-               }\r
-               \r
-               xwidth = ceil(0 - groupMin[0]) + 2; // move right of origin and avoid overlap\r
-               ywidth = ceil(0 - groupMin[1]) + 2; // move "above" origin\r
-               \r
-               for ( j=0; j < numverts; j++)\r
-               {\r
-                       uFinal[finalcount] = uvs[j][0] + xwidth + xbase;                        \r
-                       vFinal[finalcount] = uvs[j][1] + ywidth;\r
-                       if (uFinal[finalcount] < uvwMin[0])\r
-                       {\r
-                               uvwMin[0] = uFinal[finalcount];\r
-                       }\r
-                       if (uFinal[finalcount] > uvwMax[0])\r
-                       {\r
-                               uvwMax[0] = uFinal[finalcount];\r
-                       }\r
-                       if (vFinal[finalcount] < uvwMin[1])\r
-                       {\r
-                               uvwMin[1] = vFinal[finalcount];\r
-                       }\r
-                       if (vFinal[finalcount] > uvwMax[1])\r
-                       {\r
-                               uvwMax[1] = vFinal[finalcount];\r
-                       }\r
-                       finalcount++;\r
-               }\r
-\r
-               fprintf(grpfile,"svdPlaned Group min: ( %f , %f )\n",groupMin[0] + xwidth + xbase, groupMin[1] + ywidth);\r
-               fprintf(grpfile,"svdPlaned Group max: ( %f , %f )\n",groupMax[0] + xwidth + xbase, groupMax[1] + ywidth);\r
-               \r
-               finalcount = finalstart;\r
-\r
-               for ( count = 0; count < numverts; count++)\r
-               {\r
-                       fprintf(grpfile,"Vertex %d: ( %f , %f , %f )\n",count,vertices[count][0],vertices[count][1],vertices[count][2]);\r
-                       fprintf(grpfile,"svdPlaned: ( %f , %f )\n",uFinal[finalcount],vFinal[finalcount++]);\r
-               }\r
-               \r
-               finalstart = finalcount;\r
-\r
-               fprintf(grpfile,"\n");\r
-               \r
-               free(vertices);\r
-               free(uvs);\r
-               free(grouptris);\r
-\r
-               xbase += ceil(groupMax[0] - groupMin[0]) + 2;           \r
-\r
-       }       \r
-\r
-       fprintf(grpfile,"Global Min ( %f , %f )\n",uvwMin[0],uvwMin[1]);\r
-       fprintf(grpfile,"Global Max ( %f , %f )\n",uvwMax[0],uvwMax[1]);\r
-\r
-\r
-       ScaleTris(uvwMin, uvwMax, width, height, uFinal, vFinal, finalcount);\r
-\r
-       for (k = 0; k < finalcount; k++)\r
-       {\r
-               fprintf(grpfile, "scaled vertex %d: ( %f , %f )\n",k,uFinal[k],vFinal[k]);\r
-       }\r
-       \r
-       //  i've got the array of vertices in uFinal and vFinal.  Now I need to write them and draw lines\r
-\r
-       datasize = width * height*sizeof(unsigned char);\r
-       newpic = (unsigned char*)SafeMalloc(datasize, "NewGen");\r
-       memset(newpic,0,datasize);\r
-       memset(pic_palette,0,sizeof(pic_palette));\r
-       pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;\r
-\r
-       k = 0;\r
-       while (k < finalcount)\r
-       {\r
-               NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);\r
-               k++;\r
-               NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);\r
-               k++;\r
-               NewDrawLine(uFinal[k], vFinal[k], uFinal[k-2], vFinal[k-2], newpic, width, height);\r
-               k++;\r
-               fprintf(grpfile, "output tri with verts %d, %d, %d", k-2, k-1, k);\r
-       }\r
-\r
-       WritePCXfile (OutputName, newpic, width, height, pic_palette);\r
-\r
-       fclose(grpfile);\r
-\r
-       free(todo);\r
-       free(done);\r
-       free(triangles);\r
-       free(newpic);   \r
-       return;\r
-}\r
-void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height)\r
-{\r
-       long dx, dy;\r
-       long adx, ady;\r
-       long count;\r
-       float xfrac, yfrac, xstep, ystep;\r
-       unsigned long sx, sy;\r
-       float u, v;\r
-\r
-       dx = x2 - x1;\r
-       dy = y2 - y1;\r
-       adx = abs(dx);\r
-       ady = abs(dy);\r
-\r
-       count = adx > ady ? adx : ady;\r
-       count++;\r
-\r
-       if(count > 300)\r
-       {\r
-               printf("Bad count\n");\r
-               return; // don't ever hang up on bad data\r
-       }\r
-               \r
-       xfrac = x1;\r
-       yfrac = y1;\r
-       \r
-       xstep = (float)dx/count;\r
-       ystep = (float)dy/count;\r
-\r
-       switch(LineType)\r
-       {\r
-               case LINE_NORMAL:\r
-                       do\r
-                       {\r
-                               if(xfrac < width && yfrac < height)\r
-                               {\r
-                                       picture[(long)yfrac*width+(long)xfrac] = LineColor;\r
-                               }\r
-                               xfrac += xstep;\r
-                               yfrac += ystep;\r
-                               count--;\r
-                       } while (count > 0);\r
-                       break;\r
-               case LINE_FAT:\r
-                       do\r
-                       {\r
-                               for (u=-0.1 ; u<=0.9 ; u+=0.999)\r
-                               {\r
-                                       for (v=-0.1 ; v<=0.9 ; v+=0.999)\r
-                                       {\r
-                                               sx = xfrac+u;\r
-                                               sy = yfrac+v;\r
-                                               if(sx < width && sy < height)\r
-                                               {\r
-                                                       picture[sy*width+sx] = LineColor;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               xfrac += xstep;\r
-                               yfrac += ystep;\r
-                               count--;\r
-                       } while (count > 0);\r
-                       break;\r
-               case LINE_DOTTED:\r
-                       do\r
-                       {\r
-                               if(count&1 && xfrac < width &&\r
-                                       yfrac < height)\r
-                               {\r
-                                       picture[(long)yfrac*width+(long)xfrac] = LineColor;\r
-                               }\r
-                               xfrac += xstep;\r
-                               yfrac += ystep;\r
-                               count--;\r
-                       } while (count > 0);\r
-                       break;\r
-               default:\r
-                       Error("Unknown <linetype> %d.\n", LineType);\r
-       }\r
-}\r
-*/\r
-void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts)\r
-{\r
-\r
-       int             i;\r
-       float   hscale, vscale;\r
-       float   scale;\r
-\r
-       hscale = max[0];\r
-       vscale = max[1];\r
-\r
-       hscale = (Width-2) / max[0];\r
-       vscale = (Height-2) / max[1];\r
-\r
-       scale = hscale;\r
-       if (scale > vscale)\r
-       {\r
-               scale = vscale;\r
-       }\r
-       for ( i = 0; i<verts; i++)\r
-       {\r
-               u[i] *= scale;\r
-               v[i] *= scale;\r
-       }\r
-       return;\r
-}\r
-\r
-\r
-void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles)\r
-{\r
-       int     i;\r
-       int j;\r
-\r
-       j = 0;\r
-       for (i = 0; i < fmheader.num_tris; i++)\r
-       {\r
-               if (tris[i].group == grp)\r
-               {\r
-                       triangles[j++] = tris[i].triangle;\r
-               }\r
-       }\r
-       return;\r
-}\r
-\r
-\r
-int GetNumTris( trigroup_t *tris, int grp)\r
-{\r
-       int i;\r
-       int verts;\r
-\r
-       verts = 0;\r
-       for (i = 0; i < fmheader.num_tris; i++)\r
-       {\r
-               if (tris[i].group == grp)\r
-               {\r
-                       verts++;\r
-               }\r
-       }\r
-       return verts;\r
-}\r
-\r
-\r
-int ShareVertex( trigroup_t trione, trigroup_t tritwo)\r
-{\r
-       int     i;\r
-       int     j;\r
-\r
-       i = 1;\r
-       j = 1;\r
-       for ( i = 0; i < 3; i++)\r
-       {\r
-               for ( j = 0; j < 3; j++)\r
-               {\r
-                       if (DistBetween(trione.triangle.verts[i],tritwo.triangle.verts[j]) < TRIVERT_DIST)\r
-                       {\r
-                               return true; \r
-                       }\r
-               }\r
-       }\r
-       return false;\r
-}\r
-\r
-\r
-float DistBetween(vec3_t point1, vec3_t point2)\r
-{\r
-       float   dist;\r
-\r
-       dist = (point1[0] - point2[0]);\r
-       dist *= dist;\r
-       dist += (point1[1] - point2[1])*(point1[1]-point2[1]);\r
-       dist += (point1[2] - point2[2])*(point1[2]-point2[2]);\r
-       dist = sqrt(dist);\r
-       return dist;\r
-}\r
-\r
-\r
-void GenSkin(char *ModelFile, char *OutputName, int Width, int Height)\r
-{\r
-       triangle_t      *ptri;\r
-       mesh_node_t *pmnodes;\r
-       int                     i;\r
-\r
-       pmnodes = NULL;\r
-       ptri = NULL;\r
-\r
-       LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);\r
-       if (g_ignoreTriUV)\r
-       {\r
-               for (i=0;i<fmheader.num_tris;i++)\r
-               {\r
-                       ptri[i].HasUV=0;\r
-               }\r
-       }\r
-\r
-       memset(pic,0,sizeof(pic));\r
-       memset(pic_palette,0,sizeof(pic_palette));\r
-       pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;\r
-\r
-       ScaleWidth = Width;\r
-       ScaleHeight = Height;\r
-\r
-       BuildST (ptri, fmheader.num_tris, true);\r
-\r
-       WritePCXfile (OutputName, pic, SKINPAGE_WIDTH, SKINPAGE_HEIGHT, pic_palette);\r
-\r
-       printf("Gen Skin Stats:\n");\r
-       printf("   Input Base: %s\n",ModelFile);\r
-       printf("   Input Dimensions: %d,%d\n",Width,Height);\r
-       printf("\n");\r
-       printf("   Output File: %s\n",OutputName);\r
-       printf("   Output Dimensions: %d,%d\n",ScaleWidth,ScaleHeight);\r
-\r
-       if (fmheader.num_mesh_nodes)\r
-       {\r
-               printf("\nNodes:\n");\r
-               for(i=0;i<fmheader.num_mesh_nodes;i++)\r
-               {\r
-                       printf("   %s\n",pmnodes[i].name);\r
-               }\r
-       }\r
-\r
-       free(ptri);\r
-       free(pmnodes);\r
-}\r
-\r
-\r
-void Cmd_FMBeginGroup (void)\r
-{\r
-       GetScriptToken (false);\r
-\r
-       g_no_opimizations = false;\r
-\r
-       groups[num_groups].start_frame = fmheader.num_frames;\r
-       groups[num_groups].num_frames = 0;\r
-\r
-       groups[num_groups].degrees = atol(token);\r
-       if (groups[num_groups].degrees < 1 || groups[num_groups].degrees > 32)\r
-       {\r
-               Error ("Degrees of freedom out of range: %d",groups[num_groups].degrees);\r
-       }\r
-}\r
-\r
-void Cmd_FMEndGroup (void)\r
-{\r
-       groups[num_groups].num_frames = fmheader.num_frames - groups[num_groups].start_frame;\r
-\r
-       if(num_groups < MAX_GROUPS - 1)\r
-       {\r
-               num_groups++;\r
-       }\r
-       else\r
-       {\r
-               Error("Number of compression groups exceded: %i\n", MAX_GROUPS);\r
-       }\r
-}\r
-\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "qd_fmodel.h"
+#include "animcomp.h"
+#include "qd_skeletons.h"
+#include "skeletons.h"
+#include "qdata.h"
+#include "flex.h"
+#include "reference.h"
+
+#include <assert.h>
+
+/*
+========================================================================
+
+.FM triangle flexible model file format
+
+========================================================================
+*/
+
+//=================================================================
+
+#define NUMVERTEXNORMALS       162
+
+extern float   avertexnormals[NUMVERTEXNORMALS][3];
+
+#define MAX_GROUPS     128
+
+typedef struct
+{
+       triangle_t      triangle;
+       int             group;
+} trigroup_t;
+
+#define TRIVERT_DIST   .1
+
+typedef struct
+{
+       int                     start_frame;
+       int             num_frames;
+       int                     degrees;
+       char *mat;
+       char *ccomp;
+       char *cbase;
+       float *cscale;
+       float *coffset;
+       float trans[3];
+       float scale[3];
+       float bmin[3];
+       float bmax[3];
+} fmgroup_t;
+
+//================================================================
+
+// Initial
+fmheader_t     fmheader;
+
+// Skin
+extern char                    g_skins[MAX_FM_SKINS][64];
+
+// ST Coord
+extern fmstvert_t      base_st[MAX_FM_VERTS];
+
+// Triangles
+extern fmtriangle_t    triangles[MAX_FM_TRIANGLES];
+
+// Frames
+fmframe_t      g_frames[MAX_FM_FRAMES];
+//fmframe_t    *g_FMframes;
+
+// GL Commands
+extern int                     commands[16384];
+extern int                     numcommands;
+
+
+//
+// varibles set by commands
+//
+extern float           scale_up;                                               // set by $scale
+extern vec3_t          adjust;                                                 // set by $origin
+extern int                     g_fixedwidth, g_fixedheight;    // set by $skinsize
+extern char                    modelname[64];                                  // set by $modelname
+
+
+extern         char            *g_outputDir;
+
+
+// Mesh Nodes
+mesh_node_t    *pmnodes = NULL;
+fmmeshnode_t   mesh_nodes[MAX_FM_MESH_NODES]; 
+
+fmgroup_t      groups[MAX_GROUPS];
+int                    num_groups;
+int    frame_to_group[MAX_FM_FRAMES];
+
+//
+// variables set by command line arguments
+//
+qboolean       g_no_opimizations = false;
+
+
+//
+// base frame info
+//
+static int                     triangle_st[MAX_FM_TRIANGLES][3][2];
+
+
+// number of gl vertices
+extern int                     numglverts;
+// indicates if a triangle has already been used in a glcmd
+extern int                     used[MAX_FM_TRIANGLES];
+// indicates if a triangle has translucency in it or not
+static qboolean                translucent[MAX_FM_TRIANGLES];
+
+// main output file handle
+extern FILE                    *headerouthandle;
+// output sizes of buildst()
+static int                     skin_width, skin_height;
+
+
+// statistics
+static int                     total_skin_pixels;
+static int                     skin_pixels_used;
+
+int ShareVertex( trigroup_t trione, trigroup_t tritwo);
+float DistBetween(vec3_t point1, vec3_t point2);
+int GetNumTris( trigroup_t *tris, int group);
+void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles);
+void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts);
+void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height);
+
+#ifndef _WIN32
+
+void strupr(char *string)
+{
+       int i;
+
+       for (i=0 ; i<strlen(string); i++)
+               toupper(string[i]);
+
+       return;
+}
+
+#endif
+//==============================================================
+
+/*
+===============
+ClearModel
+===============
+*/
+static void ClearModel (void)
+{
+       memset (&fmheader, 0, sizeof(fmheader));
+
+       modelname[0] = 0;
+       scale_up = 1.0; 
+       VectorCopy (vec3_origin, adjust);
+       g_fixedwidth = g_fixedheight = 0;
+       g_skipmodel = false;
+       num_groups = 0;
+
+       if (pmnodes)
+       {
+               free(pmnodes);
+               pmnodes = NULL;
+       }
+
+       ClearSkeletalModel();
+}
+
+
+extern void H_printf(char *fmt, ...);
+
+
+void WriteHeader(FILE *FH, char *Ident, int Version, int Size, void *Data)
+{
+       header_t        header;
+       static long     pos = -1;
+       long            CurrentPos;
+
+       if (Size == 0)
+       {       // Don't write out empty packets
+               return;
+       }
+
+       if (pos != -1)
+       {
+               CurrentPos = ftell(FH);
+               Size = CurrentPos - pos + sizeof(header_t);
+               fseek(FH, pos, SEEK_SET);
+               pos = -2;
+       }
+       else if (Size == -1)
+       {
+               pos = ftell(FH);
+       }
+
+       memset(&header,0,sizeof(header));
+       strcpy(header.ident,Ident);
+       header.version = Version;
+       header.size = Size;
+
+       SafeWrite (FH, &header, sizeof(header));
+
+       if (Data)
+       {
+               SafeWrite (FH, Data, Size);
+       }
+
+       if (pos == -2)
+       {
+               pos = -1;
+               fseek(FH, 0, SEEK_END);
+       }
+}
+
+/*
+============
+WriteModelFile
+============
+*/
+static void WriteModelFile (FILE *modelouthandle)
+{
+       int                             i;
+       int                             j, k;
+       fmframe_t               *in;
+       fmaliasframe_t  *out;
+       byte                    buffer[MAX_FM_VERTS*4+128];
+       float                   v;
+       int                             c_on, c_off;
+       IntListNode_t   *current, *toFree;
+       qboolean framesWritten = false;
+       size_t temp ,size = 0;
+
+       // probably should do this dynamically one of these days
+       struct
+       {
+               float           scale[3];       // multiply byte verts by this
+               float           translate[3];   // then add this
+       } outFrames[MAX_FM_FRAMES];
+
+#define DATA_SIZE 0x60000              // 384K had better be enough, particularly for the reference points
+       byte data[DATA_SIZE];
+       byte data2[DATA_SIZE];
+
+       fmheader.num_glcmds = numcommands;
+       fmheader.framesize = (int)&((fmaliasframe_t *)0)->verts[fmheader.num_xyz];
+
+       WriteHeader(modelouthandle, FM_HEADER_NAME, FM_HEADER_VER, sizeof(fmheader), &fmheader);
+
+       //
+       // write out the skin names
+       //
+
+       WriteHeader(modelouthandle, FM_SKIN_NAME, FM_SKIN_VER, fmheader.num_skins * MAX_FM_SKINNAME, g_skins);
+
+       //
+       // write out the texture coordinates
+       //
+       c_on = c_off = 0;
+       for (i=0 ; i<fmheader.num_st ; i++)
+       {
+               base_st[i].s = LittleShort (base_st[i].s);
+               base_st[i].t = LittleShort (base_st[i].t);
+       }
+
+       WriteHeader(modelouthandle, FM_ST_NAME, FM_ST_VER, fmheader.num_st * sizeof(base_st[0]), base_st);
+
+       //
+       // write out the triangles
+       //
+       WriteHeader(modelouthandle, FM_TRI_NAME, FM_TRI_VER, fmheader.num_tris * sizeof(fmtriangle_t), NULL);
+
+       for (i=0 ; i<fmheader.num_tris ; i++)
+       {
+               int                             j;
+               fmtriangle_t    tri;
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       tri.index_xyz[j] = LittleShort (triangles[i].index_xyz[j]);
+                       tri.index_st[j] = LittleShort (triangles[i].index_st[j]);
+               }
+
+               SafeWrite (modelouthandle, &tri, sizeof(tri));
+       }
+
+       if (!num_groups)
+       {
+               //
+               // write out the frames
+               //
+               WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, fmheader.num_frames * fmheader.framesize, NULL);
+       //      WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
+
+               for (i=0 ; i<fmheader.num_frames ; i++)
+               {
+                       in = &g_frames[i];
+                       out = (fmaliasframe_t *)buffer;
+
+                       strcpy (out->name, in->name);
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               out->scale[j] = (in->maxs[j] - in->mins[j])/255;
+                               out->translate[j] = in->mins[j];
+
+                               outFrames[i].scale[j] = out->scale[j];
+                               outFrames[i].translate[j] = out->translate[j];
+                       }
+
+                       for (j=0 ; j<fmheader.num_xyz ; j++)
+                       {
+                       // all of these are byte values, so no need to deal with endianness
+                               out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
+
+                               for (k=0 ; k<3 ; k++)
+                               {
+                               // scale to byte values & min/max check
+                                       v = Q_rint ( (in->v[j].v[k] - out->translate[k]) / out->scale[k] );
+
+                               // clamp, so rounding doesn't wrap from 255.6 to 0
+                                       if (v > 255.0)
+                                               v = 255.0;
+                                       if (v < 0)
+                                               v = 0;
+                                       out->verts[j].v[k] = v;
+                               }
+                       }
+
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               out->scale[j] = LittleFloat (out->scale[j]);
+                               out->translate[j] = LittleFloat (out->translate[j]);
+                       }
+
+                       SafeWrite (modelouthandle, out, fmheader.framesize);
+               }
+
+               // Go back and finish the header
+       //      WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
+       }
+       else
+       {
+               WriteHeader(modelouthandle, FM_SHORT_FRAME_NAME, FM_SHORT_FRAME_VER,FRAME_NAME_LEN*fmheader.num_frames, NULL);
+               for (i=0 ; i<fmheader.num_frames ; i++)
+               {
+                       in = &g_frames[i];
+                       SafeWrite (modelouthandle,in->name,FRAME_NAME_LEN);
+               }
+               WriteHeader(modelouthandle, FM_NORMAL_NAME, FM_NORMAL_VER,fmheader.num_xyz, NULL);
+               in = &g_frames[0];
+               for (j=0 ; j<fmheader.num_xyz ; j++)
+                       SafeWrite (modelouthandle,&in->v[j].lightnormalindex,1);
+       }
+
+       //
+       // write out glcmds
+       //
+       WriteHeader(modelouthandle, FM_GLCMDS_NAME, FM_GLCMDS_VER, numcommands*4, commands);
+
+       //
+       // write out mesh nodes
+       //
+       for(i=0;i<fmheader.num_mesh_nodes;i++)
+       {
+               memcpy(mesh_nodes[i].tris, pmnodes[i].tris, sizeof(mesh_nodes[i].tris));
+               memcpy(mesh_nodes[i].verts, pmnodes[i].verts, sizeof(mesh_nodes[i].verts));
+               mesh_nodes[i].start_glcmds = LittleShort((short)pmnodes[i].start_glcmds);
+               mesh_nodes[i].num_glcmds = LittleShort((short)pmnodes[i].num_glcmds);
+       }
+
+       WriteHeader(modelouthandle, FM_MESH_NAME, FM_MESH_VER, sizeof(fmmeshnode_t) * fmheader.num_mesh_nodes, mesh_nodes);
+
+       if (num_groups)
+       {
+
+/*
+typedef struct
+{
+       int                     start_frame;
+       int                     num_frames;
+       int                     degrees;
+       char *mat; fmheader.num_xyz*3*g->degrees*sizeof(char)
+       char *ccomp; g->num_frames*g->degrees*sizeof(char)
+       char *cbase; fmheader.num_xyz*3*sizeof(unsigned char)
+       float *cscale; g->degrees*sizeof(float)
+       float *coffset; g->degrees*sizeof(float)
+       float trans[3]; 3*sizeof(float)
+       float scale[3]; 3*sizeof(float)
+} fmgroup_t;
+*/
+               int tmp,k;
+               fmgroup_t *g;
+               size=sizeof(int)+fmheader.num_frames*sizeof(int);
+               for (k=0;k<num_groups;k++)
+               {
+                       g=&groups[k];
+                       size+=sizeof(int)*3;
+                       size+=fmheader.num_xyz*3*g->degrees*sizeof(char);
+                       size+=g->num_frames*g->degrees*sizeof(char);
+                       size+=fmheader.num_xyz*3*sizeof(unsigned char);
+                       size+=g->degrees*sizeof(float);
+                       size+=g->degrees*sizeof(float);
+                       size+=12*sizeof(float);
+               }
+               WriteHeader(modelouthandle, FM_COMP_NAME, FM_COMP_VER,size, NULL);
+               SafeWrite (modelouthandle,&num_groups,sizeof(int));
+               SafeWrite (modelouthandle,frame_to_group,sizeof(int)*fmheader.num_frames);
+
+               for (k=0;k<num_groups;k++)
+               {
+                       g=&groups[k];
+                       tmp=LittleLong(g->start_frame);
+                       SafeWrite (modelouthandle,&tmp,sizeof(int));
+                       tmp=LittleLong(g->num_frames);
+                       SafeWrite (modelouthandle,&tmp,sizeof(int));
+                       tmp=LittleLong(g->degrees);
+                       SafeWrite (modelouthandle,&tmp,sizeof(int));
+
+                       SafeWrite (modelouthandle,g->mat,fmheader.num_xyz*3*g->degrees*sizeof(char));
+                       SafeWrite (modelouthandle,g->ccomp,g->num_frames*g->degrees*sizeof(char));
+                       SafeWrite (modelouthandle,g->cbase,fmheader.num_xyz*3*sizeof(unsigned char));
+                       SafeWrite (modelouthandle,g->cscale,g->degrees*sizeof(float));
+                       SafeWrite (modelouthandle,g->coffset,g->degrees*sizeof(float));
+                       SafeWrite (modelouthandle,g->trans,3*sizeof(float));
+                       SafeWrite (modelouthandle,g->scale,3*sizeof(float));
+                       SafeWrite (modelouthandle,g->bmin,3*sizeof(float));
+                       SafeWrite (modelouthandle,g->bmax,3*sizeof(float));
+                       free(g->mat);
+                       free(g->ccomp);
+                       free(g->cbase);
+                       free(g->cscale);
+                       free(g->coffset);
+               }
+       }
+
+       // write the skeletal info
+       if(g_skelModel.type != SKEL_NULL)
+       {
+               size = 0;
+
+               temp = sizeof(int);             // change this to a byte
+               memcpy(data + size, &g_skelModel.type, temp);
+               size += temp;
+
+               // number of joints
+               temp = sizeof(int);             // change this to a byte
+               memcpy(data + size, &numJointsInSkeleton[g_skelModel.type], temp);
+               size += temp;
+
+               // number of verts in each joint cluster
+               temp = sizeof(int)*numJointsInSkeleton[g_skelModel.type]; // change this to shorts
+               memcpy(data + size, &g_skelModel.new_num_verts[1], temp);
+               size += temp;
+
+               // cluster verts
+               for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)
+               {
+                       current = g_skelModel.vertLists[i];
+                       while(current)
+                       {
+                               temp = sizeof(int);     // change this to a short
+                               memcpy(data + size, &current->data, temp);
+                               size += temp;
+                               toFree = current;
+                               current = current->next;
+                               free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
+                       }
+               }
+
+               if(!num_groups) // joints are stored with regular verts for compressed models
+               {
+                       framesWritten = true;
+
+                       temp = sizeof(int);     // change this to a byte
+                       memcpy(data + size, &framesWritten, temp);
+                       size += temp;
+
+                       for (i = 0; i < fmheader.num_frames; ++i)
+                       {
+                               in = &g_frames[i];
+
+                               for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)
+                               {
+                                       for (k=0 ; k<3 ; k++)
+                                       {
+                                               // scale to byte values & min/max check
+                                               v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+                                               // write out origin as a float since they arn't clamped
+                                               temp = sizeof(float);   // change this to a short
+                                               assert(size+temp < DATA_SIZE);
+                                               memcpy(data + size, &v, temp);
+                                               size += temp;
+                                       }
+
+                                       for (k=0 ; k<3 ; k++)
+                                       {
+                                               v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+                                               // write out origin as a float since they arn't clamped
+                                               temp = sizeof(float);   // change this to a short
+                                               assert(size+temp < DATA_SIZE);
+                                               memcpy(data + size, &v, temp);
+                                               size += temp;
+                                       }
+
+                                       for (k=0 ; k<3 ; k++)
+                                       {
+                                               v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+                                               // write out origin as a float since they arn't clamped
+                                               temp = sizeof(float);   // change this to a short
+                                               assert(size+temp < DATA_SIZE);
+                                               memcpy(data + size, &v, temp);
+                                               size += temp;
+                                       }
+                               }
+                       }
+
+               }
+               else
+               {
+                       temp = sizeof(int);     // change this to a byte
+                       memcpy(data + size, &framesWritten, temp);
+                       size += temp;
+               }
+
+               WriteHeader(modelouthandle, FM_SKELETON_NAME, FM_SKELETON_VER, size, data);
+       }
+
+       if(g_skelModel.references != REF_NULL)
+       {
+               int refnum;
+
+               size = 0;
+               if (RefPointNum <= 0)
+               {       // Hard-coded labels
+                       refnum = numReferences[g_skelModel.references];
+               }
+               else
+               {       // Labels indicated in QDT
+                       refnum = RefPointNum;
+               }
+
+               temp = sizeof(int);     // change this to a byte
+               memcpy(data2 + size, &g_skelModel.references, temp);
+               size += temp;
+
+               if(!num_groups)
+               {
+                       framesWritten = true;
+
+                       temp = sizeof(int);     // change this to a byte
+                       memcpy(data2 + size, &framesWritten, temp);
+                       size += temp;
+
+                       for (i = 0; i < fmheader.num_frames; ++i)
+                       {
+                               in = &g_frames[i];
+
+                               for (j = 0 ; j < refnum; ++j)
+                               {
+                                       for (k=0 ; k<3 ; k++)
+                                       {
+                                               // scale to byte values & min/max check
+                                               v = Q_rint ( (in->references[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+                                               // write out origin as a float since they arn't clamped
+                                               temp = sizeof(float);   // change this to a short
+                                               assert(size+temp < DATA_SIZE);
+                                               memcpy(data2 + size, &v, temp);
+                                               size += temp;
+                                       }
+
+                                       for (k=0 ; k<3 ; k++)
+                                       {
+                                               v = Q_rint ( (in->references[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+                                               // write out origin as a float since they arn't clamped
+                                               temp = sizeof(float);   // change this to a short
+                                               assert(size+temp < DATA_SIZE);
+                                               memcpy(data2 + size, &v, temp);
+                                               size += temp;
+                                       }
+
+                                       for (k=0 ; k<3 ; k++)
+                                       {
+                                               v = Q_rint ( (in->references[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+                                               // write out origin as a float since they arn't clamped
+                                               temp = sizeof(float);   // change this to a short
+                                               assert(size+temp < DATA_SIZE);
+                                               memcpy(data2 + size, &v, temp);
+                                               size += temp;
+                                       }
+                               }
+                       }
+               }
+               else    // FINISH ME: references need to be stored with regular verts for compressed models
+               {
+                       framesWritten = false;
+
+                       temp = sizeof(int);     // change this to a byte
+                       memcpy(data2 + size, &framesWritten, temp);
+                       size += temp;
+               }
+
+               WriteHeader(modelouthandle, FM_REFERENCES_NAME, FM_REFERENCES_VER, size, data2);
+       }
+}
+
+static void CompressFrames()
+{
+       fmgroup_t *g;
+       int i,j,k;
+       fmframe_t       *in;
+
+       j=0;
+       for (i=0;i<fmheader.num_frames;i++)
+       {
+               while (i>=groups[j].start_frame+groups[j].num_frames&&j<num_groups-1)
+                       j++;
+               frame_to_group[i]=j;
+       }
+
+       for (k=0;k<num_groups;k++)
+       {
+               g=&groups[k];
+
+               printf("\nCompressing Frames for group %i...\n", k);
+               AnimCompressInit(g->num_frames,fmheader.num_xyz,g->degrees);
+               for (i=0;i<g->num_frames;i++)
+               {
+                       in = &g_frames[i+g->start_frame];
+                       for (j=0;j<fmheader.num_xyz;j++)
+                               AnimSetFrame(i,j,in->v[j].v[0],in->v[j].v[1],in->v[j].v[2]);
+               }
+               AnimCompressDoit();
+               g->mat= (char *) SafeMalloc(fmheader.num_xyz*3*g->degrees*sizeof(char), "CompressFrames");
+               g->ccomp=(char *) SafeMalloc(g->num_frames*g->degrees*sizeof(char), "CompressFrames");
+               g->cbase=(char *) SafeMalloc(fmheader.num_xyz*3*sizeof(unsigned char), "CompressFrames");
+               g->cscale=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");
+               g->coffset=(float *) SafeMalloc(g->degrees*sizeof(float), "CompressFrames");
+               AnimCompressToBytes(g->trans,g->scale,g->mat,g->ccomp,g->cbase,g->cscale,g->coffset,g->bmin,g->bmax);
+               AnimCompressEnd();
+       }
+}
+
+static void OptimizeVertices(void)
+{
+       qboolean        vert_used[MAX_FM_VERTS];
+       short           vert_replacement[MAX_FM_VERTS];
+       int                     i,j,k,l,pos,bit,set_pos,set_bit;
+       fmframe_t       *in;
+       qboolean        Found;
+       int                     num_unique;
+       static IntListNode_t *newVertLists[NUM_CLUSTERS];
+       static int newNum_verts[NUM_CLUSTERS];
+       IntListNode_t *current, *next;
+
+       printf("Optimizing vertices...");
+
+       memset(vert_used, 0, sizeof(vert_used));
+
+       if(g_skelModel.clustered == true)
+       {
+               memset(newNum_verts, 0, sizeof(newNum_verts));
+               memset(newVertLists, 0, sizeof(newVertLists));
+       }
+
+       num_unique = 0;
+
+       // search for common points among all the frames
+       for (i=0 ; i<fmheader.num_frames ; i++)
+       {
+               in = &g_frames[i];
+
+               for(j=0;j<fmheader.num_xyz;j++)
+               {
+                       for(k=0,Found=false;k<j;k++)
+                       {       // starting from the beginning always ensures vert_replacement points to the first point in the array
+                               if (in->v[j].v[0] == in->v[k].v[0] &&
+                                       in->v[j].v[1] == in->v[k].v[1] &&
+                                       in->v[j].v[2] == in->v[k].v[2])
+                               {
+                                       Found = true;
+                                       vert_replacement[j] = k;
+                                       break;
+                               }
+
+                       }
+
+                       if (!Found)
+                       {
+                               if (!vert_used[j])
+                               {
+                                       num_unique++;
+                               }
+                               vert_used[j] = true;
+                       }
+               }
+       }
+
+       // recompute the light normals
+       for (i=0 ; i<fmheader.num_frames ; i++)
+       {
+               in = &g_frames[i];
+
+               for(j=0;j<fmheader.num_xyz;j++)
+               {
+                       if (!vert_used[j])
+                       {
+                               k = vert_replacement[j];
+
+                               VectorAdd (in->v[j].vnorm.normalsum, in->v[k].vnorm.normalsum, in->v[k].vnorm.normalsum);
+                               in->v[k].vnorm.numnormals += in->v[j].vnorm.numnormals++;
+                       }
+               }
+
+               for (j=0 ; j<fmheader.num_xyz ; j++)
+               {
+                       vec3_t  v;
+                       float   maxdot;
+                       int             maxdotindex;
+                       int             c;
+
+                       c = in->v[j].vnorm.numnormals;
+                       if (!c)
+                               Error ("Vertex with no triangles attached");
+
+                       VectorScale (in->v[j].vnorm.normalsum, 1.0/c, v);
+                       VectorNormalize (v, v);
+
+                       maxdot = -999999.0;
+                       maxdotindex = -1;
+
+                       for (k=0 ; k<NUMVERTEXNORMALS ; k++)
+                       {
+                               float   dot;
+
+                               dot = DotProduct (v, avertexnormals[k]);
+                               if (dot > maxdot)
+                               {
+                                       maxdot = dot;
+                                       maxdotindex = k;
+                               }
+                       }
+
+                       in->v[j].lightnormalindex = maxdotindex;
+               }
+       }
+
+       // create substitution list
+       num_unique = 0;
+       for(i=0;i<fmheader.num_xyz;i++)
+       {
+               if (vert_used[i])
+               {
+                       vert_replacement[i] = num_unique;
+                       num_unique++;
+               }
+               else
+               {
+                       vert_replacement[i] = vert_replacement[vert_replacement[i]];
+               }
+
+               // vert_replacement[i] is the new index, i is the old index
+               // need to add the new index to the cluster list if old index was in it
+               if(g_skelModel.clustered == true)
+               {
+                       for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)
+                       {
+                               for(l = 0, current = g_skelModel.vertLists[k]; 
+                                       l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)
+                               {
+                                       if(current->data == i)
+                                       {
+                                               IntListNode_t *current2;
+                                               int m;
+                                               qboolean added = false;
+
+                                               for(m = 0, current2 = newVertLists[k]; m < newNum_verts[k+1];
+                                                       ++m, current2 = current2->next)
+                                               {
+                                                       if(current2->data == vert_replacement[i])
+                                                       {
+                                                               added = true;
+                                                               break;
+                                                       }
+                                               }
+
+                                               if(!added)
+                                               {
+                                                       ++newNum_verts[k+1];
+
+                                                       next = newVertLists[k];
+
+                                                       newVertLists[k] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "OptimizeVertices");
+                                                       // freed after model write out
+
+                                                       newVertLists[k]->data = vert_replacement[i];
+                                                       newVertLists[k]->next = next;
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // substitute
+       for (i=0 ; i<fmheader.num_frames ; i++)
+       {
+               in = &g_frames[i];
+
+               for(j=0;j<fmheader.num_xyz;j++)
+               {
+                       in->v[vert_replacement[j]] = in->v[j];
+               }
+
+       }
+
+       for(i = 0; i < numJointsInSkeleton[g_skelModel.type]; ++i)
+       {
+               IntListNode_t *toFree;
+               current = g_skelModel.vertLists[i];
+
+               while(current)
+               {
+                       toFree = current;
+                       current = current->next;
+                       free(toFree);  // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
+               }
+
+               g_skelModel.vertLists[i] = newVertLists[i];
+               g_skelModel.new_num_verts[i+1] = newNum_verts[i+1];
+       }
+
+#ifndef NDEBUG
+       for(k = 0; k < numJointsInSkeleton[g_skelModel.type]; ++k)
+       {
+               for(l = 0, current = g_skelModel.vertLists[k]; 
+                       l < g_skelModel.new_num_verts[k+1]; ++l, current = current->next)
+               {
+                       IntListNode_t *current2;
+                       int m;
+
+                       for(m = l+1, current2 = current->next; m < newNum_verts[k+1];
+                               ++m, current2 = current2->next)
+                       {
+                               if(current->data == current2->data)
+                               {
+                                       printf("Warning duplicate vertex:  %d\n", current->data);
+                                       break;
+                               }
+                       }
+               }
+       }
+#endif
+
+       for(i=0;i<fmheader.num_mesh_nodes;i++)
+       {       // reset the vert bits
+               memset(pmnodes[i].verts,0,sizeof(pmnodes[i].verts));
+       }
+
+       // repleace the master triangle list vertex indexes and update the vert bits for each mesh node
+       for (i=0 ; i<fmheader.num_tris ; i++)
+       {
+               pos = i >> 3;
+               bit = 1 << (i & 7 );
+
+               for (j=0 ; j<3 ; j++)
+               {       
+                       set_bit = set_pos = triangles[i].index_xyz[j] = vert_replacement[triangles[i].index_xyz[j]];
+
+                       set_pos >>= 3;
+                       set_bit = 1 << (set_bit & 7);
+
+                       for(k=0;k<fmheader.num_mesh_nodes;k++)
+                       {
+                               if (!(pmnodes[k].tris[pos] & bit))
+                               {
+                                       continue;
+                               }
+                               pmnodes[k].verts[set_pos] |= set_bit;
+                       }
+               }
+       }
+
+       for (i=0;i<numcommands;i++)
+       {
+               j = commands[i];
+               if (!j) continue;
+
+               j = abs(j);
+               for(i++;j;j--,i+=3)
+               {
+                       commands[i+2] = vert_replacement[commands[i+2]];
+               }
+               i--;
+       }
+
+       printf("Reduced by %d\n",fmheader.num_xyz - num_unique);
+       
+       fmheader.num_xyz = num_unique;
+       if (num_groups)
+       {
+               // tack on the reference verts to the regular verts
+               if(g_skelModel.references != REF_NULL)
+               {
+                       fmframe_t       *in;
+                       int index;
+                       int refnum;
+
+                       if (RefPointNum <= 0)
+                       {       // Hard-coded labels
+                               refnum = numReferences[g_skelModel.references];
+                       }
+                       else
+                       {       // Labels indicated in QDT
+                               refnum = RefPointNum;
+                       }
+
+
+                       for (i = 0; i < fmheader.num_frames; ++i)
+                       {
+                               in = &g_frames[i];
+                               index = fmheader.num_xyz;
+
+                               for (j = 0 ; j < refnum; ++j)
+                               {
+                                       VectorCopy(in->references[j].placement.origin, in->v[index].v);
+                                       index++;
+
+                                       VectorCopy(in->references[j].placement.direction, in->v[index].v);
+                                       index++;
+
+                                       VectorCopy(in->references[j].placement.up, in->v[index].v);
+                                       index++;
+                               }
+                       }
+
+                       fmheader.num_xyz += refnum*3;
+               }
+
+               // tack on the skeletal joint verts to the regular verts
+               if(g_skelModel.type != SKEL_NULL)
+               {
+                       fmframe_t       *in;
+                       int index;
+
+                       for (i = 0; i < fmheader.num_frames; ++i)
+                       {
+                               in = &g_frames[i];
+                               index = fmheader.num_xyz;
+
+                               for (j = 0 ; j < numJointsInSkeleton[g_skelModel.type]; ++j)
+                               {
+                                       VectorCopy(in->joints[j].placement.origin, in->v[index].v);
+                                       index++;
+
+                                       VectorCopy(in->joints[j].placement.direction, in->v[index].v);
+                                       index++;
+
+                                       VectorCopy(in->joints[j].placement.up, in->v[index].v);
+                                       index++;
+                               }
+                       }
+
+                       fmheader.num_xyz += numJointsInSkeleton[g_skelModel.type]*3;
+               }
+
+               CompressFrames();
+       }
+}
+
+
+/*
+===============
+FinishModel
+===============
+*/
+void FMFinishModel (void)
+{
+       FILE            *modelouthandle;
+       int                     i,j,length,tris,verts,bit,pos,total_tris,total_verts;
+       char            name[1024];
+       int                     trans_count;
+       
+       if (!fmheader.num_frames)
+               return;
+       
+//
+// copy to release directory tree if doing a release build
+//
+       if (g_release)
+       {
+               if (modelname[0])
+                       sprintf (name, "%s", modelname);
+               else
+                       sprintf (name, "%s/tris.fm", cdpartial);
+               ReleaseFile (name);
+
+               for (i=0 ; i<fmheader.num_skins ; i++)
+               {
+                       ReleaseFile (g_skins[i]);
+               }
+               fmheader.num_frames = 0;
+               return;
+       }
+
+       printf("\n");
+
+       trans_count = 0;
+       for(i=0;i<fmheader.num_tris;i++)
+               if (translucent[i])
+                       trans_count++;
+
+       if (!g_no_opimizations)
+       {
+               OptimizeVertices();
+       }
+
+//
+// write the model output file
+//
+       if (modelname[0])
+               sprintf (name, "%s%s", g_outputDir, modelname);
+       else
+               sprintf (name, "%s/tris.fm", g_outputDir);
+       printf ("saving to %s\n", name);
+       CreatePath (name);
+       modelouthandle = SafeOpenWrite (name);
+
+       WriteModelFile (modelouthandle);
+       
+       printf ("%3dx%3d skin\n", fmheader.skinwidth, fmheader.skinheight);
+       printf ("First frame boundaries:\n");
+       printf ("       minimum x: %3f\n", g_frames[0].mins[0]);
+       printf ("       maximum x: %3f\n", g_frames[0].maxs[0]);
+       printf ("       minimum y: %3f\n", g_frames[0].mins[1]);
+       printf ("       maximum y: %3f\n", g_frames[0].maxs[1]);
+       printf ("       minimum z: %3f\n", g_frames[0].mins[2]);
+       printf ("       maximum z: %3f\n", g_frames[0].maxs[2]);
+       printf ("%4d vertices\n", fmheader.num_xyz);
+       printf ("%4d triangles, %4d of them translucent\n", fmheader.num_tris, trans_count);
+       printf ("%4d frame\n", fmheader.num_frames);
+       printf ("%4d glverts\n", numglverts);
+       printf ("%4d glcmd\n", fmheader.num_glcmds);
+       printf ("%4d skins\n", fmheader.num_skins);
+       printf ("%4d mesh nodes\n", fmheader.num_mesh_nodes);
+       printf ("wasted pixels: %d / %d (%5.2f Percent)\n",total_skin_pixels - skin_pixels_used, 
+               total_skin_pixels, (double)(total_skin_pixels - skin_pixels_used) / (double)total_skin_pixels * 100.0);
+
+       printf ("file size: %d\n", (int)ftell (modelouthandle) );
+       printf ("---------------------\n");
+       
+       if (g_verbose)
+       {
+               if (fmheader.num_mesh_nodes)
+               {
+                       total_tris = total_verts = 0;
+                       printf("Node Name                         Tris Verts\n");
+                       printf("--------------------------------- ---- -----\n");
+                       for(i=0;i<fmheader.num_mesh_nodes;i++)
+                       {
+                               tris = 0;
+                               verts = 0;
+                               for(j=0;j<MAXTRIANGLES;j++)
+                               {
+                                       pos = (j) >> 3;
+                                       bit = 1 << ((j) & 7 );
+                                       if (pmnodes[i].tris[pos] & bit)
+                                       {
+                                               tris++;
+                                       }
+                               }
+                               for(j=0;j<MAX_FM_VERTS;j++)
+                               {
+                                       pos = (j) >> 3;
+                                       bit = 1 << ((j) & 7 );
+                                       if (pmnodes[i].verts[pos] & bit)
+                                       {
+                                               verts++;
+                                       }
+                               }
+
+                               printf("%-33s %4d %5d\n",pmnodes[i].name,tris,verts);
+
+                               total_tris += tris;
+                               total_verts += verts;
+                       }
+                       printf("--------------------------------- ---- -----\n");
+                       printf("%-33s %4d %5d\n","TOTALS",total_tris,total_verts);
+               }
+       }
+       fclose (modelouthandle);
+
+       // finish writing header file
+       H_printf("\n");
+
+       // scale_up is usefull to allow step distances to be adjusted
+       H_printf("#define MODEL_SCALE\t\t%f\n", scale_up);
+
+       // mesh nodes
+       if (fmheader.num_mesh_nodes)
+       {
+               H_printf("\n");
+               H_printf("#define NUM_MESH_NODES\t\t%d\n\n",fmheader.num_mesh_nodes);
+               for(i=0;i<fmheader.num_mesh_nodes;i++)
+               {
+                       strcpy(name, pmnodes[i].name);
+                       strupr(name);
+                       length = strlen(name);
+                       for(j=0;j<length;j++)
+                       {
+                               if (name[j] == ' ')
+                               {
+                                       name[j] = '_';
+                               }
+                       }
+                       H_printf("#define MESH_%s\t\t%d\n", name, i);
+               }
+       }
+
+       fclose (headerouthandle);
+       headerouthandle = NULL;
+       free (pmnodes);
+}
+
+
+/*
+=================================================================
+
+ALIAS MODEL DISPLAY LIST GENERATION
+
+=================================================================
+*/
+
+extern int             strip_xyz[128];
+extern int             strip_st[128];
+extern int             strip_tris[128];
+extern int             stripcount;
+
+/*
+================
+StripLength
+================
+*/
+static int     StripLength (int starttri, int startv, int num_tris, int node)
+{
+       int                             m1, m2;
+       int                             st1, st2;
+       int                             j;
+       fmtriangle_t    *last, *check;
+       int                             k;
+       int                             pos, bit;
+
+       used[starttri] = 2;
+
+       last = &triangles[starttri];
+
+       strip_xyz[0] = last->index_xyz[(startv)%3];
+       strip_xyz[1] = last->index_xyz[(startv+1)%3];
+       strip_xyz[2] = last->index_xyz[(startv+2)%3];
+       strip_st[0] = last->index_st[(startv)%3];
+       strip_st[1] = last->index_st[(startv+1)%3];
+       strip_st[2] = last->index_st[(startv+2)%3];
+
+       strip_tris[0] = starttri;
+       stripcount = 1;
+
+       m1 = last->index_xyz[(startv+2)%3];
+       st1 = last->index_st[(startv+2)%3];
+       m2 = last->index_xyz[(startv+1)%3];
+       st2 = last->index_st[(startv+1)%3];
+
+       // look for a matching triangle
+nexttri:
+       for (j=starttri+1, check=&triangles[starttri+1]
+               ; j<num_tris ; j++, check++)
+       {
+               pos = j >> 3;
+               bit = 1 << (j & 7 );
+               if (!(pmnodes[node].tris[pos] & bit))
+               {
+                       continue;
+               }
+               for (k=0 ; k<3 ; k++)
+               {
+                       if (check->index_xyz[k] != m1)
+                               continue;
+                       if (check->index_st[k] != st1)
+                               continue;
+                       if (check->index_xyz[ (k+1)%3 ] != m2)
+                               continue;
+                       if (check->index_st[ (k+1)%3 ] != st2)
+                               continue;
+
+                       // this is the next part of the fan
+
+                       // if we can't use this triangle, this tristrip is done
+                       if (used[j] || translucent[j] != translucent[starttri])
+                               goto done;
+
+                       // the new edge
+                       if (stripcount & 1)
+                       {
+                               m2 = check->index_xyz[ (k+2)%3 ];
+                               st2 = check->index_st[ (k+2)%3 ];
+                       }
+                       else
+                       {
+                               m1 = check->index_xyz[ (k+2)%3 ];
+                               st1 = check->index_st[ (k+2)%3 ];
+                       }
+
+                       strip_xyz[stripcount+2] = check->index_xyz[ (k+2)%3 ];
+                       strip_st[stripcount+2] = check->index_st[ (k+2)%3 ];
+                       strip_tris[stripcount] = j;
+                       stripcount++;
+
+                       used[j] = 2;
+                       goto nexttri;
+               }
+       }
+done:
+
+       // clear the temp used flags
+       for (j=starttri+1 ; j<num_tris ; j++)
+               if (used[j] == 2)
+                       used[j] = 0;
+
+       return stripcount;
+}
+
+
+/*
+===========
+FanLength
+===========
+*/
+static int     FanLength (int starttri, int startv, int num_tris, int node)
+{
+       int                             m1, m2;
+       int                             st1, st2;
+       int                             j;
+       fmtriangle_t    *last, *check;
+       int                             k;
+       int                             pos, bit;
+
+       used[starttri] = 2;
+
+       last = &triangles[starttri];
+
+       strip_xyz[0] = last->index_xyz[(startv)%3];
+       strip_xyz[1] = last->index_xyz[(startv+1)%3];
+       strip_xyz[2] = last->index_xyz[(startv+2)%3];
+       strip_st[0] = last->index_st[(startv)%3];
+       strip_st[1] = last->index_st[(startv+1)%3];
+       strip_st[2] = last->index_st[(startv+2)%3];
+
+       strip_tris[0] = starttri;
+       stripcount = 1;
+
+       m1 = last->index_xyz[(startv+0)%3];
+       st1 = last->index_st[(startv+0)%3];
+       m2 = last->index_xyz[(startv+2)%3];
+       st2 = last->index_st[(startv+2)%3];
+
+
+       // look for a matching triangle
+nexttri:
+       for (j=starttri+1, check=&triangles[starttri+1] 
+               ; j<num_tris ; j++, check++)
+       {
+               pos = j >> 3;
+               bit = 1 << (j & 7 );
+               if (!(pmnodes[node].tris[pos] & bit))
+               {
+                       continue;
+               }
+               for (k=0 ; k<3 ; k++)
+               {
+                       if (check->index_xyz[k] != m1)
+                               continue;
+                       if (check->index_st[k] != st1)
+                               continue;
+                       if (check->index_xyz[ (k+1)%3 ] != m2)
+                               continue;
+                       if (check->index_st[ (k+1)%3 ] != st2)
+                               continue;
+
+                       // this is the next part of the fan
+
+                       // if we can't use this triangle, this tristrip is done
+                       if (used[j] || translucent[j] != translucent[starttri])
+                               goto done;
+
+                       // the new edge
+                       m2 = check->index_xyz[ (k+2)%3 ];
+                       st2 = check->index_st[ (k+2)%3 ];
+
+                       strip_xyz[stripcount+2] = m2;
+                       strip_st[stripcount+2] = st2;
+                       strip_tris[stripcount] = j;
+                       stripcount++;
+
+                       used[j] = 2;
+                       goto nexttri;
+               }
+       }
+done:
+
+       // clear the temp used flags
+       for (j=starttri+1 ; j<num_tris ; j++)
+               if (used[j] == 2)
+                       used[j] = 0;
+
+       return stripcount;
+}
+
+
+
+/*
+================
+BuildGlCmds
+
+Generate a list of trifans or strips
+for the model, which holds for all frames
+================
+*/
+static void BuildGlCmds (void)
+{
+       int             i, j, k, l;
+       int             startv;
+       float   s, t;
+       int             len, bestlen, besttype;
+       int             best_xyz[1024];
+       int             best_st[1024];
+       int             best_tris[1024];
+       int             type;
+       int             trans_check;
+       int             bit,pos;
+
+       //
+       // build tristrips
+       //
+       numcommands = 0;
+       numglverts = 0;
+
+
+       for(l=0;l<fmheader.num_mesh_nodes;l++)
+       {
+               memset (used, 0, sizeof(used));
+
+               pmnodes[l].start_glcmds = numcommands;
+
+               for(trans_check = 0; trans_check<2; trans_check++)
+               {
+                       for (i=0 ; i < fmheader.num_tris ; i++)
+                       {
+                               pos = i >> 3;
+                               bit = 1 << (i & 7 );
+                               if (!(pmnodes[l].tris[pos] & bit))
+                               {
+                                       continue;
+                               }
+
+                               // pick an unused triangle and start the trifan
+                               if (used[i] || trans_check != translucent[i])
+                               {
+                                       continue;
+                               }
+
+                               bestlen = 0;
+                               for (type = 0 ; type < 2 ; type++)
+               //      type = 1;
+                               {
+                                       for (startv =0 ; startv < 3 ; startv++)
+                                       {
+                                               if (type == 1)
+                                                       len = StripLength (i, startv, fmheader.num_tris, l);
+                                               else
+                                                       len = FanLength (i, startv, fmheader.num_tris, l);
+                                               if (len > bestlen)
+                                               {
+                                                       besttype = type;
+                                                       bestlen = len;
+                                                       for (j=0 ; j<bestlen+2 ; j++)
+                                                       {
+                                                               best_st[j] = strip_st[j];
+                                                               best_xyz[j] = strip_xyz[j];
+                                                       }
+                                                       for (j=0 ; j<bestlen ; j++)
+                                                               best_tris[j] = strip_tris[j];
+                                               }
+                                       }
+                               }
+
+                               // mark the tris on the best strip/fan as used
+                               for (j=0 ; j<bestlen ; j++)
+                                       used[best_tris[j]] = 1;
+
+                               if (besttype == 1)
+                                       commands[numcommands++] = (bestlen+2);
+                               else
+                                       commands[numcommands++] = -(bestlen+2);
+
+                               numglverts += bestlen+2;
+
+                               for (j=0 ; j<bestlen+2 ; j++)
+                               {
+                                       // emit a vertex into the reorder buffer
+                                       k = best_st[j];
+
+                                       // emit s/t coords into the commands stream
+                                       s = base_st[k].s;
+                                       t = base_st[k].t;
+
+                                       s = (s  ) / fmheader.skinwidth;
+                                       t = (t  ) / fmheader.skinheight;
+
+                                       *(float *)&commands[numcommands++] = s;
+                                       *(float *)&commands[numcommands++] = t;
+                                       *(int *)&commands[numcommands++] = best_xyz[j];
+                               }
+                       }
+               }
+               commands[numcommands++] = 0;            // end of list marker
+               pmnodes[l].num_glcmds = numcommands - pmnodes[l].start_glcmds;
+       }
+}
+
+
+/*
+===============================================================
+
+BASE FRAME SETUP
+
+===============================================================
+*/
+
+
+#define LINE_NORMAL 1
+#define LINE_FAT 2
+#define LINE_DOTTED 3
+
+
+#define ASCII_SPACE 32
+
+int LineType = LINE_NORMAL;
+extern unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];
+unsigned char LineColor = 255;
+int ScaleWidth, ScaleHeight;
+
+
+static char *CharDefs[] =
+{
+       "-------------------------",
+       "-------------------------", // !
+       "-------------------------", // "
+       "-------------------------", // #
+       "-------------------------", // $
+       "-------------------------", // %
+       "-------------------------", // &
+       "--*----*-----------------", // '
+       "-*---*----*----*-----*---", // (
+       "*-----*----*----*---*----", // )
+       "-----*--*--**---**--*--*-", // *
+       "-------------------------", // +
+       "----------------**--**---", // ,
+       "-------------------------", // -
+       "----------------**---**--", // .
+       "-------------------------", // /
+       " *** *  *** * ***  * *** ", // 0
+       "   *   **    *    *    * ",
+       "****     * *** *    *****",
+       "****     * ***     ***** ",
+       "  **  * * *  * *****   * ",
+       "**** *    ****     ***** ",
+       " *** *    **** *   * *** ",
+       "*****    *   *   *    *  ",
+       " *** *   * *** *   * *** ",
+       " *** *   * ****    * *** ", // 9
+       "-**---**--------**---**--", // :
+       "-------------------------", // ;
+       "-------------------------", // <
+       "-------------------------", // =
+       "-------------------------", // >
+       "-------------------------", // ?
+       "-------------------------", // @
+       "-***-*---*******---**---*", // A
+       "****-*---*****-*---*****-",
+       "-*****----*----*-----****",
+       "****-*---**---**---*****-",
+       "******----****-*----*****",
+       "******----****-*----*----",
+       "-*****----*--***---*-****",
+       "*---**---*******---**---*",
+       "-***---*----*----*---***-",
+       "----*----*----**---*-***-",
+       "-*--*-*-*--**---*-*--*--*",
+       "-*----*----*----*----****",
+       "*---***-***-*-**---**---*",
+       "*---***--**-*-**--***---*",
+       "-***-*---**---**---*-***-",
+       "****-*---*****-*----*----",
+       "-***-*---**---*-***----**",
+       "****-*---*****-*-*--*--**",
+       "-*****-----***-----*****-",
+       "*****--*----*----*----*--",
+       "*---**---**---**---******",
+       "*---**---**---*-*-*---*--",
+       "*---**---**-*-***-***---*",
+       "*---*-*-*---*---*-*-*---*",
+       "*---**---*-*-*---*----*--",
+       "*****---*---*---*---*****" // Z
+};
+
+void DrawLine(int x1, int y1, int x2, int y2)
+{
+       int dx, dy;
+       int adx, ady;
+       int count;
+       float xfrac, yfrac, xstep, ystep;
+       unsigned sx, sy;
+       float u, v;
+
+       dx = x2 - x1;
+       dy = y2 - y1;
+       adx = abs(dx);
+       ady = abs(dy);
+
+       count = adx > ady ? adx : ady;
+       count++;
+
+       if(count > 300)
+       {
+               printf("Bad count\n");
+               return; // don't ever hang up on bad data
+       }
+               
+       xfrac = x1;
+       yfrac = y1;
+       
+       xstep = (float)dx/count;
+       ystep = (float)dy/count;
+
+       switch(LineType)
+       {
+               case LINE_NORMAL:
+                       do
+                       {
+                               if(xfrac < SKINPAGE_WIDTH && yfrac < SKINPAGE_HEIGHT)
+                               {
+                                       pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;
+                               }
+                               xfrac += xstep;
+                               yfrac += ystep;
+                               count--;
+                       } while (count > 0);
+                       break;
+               case LINE_FAT:
+                       do
+                       {
+                               for (u=-0.1 ; u<=0.9 ; u+=0.999)
+                               {
+                                       for (v=-0.1 ; v<=0.9 ; v+=0.999)
+                                       {
+                                               sx = xfrac+u;
+                                               sy = yfrac+v;
+                                               if(sx < SKINPAGE_WIDTH && sy < SKINPAGE_HEIGHT)
+                                               {
+                                                       pic[sy*SKINPAGE_WIDTH+sx] = LineColor;
+                                               }
+                                       }
+                               }
+                               xfrac += xstep;
+                               yfrac += ystep;
+                               count--;
+                       } while (count > 0);
+                       break;
+               case LINE_DOTTED:
+                       do
+                       {
+                               if(count&1 && xfrac < SKINPAGE_WIDTH &&
+                                       yfrac < SKINPAGE_HEIGHT)
+                               {
+                                       pic[(int)yfrac*SKINPAGE_WIDTH+(int)xfrac] = LineColor;
+                               }
+                               xfrac += xstep;
+                               yfrac += ystep;
+                               count--;
+                       } while (count > 0);
+                       break;
+               default:
+                       Error("Unknown <linetype> %d.\n", LineType);
+       }
+}
+
+//==========================================================================
+//
+// DrawCharacter
+//
+//==========================================================================
+
+static void DrawCharacter(int x, int y, int character)
+{
+       int r, c;
+       char *def;
+
+       character = toupper(character);
+       if(character < ASCII_SPACE || character > 'Z')
+       {
+               character = ASCII_SPACE;
+       }
+       character -= ASCII_SPACE;
+       for(def = CharDefs[character], r = 0; r < 5; r++)
+       {
+               for(c = 0; c < 5; c++)
+               {
+                       pic[(y+r)*SKINPAGE_WIDTH+x+c] = *def++ == '*' ? 255 : 0;
+               }
+       }
+}
+
+//==========================================================================
+//
+// DrawTextChar
+//
+//==========================================================================
+
+void DrawTextChar(int x, int y, char *text)
+{
+       int c;
+
+       while((c = *text++) != '\0')
+       {
+               DrawCharacter(x, y, c);
+               x += 6;
+       }
+}
+
+
+extern void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight);
+
+//==========================================================================
+// ExtractDigit
+
+static int ExtractDigit(byte *pic, int x, int y)
+{
+       int             i;
+       int             r, c;
+       char    digString[32];
+       char    *buffer;
+       byte    backColor;
+       char    **DigitDefs;
+
+       backColor = pic[(SKINPAGE_HEIGHT - 1) * SKINPAGE_WIDTH];
+       DigitDefs = &CharDefs['0' - ASCII_SPACE];
+
+       buffer = digString;
+       for(r = 0; r < 5; r++)
+       {
+               for(c = 0; c < 5; c++)
+               {
+                       *buffer++ = (pic[(y + r) * SKINPAGE_WIDTH + x + c] == backColor) ? ' ' : '*';
+               }
+       }
+       *buffer = '\0';
+       for(i = 0; i < 10; i++)
+       {
+               if(strcmp(DigitDefs[i], digString) == 0)
+               {
+                       return i;
+               }
+       }
+
+       Error("Unable to extract scaling info from skin PCX.");
+       return 0;
+}
+
+//==========================================================================
+// ExtractNumber
+
+int ExtractNumber(byte *pic, int x, int y)
+{
+       return ExtractDigit(pic, x, y) * 100 + ExtractDigit(pic, x + 6, y) * 10 + ExtractDigit(pic, x + 12, y);
+}
+
+
+
+
+
+/*
+============
+BuildST
+
+Builds the triangle_st array for the base frame and
+fmheader.skinwidth / fmheader.skinheight
+
+  FIXME: allow this to be loaded from a file for
+  arbitrary mappings
+============
+*/
+static void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)
+{
+       int                     backface_flag;
+       int                     i, j;
+       int                     width, height, iwidth, iheight, swidth;
+       float           basex, basey;
+       float           scale;
+       vec3_t          mins, maxs;
+       float           *pbasevert;
+       vec3_t          vtemp1, vtemp2, normal;
+       float           s_scale, t_scale;
+       float           scWidth;
+       float           scHeight;
+       int skinwidth;
+       int skinheight;
+
+       //
+       // find bounds of all the verts on the base frame
+       //
+       ClearBounds (mins, maxs);
+       backface_flag = false;
+       
+       if (ptri[0].HasUV)      // if we have the uv already, we don't want to double up or scale
+       {
+               iwidth = ScaleWidth;
+               iheight = ScaleHeight;
+               
+               t_scale = s_scale = 1.0;
+       }
+       else
+       {
+               for (i=0 ; i<numtri ; i++)
+                       for (j=0 ; j<3 ; j++)
+                               AddPointToBounds (ptri[i].verts[j], mins, maxs);
+                       
+                       for (i=0 ; i<3 ; i++)
+                       {
+                               mins[i] = floor(mins[i]);
+                               maxs[i] = ceil(maxs[i]);
+                       }
+                       
+                       width = maxs[0] - mins[0];
+                       height = maxs[2] - mins[2];
+                       
+                       for (i=0 ; i<numtri ; i++)
+                       {
+                               VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+                               VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+                               CrossProduct (vtemp1, vtemp2, normal);
+                               
+                               if (normal[1] > 0)
+                               {
+                                       backface_flag = true;
+                                       break;
+                               }
+                       }
+                       scWidth = ScaleWidth*SCALE_ADJUST_FACTOR;
+                       if (backface_flag)      //we are doubling
+                               scWidth /= 2;
+                       
+                       scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;
+                       
+                       scale = scWidth/width;
+                       
+                       if(height*scale >= scHeight)
+                       {
+                               scale = scHeight/height;
+                       }
+                       
+                       iwidth = ceil(width*scale)+4;
+                       iheight = ceil(height*scale)+4;
+                       
+                       s_scale = (float)(iwidth-4) / width;
+                       t_scale = (float)(iheight-4) / height;
+                       t_scale = s_scale;
+       }
+       if (DrawSkin)
+       {
+               if(backface_flag)
+                       DrawScreen(s_scale, t_scale, iwidth*2, iheight);
+               else
+                       DrawScreen(s_scale, t_scale, iwidth, iheight);
+       }
+       if (backface_flag)
+               skinwidth=iwidth*2;
+       else
+               skinwidth=iwidth;
+       skinheight=iheight;
+
+
+/*     if (!g_fixedwidth)
+       {       // old style
+               scale = 8;
+               if (width*scale >= 150)
+                       scale = 150.0 / width;  
+               if (height*scale >= 190)
+                       scale = 190.0 / height;
+
+               s_scale = t_scale = scale;
+
+               iwidth = ceil(width*s_scale);
+               iheight = ceil(height*t_scale);
+
+               iwidth += 4;
+               iheight += 4;
+       }
+       else
+       {       // new style
+               iwidth = g_fixedwidth / 2;
+               iheight = g_fixedheight;
+
+               s_scale = (float)(iwidth-4) / width;
+               t_scale = (float)(iheight-4) / height;
+       }*/
+
+//
+// determine which side of each triangle to map the texture to
+//
+       basey = 2;
+       for (i=0 ; i<numtri ; i++)
+       {
+               if (ptri[i].HasUV)
+               {
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*skinwidth);
+                               triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*skinheight);
+                       }
+               }
+               else
+               {
+                       VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+                       VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+                       CrossProduct (vtemp1, vtemp2, normal);
+
+                       if (normal[1] > 0)
+                       {
+                               basex = iwidth + 2;
+                       }
+                       else
+                       {
+                               basex = 2;
+                       }
+                       
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               pbasevert = ptri[i].verts[j];
+
+                               triangle_st[i][j][0] = Q_rint((pbasevert[0] - mins[0]) * s_scale + basex);
+                               triangle_st[i][j][1] = Q_rint((maxs[2] - pbasevert[2]) * t_scale + basey);
+                       }
+               }
+
+               if (DrawSkin)
+               {
+                       DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],
+                               triangle_st[i][1][0], triangle_st[i][1][1]);
+                       DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],
+                               triangle_st[i][2][0], triangle_st[i][2][1]);
+                       DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],
+                               triangle_st[i][0][0], triangle_st[i][0][1]);
+               }
+       }
+
+// make the width a multiple of 4; some hardware requires this, and it ensures
+// dword alignment for each scan
+
+       swidth = iwidth;
+       if(backface_flag)
+               swidth *= 2;
+       fmheader.skinwidth = (swidth + 3) & ~3;
+       fmheader.skinheight = iheight;
+
+       skin_width = iwidth;
+       skin_height = iheight;
+}
+
+
+static void BuildNewST (triangle_t *ptri, int numtri, qboolean DrawSkin)
+{
+       int                     i, j;
+
+       for (i=0 ; i<numtri ; i++)
+       {
+               if (ptri[i].HasUV)
+               {
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*(ScaleWidth-1));
+                               triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*(ScaleHeight-1));
+                       }
+               }
+
+               if (DrawSkin)
+               {
+                       DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],
+                               triangle_st[i][1][0], triangle_st[i][1][1]);
+                       DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],
+                               triangle_st[i][2][0], triangle_st[i][2][1]);
+                       DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],
+                               triangle_st[i][0][0], triangle_st[i][0][1]);
+               }
+       }
+
+// make the width a multiple of 4; some hardware requires this, and it ensures
+// dword alignment for each scan
+
+       fmheader.skinwidth = (ScaleWidth + 3) & ~3;
+       fmheader.skinheight = ScaleHeight;
+
+       skin_width = ScaleWidth;
+       skin_height = ScaleHeight;
+}
+
+
+
+
+byte                   *BasePalette;
+byte                   *BasePixels,*TransPixels;
+int                            BaseWidth, BaseHeight, TransWidth, TransHeight;
+qboolean               BaseTrueColor;
+static qboolean SetPixel = false;
+
+int CheckTransRecursiveTri (int *lp1, int *lp2, int *lp3)
+{
+       int             *temp;
+       int             d;
+       int             new[2];
+
+       d = lp2[0] - lp1[0];
+       if (d < -1 || d > 1)
+               goto split;
+       d = lp2[1] - lp1[1];
+       if (d < -1 || d > 1)
+               goto split;
+
+       d = lp3[0] - lp2[0];
+       if (d < -1 || d > 1)
+               goto split2;
+       d = lp3[1] - lp2[1];
+       if (d < -1 || d > 1)
+               goto split2;
+
+       d = lp1[0] - lp3[0];
+       if (d < -1 || d > 1)
+               goto split3;
+       d = lp1[1] - lp3[1];
+       if (d < -1 || d > 1)
+       {
+split3:
+               temp = lp1;
+               lp1 = lp3;
+               lp3 = lp2;
+               lp2 = temp;
+
+               goto split;
+       }
+
+       return 0;                       // entire tri is filled
+
+split2:
+       temp = lp1;
+       lp1 = lp2;
+       lp2 = lp3;
+       lp3 = temp;
+
+split:
+// split this edge
+       new[0] = (lp1[0] + lp2[0]) >> 1;
+       new[1] = (lp1[1] + lp2[1]) >> 1;
+
+// draw the point if splitting a leading edge
+       if (lp2[1] > lp1[1])
+               goto nodraw;
+       if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0]))
+               goto nodraw;
+
+       if (SetPixel)
+       {
+               assert ((new[1]*BaseWidth) + new[0] < BaseWidth*BaseHeight);
+
+               if (BaseTrueColor)
+               {
+                       BasePixels[((new[1]*BaseWidth) + new[0]) * 4] = 1;
+               }
+               else
+               {
+                       BasePixels[(new[1]*BaseWidth) + new[0]] = 1;
+               }
+       }
+       else
+       {
+               if (TransPixels)
+               {
+                       if (TransPixels[(new[1]*TransWidth) + new[0]] != 255)
+                               return 1;
+               }
+               else if (BaseTrueColor)
+               {
+                       if (BasePixels[(((new[1]*BaseWidth) + new[0]) * 4) + 3] != 255)
+                               return 1;
+               }
+               else
+               {
+//                     pixel = BasePixels[(new[1]*BaseWidth) + new[0]];
+               }
+       }
+
+nodraw:
+// recursively continue
+       if (CheckTransRecursiveTri(lp3, lp1, new)) 
+               return 1;
+
+       return CheckTransRecursiveTri(lp3, new, lp2);
+}
+
+static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters, 
+       IntListNode_t **vertLists, int *num_verts, int *new_num_verts)
+{
+       int i, j;
+       IntListNode_t *next;
+
+       for(j = 0; j < numJointsInSkeleton[g_skelModel.type]; ++j)
+       {
+               if(!clusters[j])
+               {
+                       continue;
+               }
+
+               for(i = 0; i < num_verts[j+1]; ++i)
+               {
+                       if(clusters[j][i] == oldindex)
+                       {
+                               ++new_num_verts[j+1];
+
+                               next = vertLists[j];
+
+                               vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");
+                               // Currently freed in WriteJointedModelFile only
+
+                               vertLists[j]->data = newIndex;
+                               vertLists[j]->next = next;
+                       }
+               }
+       }
+}
+
+#define        FUDGE_EPSILON   0.002
+
+qboolean VectorFudgeCompare (vec3_t v1, vec3_t v2)
+{
+       int             i;
+       
+       for (i=0 ; i<3 ; i++)
+               if (fabs(v1[i]-v2[i]) > FUDGE_EPSILON)
+                       return false;
+                       
+       return true;
+}
+
+/*
+=================
+Cmd_Base
+=================
+*/
+void Cmd_FMBase (qboolean GetST)
+{
+       triangle_t      *ptri, *st_tri;
+       int                     num_st_tris;
+       int                     i, j, k, l;
+       int                     x,y,z;
+//     int                     time1;
+       char            file1[1024],file2[1024],trans_file[1024], stfile[1024], extension[256];
+       vec3_t          base_xyz[MAX_FM_VERTS];
+       FILE            *FH;
+       int                     pos,bit;
+       qboolean        NewSkin;
+
+       GetScriptToken (false);
+
+       if (g_skipmodel || g_release || g_archive)
+               return;
+
+       printf ("---------------------\n");
+       sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);
+       printf ("%s ", file1);
+
+       ExpandPathAndArchive (file1);
+
+       // Use the input filepath for this one.
+       sprintf (file1, "%s/%s", cddir, token);
+
+//     time1 = FileTime (file1);
+//     if (time1 == -1)
+//             Error ("%s doesn't exist", file1);
+
+//
+// load the base triangles
+//
+       if (do3ds)
+               Load3DSTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
+       else
+               LoadTriangleList (file1, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
+
+       if (g_ignoreTriUV)
+       {
+               for (i=0;i<fmheader.num_tris;i++)
+               {
+                       ptri[i].HasUV=0;
+               }
+       }
+
+       GetScriptToken (false);
+       sprintf (file2, "%s/%s", cddir, token);
+       sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);
+
+       ExtractFileExtension (file2, extension);
+       if (extension[0] == 0)
+       {
+               strcat(file2, ".pcx");
+       }
+       printf ("skin: %s\n", file2);
+
+       BaseTrueColor = LoadAnyImage (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);
+
+       NewSkin = false;
+       if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)
+       {
+               if (g_allow_newskin)
+               {
+                       ScaleWidth = BaseWidth;
+                       ScaleHeight = BaseHeight;
+                       NewSkin = true;
+               }
+               else
+               {
+                       Error("Invalid skin page size: (%d,%d) should be (%d,%d)",
+                               BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);
+               }
+       }
+       else if (!BaseTrueColor)
+       {
+               ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,
+                       ENCODED_WIDTH_Y);
+               ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,
+                       ENCODED_HEIGHT_Y);
+       }
+       else
+       {
+               Error("Texture coordinates not supported on true color image");
+       }
+
+       if (GetST)
+       {
+               GetScriptToken (false);
+
+               sprintf (stfile, "%s/%s.%s", cdarchive, token, trifileext);
+               printf ("ST: %s ", stfile);
+
+               sprintf (stfile, "%s/%s", cddir, token);
+
+               if (do3ds)
+                       Load3DSTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);
+               else
+                       LoadTriangleList (stfile, &st_tri, &num_st_tris, NULL, NULL);
+
+               if (num_st_tris != fmheader.num_tris)
+               {
+                       Error ("num st tris mismatch: st %d / base %d", num_st_tris, fmheader.num_tris);
+               }
+
+               printf("   matching triangles...\n");
+               for(i=0;i<fmheader.num_tris;i++)
+               {
+                       k = -1;
+                       for(j=0;j<num_st_tris;j++)
+                       {
+                               for(x=0;x<3;x++)
+                               {
+                                       for(y=0;y<3;y++)
+                                       {
+                                               if (x == y)
+                                               {
+                                                       continue;
+                                               }
+                                               for(z=0;z<3;z++)
+                                               {
+                                                       if (z == x || z == y) 
+                                                       {
+                                                               continue;
+                                                       }
+
+                                                       if (VectorFudgeCompare (ptri[i].verts[0], st_tri[j].verts[x]) &&
+                                                           VectorFudgeCompare (ptri[i].verts[1], st_tri[j].verts[y]) &&
+                                                               VectorFudgeCompare (ptri[i].verts[2], st_tri[j].verts[z]))
+                                                       {
+                                                               if (k == -1)
+                                                               {
+                                                                       k = j;
+                                                                       ptri[i].HasUV = st_tri[k].HasUV;
+                                                                       ptri[i].uv[0][0] = st_tri[k].uv[x][0];
+                                                                       ptri[i].uv[0][1] = st_tri[k].uv[x][1];
+                                                                       ptri[i].uv[1][0] = st_tri[k].uv[y][0];
+                                                                       ptri[i].uv[1][1] = st_tri[k].uv[y][1];
+                                                                       ptri[i].uv[2][0] = st_tri[k].uv[z][0];
+                                                                       ptri[i].uv[2][1] = st_tri[k].uv[z][1];
+                                                                       x = y = z = 999;
+                                                               }
+                                                               else if (k != j)
+                                                               {
+                                                                       printf("Duplicate triangle %d found in st file: %d and %d\n",i,k,j);
+                                                                       printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
+                                                                               ptri[i].verts[0][0],ptri[i].verts[0][1],ptri[i].verts[0][2],
+                                                                               ptri[i].verts[1][0],ptri[i].verts[1][1],ptri[i].verts[1][2],
+                                                                               ptri[i].verts[2][0],ptri[i].verts[2][1],ptri[i].verts[2][2]);
+                                                                       printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
+                                                                               st_tri[k].verts[0][0],st_tri[k].verts[0][1],st_tri[k].verts[0][2],
+                                                                               st_tri[k].verts[1][0],st_tri[k].verts[1][1],st_tri[k].verts[1][2],
+                                                                               st_tri[k].verts[2][0],st_tri[k].verts[2][1],st_tri[k].verts[2][2]);
+                                                                       printf("   (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f)\n",
+                                                                               st_tri[j].verts[0][0],st_tri[j].verts[0][1],st_tri[j].verts[0][2],
+                                                                               st_tri[j].verts[1][0],st_tri[j].verts[1][1],st_tri[j].verts[1][2],
+                                                                               st_tri[j].verts[2][0],st_tri[j].verts[2][1],st_tri[j].verts[2][2]);
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       if (k == -1)
+                       {
+                               printf("No matching triangle %d\n",i);
+                       }
+               }
+               free (st_tri);
+       }
+
+//
+// get the ST values
+//
+       if (ptri && ptri[0].HasUV)
+       {
+               if (!NewSkin)
+               {
+                       Error("Base has UVs with old style skin page\nMaybe you want to use -ignoreUV");
+               }
+               else
+               {
+                       BuildNewST (ptri, fmheader.num_tris, false);
+               }
+       }
+       else
+       {
+               if (NewSkin)
+               {
+                       Error("Base has new style skin without UVs");
+               }
+               else
+               {
+                       BuildST (ptri, fmheader.num_tris, false);
+               }
+       }
+
+       TransPixels = NULL;
+       if (!BaseTrueColor)
+       {
+               FH = fopen(trans_file,"rb");
+               if (FH)
+               {
+                       fclose(FH);
+                       Load256Image (trans_file, &TransPixels, NULL, &TransWidth, &TransHeight);
+                       if (TransWidth != fmheader.skinwidth || TransHeight != fmheader.skinheight)
+                       {
+                               Error ("source image %s dimensions (%d,%d) are not the same as alpha image (%d,%d)\n",file2,fmheader.skinwidth,fmheader.skinheight,TransWidth,TransHeight);
+                       }
+               }
+       }
+
+//
+// run through all the base triangles, storing each unique vertex in the
+// base vertex list and setting the indirect triangles to point to the base
+// vertices
+//
+       for(l=0;l<fmheader.num_mesh_nodes;l++)
+       {
+               for (i=0 ; i < fmheader.num_tris ; i++)
+               {
+                       pos = i >> 3;
+                       bit = 1 << (i & 7 );
+                       if (!(pmnodes[l].tris[pos] & bit))
+                       {
+                               continue;
+                       }
+
+                       for (j=0 ; j<3 ; j++)
+                       {
+                               // get the xyz index
+                               for (k=0 ; k<fmheader.num_xyz ; k++)
+                               {
+                                       if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
+                                       {
+                                               break;  // this vertex is already in the base vertex list
+                                       }
+                               }
+
+                               if (k == fmheader.num_xyz)
+                               { // new index
+                                       VectorCopy (ptri[i].verts[j], base_xyz[fmheader.num_xyz]);
+
+                                       if(pmnodes[l].clustered == true)
+                                       {
+                                               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);
+                                       }
+
+                                       fmheader.num_xyz++;
+                               }
+
+                               pos = k >> 3;
+                               bit = 1 << (k & 7);
+                               pmnodes[l].verts[pos] |= bit;
+
+                               triangles[i].index_xyz[j] = k;
+
+                               // get the st index
+                               for (k=0 ; k<fmheader.num_st ; k++)
+                               {
+                                       if (triangle_st[i][j][0] == base_st[k].s
+                                       && triangle_st[i][j][1] == base_st[k].t)
+                                       {
+                                               break;  // this vertex is already in the base vertex list
+                                       }
+                               }
+
+                               if (k == fmheader.num_st)
+                               { // new index
+                                       base_st[fmheader.num_st].s = triangle_st[i][j][0];
+                                       base_st[fmheader.num_st].t = triangle_st[i][j][1];
+                                       fmheader.num_st++;
+                               }
+
+                               triangles[i].index_st[j] = k;
+                       }
+
+                       if (TransPixels || BaseTrueColor)
+                       {
+                               translucent[i] = CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);
+                       }
+                       else
+                       {
+                               translucent[i] = false;
+                       }
+               }
+       }
+
+       if (!BaseTrueColor)
+       {
+               SetPixel = true;
+               memset(BasePixels,0,BaseWidth*BaseHeight);
+               for (i=0 ; i < fmheader.num_tris ; i++)
+               {
+                       CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);
+               }
+               SetPixel = false;
+
+               skin_pixels_used = 0;
+               for(i=0;i<fmheader.skinheight;i++)
+               {
+                       for(j=0;j<fmheader.skinwidth;j++)
+                       {
+                               skin_pixels_used += BasePixels[(i*BaseWidth) + j];
+                       }
+               }
+               total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;
+       }
+       else
+       {
+               SetPixel = true;
+               memset(BasePixels,0,BaseWidth*BaseHeight*4);
+               for (i=0 ; i < fmheader.num_tris ; i++)
+               {
+                       CheckTransRecursiveTri(triangle_st[i][0], triangle_st[i][1], triangle_st[i][2]);
+               }
+               SetPixel = false;
+
+               skin_pixels_used = 0;
+               for(i=0;i<fmheader.skinheight;i++)
+               {
+                       for(j=0;j<fmheader.skinwidth;j++)
+                       {
+                               skin_pixels_used += BasePixels[((i*BaseWidth) + j)*4];
+                       }
+               }
+               total_skin_pixels = fmheader.skinheight*fmheader.skinwidth;
+       }
+
+       // build triangle strips / fans
+       BuildGlCmds ();
+
+       if (TransPixels)
+       {
+               free(TransPixels);
+       }
+       free (BasePixels);
+       if (BasePalette)
+       {
+               free (BasePalette);
+       }
+       free(ptri);
+}
+
+void Cmd_FMNodeOrder(void)
+{
+       mesh_node_t     *newnodes, *pos;
+       int                     i,j;
+
+       if (!pmnodes)
+       {
+               Error ("Base has not been established yet");
+       }
+
+       pos = newnodes = malloc(sizeof(mesh_node_t) * fmheader.num_mesh_nodes);
+
+       for(i=0;i<fmheader.num_mesh_nodes;i++)
+       {
+               GetScriptToken (false);
+
+               for(j=0;j<fmheader.num_mesh_nodes;j++)
+               {
+                       if (strcmpi(pmnodes[j].name, token) == 0)
+                       {
+                               *pos = pmnodes[j];
+                               pos++;
+                               break;
+                       }
+               }
+               if (j >= fmheader.num_mesh_nodes)
+               {
+                       Error("Node '%s' not in base list!\n", token);
+               }
+       }
+
+       free(pmnodes);
+       pmnodes = newnodes;
+}
+
+//===============================================================
+
+extern char    *FindFrameFile (char *frame);
+
+
+/*
+===============
+GrabFrame
+===============
+*/
+void GrabFrame (char *frame)
+{
+       triangle_t              *ptri;
+       int                             i, j;
+       fmtrivert_t             *ptrivert;
+       int                             num_tris;
+       char                    file1[1024];
+       fmframe_t               *fr;
+       int                             index_xyz;
+       char                    *framefile;
+
+       // the frame 'run1' will be looked for as either
+       // run.1 or run1.tri, so the new alias sequence save
+       // feature an be used
+       framefile = FindFrameFile (frame);
+
+       sprintf (file1, "%s/%s", cdarchive, framefile);
+       ExpandPathAndArchive (file1);
+
+       sprintf (file1, "%s/%s",cddir, framefile);
+
+       printf ("grabbing %s  ", file1);
+
+       if (fmheader.num_frames >= MAX_FM_FRAMES)
+               Error ("fmheader.num_frames >= MAX_FM_FRAMES");
+       fr = &g_frames[fmheader.num_frames];
+       fmheader.num_frames++;
+
+       strcpy (fr->name, frame);
+
+//
+// load the frame
+//
+       if (do3ds)
+               Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);
+       else
+               LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);
+
+       if (num_tris != fmheader.num_tris)
+               Error ("%s: number of triangles (%d) doesn't match base frame (%d)\n", file1, num_tris, fmheader.num_tris);
+
+//
+// allocate storage for the frame's vertices
+//
+       ptrivert = fr->v;
+
+       for (i=0 ; i<fmheader.num_xyz ; i++)
+       {
+               ptrivert[i].vnorm.numnormals = 0;
+               VectorClear (ptrivert[i].vnorm.normalsum);
+       }
+       ClearBounds (fr->mins, fr->maxs);
+
+//
+// store the frame's vertices in the same order as the base. This assumes the
+// triangles and vertices in this frame are in exactly the same order as in the
+// base
+//
+       for (i=0 ; i<num_tris ; i++)
+       {
+               vec3_t  vtemp1, vtemp2, normal;
+               float   ftemp;
+
+               VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);
+               VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);
+               CrossProduct (vtemp1, vtemp2, normal);
+
+               VectorNormalize (normal, normal);
+
+       // rotate the normal so the model faces down the positive x axis
+               ftemp = normal[0];
+               normal[0] = -normal[1];
+               normal[1] = ftemp;
+
+               for (j=0 ; j<3 ; j++)
+               {
+                       index_xyz = triangles[i].index_xyz[j];
+
+               // rotate the vertices so the model faces down the positive x axis
+               // also adjust the vertices to the desired origin
+                       ptrivert[index_xyz].v[0] = ((-ptri[i].verts[j][1]) * scale_up) +
+                                                                               adjust[0];
+                       ptrivert[index_xyz].v[1] = (ptri[i].verts[j][0] * scale_up) +
+                                                                               adjust[1];
+                       ptrivert[index_xyz].v[2] = (ptri[i].verts[j][2] * scale_up) +
+                                                                               adjust[2];
+
+                       AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
+
+                       VectorAdd (ptrivert[index_xyz].vnorm.normalsum, normal, ptrivert[index_xyz].vnorm.normalsum);
+                       ptrivert[index_xyz].vnorm.numnormals++;
+               }
+       }
+
+//
+// calculate the vertex normals, match them to the template list, and store the
+// index of the best match
+//
+       for (i=0 ; i<fmheader.num_xyz ; i++)
+       {
+               int             j;
+               vec3_t  v;
+               float   maxdot;
+               int             maxdotindex;
+               int             c;
+
+               c = ptrivert[i].vnorm.numnormals;
+               if (!c)
+                       Error ("Vertex with no triangles attached");
+
+               VectorScale (ptrivert[i].vnorm.normalsum, 1.0/c, v);
+               VectorNormalize (v, v);
+
+               maxdot = -999999.0;
+               maxdotindex = -1;
+
+               for (j=0 ; j<NUMVERTEXNORMALS ; j++)
+               {
+                       float   dot;
+
+                       dot = DotProduct (v, avertexnormals[j]);
+                       if (dot > maxdot)
+                       {
+                               maxdot = dot;
+                               maxdotindex = j;
+                       }
+               }
+
+               ptrivert[i].lightnormalindex = maxdotindex;
+       }
+
+       free (ptri);
+}
+
+/*
+===============
+Cmd_Frame      
+===============
+*/
+void Cmd_FMFrame (void)
+{
+       while (ScriptTokenAvailable())
+       {
+               GetScriptToken (false);
+               if (g_skipmodel)
+                       continue;
+               if (g_release || g_archive)
+               {
+                       fmheader.num_frames = 1;        // don't skip the writeout
+                       continue;
+               }
+
+               H_printf("#define FRAME_%-16s\t%i\n", token, fmheader.num_frames);
+
+               if((g_skelModel.type != SKEL_NULL) || (g_skelModel.references != REF_NULL))
+               {
+                       GrabModelTransform(token);
+               }
+
+               GrabFrame (token);
+
+               if(g_skelModel.type != SKEL_NULL)
+               {
+                       GrabSkeletalFrame(token);
+               }
+
+               if(g_skelModel.references != REF_NULL)
+               {
+                       GrabReferencedFrame(token);
+               }
+
+               // need to add the up and dir points to the frame bounds here
+               // using AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
+               // then remove fudge in determining scale on frame write out
+       }
+}
+
+/*
+===============
+Cmd_Skin
+
+Skins aren't actually stored in the file, only a reference
+is saved out to the header file.
+===============
+*/
+void Cmd_FMSkin (void)
+{
+       byte            *palette;
+       byte            *pixels;
+       int                     width, height;
+       byte            *cropped;
+       int                     y;
+       char            name[1024], savename[1024], transname[1024], extension[256];
+       miptex32_t      *qtex32;
+       int                     size;
+       FILE            *FH;
+       qboolean        TrueColor;
+
+       GetScriptToken (false);
+
+       if (fmheader.num_skins == MAX_FM_SKINS)
+               Error ("fmheader.num_skins == MAX_FM_SKINS");
+
+       if (g_skipmodel)
+               return;
+
+       sprintf (name, "%s/%s", cdarchive, token);
+       strcpy (name, ExpandPathAndArchive( name ) );
+//     sprintf (name, "%s/%s.lbm", cddir, token);
+
+       if (ScriptTokenAvailable())
+       {
+               GetScriptToken (false);
+               sprintf (g_skins[fmheader.num_skins], "!%s", token);
+               sprintf (savename, "%s!%s", g_outputDir, token);
+               sprintf (transname, "%s!%s_a.pcx", gamedir, token);
+       }
+       else
+       {
+               sprintf (g_skins[fmheader.num_skins], "%s/!%s", cdpartial, token);
+               sprintf (savename, "%s/!%s", g_outputDir, token);
+               sprintf (transname, "%s/!%s_a.pcx", cddir, token);
+       }
+
+       fmheader.num_skins++;
+
+       if (g_skipmodel || g_release || g_archive)
+               return;
+
+       // load the image
+       printf ("loading %s\n", name);
+       ExtractFileExtension (name, extension);
+       if (extension[0] == 0)
+       {
+               strcat(name, ".pcx");
+       }
+
+
+       TrueColor = LoadAnyImage (name, &pixels, &palette, &width, &height);
+//     RemapZero (pixels, palette, width, height);
+
+       // crop it to the proper size
+
+       if (!TrueColor)
+       {
+               cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight, "Cmd_FMSkin");
+               for (y=0 ; y<fmheader.skinheight ; y++)
+               {
+                       memcpy (cropped+y*fmheader.skinwidth,
+                               pixels+y*width, fmheader.skinwidth);
+               }
+
+               TransPixels = NULL;
+               FH = fopen(transname,"rb");
+               if (FH)
+               {
+                       fclose(FH);
+
+                       strcat(g_skins[fmheader.num_skins-1],".pcx");
+                       strcat(savename,".pcx");
+
+                       // save off the new image
+                       printf ("saving %s\n", savename);
+                       CreatePath (savename);
+                       WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);
+               }
+               else
+               {
+       #if 1
+               miptex_t        *qtex;
+                       qtex = CreateMip(cropped, fmheader.skinwidth, fmheader.skinheight, palette, &size, true);
+
+                       strcat(g_skins[fmheader.num_skins-1],".m8");
+                       strcat(savename,".m8");
+
+                       printf ("saving %s\n", savename);
+                       CreatePath (savename);
+                       SaveFile (savename, (byte *)qtex, size);
+                       free(qtex);
+       #else
+                       strcat(g_skins[fmheader.num_skins-1],".pcx");
+                       strcat(savename,".pcx");
+
+                       // save off the new image
+                       printf ("saving %s\n", savename);
+                       CreatePath (savename);
+                       WritePCXfile (savename, cropped, fmheader.skinwidth, fmheader.skinheight, palette);
+       #endif
+               }
+       }
+       else
+       {
+               cropped = (byte *) SafeMalloc (fmheader.skinwidth*fmheader.skinheight*4, "Cmd_FMSkin");
+               for (y=0 ; y<fmheader.skinheight ; y++)
+               {
+                       memcpy (cropped+((y*fmheader.skinwidth)*4), pixels+(y*width*4), fmheader.skinwidth*4);
+               }
+
+               qtex32 = CreateMip32((unsigned *)cropped, fmheader.skinwidth, fmheader.skinheight, &size, true);
+
+           StripExtension(g_skins[fmheader.num_skins-1]);
+               strcat(g_skins[fmheader.num_skins-1],".m32");
+           StripExtension(savename);
+               strcat(savename,".m32");
+
+               printf ("saving %s\n", savename);
+               CreatePath (savename);
+               SaveFile (savename, (byte *)qtex32, size);
+       }
+
+       free (pixels);
+       if (palette)
+       {
+               free (palette);
+       }
+       free (cropped);
+}
+
+
+/*
+===============
+Cmd_Cd
+===============
+*/
+void Cmd_FMCd (void)
+{
+       char temp[256];
+
+       FinishModel ();
+       ClearModel ();
+
+       GetScriptToken (false);
+
+       // this is a silly mess...
+       sprintf(cdpartial, "models/%s", token); 
+       sprintf(cdarchive, "%smodels/%s", gamedir+strlen(qdir), token); 
+       sprintf(cddir, "%s%s", gamedir, cdpartial);
+
+       // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.
+       sprintf(temp, "%s%s", g_outputDir, cdpartial);
+       strcpy(g_outputDir, temp);
+
+       // if -only was specified and this cd doesn't match,
+       // skip the model (you only need to match leading chars,
+       // so you could regrab all monsters with -only monsters)
+       if (!g_only[0])
+               return;
+       if (strncmp(token, g_only, strlen(g_only)))
+       {
+               g_skipmodel = true;
+               printf ("skipping %s\n", cdpartial);
+       }
+}
+
+
+/*
+
+//=======================
+//             NEW GEN
+//=======================
+
+void NewGen (char *ModelFile, char *OutputName, int width, int height)
+{
+       trigroup_t  *triangles;
+       triangle_t      *ptri;
+       triangle_t      *grouptris;
+       mesh_node_t     *pmnodes;
+
+       vec3_t          *vertices;
+       vec3_t          *uvs;
+       vec3_t          aveNorm, crossvect;
+       vec3_t          diffvect1, diffvect2;
+       vec3_t          v0, v1, v2;
+       vec3_t          n, u, v;
+       vec3_t          base, zaxis, yaxis;
+       vec3_t          uvwMin, uvwMax;
+       vec3_t          groupMin, groupMax;
+       vec3_t          uvw;
+       
+       float           *uFinal, *vFinal;
+       unsigned char   *newpic;
+
+       int                     finalstart = 0, finalcount = 0;
+       int                     xbase = 0, xwidth = 0, ywidth = 0;
+       int                     *todo, *done, finished;
+       int                     i, j, k, l; //counters
+       int                     groupnum, numtris, numverts, num;
+       int                     count;
+       FILE            *grpfile;
+       long            datasize;
+
+       for ( i = 0; i<3; i++)
+       {
+               aveNorm[i] = 0;
+               uvwMin[i] = 1e30f;
+               uvwMax[i] = -1e30f;
+       }
+
+       pmnodes = NULL;
+       ptri = NULL;
+       triangles = NULL;
+       
+       zaxis[0] = 0;
+       zaxis[1] = 0;
+       zaxis[2] = 1;
+
+       yaxis[0] = 0;
+       yaxis[1] = 1;
+       yaxis[2] = 0;
+
+       LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
+
+       todo = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");
+       done = (int*)SafeMalloc(fmheader.num_tris*sizeof(int), "NewGen");
+       triangles = (trigroup_t*)SafeMalloc(fmheader.num_tris*sizeof(trigroup_t), "NewGen");
+       
+       for ( i=0; i < fmheader.num_tris; i++)
+       {
+               todo[i] = false;
+               done[i] = false;
+               triangles[i].triangle = ptri[i];
+               triangles[i].group = 0;
+       }
+
+       groupnum = 0;
+
+//  transitive closure algorithm follows
+//  put all triangles who transitively share vertices into separate groups
+
+       while (1)
+       {
+               for ( i = 0; i < fmheader.num_tris; i++)
+               {
+                       if (!done[i])
+                       {
+                               break;
+                       }
+               }
+               if ( i == fmheader.num_tris)
+               {
+                       break;
+               }
+               finished = false;
+               todo[i] = true;
+               while (!finished)
+               {
+                       finished = true;
+                       for ( i = 0; i < fmheader.num_tris; i++)
+                       {
+                               if (todo[i])
+                               {
+                                       done[i] = true;
+                                       triangles[i].group = groupnum;
+                                       todo[i] = false;
+                                       for ( j = 0; j < fmheader.num_tris; j++)
+                                       {
+                                               if ((!done[j]) && (ShareVertex(triangles[i],triangles[j])))
+                                               {
+                                                       todo[j] = true;
+                                                       finished = false;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               groupnum++;
+       }
+               uFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");
+               vFinal = (float*)SafeMalloc(3*fmheader.num_tris*sizeof(float), "NewGen");
+
+       grpfile = fopen("grpdebug.txt","w");
+
+       
+       for (i = 0; i < groupnum; i++)
+       {
+
+               fprintf(grpfile,"Group Number: %d\n", i);
+               
+               numtris = GetNumTris(triangles, i); // number of triangles in group i
+               numverts = numtris * 3;
+
+               fprintf(grpfile,"%d triangles.\n", numtris);
+
+               vertices = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");
+               uvs = (vec3_t*)SafeMalloc(numverts*sizeof(vec3_t), "NewGen");
+               grouptris = (triangle_t*)SafeMalloc(numtris*sizeof(triangle_t), "NewGen");
+               
+               for (count = 0; count < fmheader.num_tris; count++)
+               {
+                       if (triangles[count].group == i)
+                       {
+                               fprintf(grpfile,"Triangle %d\n", count);
+                       }
+               }
+               fprintf(grpfile,"\n");
+
+               
+               
+               
+               GetOneGroup(triangles, i, grouptris);           
+               
+               num = 0;
+               for (j = 0; j < numtris; j++)
+               {
+                       VectorCopy(grouptris[j].verts[0], v0);
+                       VectorCopy(grouptris[j].verts[1], v1);
+                       VectorCopy(grouptris[j].verts[2], v2);
+                       VectorSubtract(v1, v0, diffvect1);
+                       VectorSubtract(v2, v1, diffvect2);
+                       CrossProduct( diffvect1, diffvect2, crossvect);
+                       VectorAdd(aveNorm, crossvect, aveNorm); 
+                       VectorCopy(v0,vertices[num]);
+                       num++;                                  //  FIXME
+                       VectorCopy(v1,vertices[num]);
+                       num++;                                  //  add routine to add only verts that
+                       VectorCopy(v2,vertices[num]);
+                       num++;                                  // have not already been added
+               }
+
+               assert (num >= 3);
+// figure out the best plane projections
+               DOsvdPlane ((float*)vertices, num, (float *)&n, (float *)&base);
+               
+               if (DotProduct(aveNorm,n) < 0.0f)
+               {
+                       VectorScale(n, -1.0f, n);
+               }
+               VectorNormalize(n,n);
+               if (fabs(n[2]) < .57)
+               {
+                       CrossProduct( zaxis, n, crossvect);
+                       VectorCopy(crossvect, u);
+               }
+               else
+               {
+                       CrossProduct( yaxis, n, crossvect);
+                       VectorCopy(crossvect, u);
+               }
+               VectorNormalize(u,u);
+               CrossProduct( n, u, crossvect);
+               VectorCopy(crossvect, v);
+               VectorNormalize(v,v);
+
+               num = 0;
+
+               for ( j = 0; j < 3; j++)
+               {
+                       groupMin[j] = 1e30f;
+                       groupMax[j] = -1e30f;
+               }
+               
+               for ( j = 0; j < numtris; j++)
+               {
+                       for ( k = 0; k < 3; k++)
+                       {
+                               VectorCopy(grouptris[j].verts[k],v0);
+                               VectorSubtract(v0, base, v0);
+                               uvw[0] = DotProduct(v0, u);
+                               uvw[1] = DotProduct(v0, v);
+                               uvw[2] = DotProduct(v0, n);
+                               VectorCopy(uvw,uvs[num]);
+                               num++;
+                               for ( l = 0; l < 3; l++)
+                               {
+                                       if (uvw[l] < groupMin[l])
+                                       {
+                                               groupMin[l] = uvw[l];
+                                       }
+                                       if (uvw[l] > groupMax[l])
+                                       {
+                                               groupMax[l] = uvw[l];
+                                       }
+                               }
+                       }
+               }
+               
+               xwidth = ceil(0 - groupMin[0]) + 2; // move right of origin and avoid overlap
+               ywidth = ceil(0 - groupMin[1]) + 2; // move "above" origin
+               
+               for ( j=0; j < numverts; j++)
+               {
+                       uFinal[finalcount] = uvs[j][0] + xwidth + xbase;                        
+                       vFinal[finalcount] = uvs[j][1] + ywidth;
+                       if (uFinal[finalcount] < uvwMin[0])
+                       {
+                               uvwMin[0] = uFinal[finalcount];
+                       }
+                       if (uFinal[finalcount] > uvwMax[0])
+                       {
+                               uvwMax[0] = uFinal[finalcount];
+                       }
+                       if (vFinal[finalcount] < uvwMin[1])
+                       {
+                               uvwMin[1] = vFinal[finalcount];
+                       }
+                       if (vFinal[finalcount] > uvwMax[1])
+                       {
+                               uvwMax[1] = vFinal[finalcount];
+                       }
+                       finalcount++;
+               }
+
+               fprintf(grpfile,"svdPlaned Group min: ( %f , %f )\n",groupMin[0] + xwidth + xbase, groupMin[1] + ywidth);
+               fprintf(grpfile,"svdPlaned Group max: ( %f , %f )\n",groupMax[0] + xwidth + xbase, groupMax[1] + ywidth);
+               
+               finalcount = finalstart;
+
+               for ( count = 0; count < numverts; count++)
+               {
+                       fprintf(grpfile,"Vertex %d: ( %f , %f , %f )\n",count,vertices[count][0],vertices[count][1],vertices[count][2]);
+                       fprintf(grpfile,"svdPlaned: ( %f , %f )\n",uFinal[finalcount],vFinal[finalcount++]);
+               }
+               
+               finalstart = finalcount;
+
+               fprintf(grpfile,"\n");
+               
+               free(vertices);
+               free(uvs);
+               free(grouptris);
+
+               xbase += ceil(groupMax[0] - groupMin[0]) + 2;           
+
+       }       
+
+       fprintf(grpfile,"Global Min ( %f , %f )\n",uvwMin[0],uvwMin[1]);
+       fprintf(grpfile,"Global Max ( %f , %f )\n",uvwMax[0],uvwMax[1]);
+
+
+       ScaleTris(uvwMin, uvwMax, width, height, uFinal, vFinal, finalcount);
+
+       for (k = 0; k < finalcount; k++)
+       {
+               fprintf(grpfile, "scaled vertex %d: ( %f , %f )\n",k,uFinal[k],vFinal[k]);
+       }
+       
+       //  i've got the array of vertices in uFinal and vFinal.  Now I need to write them and draw lines
+
+       datasize = width * height*sizeof(unsigned char);
+       newpic = (unsigned char*)SafeMalloc(datasize, "NewGen");
+       memset(newpic,0,datasize);
+       memset(pic_palette,0,sizeof(pic_palette));
+       pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;
+
+       k = 0;
+       while (k < finalcount)
+       {
+               NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);
+               k++;
+               NewDrawLine(uFinal[k], vFinal[k], uFinal[k+1], vFinal[k+1], newpic, width, height);
+               k++;
+               NewDrawLine(uFinal[k], vFinal[k], uFinal[k-2], vFinal[k-2], newpic, width, height);
+               k++;
+               fprintf(grpfile, "output tri with verts %d, %d, %d", k-2, k-1, k);
+       }
+
+       WritePCXfile (OutputName, newpic, width, height, pic_palette);
+
+       fclose(grpfile);
+
+       free(todo);
+       free(done);
+       free(triangles);
+       free(newpic);   
+       return;
+}
+void NewDrawLine(int x1, int y1, int x2, int y2, unsigned char* picture, int width, int height)
+{
+       long dx, dy;
+       long adx, ady;
+       long count;
+       float xfrac, yfrac, xstep, ystep;
+       unsigned long sx, sy;
+       float u, v;
+
+       dx = x2 - x1;
+       dy = y2 - y1;
+       adx = abs(dx);
+       ady = abs(dy);
+
+       count = adx > ady ? adx : ady;
+       count++;
+
+       if(count > 300)
+       {
+               printf("Bad count\n");
+               return; // don't ever hang up on bad data
+       }
+               
+       xfrac = x1;
+       yfrac = y1;
+       
+       xstep = (float)dx/count;
+       ystep = (float)dy/count;
+
+       switch(LineType)
+       {
+               case LINE_NORMAL:
+                       do
+                       {
+                               if(xfrac < width && yfrac < height)
+                               {
+                                       picture[(long)yfrac*width+(long)xfrac] = LineColor;
+                               }
+                               xfrac += xstep;
+                               yfrac += ystep;
+                               count--;
+                       } while (count > 0);
+                       break;
+               case LINE_FAT:
+                       do
+                       {
+                               for (u=-0.1 ; u<=0.9 ; u+=0.999)
+                               {
+                                       for (v=-0.1 ; v<=0.9 ; v+=0.999)
+                                       {
+                                               sx = xfrac+u;
+                                               sy = yfrac+v;
+                                               if(sx < width && sy < height)
+                                               {
+                                                       picture[sy*width+sx] = LineColor;
+                                               }
+                                       }
+                               }
+                               xfrac += xstep;
+                               yfrac += ystep;
+                               count--;
+                       } while (count > 0);
+                       break;
+               case LINE_DOTTED:
+                       do
+                       {
+                               if(count&1 && xfrac < width &&
+                                       yfrac < height)
+                               {
+                                       picture[(long)yfrac*width+(long)xfrac] = LineColor;
+                               }
+                               xfrac += xstep;
+                               yfrac += ystep;
+                               count--;
+                       } while (count > 0);
+                       break;
+               default:
+                       Error("Unknown <linetype> %d.\n", LineType);
+       }
+}
+*/
+void ScaleTris( vec3_t min, vec3_t max, int Width, int Height, float* u, float* v, int verts)
+{
+
+       int             i;
+       float   hscale, vscale;
+       float   scale;
+
+       hscale = max[0];
+       vscale = max[1];
+
+       hscale = (Width-2) / max[0];
+       vscale = (Height-2) / max[1];
+
+       scale = hscale;
+       if (scale > vscale)
+       {
+               scale = vscale;
+       }
+       for ( i = 0; i<verts; i++)
+       {
+               u[i] *= scale;
+               v[i] *= scale;
+       }
+       return;
+}
+
+
+void GetOneGroup(trigroup_t *tris, int grp, triangle_t* triangles)
+{
+       int     i;
+       int j;
+
+       j = 0;
+       for (i = 0; i < fmheader.num_tris; i++)
+       {
+               if (tris[i].group == grp)
+               {
+                       triangles[j++] = tris[i].triangle;
+               }
+       }
+       return;
+}
+
+
+int GetNumTris( trigroup_t *tris, int grp)
+{
+       int i;
+       int verts;
+
+       verts = 0;
+       for (i = 0; i < fmheader.num_tris; i++)
+       {
+               if (tris[i].group == grp)
+               {
+                       verts++;
+               }
+       }
+       return verts;
+}
+
+
+int ShareVertex( trigroup_t trione, trigroup_t tritwo)
+{
+       int     i;
+       int     j;
+
+       i = 1;
+       j = 1;
+       for ( i = 0; i < 3; i++)
+       {
+               for ( j = 0; j < 3; j++)
+               {
+                       if (DistBetween(trione.triangle.verts[i],tritwo.triangle.verts[j]) < TRIVERT_DIST)
+                       {
+                               return true; 
+                       }
+               }
+       }
+       return false;
+}
+
+
+float DistBetween(vec3_t point1, vec3_t point2)
+{
+       float   dist;
+
+       dist = (point1[0] - point2[0]);
+       dist *= dist;
+       dist += (point1[1] - point2[1])*(point1[1]-point2[1]);
+       dist += (point1[2] - point2[2])*(point1[2]-point2[2]);
+       dist = sqrt(dist);
+       return dist;
+}
+
+
+void GenSkin(char *ModelFile, char *OutputName, int Width, int Height)
+{
+       triangle_t      *ptri;
+       mesh_node_t *pmnodes;
+       int                     i;
+
+       pmnodes = NULL;
+       ptri = NULL;
+
+       LoadTriangleList (ModelFile, &ptri, &fmheader.num_tris, &pmnodes, &fmheader.num_mesh_nodes);
+       if (g_ignoreTriUV)
+       {
+               for (i=0;i<fmheader.num_tris;i++)
+               {
+                       ptri[i].HasUV=0;
+               }
+       }
+
+       memset(pic,0,sizeof(pic));
+       memset(pic_palette,0,sizeof(pic_palette));
+       pic_palette[767] = pic_palette[766] = pic_palette[765] = 255;
+
+       ScaleWidth = Width;
+       ScaleHeight = Height;
+
+       BuildST (ptri, fmheader.num_tris, true);
+
+       WritePCXfile (OutputName, pic, SKINPAGE_WIDTH, SKINPAGE_HEIGHT, pic_palette);
+
+       printf("Gen Skin Stats:\n");
+       printf("   Input Base: %s\n",ModelFile);
+       printf("   Input Dimensions: %d,%d\n",Width,Height);
+       printf("\n");
+       printf("   Output File: %s\n",OutputName);
+       printf("   Output Dimensions: %d,%d\n",ScaleWidth,ScaleHeight);
+
+       if (fmheader.num_mesh_nodes)
+       {
+               printf("\nNodes:\n");
+               for(i=0;i<fmheader.num_mesh_nodes;i++)
+               {
+                       printf("   %s\n",pmnodes[i].name);
+               }
+       }
+
+       free(ptri);
+       free(pmnodes);
+}
+
+
+void Cmd_FMBeginGroup (void)
+{
+       GetScriptToken (false);
+
+       g_no_opimizations = false;
+
+       groups[num_groups].start_frame = fmheader.num_frames;
+       groups[num_groups].num_frames = 0;
+
+       groups[num_groups].degrees = atol(token);
+       if (groups[num_groups].degrees < 1 || groups[num_groups].degrees > 32)
+       {
+               Error ("Degrees of freedom out of range: %d",groups[num_groups].degrees);
+       }
+}
+
+void Cmd_FMEndGroup (void)
+{
+       groups[num_groups].num_frames = fmheader.num_frames - groups[num_groups].start_frame;
+
+       if(num_groups < MAX_GROUPS - 1)
+       {
+               num_groups++;
+       }
+       else
+       {
+               Error("Number of compression groups exceded: %i\n", MAX_GROUPS);
+       }
+}
+