-\r
-#include "qdata.h"\r
-#include "inout.h"\r
-\r
-//=================================================================\r
-\r
-typedef struct \r
-{\r
- int numnormals;\r
- vec3_t normalsum;\r
-} vertexnormals_t;\r
-\r
-typedef struct\r
-{\r
- vec3_t v;\r
- int lightnormalindex;\r
-} trivert_t;\r
-\r
-typedef struct\r
-{\r
- vec3_t mins, maxs;\r
- char name[16];\r
- trivert_t v[MAX_VERTS];\r
-} frame_t;\r
-\r
-//================================================================\r
-\r
-frame_t g_frames[MAX_FRAMES];\r
-\r
-dmdl_t model;\r
-\r
-\r
-float scale_up; // set by $scale\r
-vec3_t adjust; // set by $origin\r
-int g_fixedwidth, g_fixedheight; // set by $skinsize\r
-\r
-\r
-//\r
-// base frame info\r
-//\r
-vec3_t base_xyz[MAX_VERTS];\r
-dstvert_t base_st[MAX_VERTS];\r
-dtriangle_t triangles[MAX_TRIANGLES];\r
-\r
-int triangle_st[MAX_TRIANGLES][3][2];\r
-\r
-// the command list holds counts, s/t values, and xyz indexes\r
-// that are valid for every frame\r
-int commands[16384];\r
-int numcommands;\r
-int numglverts;\r
-int used[MAX_TRIANGLES];\r
-\r
-char g_skins[MAX_MD2SKINS][64];\r
-\r
-char cdarchive[1024];\r
-char cdpartial[1024];\r
-char cddir[1024];\r
-\r
-char modelname[64]; // empty unless $modelname issued (players)\r
-\r
-#define NUMVERTEXNORMALS 162\r
-\r
-float avertexnormals[NUMVERTEXNORMALS][3] = {\r
-#include "anorms.h"\r
-};\r
-\r
-FILE *headerouthandle = NULL;\r
-\r
-//==============================================================\r
-\r
-/*\r
-===============\r
-ClearModel\r
-===============\r
-*/\r
-void ClearModel (void)\r
-{\r
- memset (&model, 0, sizeof(model));\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
-}\r
-\r
-\r
-void H_printf(char *fmt, ...)\r
-{\r
- va_list argptr;\r
- char name[1024];\r
-\r
- if (!headerouthandle)\r
- {\r
- sprintf (name, "%s/tris.h", cddir);\r
- headerouthandle = SafeOpenWrite (name);\r
- fprintf(headerouthandle, "// %s\n\n", cddir);\r
- fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n");\r
- }\r
-\r
- va_start (argptr, fmt);\r
- vfprintf (headerouthandle, fmt, argptr);\r
- va_end (argptr);\r
-}\r
-\r
-\r
-/*\r
-============\r
-WriteModelFile\r
-============\r
-*/\r
-void WriteModelFile (FILE *modelouthandle)\r
-{\r
- int i;\r
- dmdl_t modeltemp;\r
- int j, k;\r
- frame_t *in;\r
- daliasframe_t *out;\r
- byte buffer[MAX_VERTS*4+128];\r
- float v;\r
- int c_on, c_off;\r
-\r
- model.ident = IDALIASHEADER;\r
- model.version = ALIAS_VERSION;\r
- model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];\r
- model.num_glcmds = numcommands;\r
- model.ofs_skins = sizeof(dmdl_t);\r
- model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;\r
- model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);\r
- model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);\r
- model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;\r
- model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;\r
-\r
- //\r
- // write out the model header\r
- //\r
- for (i=0 ; i<sizeof(dmdl_t)/4 ; i++)\r
- ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);\r
-\r
- SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));\r
-\r
- //\r
- // write out the skin names\r
- //\r
- SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);\r
-\r
- //\r
- // write out the texture coordinates\r
- //\r
- c_on = c_off = 0;\r
- for (i=0 ; i<model.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
- SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));\r
-\r
- //\r
- // write out the triangles\r
- //\r
- for (i=0 ; i<model.num_tris ; i++)\r
- {\r
- int j;\r
- dtriangle_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
- //\r
- // write out the frames\r
- //\r
- for (i=0 ; i<model.num_frames ; i++)\r
- {\r
- in = &g_frames[i];\r
- out = (daliasframe_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
-\r
- for (j=0 ; j<model.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, model.framesize);\r
- }\r
-\r
- //\r
- // write out glcmds\r
- //\r
- SafeWrite (modelouthandle, commands, numcommands*4);\r
-}\r
-\r
-\r
-/*\r
-===============\r
-FinishModel\r
-===============\r
-*/\r
-void FinishModel (void)\r
-{\r
- FILE *modelouthandle;\r
- int i;\r
- char name[1024];\r
- \r
- if (!model.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.md2", cdpartial);\r
- ReleaseFile (name);\r
-\r
- for (i=0 ; i<model.num_skins ; i++)\r
- {\r
- ReleaseFile (g_skins[i]);\r
- }\r
- model.num_frames = 0;\r
- return;\r
- }\r
- \r
-//\r
-// write the model output file\r
-//\r
- if (modelname[0])\r
- sprintf (name, "%s%s", gamedir, modelname);\r
- else\r
- sprintf (name, "%s/tris.md2", cddir);\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", model.skinwidth, model.skinheight);\r
- printf ("%4d vertexes\n", model.num_xyz);\r
- printf ("%4d triangles\n", model.num_tris);\r
- printf ("%4d frame\n", model.num_frames);\r
- printf ("%4d glverts\n", numglverts);\r
- printf ("%4d glcmd\n", model.num_glcmds);\r
- printf ("%4d skins\n", model.num_skins);\r
- printf ("file size: %d\n", (int)ftell (modelouthandle) );\r
- printf ("---------------------\n");\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
- fclose (headerouthandle);\r
- headerouthandle = NULL;\r
-}\r
-\r
-\r
-/*\r
-=================================================================\r
-\r
-ALIAS MODEL DISPLAY LIST GENERATION\r
-\r
-=================================================================\r
-*/\r
-\r
-int strip_xyz[128];\r
-int strip_st[128];\r
-int strip_tris[128];\r
-int stripcount;\r
-\r
-/*\r
-================\r
-StripLength\r
-================\r
-*/\r
-int StripLength (int starttri, int startv)\r
-{\r
- int m1, m2;\r
- int st1, st2;\r
- int j;\r
- dtriangle_t *last, *check;\r
- int k;\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<model.num_tris ; j++, check++)\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])\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<model.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
-int FanLength (int starttri, int startv)\r
-{\r
- int m1, m2;\r
- int st1, st2;\r
- int j;\r
- dtriangle_t *last, *check;\r
- int k;\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<model.num_tris ; j++, check++)\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])\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<model.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
-void BuildGlCmds (void)\r
-{\r
- int i, j, k;\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
-\r
- //\r
- // build tristrips\r
- //\r
- numcommands = 0;\r
- numglverts = 0;\r
- memset (used, 0, sizeof(used));\r
- for (i=0 ; i<model.num_tris ; i++)\r
- {\r
- // pick an unused triangle and start the trifan\r
- if (used[i])\r
- continue;\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);\r
- else\r
- len = FanLength (i, startv);\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 + 0.5) / model.skinwidth;\r
- t = (t + 0.5) / model.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
-}\r
-\r
-\r
-/*\r
-===============================================================\r
-\r
-BASE FRAME SETUP\r
-\r
-===============================================================\r
-*/\r
-\r
-/*\r
-============\r
-BuildST\r
-\r
-Builds the triangle_st array for the base frame and\r
-model.skinwidth / model.skinheight\r
-\r
- FIXME: allow this to be loaded from a file for\r
- arbitrary mappings\r
-============\r
-*/\r
-void BuildST (triangle_t *ptri, int numtri)\r
-{\r
- int i, j;\r
- int width, height, iwidth, iheight, swidth;\r
- float basex, basey;\r
- float s_scale, t_scale;\r
- float scale;\r
- vec3_t mins, maxs;\r
- float *pbasevert;\r
- vec3_t vtemp1, vtemp2, normal;\r
-\r
- //\r
- // find bounds of all the verts on the base frame\r
- //\r
- ClearBounds (mins, maxs);\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
- 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
- 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
- basex = iwidth + 2;\r
- }\r
- else\r
- {\r
- basex = 2;\r
- }\r
- basey = 2;\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
-// make the width a multiple of 4; some hardware requires this, and it ensures\r
-// dword alignment for each scan\r
- swidth = iwidth*2;\r
- model.skinwidth = (swidth + 3) & ~3;\r
- model.skinheight = iheight;\r
-}\r
-\r
-\r
-/*\r
-=================\r
-Cmd_Base\r
-=================\r
-*/\r
-void Cmd_Base (void)\r
-{\r
- triangle_t *ptri;\r
- int i, j, k;\r
- int time1;\r
- char file1[1024];\r
-\r
- GetToken (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\n", file1);\r
-\r
- ExpandPathAndArchive (file1);\r
-\r
- sprintf (file1, "%s/%s.%s", cddir, token, trifileext);\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, &model.num_tris);\r
- else\r
- LoadTriangleList (file1, &ptri, &model.num_tris);\r
-\r
-//\r
-// get the ST values\r
-//\r
- BuildST (ptri, model.num_tris);\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 (i=0 ; i<model.num_tris ; i++)\r
- {\r
- for (j=0 ; j<3 ; j++)\r
- {\r
- // get the xyz index\r
- for (k=0 ; k<model.num_xyz ; k++)\r
- if (VectorCompare (ptri[i].verts[j], base_xyz[k]))\r
- break; // this vertex is already in the base vertex list\r
-\r
- if (k == model.num_xyz)\r
- { // new index\r
- VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);\r
- model.num_xyz++;\r
- }\r
-\r
- triangles[i].index_xyz[j] = k;\r
-\r
- // get the st index\r
- for (k=0 ; k<model.num_st ; k++)\r
- if (triangle_st[i][j][0] == base_st[k].s\r
- && triangle_st[i][j][1] == base_st[k].t)\r
- break; // this vertex is already in the base vertex list\r
-\r
- if (k == model.num_st)\r
- { // new index\r
- base_st[model.num_st].s = triangle_st[i][j][0];\r
- base_st[model.num_st].t = triangle_st[i][j][1];\r
- model.num_st++;\r
- }\r
-\r
- triangles[i].index_st[j] = k;\r
- }\r
- }\r
-\r
- // build triangle strips / fans\r
- BuildGlCmds ();\r
-}\r
-\r
-//===============================================================\r
-\r
-char *FindFrameFile (char *frame)\r
-{\r
- int time1;\r
- char file1[1024];\r
- static char retname[1024];\r
- char base[32];\r
- char suffix[32];\r
- char *s;\r
-\r
- if (strstr (frame, "."))\r
- return frame; // allready in dot format\r
-\r
- // split 'run1' into 'run' and '1'\r
- s = frame + strlen(frame)-1;\r
-\r
- while (s != frame && *s >= '0' && *s <= '9')\r
- s--;\r
-\r
- strcpy (suffix, s+1);\r
- strcpy (base, frame);\r
- base[s-frame+1] = 0;\r
-\r
- // check for 'run1.tri'\r
- sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, trifileext);\r
- time1 = FileTime (file1);\r
- if (time1 != -1)\r
- {\r
- sprintf (retname, "%s%s.%s", base, suffix, trifileext);\r
- return retname;\r
- }\r
-\r
- // check for 'run.1'\r
- sprintf (file1, "%s/%s.%s",cddir, base, suffix);\r
- time1 = FileTime (file1);\r
- if (time1 != -1)\r
- {\r
- sprintf (retname, "%s.%s", base, suffix);\r
- return retname;\r
- }\r
-\r
- Error ("frame %s could not be found",frame);\r
- return NULL;\r
-}\r
-\r
-/*\r
-===============\r
-GrabFrame\r
-===============\r
-*/\r
-void GrabFrame (char *frame)\r
-{\r
- triangle_t *ptri;\r
- int i, j;\r
- trivert_t *ptrivert;\r
- int num_tris;\r
- char file1[1024];\r
- frame_t *fr;\r
- vertexnormals_t vnorms[MAX_VERTS];\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\n", file1);\r
-\r
- if (model.num_frames >= MAX_FRAMES)\r
- Error ("model.num_frames >= MAX_FRAMES");\r
- fr = &g_frames[model.num_frames];\r
- model.num_frames++;\r
-\r
- strcpy (fr->name, frame);\r
-\r
-//\r
-// load the frame\r
-//\r
- if (do3ds)\r
- Load3DSTriangleList (file1, &ptri, &num_tris);\r
- else\r
- LoadTriangleList (file1, &ptri, &num_tris);\r
-\r
- if (num_tris != model.num_tris)\r
- Error ("%s: number of triangles doesn't match base frame\n", file1);\r
-\r
-//\r
-// allocate storage for the frame's vertices\r
-//\r
- ptrivert = fr->v;\r
-\r
- for (i=0 ; i<model.num_xyz ; i++)\r
- {\r
- vnorms[i].numnormals = 0;\r
- VectorClear (vnorms[i].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 (vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum);\r
- vnorms[index_xyz].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<model.num_xyz ; i++)\r
- {\r
- int j;\r
- vec3_t v;\r
- float maxdot;\r
- int maxdotindex;\r
- int c;\r
-\r
- c = vnorms[i].numnormals;\r
- if (!c)\r
- Error ("Vertex with no triangles attached");\r
-\r
- VectorScale (vnorms[i].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_Frame (void)\r
-{\r
- while (TokenAvailable())\r
- {\r
- GetToken (false);\r
- if (g_skipmodel)\r
- continue;\r
- if (g_release || g_archive)\r
- {\r
- model.num_frames = 1; // don't skip the writeout\r
- continue;\r
- }\r
-\r
- H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames);\r
-\r
- GrabFrame (token);\r
- }\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_Skin (void)\r
-{\r
- byte *palette;\r
- byte *pixels;\r
- int width, height;\r
- byte *cropped;\r
- int y;\r
- char name[1024], savename[1024];\r
-\r
- GetToken (false);\r
-\r
- if (model.num_skins == MAX_MD2SKINS)\r
- Error ("model.num_skins == MAX_MD2SKINS");\r
-\r
- if (g_skipmodel)\r
- return;\r
-\r
- sprintf (name, "%s/%s.lbm", cdarchive, token);\r
- strcpy (name, ExpandPathAndArchive( name ) );\r
-// sprintf (name, "%s/%s.lbm", cddir, token);\r
-\r
- if (TokenAvailable())\r
- {\r
- GetToken (false);\r
- sprintf (g_skins[model.num_skins], "%s.pcx", token);\r
- sprintf (savename, "%s%s.pcx", gamedir, g_skins[model.num_skins]);\r
- }\r
- else\r
- {\r
- sprintf (savename, "%s/%s.pcx", cddir, token);\r
- sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);\r
- }\r
-\r
- model.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
- Load256Image (name, &pixels, &palette, &width, &height);\r
- RemapZero (pixels, palette, width, height);\r
-\r
- // crop it to the proper size\r
- cropped = malloc (model.skinwidth*model.skinheight);\r
- for (y=0 ; y<model.skinheight ; y++)\r
- {\r
- memcpy (cropped+y*model.skinwidth,\r
- pixels+y*width, model.skinwidth);\r
- }\r
-\r
- // save off the new image\r
- printf ("saving %s\n", savename);\r
- CreatePath (savename);\r
- WritePCXfile (savename, cropped, model.skinwidth,\r
- model.skinheight, palette);\r
-\r
- free (pixels);\r
- free (palette);\r
- free (cropped);\r
-}\r
-\r
-\r
-/*\r
-=================\r
-Cmd_Origin\r
-=================\r
-*/\r
-void Cmd_Origin (void)\r
-{\r
- // rotate points into frame of reference so model points down the\r
- // positive x axis\r
- GetToken (false);\r
- adjust[1] = -atof (token);\r
-\r
- GetToken (false);\r
- adjust[0] = atof (token);\r
-\r
- GetToken (false);\r
- adjust[2] = -atof (token);\r
-}\r
-\r
-\r
-/*\r
-=================\r
-Cmd_ScaleUp\r
-=================\r
-*/\r
-void Cmd_ScaleUp (void)\r
-{\r
- GetToken (false);\r
- scale_up = atof (token);\r
- if (g_skipmodel || g_release || g_archive)\r
- return;\r
-\r
- printf ("Scale up: %f\n", scale_up);\r
-}\r
-\r
-\r
-/*\r
-=================\r
-Cmd_Skinsize\r
-\r
-Set a skin size other than the default\r
-=================\r
-*/\r
-void Cmd_Skinsize (void)\r
-{\r
- GetToken (false);\r
- g_fixedwidth = atoi(token);\r
- GetToken (false);\r
- g_fixedheight = atoi(token);\r
-}\r
-\r
-/*\r
-=================\r
-Cmd_Modelname\r
-\r
-Gives a different name/location for the file, instead of the cddir\r
-=================\r
-*/\r
-void Cmd_Modelname (void)\r
-{\r
- GetToken (false);\r
- strcpy (modelname, token);\r
-}\r
-\r
-/*\r
-===============\r
-Cmd_Cd\r
-===============\r
-*/\r
-void Cmd_Cd (void)\r
-{\r
- FinishModel ();\r
- ClearModel ();\r
-\r
- GetToken (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
- // 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
+
+#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 ; i<sizeof(dmdl_t)/4 ; i++)
+ ((int *)&modeltemp)[i] = LittleLong (((int *)&model)[i]);
+
+ SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
+
+ //
+ // write out the skin names
+ //
+ SafeWrite (modelouthandle, g_skins, model.num_skins * MAX_SKINNAME);
+
+ //
+ // write out the texture coordinates
+ //
+ c_on = c_off = 0;
+ for (i=0 ; i<model.num_st ; i++)
+ {
+ base_st[i].s = LittleShort (base_st[i].s);
+ base_st[i].t = LittleShort (base_st[i].t);
+ }
+
+ SafeWrite (modelouthandle, base_st, model.num_st * sizeof(base_st[0]));
+
+ //
+ // write out the triangles
+ //
+ for (i=0 ; i<model.num_tris ; i++)
+ {
+ int j;
+ dtriangle_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));
+ }
+
+ //
+ // write out the frames
+ //
+ for (i=0 ; i<model.num_frames ; i++)
+ {
+ in = &g_frames[i];
+ out = (daliasframe_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];
+ }
+
+ for (j=0 ; j<model.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, 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 ; i<model.num_skins ; i++)
+ {
+ ReleaseFile (g_skins[i]);
+ }
+ model.num_frames = 0;
+ return;
+ }
+
+//
+// write the model output file
+//
+ if (modelname[0])
+ sprintf (name, "%s%s", gamedir, modelname);
+ else
+ sprintf (name, "%s/tris.md2", cddir);
+ printf ("saving to %s\n", name);
+ CreatePath (name);
+ modelouthandle = SafeOpenWrite (name);
+
+ WriteModelFile (modelouthandle);
+
+ printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);
+ printf ("%4d vertexes\n", model.num_xyz);
+ printf ("%4d triangles\n", model.num_tris);
+ printf ("%4d frame\n", model.num_frames);
+ printf ("%4d glverts\n", numglverts);
+ printf ("%4d glcmd\n", model.num_glcmds);
+ printf ("%4d skins\n", model.num_skins);
+ printf ("file size: %d\n", (int)ftell (modelouthandle) );
+ printf ("---------------------\n");
+
+ 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);
+
+ fclose (headerouthandle);
+ headerouthandle = NULL;
+}
+
+
+/*
+=================================================================
+
+ALIAS MODEL DISPLAY LIST GENERATION
+
+=================================================================
+*/
+
+int strip_xyz[128];
+int strip_st[128];
+int strip_tris[128];
+int stripcount;
+
+/*
+================
+StripLength
+================
+*/
+int StripLength (int starttri, int startv)
+{
+ int m1, m2;
+ int st1, st2;
+ int j;
+ dtriangle_t *last, *check;
+ int k;
+
+ 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<model.num_tris ; j++, check++)
+ {
+ 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])
+ 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<model.num_tris ; j++)
+ if (used[j] == 2)
+ used[j] = 0;
+
+ return stripcount;
+}
+
+
+/*
+===========
+FanLength
+===========
+*/
+int FanLength (int starttri, int startv)
+{
+ int m1, m2;
+ int st1, st2;
+ int j;
+ dtriangle_t *last, *check;
+ int k;
+
+ 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<model.num_tris ; j++, check++)
+ {
+ 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])
+ 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<model.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
+================
+*/
+void BuildGlCmds (void)
+{
+ int i, j, k;
+ int startv;
+ float s, t;
+ int len, bestlen, besttype;
+ int best_xyz[1024];
+ int best_st[1024];
+ int best_tris[1024];
+ int type;
+
+ //
+ // build tristrips
+ //
+ numcommands = 0;
+ numglverts = 0;
+ memset (used, 0, sizeof(used));
+ for (i=0 ; i<model.num_tris ; i++)
+ {
+ // pick an unused triangle and start the trifan
+ if (used[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);
+ else
+ len = FanLength (i, startv);
+ 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 + 0.5) / model.skinwidth;
+ t = (t + 0.5) / model.skinheight;
+
+ *(float *)&commands[numcommands++] = s;
+ *(float *)&commands[numcommands++] = t;
+ *(int *)&commands[numcommands++] = best_xyz[j];
+ }
+ }
+
+ commands[numcommands++] = 0; // end of list marker
+}
+
+
+/*
+===============================================================
+
+BASE FRAME SETUP
+
+===============================================================
+*/
+
+/*
+============
+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)
+{
+ int i, j;
+ int width, height, iwidth, iheight, swidth;
+ float basex, basey;
+ float s_scale, t_scale;
+ float scale;
+ vec3_t mins, maxs;
+ float *pbasevert;
+ vec3_t vtemp1, vtemp2, normal;
+
+ //
+ // find bounds of all the verts on the base frame
+ //
+ ClearBounds (mins, maxs);
+
+ 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];
+
+ 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<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)
+ {
+ 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<model.num_tris ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ // get the xyz index
+ for (k=0 ; k<model.num_xyz ; k++)
+ if (VectorCompare (ptri[i].verts[j], base_xyz[k]))
+ break; // this vertex is already in the base vertex list
+
+ if (k == model.num_xyz)
+ { // new index
+ VectorCopy (ptri[i].verts[j], base_xyz[model.num_xyz]);
+ model.num_xyz++;
+ }
+
+ triangles[i].index_xyz[j] = k;
+
+ // get the st index
+ for (k=0 ; k<model.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 == model.num_st)
+ { // new index
+ base_st[model.num_st].s = triangle_st[i][j][0];
+ base_st[model.num_st].t = triangle_st[i][j][1];
+ model.num_st++;
+ }
+
+ triangles[i].index_st[j] = k;
+ }
+ }
+
+ // build triangle strips / fans
+ BuildGlCmds ();
+}
+
+//===============================================================
+
+char *FindFrameFile (char *frame)
+{
+ int time1;
+ char file1[1024];
+ static char retname[1024];
+ char base[32];
+ char suffix[32];
+ char *s;
+
+ if (strstr (frame, "."))
+ return frame; // allready in dot format
+
+ // split 'run1' into 'run' and '1'
+ s = frame + strlen(frame)-1;
+
+ while (s != frame && *s >= '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 ; i<model.num_xyz ; i++)
+ {
+ vnorms[i].numnormals = 0;
+ VectorClear (vnorms[i].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 (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<model.num_xyz ; i++)
+ {
+ int j;
+ vec3_t v;
+ float maxdot;
+ int maxdotindex;
+ int c;
+
+ c = vnorms[i].numnormals;
+ if (!c)
+ Error ("Vertex with no triangles attached");
+
+ VectorScale (vnorms[i].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_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<model.skinheight ; y++)
+ {
+ memcpy (cropped+y*model.skinwidth,
+ pixels+y*width, model.skinwidth);
+ }
+
+ // save off the new image
+ printf ("saving %s\n", savename);
+ CreatePath (savename);
+ WritePCXfile (savename, cropped, model.skinwidth,
+ model.skinheight, palette);
+
+ free (pixels);
+ free (palette);
+ free (cropped);
+}
+
+
+/*
+=================
+Cmd_Origin
+=================
+*/
+void Cmd_Origin (void)
+{
+ // rotate points into frame of reference so model points down the
+ // positive x axis
+ GetToken (false);
+ adjust[1] = -atof (token);
+
+ GetToken (false);
+ adjust[0] = atof (token);
+
+ GetToken (false);
+ adjust[2] = -atof (token);
+}
+
+
+/*
+=================
+Cmd_ScaleUp
+=================
+*/
+void Cmd_ScaleUp (void)
+{
+ GetToken (false);
+ scale_up = atof (token);
+ if (g_skipmodel || g_release || g_archive)
+ return;
+
+ printf ("Scale up: %f\n", scale_up);
+}
+
+
+/*
+=================
+Cmd_Skinsize
+
+Set a skin size other than the default
+=================
+*/
+void Cmd_Skinsize (void)
+{
+ GetToken (false);
+ g_fixedwidth = atoi(token);
+ GetToken (false);
+ g_fixedheight = atoi(token);
+}
+
+/*
+=================
+Cmd_Modelname
+
+Gives a different name/location for the file, instead of the cddir
+=================
+*/
+void Cmd_Modelname (void)
+{
+ GetToken (false);
+ strcpy (modelname, token);
+}
+
+/*
+===============
+Cmd_Cd
+===============
+*/
+void Cmd_Cd (void)
+{
+ FinishModel ();
+ ClearModel ();
+
+ GetToken (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);
+
+ // 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);
+ }
+}
+