--- /dev/null
+/*\r
+Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
+For a list of contributors, see the accompanying CONTRIBUTORS file.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+*/\r
+\r
+\r
+#include "qdata.h"\r
+#include <assert.h>\r
+#include "jointed.h"\r
+#include "fmodel.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
+ QDataJoint_t joints[NUM_CLUSTERS]; // ,this\r
+} frame_t;\r
+\r
+// ,and all of this should get out of here, need to use new stuff in fmodels instead\r
+\r
+typedef struct IntListNode_s\r
+{\r
+ int data;\r
+ struct IntListNode_s *next;\r
+} IntListNode_t; // gaak\r
+\r
+typedef struct\r
+{\r
+ float scale[3]; // multiply byte verts by this\r
+ float translate[3]; // then add this\r
+} PartialAliasFrame_t;\r
+\r
+int jointed;\r
+int clustered;\r
+\r
+int *clusters[NUM_CLUSTERS];\r
+IntListNode_t *vertLists[NUM_CLUSTERS];\r
+int num_verts[NUM_CLUSTERS + 1];\r
+int new_num_verts[NUM_CLUSTERS + 1];\r
+\r
+// end that\r
+\r
+//================================================================\r
+\r
+frame_t g_frames[MAX_FRAMES];\r
+//frame_t *g_frames;\r
+\r
+static 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
+dstvert_t base_st[MAX_VERTS];\r
+dtriangle_t triangles[MAX_TRIANGLES];\r
+\r
+static 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
+extern char *g_outputDir;\r
+\r
+#define NUMVERTEXNORMALS 162\r
+\r
+float avertexnormals[NUMVERTEXNORMALS][3] = \r
+{\r
+ #include "anorms.h"\r
+};\r
+\r
+unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];\r
+\r
+FILE *headerouthandle = NULL;\r
+\r
+//==============================================================\r
+\r
+/*\r
+===============\r
+ClearModel\r
+===============\r
+*/\r
+static void ClearModel (void)\r
+{\r
+ memset (&model, 0, sizeof(model));\r
+\r
+ modelname[0] = 0;\r
+ jointed = NOT_JOINTED;\r
+ clustered = 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
+#if 1\r
+/*\r
+============\r
+WriteModelFile\r
+============\r
+*/\r
+void WriteCommonModelFile (FILE *modelouthandle, PartialAliasFrame_t *outFrames)\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.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*sizeof(int);\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
+ if(outFrames)\r
+ {\r
+ outFrames[i].scale[j] = out->scale[j];\r
+ outFrames[i].translate[j] = out->translate[j];\r
+ }\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
+WriteModelFile\r
+============\r
+*/\r
+void WriteModelFile (FILE *modelouthandle)\r
+{\r
+ model.ident = IDALIASHEADER;\r
+\r
+ WriteCommonModelFile(modelouthandle, NULL);\r
+}\r
+\r
+/*\r
+============\r
+WriteJointedModelFile\r
+============\r
+*/\r
+void WriteJointedModelFile (FILE *modelouthandle)\r
+{\r
+ int i;\r
+ int j, k;\r
+ frame_t *in;\r
+ float v;\r
+ IntListNode_t *current, *toFree;\r
+ PartialAliasFrame_t outFrames[MAX_FRAMES];\r
+\r
+ model.ident = IDJOINTEDALIASHEADER;\r
+ \r
+ WriteCommonModelFile(modelouthandle, outFrames);\r
+\r
+ // Skeletal Type\r
+ SafeWrite(modelouthandle, &jointed, sizeof(int));\r
+\r
+ // number of joints\r
+ SafeWrite(modelouthandle, &numJointsForSkeleton[jointed], sizeof(int));\r
+\r
+ // number of verts in each cluster\r
+ SafeWrite(modelouthandle, &new_num_verts[1], sizeof(int)*numJointsForSkeleton[jointed]);\r
+\r
+ // cluster verts\r
+ for(i = 0; i < new_num_verts[0]; ++i)\r
+ {\r
+ current = vertLists[i];\r
+ while(current)\r
+ {\r
+ SafeWrite (modelouthandle, ¤t->data, sizeof(int));\r
+ toFree = current;\r
+ current = current->next;\r
+ free(toFree); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base\r
+ }\r
+ }\r
+\r
+ for (i=0 ; i<model.num_frames ; i++)\r
+ {\r
+ in = &g_frames[i];\r
+\r
+ for (j = 0 ; j < new_num_verts[0]; ++j)\r
+ {\r
+ for (k=0 ; k<3 ; k++)\r
+ {\r
+ // scale to byte values & min/max check\r
+ v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
+\r
+ // clamp, so rounding doesn't wrap from 255.6 to 0\r
+ if (v > 255.0)\r
+ {\r
+ v = 255.0;\r
+ }\r
+\r
+ if (v < 0)\r
+ {\r
+ v = 0;\r
+ }\r
+\r
+ // write out origin as a float (there's only a few per model, so it's not really \r
+ // a size issue)\r
+ SafeWrite (modelouthandle, &v, sizeof(float));\r
+ }\r
+\r
+ for (k=0 ; k<3 ; k++)\r
+ {\r
+ v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
+\r
+ // clamp, so rounding doesn't wrap from 255.6 to 0\r
+ if (v > 255.0)\r
+ {\r
+ v = 255.0;\r
+ }\r
+\r
+ if (v < 0)\r
+ {\r
+ v = 0;\r
+ }\r
+\r
+ // write out origin as a float (there's only a few per model, so it's not really \r
+ // a size issue)\r
+ SafeWrite (modelouthandle, &v, sizeof(float));\r
+ }\r
+\r
+ for (k=0 ; k<3 ; k++)\r
+ {\r
+ v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );\r
+\r
+ // clamp, so rounding doesn't wrap from 255.6 to 0\r
+ if (v > 255.0)\r
+ {\r
+ v = 255.0;\r
+ }\r
+\r
+ if (v < 0)\r
+ {\r
+ v = 0;\r
+ }\r
+\r
+ // write out origin as a float (there's only a few per model, so it's not really \r
+ // a size issue)\r
+ SafeWrite (modelouthandle, &v, sizeof(float));\r
+ }\r
+ }\r
+ }\r
+}\r
+#else\r
+/*\r
+============\r
+WriteModelFile\r
+============\r
+*/\r
+static 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
+#endif\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", g_outputDir, modelname);\r
+ else\r
+ sprintf (name, "%s/tris.md2", g_outputDir);\r
+ printf ("saving to %s\n", name);\r
+ CreatePath (name);\r
+ modelouthandle = SafeOpenWrite (name);\r
+\r
+#if 1\r
+ if(jointed != NOT_JOINTED)\r
+ WriteJointedModelFile(modelouthandle);\r
+ else\r
+#endif\r
+ WriteModelFile(modelouthandle);\r
+ \r
+ printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);\r
+ printf ("First frame boundaries:\n");\r
+ printf (" minimum x: %3f\n", g_frames[0].mins[0]);\r
+ printf (" maximum x: %3f\n", g_frames[0].maxs[0]);\r
+ printf (" minimum y: %3f\n", g_frames[0].mins[1]);\r
+ printf (" maximum y: %3f\n", g_frames[0].maxs[1]);\r
+ printf (" minimum z: %3f\n", g_frames[0].mins[2]);\r
+ printf (" maximum z: %3f\n", g_frames[0].maxs[2]);\r
+ printf ("%4d vertices\n", 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
+static 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
+static 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
+static 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
+#if 0\r
+static void OldBuildST (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
+#endif\r
+\r
+//==========================================================================\r
+//\r
+// DrawScreen\r
+//\r
+//==========================================================================\r
+\r
+void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight)\r
+{\r
+ int i;\r
+ byte *scrpos;\r
+ char buffer[256];\r
+\r
+ // Divider\r
+ scrpos = &pic[(INFO_Y-2)*SKINPAGE_WIDTH];\r
+ for(i = 0; i < SKINPAGE_WIDTH; i++)\r
+ {\r
+ *scrpos++ = 255;\r
+ }\r
+\r
+ sprintf(buffer, "GENSKIN: ");\r
+ DrawTextChar(16, INFO_Y, buffer);\r
+ \r
+ sprintf(buffer, "( %03d * %03d ) SCALE %f %f, SKINWIDTH %d,"\r
+ " SKINHEIGHT %d", (int)ScaleWidth, (int)ScaleHeight, s_scale, t_scale, (int)iwidth*2, (int)iheight);\r
+ DrawTextChar(80, INFO_Y, buffer);\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, qboolean DrawSkin)\r
+{\r
+ int i, j;\r
+ int width, height, iwidth, iheight, swidth;\r
+ float basex, basey;\r
+ float scale;\r
+ vec3_t mins, maxs;\r
+ float *pbasevert;\r
+ vec3_t vtemp1, vtemp2, normal;\r
+ float s_scale, t_scale;\r
+ float scWidth;\r
+ float scHeight;\r
+\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
+\r
+ scWidth = (ScaleWidth/2)*SCALE_ADJUST_FACTOR;\r
+ scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;\r
+\r
+ scale = scWidth/width;\r
+\r
+ if(height*scale >= scHeight)\r
+ {\r
+ scale = scHeight/height;\r
+ }\r
+\r
+ iwidth = ceil(width*scale)+4;\r
+ iheight = ceil(height*scale)+4;\r
+\r
+ s_scale = (float)(iwidth-4) / width;\r
+ t_scale = (float)(iheight-4) / height;\r
+ t_scale = s_scale;\r
+\r
+ if (DrawSkin)\r
+ DrawScreen(s_scale, t_scale, iwidth, iheight);\r
+\r
+\r
+/* if (!g_fixedwidth)\r
+ { // old style\r
+ scale = 8;\r
+ if (width*scale >= 150)\r
+ scale = 150.0 / width; \r
+ if (height*scale >= 190)\r
+ scale = 190.0 / height;\r
+\r
+ s_scale = t_scale = scale;\r
+\r
+ iwidth = ceil(width*s_scale);\r
+ iheight = ceil(height*t_scale);\r
+\r
+ iwidth += 4;\r
+ iheight += 4;\r
+ }\r
+ else\r
+ { // new style\r
+ iwidth = g_fixedwidth / 2;\r
+ iheight = g_fixedheight;\r
+\r
+ s_scale = (float)(iwidth-4) / width;\r
+ t_scale = (float)(iheight-4) / height;\r
+ }*/\r
+\r
+//\r
+// determine which side of each triangle to map the texture to\r
+//\r
+ for (i=0 ; i<numtri ; i++)\r
+ {\r
+ if (ptri[i].HasUV)\r
+ {\r
+ for (j=0 ; j<3 ; j++)\r
+ {\r
+ triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*iwidth);\r
+ triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*iheight);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ VectorSubtract (ptri[i].verts[0], ptri[i].verts[1], vtemp1);\r
+ VectorSubtract (ptri[i].verts[2], ptri[i].verts[1], vtemp2);\r
+ CrossProduct (vtemp1, vtemp2, normal);\r
+\r
+ if (normal[1] > 0)\r
+ {\r
+ basex = iwidth + 2;\r
+ }\r
+ else\r
+ {\r
+ basex = 2;\r
+ }\r
+ 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
+ DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],\r
+ triangle_st[i][1][0], triangle_st[i][1][1]);\r
+ DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],\r
+ triangle_st[i][2][0], triangle_st[i][2][1]);\r
+ DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],\r
+ triangle_st[i][0][0], triangle_st[i][0][1]); \r
+ }\r
+\r
+// make the width a multiple of 4; some hardware requires this, and it ensures\r
+// dword alignment for each scan\r
+\r
+ swidth = iwidth*2;\r
+ model.skinwidth = (swidth + 3) & ~3;\r
+ model.skinheight = iheight;\r
+}\r
+\r
+\r
+static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters, \r
+ IntListNode_t **vertLists, int *num_verts, int *new_num_verts)\r
+{\r
+ int i, j;\r
+ IntListNode_t *next;\r
+\r
+ for(j = 0; j < num_verts[0]; ++j)\r
+ {\r
+ for(i = 0; i < num_verts[j+1]; ++i)\r
+ {\r
+ if(clusters[j][i] == oldindex)\r
+ {\r
+ ++new_num_verts[j+1];\r
+\r
+ next = vertLists[j];\r
+\r
+ vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");\r
+ // Currently freed in WriteJointedModelFile only\r
+\r
+ vertLists[j]->data = newIndex;\r
+ vertLists[j]->next = next;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/*\r
+=================\r
+Cmd_Base\r
+=================\r
+*/\r
+void Cmd_Base (void)\r
+{\r
+ vec3_t base_xyz[MAX_VERTS];\r
+ triangle_t *ptri;\r
+ int i, j, k;\r
+#if 1\r
+#else\r
+ int time1;\r
+#endif\r
+ char file1[1024];\r
+ char file2[1024];\r
+\r
+ GetScriptToken (false);\r
+\r
+ if (g_skipmodel || g_release || g_archive)\r
+ return;\r
+\r
+ printf ("---------------------\n");\r
+#if 1\r
+ sprintf (file1, "%s/%s", cdpartial, token);\r
+ printf ("%s ", file1);\r
+\r
+ ExpandPathAndArchive (file1);\r
+\r
+ sprintf (file1, "%s/%s", cddir, token);\r
+#else\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
+#endif\r
+//\r
+// load the base triangles\r
+//\r
+ if (do3ds)\r
+ Load3DSTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);\r
+ else\r
+ LoadTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);\r
+\r
+\r
+ GetScriptToken (false);\r
+ sprintf (file2, "%s/%s.pcx", cddir, token);\r
+// sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);\r
+\r
+ printf ("skin: %s\n", file2);\r
+ Load256Image (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);\r
+\r
+ if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)\r
+ {\r
+ if (g_allow_newskin)\r
+ {\r
+ ScaleWidth = BaseWidth;\r
+ ScaleHeight = BaseHeight;\r
+ }\r
+ else\r
+ {\r
+ Error("Invalid skin page size: (%d,%d) should be (%d,%d)",\r
+ BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,\r
+ ENCODED_WIDTH_Y);\r
+ ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,\r
+ ENCODED_HEIGHT_Y);\r
+ }\r
+\r
+//\r
+// get the ST values\r
+//\r
+ BuildST (ptri, model.num_tris,false);\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
+\r
+ if(clustered)\r
+ ReplaceClusterIndex(k, ptri[i].indicies[j], (int **)&clusters, (IntListNode_t **)&vertLists, (int *)&num_verts, (int *)&new_num_verts);\r
+\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
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "hrc");\r
+ time1 = FileTime (file1);\r
+ if (time1 != -1)\r
+ {\r
+ sprintf (retname, "%s%s.%s", base, suffix, "hrc");\r
+ return retname;\r
+ }\r
+\r
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "asc");\r
+ time1 = FileTime (file1);\r
+ if (time1 != -1)\r
+ {\r
+ sprintf (retname, "%s%s.%s", base, suffix, "asc");\r
+ return retname;\r
+ }\r
+\r
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "tri");\r
+ time1 = FileTime (file1);\r
+ if (time1 != -1)\r
+ {\r
+ sprintf (retname, "%s%s.%s", base, suffix, "tri");\r
+ return retname;\r
+ }\r
+\r
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "3ds");\r
+ time1 = FileTime (file1);\r
+ if (time1 != -1)\r
+ {\r
+ sprintf (retname, "%s%s.%s", base, suffix, "3ds");\r
+ return retname;\r
+ }\r
+\r
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "htr");\r
+ time1 = FileTime (file1);\r
+ if (time1 != -1)\r
+ {\r
+ sprintf (retname, "%s%s.%s", base, suffix, "htr");\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
+static 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 ", 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, NULL, NULL);\r
+ else\r
+ LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);\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
+GrabJointedFrame\r
+===============\r
+*/\r
+void GrabJointedFrame(char *frame)\r
+{\r
+ char file1[1024];\r
+ char *framefile;\r
+ frame_t *fr;\r
+\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
+ fr = &g_frames[model.num_frames - 1]; // last frame read in\r
+\r
+ LoadJointList(file1, fr->joints, jointed);\r
+}\r
+\r
+/*\r
+===============\r
+GrabGlobals\r
+===============\r
+*/\r
+void GrabGlobals(char *frame)\r
+{\r
+ char file1[1024];\r
+ char *framefile;\r
+ frame_t *fr;\r
+\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
+ fr = &g_frames[model.num_frames - 1]; // last frame read in\r
+\r
+ LoadGlobals(file1);\r
+}\r
+\r
+/*\r
+===============\r
+Cmd_Frame \r
+===============\r
+*/\r
+void Cmd_Frame (void)\r
+{\r
+ while (ScriptTokenAvailable())\r
+ {\r
+ GetScriptToken (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
+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
+ GetScriptToken (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
+#if 1\r
+ sprintf (name, "%s/%s.pcx", cddir, token);\r
+ sprintf (savename, "%s/!%s.pcx", g_outputDir, token);\r
+ sprintf (g_skins[model.num_skins], "%s/!%s.pcx", cdpartial, token);\r
+#else\r
+ sprintf (name, "%s/%s.lbm", cdarchive, token);\r
+ strcpy (name, ExpandPathAndArchive( name ) );\r
+// sprintf (name, "%s/%s.lbm", cddir, token);\r
+\r
+ if (ScriptTokenAvailable())\r
+ {\r
+ GetScriptToken (false);\r
+ sprintf (g_skins[model.num_skins], "%s.pcx", token);\r
+ sprintf (savename, "%s%s.pcx", g_outputDir, g_skins[model.num_skins]);\r
+ }\r
+ else\r
+ {\r
+ sprintf (savename, "%s/%s.pcx", g_outputDir, token);\r
+ sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);\r
+ }\r
+#endif\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 = (byte *) SafeMalloc (model.skinwidth*model.skinheight, "Cmd_Skin");\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
+ GetScriptToken (false);\r
+ adjust[1] = -atof (token);\r
+\r
+ GetScriptToken (false);\r
+ adjust[0] = atof (token);\r
+\r
+ GetScriptToken (false);\r
+ adjust[2] = -atof (token);\r
+}\r
+\r
+\r
+/*\r
+=================\r
+Cmd_ScaleUp\r
+=================\r
+*/\r
+void Cmd_ScaleUp (void)\r
+{\r
+ GetScriptToken (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
+ GetScriptToken (false);\r
+ g_fixedwidth = atoi(token);\r
+ GetScriptToken (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
+ GetScriptToken (false);\r
+ strcpy (modelname, token);\r
+}\r
+\r
+/*\r
+===============\r
+Cmd_Cd\r
+===============\r
+*/\r
+void Cmd_Cd (void)\r
+{\r
+ char temp[256];\r
+\r
+ FinishModel ();\r
+ ClearModel ();\r
+\r
+ GetScriptToken (false);\r
+\r
+ // this is a silly mess...\r
+ sprintf (cdpartial, "models/%s", token); \r
+ sprintf (cdarchive, "%smodels/%s", gamedir+strlen(qdir), token); \r
+ sprintf (cddir, "%s%s", gamedir, cdpartial);\r
+\r
+ // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.\r
+ sprintf(temp, "%s%s", g_outputDir, cdpartial);\r
+ strcpy(g_outputDir, temp);\r
+\r
+ // if -only was specified and this cd doesn't match,\r
+ // skip the model (you only need to match leading chars,\r
+ // so you could regrab all monsters with -only monsters)\r
+ if (!g_only[0])\r
+ return;\r
+ if (strncmp(token, g_only, strlen(g_only)))\r
+ {\r
+ g_skipmodel = true;\r
+ printf ("skipping %s\n", cdpartial);\r
+ }\r
+}\r
+\r
+/*\r
+=================\r
+Cmd_Cluster\r
+=================\r
+*/\r
+void Cmd_Cluster()\r
+{\r
+ char file1[1024];\r
+\r
+ GetScriptToken (false);\r
+\r
+ printf ("---------------------\n");\r
+ sprintf (file1, "%s/%s", cdpartial, token);\r
+ printf ("%s\n", file1);\r
+\r
+ ExpandPathAndArchive (file1);\r
+\r
+ sprintf (file1, "%s/%s", cddir, token);\r
+\r
+ LoadClusters(file1, (int **)&clusters, (int *)&num_verts, jointed);\r
+\r
+ new_num_verts[0] = num_verts[0];\r
+\r
+ clustered = 1;\r
+}\r
+\r
+// Model construction cover functions.\r
+void MODELCMD_Modelname (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ Cmd_Modelname ();\r
+/*\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Modelname (); \r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMModelname ();\r
+ break;\r
+ }\r
+*/\r
+}\r
+\r
+void MODELCMD_Cd (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Cd ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMCd ();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_Origin (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ Cmd_Origin ();\r
+/* switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Origin ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMOrigin ();\r
+ break;\r
+ }\r
+*/\r
+}\r
+\r
+void MODELCMD_Cluster (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Cluster ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMCluster ();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_Base (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Base ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMBase (false);\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_BaseST (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Base ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMBase (true);\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_ScaleUp (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ Cmd_ScaleUp ();\r
+/* switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_ScaleUp ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMScaleUp ();\r
+ break;\r
+ }\r
+*/\r
+}\r
+\r
+void MODELCMD_Frame (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Frame ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMFrame ();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_Skin (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Skin ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMSkin ();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_Skinsize (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ Cmd_Skinsize ();\r
+/*\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ Cmd_Skinsize ();\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMSkinsize ();\r
+ break;\r
+ }\r
+*/\r
+}\r
+\r
+void MODELCMD_Skeleton (int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMSkeleton ();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_BeginGroup(int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMBeginGroup();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_EndGroup(int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMEndGroup();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_Referenced(int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMReferenced();\r
+ break;\r
+ }\r
+}\r
+\r
+void MODELCMD_NodeOrder(int modeltype)\r
+{\r
+ if (g_forcemodel)\r
+ modeltype = g_forcemodel;\r
+\r
+ switch(modeltype)\r
+ {\r
+ case MODEL_MD2:\r
+ break;\r
+ case MODEL_FM:\r
+ Cmd_FMNodeOrder();\r
+ break;\r
+ }\r
+}\r