/** * @file models.c * @brief */ /* Copyright (C) 1997-2001 Id Software, Inc. This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "qdata.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; /*============================================================== */ /** * @brief */ 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 = qfalse; } /** * @brief */ 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); } /** * @brief */ 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 = (intptr_t)&((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); } /** * @brief */ 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; } /** * @brief */ void Cmd_Base (void) { triangle_t *ptri; int i, j, k; int time1; char file1[1024]; GetToken (qfalse); 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; } /** * @brief */ 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); } /** * @brief */ void Cmd_Frame (void) { while (TokenAvailable()) { GetToken (qfalse); 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); } } /** * @brief 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 (qfalse); 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 (qfalse); 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