gtkradiant/tools/quake2/qdata/models.c

1164 lines
24 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 "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 );
}
}