gtkradiant/tools/quake2/qdata_heretic2/models.c
TTimo 9998050654 set eol-style
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/ZeroRadiant@183 8a3a26a2-13c4-0310-b231-cf6edde360e5
2007-11-04 03:34:51 +00:00

2050 lines
40 KiB
C

/*
Copyright (C) 1999-2007 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, &current->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;
}
}