-/*\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, ¤t->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 );
+
+#if !GDEF_OS_WINDOWS
+
+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, ¤t->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 );
+ }
+}