/* Copyright (C) 1999-2006 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 "qdata.h" #include #include "jointed.h" #include "fmodel.h" //================================================================= typedef struct { int numnormals; vec3_t normalsum; } vertexnormals_t; typedef struct { vec3_t v; int lightnormalindex; } trivert_t; typedef struct { vec3_t mins, maxs; char name[16]; trivert_t v[MAX_VERTS]; QDataJoint_t joints[NUM_CLUSTERS]; // ,this } frame_t; // ,and all of this should get out of here, need to use new stuff in fmodels instead typedef struct IntListNode_s { int data; struct IntListNode_s *next; } IntListNode_t; // gaak typedef struct { float scale[3]; // multiply byte verts by this float translate[3]; // then add this } PartialAliasFrame_t; int jointed; int clustered; int *clusters[NUM_CLUSTERS]; IntListNode_t *vertLists[NUM_CLUSTERS]; int num_verts[NUM_CLUSTERS + 1]; int new_num_verts[NUM_CLUSTERS + 1]; // end that //================================================================ frame_t g_frames[MAX_FRAMES]; //frame_t *g_frames; static dmdl_t model; float scale_up; // set by $scale vec3_t adjust; // set by $origin int g_fixedwidth, g_fixedheight; // set by $skinsize // // base frame info // dstvert_t base_st[MAX_VERTS]; dtriangle_t triangles[MAX_TRIANGLES]; static int triangle_st[MAX_TRIANGLES][3][2]; // the command list holds counts, s/t values, and xyz indexes // that are valid for every frame int commands[16384]; int numcommands; int numglverts; int used[MAX_TRIANGLES]; char g_skins[MAX_MD2SKINS][64]; char cdarchive[1024]; char cdpartial[1024]; char cddir[1024]; char modelname[64]; // empty unless $modelname issued (players) extern char *g_outputDir; #define NUMVERTEXNORMALS 162 float avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768]; FILE *headerouthandle = NULL; //============================================================== /* =============== ClearModel =============== */ static void ClearModel (void) { memset (&model, 0, sizeof(model)); modelname[0] = 0; jointed = NOT_JOINTED; clustered = 0; scale_up = 1.0; VectorCopy (vec3_origin, adjust); g_fixedwidth = g_fixedheight = 0; g_skipmodel = false; } void H_printf(char *fmt, ...) { va_list argptr; char name[1024]; if (!headerouthandle) { sprintf (name, "%s/tris.h", cddir); headerouthandle = SafeOpenWrite (name); fprintf(headerouthandle, "// %s\n\n", cddir); fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n"); } va_start (argptr, fmt); vfprintf (headerouthandle, fmt, argptr); va_end (argptr); } #if 1 /* ============ WriteModelFile ============ */ void WriteCommonModelFile (FILE *modelouthandle, PartialAliasFrame_t *outFrames) { int i; dmdl_t modeltemp; int j, k; frame_t *in; daliasframe_t *out; byte buffer[MAX_VERTS*4+128]; float v; int c_on, c_off; model.version = ALIAS_VERSION; model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz]; model.num_glcmds = numcommands; model.ofs_skins = sizeof(dmdl_t); model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME; model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t); model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t); model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize; model.ofs_end = model.ofs_glcmds + model.num_glcmds*sizeof(int); // // write out the model header // for (i=0 ; iname, in->name); for (j=0 ; j<3 ; j++) { out->scale[j] = (in->maxs[j] - in->mins[j])/255; out->translate[j] = in->mins[j]; if(outFrames) { outFrames[i].scale[j] = out->scale[j]; outFrames[i].translate[j] = out->translate[j]; } } for (j=0 ; jverts[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, model.framesize); } // // write out glcmds // SafeWrite (modelouthandle, commands, numcommands*4); } /* ============ WriteModelFile ============ */ void WriteModelFile (FILE *modelouthandle) { model.ident = IDALIASHEADER; WriteCommonModelFile(modelouthandle, NULL); } /* ============ WriteJointedModelFile ============ */ void WriteJointedModelFile (FILE *modelouthandle) { int i; int j, k; frame_t *in; float v; IntListNode_t *current, *toFree; PartialAliasFrame_t outFrames[MAX_FRAMES]; model.ident = IDJOINTEDALIASHEADER; WriteCommonModelFile(modelouthandle, outFrames); // Skeletal Type SafeWrite(modelouthandle, &jointed, sizeof(int)); // number of joints SafeWrite(modelouthandle, &numJointsForSkeleton[jointed], sizeof(int)); // number of verts in each cluster SafeWrite(modelouthandle, &new_num_verts[1], sizeof(int)*numJointsForSkeleton[jointed]); // cluster verts for(i = 0; i < new_num_verts[0]; ++i) { current = vertLists[i]; while(current) { SafeWrite (modelouthandle, ¤t->data, sizeof(int)); toFree = current; current = current->next; free(toFree); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base } } for (i=0 ; ijoints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].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; } // write out origin as a float (there's only a few per model, so it's not really // a size issue) SafeWrite (modelouthandle, &v, sizeof(float)); } for (k=0 ; k<3 ; k++) { v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].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; } // write out origin as a float (there's only a few per model, so it's not really // a size issue) SafeWrite (modelouthandle, &v, sizeof(float)); } for (k=0 ; k<3 ; k++) { v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].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; } // write out origin as a float (there's only a few per model, so it's not really // a size issue) SafeWrite (modelouthandle, &v, sizeof(float)); } } } } #else /* ============ WriteModelFile ============ */ static void WriteModelFile (FILE *modelouthandle) { int i; dmdl_t modeltemp; int j, k; frame_t *in; daliasframe_t *out; byte buffer[MAX_VERTS*4+128]; float v; int c_on, c_off; model.ident = IDALIASHEADER; model.version = ALIAS_VERSION; model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz]; model.num_glcmds = numcommands; model.ofs_skins = sizeof(dmdl_t); model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME; model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t); model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t); model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize; model.ofs_end = model.ofs_glcmds + model.num_glcmds*4; // // write out the model header // for (i=0 ; iname, in->name); for (j=0 ; j<3 ; j++) { out->scale[j] = (in->maxs[j] - in->mins[j])/255; out->translate[j] = in->mins[j]; } for (j=0 ; jverts[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, model.framesize); } // // write out glcmds // SafeWrite (modelouthandle, commands, numcommands*4); } #endif /* =============== FinishModel =============== */ void FinishModel (void) { FILE *modelouthandle; int i; char name[1024]; if (!model.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.md2", cdpartial); ReleaseFile (name); for (i=0 ; iindex_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] ; jindex_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]) 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 ; jindex_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] ; jindex_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]) 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 bestlen) { besttype = type; bestlen = len; for (j=0 ; j= 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 // for (i=0 ; i 0) { basex = iwidth + 2; } else { basex = 2; } basey = 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); } } // make the width a multiple of 4; some hardware requires this, and it ensures // dword alignment for each scan swidth = iwidth*2; model.skinwidth = (swidth + 3) & ~3; model.skinheight = iheight; } #endif //========================================================================== // // DrawScreen // //========================================================================== void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight) { int i; byte *scrpos; char buffer[256]; // Divider scrpos = &pic[(INFO_Y-2)*SKINPAGE_WIDTH]; for(i = 0; i < SKINPAGE_WIDTH; i++) { *scrpos++ = 255; } sprintf(buffer, "GENSKIN: "); DrawTextChar(16, INFO_Y, buffer); sprintf(buffer, "( %03d * %03d ) SCALE %f %f, SKINWIDTH %d," " SKINHEIGHT %d", (int)ScaleWidth, (int)ScaleHeight, s_scale, t_scale, (int)iwidth*2, (int)iheight); DrawTextChar(80, INFO_Y, buffer); } /* ============ BuildST Builds the triangle_st array for the base frame and model.skinwidth / model.skinheight FIXME: allow this to be loaded from a file for arbitrary mappings ============ */ void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin) { 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; // // find bounds of all the verts on the base frame // ClearBounds (mins, maxs); for (i=0 ; i= 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) DrawScreen(s_scale, t_scale, iwidth, 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 // for (i=0 ; i 0) { basex = iwidth + 2; } else { basex = 2; } basey = 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); } } 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*2; model.skinwidth = (swidth + 3) & ~3; model.skinheight = iheight; } 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 < num_verts[0]; ++j) { 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; } } } } /* ================= Cmd_Base ================= */ void Cmd_Base (void) { vec3_t base_xyz[MAX_VERTS]; triangle_t *ptri; int i, j, k; #if 1 #else int time1; #endif char file1[1024]; char file2[1024]; GetScriptToken (false); if (g_skipmodel || g_release || g_archive) return; printf ("---------------------\n"); #if 1 sprintf (file1, "%s/%s", cdpartial, token); printf ("%s ", file1); ExpandPathAndArchive (file1); sprintf (file1, "%s/%s", cddir, token); #else sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext); printf ("%s\n", file1); ExpandPathAndArchive (file1); sprintf (file1, "%s/%s.%s", cddir, token, trifileext); time1 = FileTime (file1); if (time1 == -1) Error ("%s doesn't exist", file1); #endif // // load the base triangles // if (do3ds) Load3DSTriangleList (file1, &ptri, &model.num_tris, NULL, NULL); else LoadTriangleList (file1, &ptri, &model.num_tris, NULL, NULL); GetScriptToken (false); sprintf (file2, "%s/%s.pcx", cddir, token); // sprintf (trans_file, "%s/!%s_a.pcx", cddir, token); printf ("skin: %s\n", file2); Load256Image (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight); if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT) { if (g_allow_newskin) { ScaleWidth = BaseWidth; ScaleHeight = BaseHeight; } else { Error("Invalid skin page size: (%d,%d) should be (%d,%d)", BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT); } } else { ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X, ENCODED_WIDTH_Y); ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X, ENCODED_HEIGHT_Y); } // // get the ST values // BuildST (ptri, model.num_tris,false); // // 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 (i=0 ; i= '0' && *s <= '9') s--; strcpy (suffix, s+1); strcpy (base, frame); base[s-frame+1] = 0; sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "hrc"); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s%s.%s", base, suffix, "hrc"); return retname; } sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "asc"); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s%s.%s", base, suffix, "asc"); return retname; } sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "tri"); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s%s.%s", base, suffix, "tri"); return retname; } sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "3ds"); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s%s.%s", base, suffix, "3ds"); return retname; } sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "htr"); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s%s.%s", base, suffix, "htr"); return retname; } // check for 'run.1' sprintf (file1, "%s/%s.%s",cddir, base, suffix); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s.%s", base, suffix); return retname; } Error ("frame %s could not be found",frame); return NULL; } /* =============== GrabFrame =============== */ static void GrabFrame (char *frame) { triangle_t *ptri; int i, j; trivert_t *ptrivert; int num_tris; char file1[1024]; frame_t *fr; vertexnormals_t vnorms[MAX_VERTS]; 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 (model.num_frames >= MAX_FRAMES) Error ("model.num_frames >= MAX_FRAMES"); fr = &g_frames[model.num_frames]; model.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 != model.num_tris) Error ("%s: number of triangles doesn't match base frame\n", file1); // // allocate storage for the frame's vertices // ptrivert = fr->v; for (i=0 ; imins, 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 ; imins, fr->maxs); VectorAdd (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum); vnorms[index_xyz].numnormals++; } } // // calculate the vertex normals, match them to the template list, and store the // index of the best match // for (i=0 ; i maxdot) { maxdot = dot; maxdotindex = j; } } ptrivert[i].lightnormalindex = maxdotindex; } free (ptri); } /* =============== GrabJointedFrame =============== */ void GrabJointedFrame(char *frame) { char file1[1024]; char *framefile; frame_t *fr; framefile = FindFrameFile (frame); sprintf (file1, "%s/%s", cdarchive, framefile); ExpandPathAndArchive (file1); sprintf (file1, "%s/%s",cddir, framefile); printf ("grabbing %s\n", file1); fr = &g_frames[model.num_frames - 1]; // last frame read in LoadJointList(file1, fr->joints, jointed); } /* =============== GrabGlobals =============== */ void GrabGlobals(char *frame) { char file1[1024]; char *framefile; frame_t *fr; framefile = FindFrameFile (frame); sprintf (file1, "%s/%s", cdarchive, framefile); ExpandPathAndArchive (file1); sprintf (file1, "%s/%s",cddir, framefile); printf ("grabbing %s\n", file1); fr = &g_frames[model.num_frames - 1]; // last frame read in LoadGlobals(file1); } /* =============== Cmd_Frame =============== */ void Cmd_Frame (void) { while (ScriptTokenAvailable()) { GetScriptToken (false); if (g_skipmodel) continue; if (g_release || g_archive) { model.num_frames = 1; // don't skip the writeout continue; } H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames); GrabFrame (token); } } /* =============== Cmd_Skin Skins aren't actually stored in the file, only a reference is saved out to the header file. =============== */ void Cmd_Skin (void) { byte *palette; byte *pixels; int width, height; byte *cropped; int y; char name[1024], savename[1024]; GetScriptToken (false); if (model.num_skins == MAX_MD2SKINS) Error ("model.num_skins == MAX_MD2SKINS"); if (g_skipmodel) return; #if 1 sprintf (name, "%s/%s.pcx", cddir, token); sprintf (savename, "%s/!%s.pcx", g_outputDir, token); sprintf (g_skins[model.num_skins], "%s/!%s.pcx", cdpartial, token); #else sprintf (name, "%s/%s.lbm", cdarchive, token); strcpy (name, ExpandPathAndArchive( name ) ); // sprintf (name, "%s/%s.lbm", cddir, token); if (ScriptTokenAvailable()) { GetScriptToken (false); sprintf (g_skins[model.num_skins], "%s.pcx", token); sprintf (savename, "%s%s.pcx", g_outputDir, g_skins[model.num_skins]); } else { sprintf (savename, "%s/%s.pcx", g_outputDir, token); sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token); } #endif model.num_skins++; if (g_skipmodel || g_release || g_archive) return; // load the image printf ("loading %s\n", name); Load256Image (name, &pixels, &palette, &width, &height); // RemapZero (pixels, palette, width, height); // crop it to the proper size cropped = (byte *) SafeMalloc (model.skinwidth*model.skinheight, "Cmd_Skin"); for (y=0 ; y