/* 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 ); } }