2007-11-04 03:34:51 +00:00
|
|
|
|
|
|
|
#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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|