--- /dev/null
+/*
+Copyright (C) 1999-2006 Id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+
+#include "qdata.h"
+#include <assert.h>
+#include "jointed.h"
+#include "fmodel.h"
+
+//=================================================================
+
+typedef struct
+{
+ int numnormals;
+ vec3_t normalsum;
+} vertexnormals_t;
+
+typedef struct
+{
+ vec3_t v;
+ int lightnormalindex;
+} trivert_t;
+
+typedef struct
+{
+ vec3_t mins, maxs;
+ char name[16];
+ trivert_t v[MAX_VERTS];
+ QDataJoint_t joints[NUM_CLUSTERS]; // ,this
+} frame_t;
+
+// ,and all of this should get out of here, need to use new stuff in fmodels instead
+
+typedef struct IntListNode_s
+{
+ int data;
+ struct IntListNode_s *next;
+} IntListNode_t; // gaak
+
+typedef struct
+{
+ float scale[3]; // multiply byte verts by this
+ float translate[3]; // then add this
+} PartialAliasFrame_t;
+
+int jointed;
+int clustered;
+
+int *clusters[NUM_CLUSTERS];
+IntListNode_t *vertLists[NUM_CLUSTERS];
+int num_verts[NUM_CLUSTERS + 1];
+int new_num_verts[NUM_CLUSTERS + 1];
+
+// end that
+
+//================================================================
+
+frame_t g_frames[MAX_FRAMES];
+//frame_t *g_frames;
+
+static dmdl_t model;
+
+
+float scale_up; // set by $scale
+vec3_t adjust; // set by $origin
+int g_fixedwidth, g_fixedheight; // set by $skinsize
+
+
+//
+// base frame info
+//
+dstvert_t base_st[MAX_VERTS];
+dtriangle_t triangles[MAX_TRIANGLES];
+
+static int triangle_st[MAX_TRIANGLES][3][2];
+
+// the command list holds counts, s/t values, and xyz indexes
+// that are valid for every frame
+int commands[16384];
+int numcommands;
+int numglverts;
+int used[MAX_TRIANGLES];
+
+char g_skins[MAX_MD2SKINS][64];
+
+char cdarchive[1024];
+char cdpartial[1024];
+char cddir[1024];
+
+char modelname[64]; // empty unless $modelname issued (players)
+
+extern char *g_outputDir;
+
+#define NUMVERTEXNORMALS 162
+
+float avertexnormals[NUMVERTEXNORMALS][3] =
+{
+ #include "anorms.h"
+};
+
+unsigned char pic[SKINPAGE_HEIGHT*SKINPAGE_WIDTH], pic_palette[768];
+
+FILE *headerouthandle = NULL;
+
+//==============================================================
+
+/*
+===============
+ClearModel
+===============
+*/
+static void ClearModel (void)
+{
+ memset (&model, 0, sizeof(model));
+
+ modelname[0] = 0;
+ jointed = NOT_JOINTED;
+ clustered = 0;
+ scale_up = 1.0;
+ VectorCopy (vec3_origin, adjust);
+ g_fixedwidth = g_fixedheight = 0;
+ g_skipmodel = false;
+}
+
+
+void H_printf(char *fmt, ...)
+{
+ va_list argptr;
+ char name[1024];
+
+ if (!headerouthandle)
+ {
+ sprintf (name, "%s/tris.h", cddir);
+ headerouthandle = SafeOpenWrite (name);
+ fprintf(headerouthandle, "// %s\n\n", cddir);
+ fprintf(headerouthandle, "// This file generated by qdata - Do NOT Modify\n\n");
+ }
+
+ va_start (argptr, fmt);
+ vfprintf (headerouthandle, fmt, argptr);
+ va_end (argptr);
+}
+
+#if 1
+/*
+============
+WriteModelFile
+============
+*/
+void WriteCommonModelFile (FILE *modelouthandle, PartialAliasFrame_t *outFrames)
+{
+ int i;
+ dmdl_t modeltemp;
+ int j, k;
+ frame_t *in;
+ daliasframe_t *out;
+ byte buffer[MAX_VERTS*4+128];
+ float v;
+ int c_on, c_off;
+
+ model.version = ALIAS_VERSION;
+ model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
+ model.num_glcmds = numcommands;
+ model.ofs_skins = sizeof(dmdl_t);
+ model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
+ model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
+ model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
+ model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
+ model.ofs_end = model.ofs_glcmds + model.num_glcmds*sizeof(int);
+ //
+ // write out the model header
+ //
+ for (i=0 ; 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];
+
+ if(outFrames)
+ {
+ outFrames[i].scale[j] = out->scale[j];
+ outFrames[i].translate[j] = out->translate[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);
+}
+
+/*
+============
+WriteModelFile
+============
+*/
+void WriteModelFile (FILE *modelouthandle)
+{
+ model.ident = IDALIASHEADER;
+
+ WriteCommonModelFile(modelouthandle, NULL);
+}
+
+/*
+============
+WriteJointedModelFile
+============
+*/
+void WriteJointedModelFile (FILE *modelouthandle)
+{
+ int i;
+ int j, k;
+ frame_t *in;
+ float v;
+ IntListNode_t *current, *toFree;
+ PartialAliasFrame_t outFrames[MAX_FRAMES];
+
+ model.ident = IDJOINTEDALIASHEADER;
+
+ WriteCommonModelFile(modelouthandle, outFrames);
+
+ // Skeletal Type
+ SafeWrite(modelouthandle, &jointed, sizeof(int));
+
+ // number of joints
+ SafeWrite(modelouthandle, &numJointsForSkeleton[jointed], sizeof(int));
+
+ // number of verts in each cluster
+ SafeWrite(modelouthandle, &new_num_verts[1], sizeof(int)*numJointsForSkeleton[jointed]);
+
+ // cluster verts
+ for(i = 0; i < new_num_verts[0]; ++i)
+ {
+ current = vertLists[i];
+ while(current)
+ {
+ SafeWrite (modelouthandle, ¤t->data, sizeof(int));
+ toFree = current;
+ current = current->next;
+ free(toFree); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
+ }
+ }
+
+ for (i=0 ; i<model.num_frames ; i++)
+ {
+ in = &g_frames[i];
+
+ for (j = 0 ; j < new_num_verts[0]; ++j)
+ {
+ for (k=0 ; k<3 ; k++)
+ {
+ // scale to byte values & min/max check
+ v = Q_rint ( (in->joints[j].placement.origin[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+ // clamp, so rounding doesn't wrap from 255.6 to 0
+ if (v > 255.0)
+ {
+ v = 255.0;
+ }
+
+ if (v < 0)
+ {
+ v = 0;
+ }
+
+ // write out origin as a float (there's only a few per model, so it's not really
+ // a size issue)
+ SafeWrite (modelouthandle, &v, sizeof(float));
+ }
+
+ for (k=0 ; k<3 ; k++)
+ {
+ v = Q_rint ( (in->joints[j].placement.direction[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+ // clamp, so rounding doesn't wrap from 255.6 to 0
+ if (v > 255.0)
+ {
+ v = 255.0;
+ }
+
+ if (v < 0)
+ {
+ v = 0;
+ }
+
+ // write out origin as a float (there's only a few per model, so it's not really
+ // a size issue)
+ SafeWrite (modelouthandle, &v, sizeof(float));
+ }
+
+ for (k=0 ; k<3 ; k++)
+ {
+ v = Q_rint ( (in->joints[j].placement.up[k] - outFrames[i].translate[k]) / outFrames[i].scale[k] );
+
+ // clamp, so rounding doesn't wrap from 255.6 to 0
+ if (v > 255.0)
+ {
+ v = 255.0;
+ }
+
+ if (v < 0)
+ {
+ v = 0;
+ }
+
+ // write out origin as a float (there's only a few per model, so it's not really
+ // a size issue)
+ SafeWrite (modelouthandle, &v, sizeof(float));
+ }
+ }
+ }
+}
+#else
+/*
+============
+WriteModelFile
+============
+*/
+static void WriteModelFile (FILE *modelouthandle)
+{
+ int i;
+ dmdl_t modeltemp;
+ int j, k;
+ frame_t *in;
+ daliasframe_t *out;
+ byte buffer[MAX_VERTS*4+128];
+ float v;
+ int c_on, c_off;
+
+ model.ident = IDALIASHEADER;
+ model.version = ALIAS_VERSION;
+ model.framesize = (int)&((daliasframe_t *)0)->verts[model.num_xyz];
+ model.num_glcmds = numcommands;
+ model.ofs_skins = sizeof(dmdl_t);
+ model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
+ model.ofs_tris = model.ofs_st + model.num_st*sizeof(dstvert_t);
+ model.ofs_frames = model.ofs_tris + model.num_tris*sizeof(dtriangle_t);
+ model.ofs_glcmds = model.ofs_frames + model.num_frames*model.framesize;
+ model.ofs_end = model.ofs_glcmds + model.num_glcmds*4;
+
+ //
+ // write out the model header
+ //
+ for (i=0 ; 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);
+}
+#endif
+
+/*
+===============
+FinishModel
+===============
+*/
+void FinishModel (void)
+{
+ FILE *modelouthandle;
+ int i;
+ char name[1024];
+
+ if (!model.num_frames)
+ return;
+
+//
+// copy to release directory tree if doing a release build
+//
+ if (g_release)
+ {
+ if (modelname[0])
+ sprintf (name, "%s", modelname);
+ else
+ sprintf (name, "%s/tris.md2", cdpartial);
+ ReleaseFile (name);
+
+ for (i=0 ; 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", g_outputDir, modelname);
+ else
+ sprintf (name, "%s/tris.md2", g_outputDir);
+ printf ("saving to %s\n", name);
+ CreatePath (name);
+ modelouthandle = SafeOpenWrite (name);
+
+#if 1
+ if(jointed != NOT_JOINTED)
+ WriteJointedModelFile(modelouthandle);
+ else
+#endif
+ WriteModelFile(modelouthandle);
+
+ printf ("%3dx%3d skin\n", model.skinwidth, model.skinheight);
+ printf ("First frame boundaries:\n");
+ printf (" minimum x: %3f\n", g_frames[0].mins[0]);
+ printf (" maximum x: %3f\n", g_frames[0].maxs[0]);
+ printf (" minimum y: %3f\n", g_frames[0].mins[1]);
+ printf (" maximum y: %3f\n", g_frames[0].maxs[1]);
+ printf (" minimum z: %3f\n", g_frames[0].mins[2]);
+ printf (" maximum z: %3f\n", g_frames[0].maxs[2]);
+ printf ("%4d vertices\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
+================
+*/
+static 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
+===========
+*/
+static 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
+================
+*/
+static 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
+============
+*/
+#if 0
+static void OldBuildST (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;
+}
+#endif
+
+//==========================================================================
+//
+// DrawScreen
+//
+//==========================================================================
+
+void DrawScreen(float s_scale, float t_scale, float iwidth, float iheight)
+{
+ int i;
+ byte *scrpos;
+ char buffer[256];
+
+ // Divider
+ scrpos = &pic[(INFO_Y-2)*SKINPAGE_WIDTH];
+ for(i = 0; i < SKINPAGE_WIDTH; i++)
+ {
+ *scrpos++ = 255;
+ }
+
+ sprintf(buffer, "GENSKIN: ");
+ DrawTextChar(16, INFO_Y, buffer);
+
+ sprintf(buffer, "( %03d * %03d ) SCALE %f %f, SKINWIDTH %d,"
+ " SKINHEIGHT %d", (int)ScaleWidth, (int)ScaleHeight, s_scale, t_scale, (int)iwidth*2, (int)iheight);
+ DrawTextChar(80, INFO_Y, buffer);
+}
+
+/*
+============
+BuildST
+
+Builds the triangle_st array for the base frame and
+model.skinwidth / model.skinheight
+
+ FIXME: allow this to be loaded from a file for
+ arbitrary mappings
+============
+*/
+void BuildST (triangle_t *ptri, int numtri, qboolean DrawSkin)
+{
+ int i, j;
+ int width, height, iwidth, iheight, swidth;
+ float basex, basey;
+ float scale;
+ vec3_t mins, maxs;
+ float *pbasevert;
+ vec3_t vtemp1, vtemp2, normal;
+ float s_scale, t_scale;
+ float scWidth;
+ float scHeight;
+
+ //
+ // find bounds of all the verts on the base frame
+ //
+ ClearBounds (mins, maxs);
+
+ for (i=0 ; i<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];
+
+
+ scWidth = (ScaleWidth/2)*SCALE_ADJUST_FACTOR;
+ scHeight = ScaleHeight*SCALE_ADJUST_FACTOR;
+
+ scale = scWidth/width;
+
+ if(height*scale >= scHeight)
+ {
+ scale = scHeight/height;
+ }
+
+ iwidth = ceil(width*scale)+4;
+ iheight = ceil(height*scale)+4;
+
+ s_scale = (float)(iwidth-4) / width;
+ t_scale = (float)(iheight-4) / height;
+ t_scale = s_scale;
+
+ if (DrawSkin)
+ DrawScreen(s_scale, t_scale, iwidth, iheight);
+
+
+/* if (!g_fixedwidth)
+ { // old style
+ scale = 8;
+ if (width*scale >= 150)
+ scale = 150.0 / width;
+ if (height*scale >= 190)
+ scale = 190.0 / height;
+
+ s_scale = t_scale = scale;
+
+ iwidth = ceil(width*s_scale);
+ iheight = ceil(height*t_scale);
+
+ iwidth += 4;
+ iheight += 4;
+ }
+ else
+ { // new style
+ iwidth = g_fixedwidth / 2;
+ iheight = g_fixedheight;
+
+ s_scale = (float)(iwidth-4) / width;
+ t_scale = (float)(iheight-4) / height;
+ }*/
+
+//
+// determine which side of each triangle to map the texture to
+//
+ for (i=0 ; i<numtri ; i++)
+ {
+ if (ptri[i].HasUV)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ triangle_st[i][j][0] = Q_rint(ptri[i].uv[j][0]*iwidth);
+ triangle_st[i][j][1] = Q_rint((1.0f-ptri[i].uv[j][1])*iheight);
+ }
+ }
+ else
+ {
+ 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);
+ }
+ }
+
+ DrawLine(triangle_st[i][0][0], triangle_st[i][0][1],
+ triangle_st[i][1][0], triangle_st[i][1][1]);
+ DrawLine(triangle_st[i][1][0], triangle_st[i][1][1],
+ triangle_st[i][2][0], triangle_st[i][2][1]);
+ DrawLine(triangle_st[i][2][0], triangle_st[i][2][1],
+ triangle_st[i][0][0], triangle_st[i][0][1]);
+ }
+
+// make the width a multiple of 4; some hardware requires this, and it ensures
+// dword alignment for each scan
+
+ swidth = iwidth*2;
+ model.skinwidth = (swidth + 3) & ~3;
+ model.skinheight = iheight;
+}
+
+
+static void ReplaceClusterIndex(int newIndex, int oldindex, int **clusters,
+ IntListNode_t **vertLists, int *num_verts, int *new_num_verts)
+{
+ int i, j;
+ IntListNode_t *next;
+
+ for(j = 0; j < num_verts[0]; ++j)
+ {
+ for(i = 0; i < num_verts[j+1]; ++i)
+ {
+ if(clusters[j][i] == oldindex)
+ {
+ ++new_num_verts[j+1];
+
+ next = vertLists[j];
+
+ vertLists[j] = (IntListNode_t *) SafeMalloc(sizeof(IntListNode_t), "ReplaceClusterIndex");
+ // Currently freed in WriteJointedModelFile only
+
+ vertLists[j]->data = newIndex;
+ vertLists[j]->next = next;
+ }
+ }
+ }
+}
+
+/*
+=================
+Cmd_Base
+=================
+*/
+void Cmd_Base (void)
+{
+ vec3_t base_xyz[MAX_VERTS];
+ triangle_t *ptri;
+ int i, j, k;
+#if 1
+#else
+ int time1;
+#endif
+ char file1[1024];
+ char file2[1024];
+
+ GetScriptToken (false);
+
+ if (g_skipmodel || g_release || g_archive)
+ return;
+
+ printf ("---------------------\n");
+#if 1
+ sprintf (file1, "%s/%s", cdpartial, token);
+ printf ("%s ", file1);
+
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s", cddir, token);
+#else
+ sprintf (file1, "%s/%s.%s", cdarchive, token, trifileext);
+ printf ("%s\n", file1);
+
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s.%s", cddir, token, trifileext);
+
+ time1 = FileTime (file1);
+ if (time1 == -1)
+ Error ("%s doesn't exist", file1);
+#endif
+//
+// load the base triangles
+//
+ if (do3ds)
+ Load3DSTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);
+ else
+ LoadTriangleList (file1, &ptri, &model.num_tris, NULL, NULL);
+
+
+ GetScriptToken (false);
+ sprintf (file2, "%s/%s.pcx", cddir, token);
+// sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);
+
+ printf ("skin: %s\n", file2);
+ Load256Image (file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight);
+
+ if (BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT)
+ {
+ if (g_allow_newskin)
+ {
+ ScaleWidth = BaseWidth;
+ ScaleHeight = BaseHeight;
+ }
+ else
+ {
+ Error("Invalid skin page size: (%d,%d) should be (%d,%d)",
+ BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT);
+ }
+ }
+ else
+ {
+ ScaleWidth = (float)ExtractNumber(BasePixels, ENCODED_WIDTH_X,
+ ENCODED_WIDTH_Y);
+ ScaleHeight = (float)ExtractNumber(BasePixels, ENCODED_HEIGHT_X,
+ ENCODED_HEIGHT_Y);
+ }
+
+//
+// get the ST values
+//
+ BuildST (ptri, model.num_tris,false);
+
+//
+// run through all the base triangles, storing each unique vertex in the
+// base vertex list and setting the indirect triangles to point to the base
+// vertices
+//
+ for (i=0 ; i<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]);
+
+ if(clustered)
+ ReplaceClusterIndex(k, ptri[i].indicies[j], (int **)&clusters, (IntListNode_t **)&vertLists, (int *)&num_verts, (int *)&new_num_verts);
+
+ 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;
+
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "hrc");
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s%s.%s", base, suffix, "hrc");
+ return retname;
+ }
+
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "asc");
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s%s.%s", base, suffix, "asc");
+ return retname;
+ }
+
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "tri");
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s%s.%s", base, suffix, "tri");
+ return retname;
+ }
+
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "3ds");
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s%s.%s", base, suffix, "3ds");
+ return retname;
+ }
+
+ sprintf (file1, "%s/%s%s.%s",cddir, base, suffix, "htr");
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s%s.%s", base, suffix, "htr");
+ return retname;
+ }
+
+ // check for 'run.1'
+ sprintf (file1, "%s/%s.%s",cddir, base, suffix);
+ time1 = FileTime (file1);
+ if (time1 != -1)
+ {
+ sprintf (retname, "%s.%s", base, suffix);
+ return retname;
+ }
+
+ Error ("frame %s could not be found",frame);
+ return NULL;
+}
+
+/*
+===============
+GrabFrame
+===============
+*/
+static void GrabFrame (char *frame)
+{
+ triangle_t *ptri;
+ int i, j;
+ trivert_t *ptrivert;
+ int num_tris;
+ char file1[1024];
+ frame_t *fr;
+ vertexnormals_t vnorms[MAX_VERTS];
+ int index_xyz;
+ char *framefile;
+
+ // the frame 'run1' will be looked for as either
+ // run.1 or run1.tri, so the new alias sequence save
+ // feature an be used
+ framefile = FindFrameFile (frame);
+
+ sprintf (file1, "%s/%s", cdarchive, framefile);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s",cddir, framefile);
+
+ printf ("grabbing %s ", file1);
+
+ if (model.num_frames >= MAX_FRAMES)
+ Error ("model.num_frames >= MAX_FRAMES");
+ fr = &g_frames[model.num_frames];
+ model.num_frames++;
+
+ strcpy (fr->name, frame);
+
+//
+// load the frame
+//
+ if (do3ds)
+ Load3DSTriangleList (file1, &ptri, &num_tris, NULL, NULL);
+ else
+ LoadTriangleList (file1, &ptri, &num_tris, NULL, NULL);
+
+ if (num_tris != model.num_tris)
+ Error ("%s: number of triangles doesn't match base frame\n", file1);
+
+//
+// allocate storage for the frame's vertices
+//
+ ptrivert = fr->v;
+
+ for (i=0 ; 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);
+}
+
+/*
+===============
+GrabJointedFrame
+===============
+*/
+void GrabJointedFrame(char *frame)
+{
+ char file1[1024];
+ char *framefile;
+ frame_t *fr;
+
+ framefile = FindFrameFile (frame);
+
+ sprintf (file1, "%s/%s", cdarchive, framefile);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s",cddir, framefile);
+
+ printf ("grabbing %s\n", file1);
+
+ fr = &g_frames[model.num_frames - 1]; // last frame read in
+
+ LoadJointList(file1, fr->joints, jointed);
+}
+
+/*
+===============
+GrabGlobals
+===============
+*/
+void GrabGlobals(char *frame)
+{
+ char file1[1024];
+ char *framefile;
+ frame_t *fr;
+
+ framefile = FindFrameFile (frame);
+
+ sprintf (file1, "%s/%s", cdarchive, framefile);
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s",cddir, framefile);
+
+ printf ("grabbing %s\n", file1);
+
+ fr = &g_frames[model.num_frames - 1]; // last frame read in
+
+ LoadGlobals(file1);
+}
+
+/*
+===============
+Cmd_Frame
+===============
+*/
+void Cmd_Frame (void)
+{
+ while (ScriptTokenAvailable())
+ {
+ GetScriptToken (false);
+ if (g_skipmodel)
+ continue;
+ if (g_release || g_archive)
+ {
+ model.num_frames = 1; // don't skip the writeout
+ continue;
+ }
+
+ H_printf("#define FRAME_%-16s\t%i\n", token, model.num_frames);
+
+ GrabFrame (token);
+ }
+}
+
+/*
+===============
+Cmd_Skin
+
+Skins aren't actually stored in the file, only a reference
+is saved out to the header file.
+===============
+*/
+void Cmd_Skin (void)
+{
+ byte *palette;
+ byte *pixels;
+ int width, height;
+ byte *cropped;
+ int y;
+ char name[1024], savename[1024];
+
+ GetScriptToken (false);
+
+ if (model.num_skins == MAX_MD2SKINS)
+ Error ("model.num_skins == MAX_MD2SKINS");
+
+ if (g_skipmodel)
+ return;
+
+#if 1
+ sprintf (name, "%s/%s.pcx", cddir, token);
+ sprintf (savename, "%s/!%s.pcx", g_outputDir, token);
+ sprintf (g_skins[model.num_skins], "%s/!%s.pcx", cdpartial, token);
+#else
+ sprintf (name, "%s/%s.lbm", cdarchive, token);
+ strcpy (name, ExpandPathAndArchive( name ) );
+// sprintf (name, "%s/%s.lbm", cddir, token);
+
+ if (ScriptTokenAvailable())
+ {
+ GetScriptToken (false);
+ sprintf (g_skins[model.num_skins], "%s.pcx", token);
+ sprintf (savename, "%s%s.pcx", g_outputDir, g_skins[model.num_skins]);
+ }
+ else
+ {
+ sprintf (savename, "%s/%s.pcx", g_outputDir, token);
+ sprintf (g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token);
+ }
+#endif
+
+ model.num_skins++;
+
+ if (g_skipmodel || g_release || g_archive)
+ return;
+
+ // load the image
+ printf ("loading %s\n", name);
+ Load256Image (name, &pixels, &palette, &width, &height);
+// RemapZero (pixels, palette, width, height);
+
+ // crop it to the proper size
+ cropped = (byte *) SafeMalloc (model.skinwidth*model.skinheight, "Cmd_Skin");
+ for (y=0 ; y<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
+ GetScriptToken (false);
+ adjust[1] = -atof (token);
+
+ GetScriptToken (false);
+ adjust[0] = atof (token);
+
+ GetScriptToken (false);
+ adjust[2] = -atof (token);
+}
+
+
+/*
+=================
+Cmd_ScaleUp
+=================
+*/
+void Cmd_ScaleUp (void)
+{
+ GetScriptToken (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)
+{
+ GetScriptToken (false);
+ g_fixedwidth = atoi(token);
+ GetScriptToken (false);
+ g_fixedheight = atoi(token);
+}
+
+/*
+=================
+Cmd_Modelname
+
+Gives a different name/location for the file, instead of the cddir
+=================
+*/
+void Cmd_Modelname (void)
+{
+ GetScriptToken (false);
+ strcpy (modelname, token);
+}
+
+/*
+===============
+Cmd_Cd
+===============
+*/
+void Cmd_Cd (void)
+{
+ char temp[256];
+
+ FinishModel ();
+ ClearModel ();
+
+ GetScriptToken (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);
+
+ // Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.
+ sprintf(temp, "%s%s", g_outputDir, cdpartial);
+ strcpy(g_outputDir, temp);
+
+ // 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);
+ }
+}
+
+/*
+=================
+Cmd_Cluster
+=================
+*/
+void Cmd_Cluster()
+{
+ char file1[1024];
+
+ GetScriptToken (false);
+
+ printf ("---------------------\n");
+ sprintf (file1, "%s/%s", cdpartial, token);
+ printf ("%s\n", file1);
+
+ ExpandPathAndArchive (file1);
+
+ sprintf (file1, "%s/%s", cddir, token);
+
+ LoadClusters(file1, (int **)&clusters, (int *)&num_verts, jointed);
+
+ new_num_verts[0] = num_verts[0];
+
+ clustered = 1;
+}
+
+// Model construction cover functions.
+void MODELCMD_Modelname (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ Cmd_Modelname ();
+/*
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Modelname ();
+ break;
+ case MODEL_FM:
+ Cmd_FMModelname ();
+ break;
+ }
+*/
+}
+
+void MODELCMD_Cd (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Cd ();
+ break;
+ case MODEL_FM:
+ Cmd_FMCd ();
+ break;
+ }
+}
+
+void MODELCMD_Origin (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ Cmd_Origin ();
+/* switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Origin ();
+ break;
+ case MODEL_FM:
+ Cmd_FMOrigin ();
+ break;
+ }
+*/
+}
+
+void MODELCMD_Cluster (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Cluster ();
+ break;
+ case MODEL_FM:
+ Cmd_FMCluster ();
+ break;
+ }
+}
+
+void MODELCMD_Base (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Base ();
+ break;
+ case MODEL_FM:
+ Cmd_FMBase (false);
+ break;
+ }
+}
+
+void MODELCMD_BaseST (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Base ();
+ break;
+ case MODEL_FM:
+ Cmd_FMBase (true);
+ break;
+ }
+}
+
+void MODELCMD_ScaleUp (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ Cmd_ScaleUp ();
+/* switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_ScaleUp ();
+ break;
+ case MODEL_FM:
+ Cmd_FMScaleUp ();
+ break;
+ }
+*/
+}
+
+void MODELCMD_Frame (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Frame ();
+ break;
+ case MODEL_FM:
+ Cmd_FMFrame ();
+ break;
+ }
+}
+
+void MODELCMD_Skin (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Skin ();
+ break;
+ case MODEL_FM:
+ Cmd_FMSkin ();
+ break;
+ }
+}
+
+void MODELCMD_Skinsize (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ Cmd_Skinsize ();
+/*
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ Cmd_Skinsize ();
+ break;
+ case MODEL_FM:
+ Cmd_FMSkinsize ();
+ break;
+ }
+*/
+}
+
+void MODELCMD_Skeleton (int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ break;
+ case MODEL_FM:
+ Cmd_FMSkeleton ();
+ break;
+ }
+}
+
+void MODELCMD_BeginGroup(int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ break;
+ case MODEL_FM:
+ Cmd_FMBeginGroup();
+ break;
+ }
+}
+
+void MODELCMD_EndGroup(int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ break;
+ case MODEL_FM:
+ Cmd_FMEndGroup();
+ break;
+ }
+}
+
+void MODELCMD_Referenced(int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ break;
+ case MODEL_FM:
+ Cmd_FMReferenced();
+ break;
+ }
+}
+
+void MODELCMD_NodeOrder(int modeltype)
+{
+ if (g_forcemodel)
+ modeltype = g_forcemodel;
+
+ switch(modeltype)
+ {
+ case MODEL_MD2:
+ break;
+ case MODEL_FM:
+ Cmd_FMNodeOrder();
+ break;
+ }
+}