/* 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 "qdata.h" #include "inout.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]; } frame_t; //================================================================ frame_t g_frames[MAX_FRAMES]; 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 // vec3_t base_xyz[MAX_VERTS]; dstvert_t base_st[MAX_VERTS]; dtriangle_t triangles[MAX_TRIANGLES]; 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) #define NUMVERTEXNORMALS 162 float avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; FILE *headerouthandle = NULL; //============================================================== /* =============== ClearModel =============== */ void ClearModel (void) { memset (&model, 0, sizeof(model)); modelname[0] = 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); } /* ============ WriteModelFile ============ */ 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); } /* =============== 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; } /* ================= Cmd_Base ================= */ void Cmd_Base (void) { triangle_t *ptri; int i, j, k; int time1; char file1[1024]; GetToken (false); if (g_skipmodel || g_release || g_archive) return; printf ("---------------------\n"); 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); // // load the base triangles // if (do3ds) Load3DSTriangleList (file1, &ptri, &model.num_tris); else LoadTriangleList (file1, &ptri, &model.num_tris); // // get the ST values // BuildST (ptri, model.num_tris); // // 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; // check for 'run1.tri' sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, trifileext); time1 = FileTime (file1); if (time1 != -1) { sprintf (retname, "%s%s.%s", base, suffix, trifileext); 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 =============== */ 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\n", 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); else LoadTriangleList (file1, &ptri, &num_tris); 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); } /* =============== Cmd_Frame =============== */ void Cmd_Frame (void) { while (TokenAvailable()) { GetToken (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]; GetToken (false); if (model.num_skins == MAX_MD2SKINS) Error ("model.num_skins == MAX_MD2SKINS"); if (g_skipmodel) return; sprintf (name, "%s/%s.lbm", cdarchive, token); strcpy (name, ExpandPathAndArchive( name ) ); // sprintf (name, "%s/%s.lbm", cddir, token); if (TokenAvailable()) { GetToken (false); sprintf (g_skins[model.num_skins], "%s.pcx", token); sprintf (savename, "%s%s.pcx", gamedir, g_skins[model.num_skins]); } else { sprintf (savename, "%s/%s.pcx", cddir, token); sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token); } 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 = malloc (model.skinwidth*model.skinheight); for (y=0 ; y