mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-10 06:31:41 +00:00
2041 lines
43 KiB
C
2041 lines
43 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, ¤t->data, sizeof( int ) );
|
|
toFree = current;
|
|
current = current->next;
|
|
free( toFree ); // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
|
|
}
|
|
}
|
|
|
|
for ( i = 0 ; i < model.num_frames ; i++ )
|
|
{
|
|
in = &g_frames[i];
|
|
|
|
for ( j = 0 ; j < new_num_verts[0]; ++j )
|
|
{
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
{
|
|
// scale to byte values & min/max check
|
|
v = Q_rint( ( in->joints[j].placement.origin[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
|
|
|
|
// clamp, so rounding doesn't wrap from 255.6 to 0
|
|
if ( v > 255.0 ) {
|
|
v = 255.0;
|
|
}
|
|
|
|
if ( v < 0 ) {
|
|
v = 0;
|
|
}
|
|
|
|
// write out origin as a float (there's only a few per model, so it's not really
|
|
// a size issue)
|
|
SafeWrite( modelouthandle, &v, sizeof( float ) );
|
|
}
|
|
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
{
|
|
v = Q_rint( ( in->joints[j].placement.direction[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
|
|
|
|
// clamp, so rounding doesn't wrap from 255.6 to 0
|
|
if ( v > 255.0 ) {
|
|
v = 255.0;
|
|
}
|
|
|
|
if ( v < 0 ) {
|
|
v = 0;
|
|
}
|
|
|
|
// write out origin as a float (there's only a few per model, so it's not really
|
|
// a size issue)
|
|
SafeWrite( modelouthandle, &v, sizeof( float ) );
|
|
}
|
|
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
{
|
|
v = Q_rint( ( in->joints[j].placement.up[k] - outFrames[i].translate[k] ) / outFrames[i].scale[k] );
|
|
|
|
// clamp, so rounding doesn't wrap from 255.6 to 0
|
|
if ( v > 255.0 ) {
|
|
v = 255.0;
|
|
}
|
|
|
|
if ( v < 0 ) {
|
|
v = 0;
|
|
}
|
|
|
|
// write out origin as a float (there's only a few per model, so it's not really
|
|
// a size issue)
|
|
SafeWrite( modelouthandle, &v, sizeof( float ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
/*
|
|
============
|
|
WriteModelFile
|
|
============
|
|
*/
|
|
static void WriteModelFile( FILE *modelouthandle ){
|
|
int i;
|
|
dmdl_t modeltemp;
|
|
int j, k;
|
|
frame_t *in;
|
|
daliasframe_t *out;
|
|
byte buffer[MAX_VERTS * 4 + 128];
|
|
float v;
|
|
int c_on, c_off;
|
|
|
|
model.ident = IDALIASHEADER;
|
|
model.version = ALIAS_VERSION;
|
|
model.framesize = (int)&( (daliasframe_t *)0 )->verts[model.num_xyz];
|
|
model.num_glcmds = numcommands;
|
|
model.ofs_skins = sizeof( dmdl_t );
|
|
model.ofs_st = model.ofs_skins + model.num_skins * MAX_SKINNAME;
|
|
model.ofs_tris = model.ofs_st + model.num_st * sizeof( dstvert_t );
|
|
model.ofs_frames = model.ofs_tris + model.num_tris * sizeof( dtriangle_t );
|
|
model.ofs_glcmds = model.ofs_frames + model.num_frames * model.framesize;
|
|
model.ofs_end = model.ofs_glcmds + model.num_glcmds * 4;
|
|
|
|
//
|
|
// write out the model header
|
|
//
|
|
for ( i = 0 ; i < sizeof( dmdl_t ) / 4 ; i++ )
|
|
( (int *)&modeltemp )[i] = LittleLong( ( (int *)&model )[i] );
|
|
|
|
SafeWrite( modelouthandle, &modeltemp, sizeof( modeltemp ) );
|
|
|
|
//
|
|
// write out the skin names
|
|
//
|
|
SafeWrite( modelouthandle, g_skins, model.num_skins * MAX_SKINNAME );
|
|
|
|
//
|
|
// write out the texture coordinates
|
|
//
|
|
c_on = c_off = 0;
|
|
for ( i = 0 ; i < model.num_st ; i++ )
|
|
{
|
|
base_st[i].s = LittleShort( base_st[i].s );
|
|
base_st[i].t = LittleShort( base_st[i].t );
|
|
}
|
|
|
|
SafeWrite( modelouthandle, base_st, model.num_st * sizeof( base_st[0] ) );
|
|
|
|
//
|
|
// write out the triangles
|
|
//
|
|
for ( i = 0 ; i < model.num_tris ; i++ )
|
|
{
|
|
int j;
|
|
dtriangle_t tri;
|
|
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
tri.index_xyz[j] = LittleShort( triangles[i].index_xyz[j] );
|
|
tri.index_st[j] = LittleShort( triangles[i].index_st[j] );
|
|
}
|
|
|
|
SafeWrite( modelouthandle, &tri, sizeof( tri ) );
|
|
}
|
|
|
|
//
|
|
// write out the frames
|
|
//
|
|
for ( i = 0 ; i < model.num_frames ; i++ )
|
|
{
|
|
in = &g_frames[i];
|
|
out = (daliasframe_t *)buffer;
|
|
|
|
strcpy( out->name, in->name );
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
out->scale[j] = ( in->maxs[j] - in->mins[j] ) / 255;
|
|
out->translate[j] = in->mins[j];
|
|
}
|
|
|
|
for ( j = 0 ; j < model.num_xyz ; j++ )
|
|
{
|
|
// all of these are byte values, so no need to deal with endianness
|
|
out->verts[j].lightnormalindex = in->v[j].lightnormalindex;
|
|
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
{
|
|
// scale to byte values & min/max check
|
|
v = Q_rint( ( in->v[j].v[k] - out->translate[k] ) / out->scale[k] );
|
|
|
|
// clamp, so rounding doesn't wrap from 255.6 to 0
|
|
if ( v > 255.0 ) {
|
|
v = 255.0;
|
|
}
|
|
if ( v < 0 ) {
|
|
v = 0;
|
|
}
|
|
out->verts[j].v[k] = v;
|
|
}
|
|
}
|
|
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
out->scale[j] = LittleFloat( out->scale[j] );
|
|
out->translate[j] = LittleFloat( out->translate[j] );
|
|
}
|
|
|
|
SafeWrite( modelouthandle, out, model.framesize );
|
|
}
|
|
|
|
//
|
|
// write out glcmds
|
|
//
|
|
SafeWrite( modelouthandle, commands, numcommands * 4 );
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
===============
|
|
FinishModel
|
|
===============
|
|
*/
|
|
void FinishModel( void ){
|
|
FILE *modelouthandle;
|
|
int i;
|
|
char name[1024];
|
|
|
|
if ( !model.num_frames ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// copy to release directory tree if doing a release build
|
|
//
|
|
if ( g_release ) {
|
|
if ( modelname[0] ) {
|
|
sprintf( name, "%s", modelname );
|
|
}
|
|
else{
|
|
sprintf( name, "%s/tris.md2", cdpartial );
|
|
}
|
|
ReleaseFile( name );
|
|
|
|
for ( i = 0 ; i < model.num_skins ; i++ )
|
|
{
|
|
ReleaseFile( g_skins[i] );
|
|
}
|
|
model.num_frames = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// write the model output file
|
|
//
|
|
if ( modelname[0] ) {
|
|
sprintf( name, "%s%s", g_outputDir, modelname );
|
|
}
|
|
else{
|
|
sprintf( name, "%s/tris.md2", g_outputDir );
|
|
}
|
|
printf( "saving to %s\n", name );
|
|
CreatePath( name );
|
|
modelouthandle = SafeOpenWrite( name );
|
|
|
|
#if 1
|
|
if ( jointed != NOT_JOINTED ) {
|
|
WriteJointedModelFile( modelouthandle );
|
|
}
|
|
else
|
|
#endif
|
|
WriteModelFile( modelouthandle );
|
|
|
|
printf( "%3dx%3d skin\n", model.skinwidth, model.skinheight );
|
|
printf( "First frame boundaries:\n" );
|
|
printf( " minimum x: %3f\n", g_frames[0].mins[0] );
|
|
printf( " maximum x: %3f\n", g_frames[0].maxs[0] );
|
|
printf( " minimum y: %3f\n", g_frames[0].mins[1] );
|
|
printf( " maximum y: %3f\n", g_frames[0].maxs[1] );
|
|
printf( " minimum z: %3f\n", g_frames[0].mins[2] );
|
|
printf( " maximum z: %3f\n", g_frames[0].maxs[2] );
|
|
printf( "%4d vertices\n", model.num_xyz );
|
|
printf( "%4d triangles\n", model.num_tris );
|
|
printf( "%4d frame\n", model.num_frames );
|
|
printf( "%4d glverts\n", numglverts );
|
|
printf( "%4d glcmd\n", model.num_glcmds );
|
|
printf( "%4d skins\n", model.num_skins );
|
|
printf( "file size: %d\n", (int)ftell( modelouthandle ) );
|
|
printf( "---------------------\n" );
|
|
|
|
fclose( modelouthandle );
|
|
|
|
// finish writing header file
|
|
H_printf( "\n" );
|
|
|
|
// scale_up is usefull to allow step distances to be adjusted
|
|
H_printf( "#define MODEL_SCALE\t\t%f\n", scale_up );
|
|
|
|
fclose( headerouthandle );
|
|
headerouthandle = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
=================================================================
|
|
|
|
ALIAS MODEL DISPLAY LIST GENERATION
|
|
|
|
=================================================================
|
|
*/
|
|
|
|
int strip_xyz[128];
|
|
int strip_st[128];
|
|
int strip_tris[128];
|
|
int stripcount;
|
|
|
|
/*
|
|
================
|
|
StripLength
|
|
================
|
|
*/
|
|
static int StripLength( int starttri, int startv ){
|
|
int m1, m2;
|
|
int st1, st2;
|
|
int j;
|
|
dtriangle_t *last, *check;
|
|
int k;
|
|
|
|
used[starttri] = 2;
|
|
|
|
last = &triangles[starttri];
|
|
|
|
strip_xyz[0] = last->index_xyz[( startv ) % 3];
|
|
strip_xyz[1] = last->index_xyz[( startv + 1 ) % 3];
|
|
strip_xyz[2] = last->index_xyz[( startv + 2 ) % 3];
|
|
strip_st[0] = last->index_st[( startv ) % 3];
|
|
strip_st[1] = last->index_st[( startv + 1 ) % 3];
|
|
strip_st[2] = last->index_st[( startv + 2 ) % 3];
|
|
|
|
strip_tris[0] = starttri;
|
|
stripcount = 1;
|
|
|
|
m1 = last->index_xyz[( startv + 2 ) % 3];
|
|
st1 = last->index_st[( startv + 2 ) % 3];
|
|
m2 = last->index_xyz[( startv + 1 ) % 3];
|
|
st2 = last->index_st[( startv + 1 ) % 3];
|
|
|
|
// look for a matching triangle
|
|
nexttri:
|
|
for ( j = starttri + 1, check = &triangles[starttri + 1]
|
|
; j < model.num_tris ; j++, check++ )
|
|
{
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
{
|
|
if ( check->index_xyz[k] != m1 ) {
|
|
continue;
|
|
}
|
|
if ( check->index_st[k] != st1 ) {
|
|
continue;
|
|
}
|
|
if ( check->index_xyz[ ( k + 1 ) % 3 ] != m2 ) {
|
|
continue;
|
|
}
|
|
if ( check->index_st[ ( k + 1 ) % 3 ] != st2 ) {
|
|
continue;
|
|
}
|
|
|
|
// this is the next part of the fan
|
|
|
|
// if we can't use this triangle, this tristrip is done
|
|
if ( used[j] ) {
|
|
goto done;
|
|
}
|
|
|
|
// the new edge
|
|
if ( stripcount & 1 ) {
|
|
m2 = check->index_xyz[ ( k + 2 ) % 3 ];
|
|
st2 = check->index_st[ ( k + 2 ) % 3 ];
|
|
}
|
|
else
|
|
{
|
|
m1 = check->index_xyz[ ( k + 2 ) % 3 ];
|
|
st1 = check->index_st[ ( k + 2 ) % 3 ];
|
|
}
|
|
|
|
strip_xyz[stripcount + 2] = check->index_xyz[ ( k + 2 ) % 3 ];
|
|
strip_st[stripcount + 2] = check->index_st[ ( k + 2 ) % 3 ];
|
|
strip_tris[stripcount] = j;
|
|
stripcount++;
|
|
|
|
used[j] = 2;
|
|
goto nexttri;
|
|
}
|
|
}
|
|
done:
|
|
|
|
// clear the temp used flags
|
|
for ( j = starttri + 1 ; j < model.num_tris ; j++ )
|
|
if ( used[j] == 2 ) {
|
|
used[j] = 0;
|
|
}
|
|
|
|
return stripcount;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
FanLength
|
|
===========
|
|
*/
|
|
static int FanLength( int starttri, int startv ){
|
|
int m1, m2;
|
|
int st1, st2;
|
|
int j;
|
|
dtriangle_t *last, *check;
|
|
int k;
|
|
|
|
used[starttri] = 2;
|
|
|
|
last = &triangles[starttri];
|
|
|
|
strip_xyz[0] = last->index_xyz[( startv ) % 3];
|
|
strip_xyz[1] = last->index_xyz[( startv + 1 ) % 3];
|
|
strip_xyz[2] = last->index_xyz[( startv + 2 ) % 3];
|
|
strip_st[0] = last->index_st[( startv ) % 3];
|
|
strip_st[1] = last->index_st[( startv + 1 ) % 3];
|
|
strip_st[2] = last->index_st[( startv + 2 ) % 3];
|
|
|
|
strip_tris[0] = starttri;
|
|
stripcount = 1;
|
|
|
|
m1 = last->index_xyz[( startv + 0 ) % 3];
|
|
st1 = last->index_st[( startv + 0 ) % 3];
|
|
m2 = last->index_xyz[( startv + 2 ) % 3];
|
|
st2 = last->index_st[( startv + 2 ) % 3];
|
|
|
|
|
|
// look for a matching triangle
|
|
nexttri:
|
|
for ( j = starttri + 1, check = &triangles[starttri + 1]
|
|
; j < model.num_tris ; j++, check++ )
|
|
{
|
|
for ( k = 0 ; k < 3 ; k++ )
|
|
{
|
|
if ( check->index_xyz[k] != m1 ) {
|
|
continue;
|
|
}
|
|
if ( check->index_st[k] != st1 ) {
|
|
continue;
|
|
}
|
|
if ( check->index_xyz[ ( k + 1 ) % 3 ] != m2 ) {
|
|
continue;
|
|
}
|
|
if ( check->index_st[ ( k + 1 ) % 3 ] != st2 ) {
|
|
continue;
|
|
}
|
|
|
|
// this is the next part of the fan
|
|
|
|
// if we can't use this triangle, this tristrip is done
|
|
if ( used[j] ) {
|
|
goto done;
|
|
}
|
|
|
|
// the new edge
|
|
m2 = check->index_xyz[ ( k + 2 ) % 3 ];
|
|
st2 = check->index_st[ ( k + 2 ) % 3 ];
|
|
|
|
strip_xyz[stripcount + 2] = m2;
|
|
strip_st[stripcount + 2] = st2;
|
|
strip_tris[stripcount] = j;
|
|
stripcount++;
|
|
|
|
used[j] = 2;
|
|
goto nexttri;
|
|
}
|
|
}
|
|
done:
|
|
|
|
// clear the temp used flags
|
|
for ( j = starttri + 1 ; j < model.num_tris ; j++ )
|
|
if ( used[j] == 2 ) {
|
|
used[j] = 0;
|
|
}
|
|
|
|
return stripcount;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
================
|
|
BuildGlCmds
|
|
|
|
Generate a list of trifans or strips
|
|
for the model, which holds for all frames
|
|
================
|
|
*/
|
|
static void BuildGlCmds( void ){
|
|
int i, j, k;
|
|
int startv;
|
|
float s, t;
|
|
int len, bestlen, besttype;
|
|
int best_xyz[1024];
|
|
int best_st[1024];
|
|
int best_tris[1024];
|
|
int type;
|
|
|
|
//
|
|
// build tristrips
|
|
//
|
|
numcommands = 0;
|
|
numglverts = 0;
|
|
memset( used, 0, sizeof( used ) );
|
|
for ( i = 0 ; i < model.num_tris ; i++ )
|
|
{
|
|
// pick an unused triangle and start the trifan
|
|
if ( used[i] ) {
|
|
continue;
|
|
}
|
|
|
|
bestlen = 0;
|
|
for ( type = 0 ; type < 2 ; type++ )
|
|
// type = 1;
|
|
{
|
|
for ( startv = 0 ; startv < 3 ; startv++ )
|
|
{
|
|
if ( type == 1 ) {
|
|
len = StripLength( i, startv );
|
|
}
|
|
else{
|
|
len = FanLength( i, startv );
|
|
}
|
|
if ( len > bestlen ) {
|
|
besttype = type;
|
|
bestlen = len;
|
|
for ( j = 0 ; j < bestlen + 2 ; j++ )
|
|
{
|
|
best_st[j] = strip_st[j];
|
|
best_xyz[j] = strip_xyz[j];
|
|
}
|
|
for ( j = 0 ; j < bestlen ; j++ )
|
|
best_tris[j] = strip_tris[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// mark the tris on the best strip/fan as used
|
|
for ( j = 0 ; j < bestlen ; j++ )
|
|
used[best_tris[j]] = 1;
|
|
|
|
if ( besttype == 1 ) {
|
|
commands[numcommands++] = ( bestlen + 2 );
|
|
}
|
|
else{
|
|
commands[numcommands++] = -( bestlen + 2 );
|
|
}
|
|
|
|
numglverts += bestlen + 2;
|
|
|
|
for ( j = 0 ; j < bestlen + 2 ; j++ )
|
|
{
|
|
// emit a vertex into the reorder buffer
|
|
k = best_st[j];
|
|
|
|
// emit s/t coords into the commands stream
|
|
s = base_st[k].s;
|
|
t = base_st[k].t;
|
|
|
|
s = ( s + 0.5 ) / model.skinwidth;
|
|
t = ( t + 0.5 ) / model.skinheight;
|
|
|
|
*(float *)&commands[numcommands++] = s;
|
|
*(float *)&commands[numcommands++] = t;
|
|
*(int *)&commands[numcommands++] = best_xyz[j];
|
|
}
|
|
}
|
|
|
|
commands[numcommands++] = 0; // end of list marker
|
|
}
|
|
|
|
|
|
/*
|
|
===============================================================
|
|
|
|
BASE FRAME SETUP
|
|
|
|
===============================================================
|
|
*/
|
|
|
|
/*
|
|
============
|
|
BuildST
|
|
|
|
Builds the triangle_st array for the base frame and
|
|
model.skinwidth / model.skinheight
|
|
|
|
FIXME: allow this to be loaded from a file for
|
|
arbitrary mappings
|
|
============
|
|
*/
|
|
#if 0
|
|
static void OldBuildST( triangle_t *ptri, int numtri ){
|
|
int i, j;
|
|
int width, height, iwidth, iheight, swidth;
|
|
float basex, basey;
|
|
float s_scale, t_scale;
|
|
float scale;
|
|
vec3_t mins, maxs;
|
|
float *pbasevert;
|
|
vec3_t vtemp1, vtemp2, normal;
|
|
|
|
//
|
|
// find bounds of all the verts on the base frame
|
|
//
|
|
ClearBounds( mins, maxs );
|
|
|
|
for ( i = 0 ; i < numtri ; i++ )
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
AddPointToBounds( ptri[i].verts[j], mins, maxs );
|
|
|
|
for ( i = 0 ; i < 3 ; i++ )
|
|
{
|
|
mins[i] = floor( mins[i] );
|
|
maxs[i] = ceil( maxs[i] );
|
|
}
|
|
|
|
width = maxs[0] - mins[0];
|
|
height = maxs[2] - mins[2];
|
|
|
|
if ( !g_fixedwidth ) { // old style
|
|
scale = 8;
|
|
if ( width * scale >= 150 ) {
|
|
scale = 150.0 / width;
|
|
}
|
|
if ( height * scale >= 190 ) {
|
|
scale = 190.0 / height;
|
|
}
|
|
|
|
s_scale = t_scale = scale;
|
|
|
|
iwidth = ceil( width * s_scale );
|
|
iheight = ceil( height * t_scale );
|
|
|
|
iwidth += 4;
|
|
iheight += 4;
|
|
}
|
|
else
|
|
{ // new style
|
|
iwidth = g_fixedwidth / 2;
|
|
iheight = g_fixedheight;
|
|
|
|
s_scale = (float)( iwidth - 4 ) / width;
|
|
t_scale = (float)( iheight - 4 ) / height;
|
|
}
|
|
|
|
//
|
|
// determine which side of each triangle to map the texture to
|
|
//
|
|
for ( i = 0 ; i < numtri ; i++ )
|
|
{
|
|
VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
|
|
VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
|
|
CrossProduct( vtemp1, vtemp2, normal );
|
|
|
|
if ( normal[1] > 0 ) {
|
|
basex = iwidth + 2;
|
|
}
|
|
else
|
|
{
|
|
basex = 2;
|
|
}
|
|
basey = 2;
|
|
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
pbasevert = ptri[i].verts[j];
|
|
|
|
triangle_st[i][j][0] = Q_rint( ( pbasevert[0] - mins[0] ) * s_scale + basex );
|
|
triangle_st[i][j][1] = Q_rint( ( maxs[2] - pbasevert[2] ) * t_scale + basey );
|
|
}
|
|
}
|
|
|
|
// make the width a multiple of 4; some hardware requires this, and it ensures
|
|
// dword alignment for each scan
|
|
swidth = iwidth * 2;
|
|
model.skinwidth = ( swidth + 3 ) & ~3;
|
|
model.skinheight = iheight;
|
|
}
|
|
#endif
|
|
|
|
//==========================================================================
|
|
//
|
|
// DrawScreen
|
|
//
|
|
//==========================================================================
|
|
|
|
void DrawScreen( float s_scale, float t_scale, float iwidth, float iheight ){
|
|
int i;
|
|
byte *scrpos;
|
|
char buffer[256];
|
|
|
|
// Divider
|
|
scrpos = &pic[( INFO_Y - 2 ) * SKINPAGE_WIDTH];
|
|
for ( i = 0; i < SKINPAGE_WIDTH; i++ )
|
|
{
|
|
*scrpos++ = 255;
|
|
}
|
|
|
|
sprintf( buffer, "GENSKIN: " );
|
|
DrawTextChar( 16, INFO_Y, buffer );
|
|
|
|
sprintf( buffer, "( %03d * %03d ) SCALE %f %f, SKINWIDTH %d,"
|
|
" SKINHEIGHT %d", (int)ScaleWidth, (int)ScaleHeight, s_scale, t_scale, (int)iwidth * 2, (int)iheight );
|
|
DrawTextChar( 80, INFO_Y, buffer );
|
|
}
|
|
|
|
/*
|
|
============
|
|
BuildST
|
|
|
|
Builds the triangle_st array for the base frame and
|
|
model.skinwidth / model.skinheight
|
|
|
|
FIXME: allow this to be loaded from a file for
|
|
arbitrary mappings
|
|
============
|
|
*/
|
|
void BuildST( triangle_t *ptri, int numtri, qboolean DrawSkin ){
|
|
int i, j;
|
|
int width, height, iwidth, iheight, swidth;
|
|
float basex, basey;
|
|
float scale;
|
|
vec3_t mins, maxs;
|
|
float *pbasevert;
|
|
vec3_t vtemp1, vtemp2, normal;
|
|
float s_scale, t_scale;
|
|
float scWidth;
|
|
float scHeight;
|
|
|
|
//
|
|
// find bounds of all the verts on the base frame
|
|
//
|
|
ClearBounds( mins, maxs );
|
|
|
|
for ( i = 0 ; i < numtri ; i++ )
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
AddPointToBounds( ptri[i].verts[j], mins, maxs );
|
|
|
|
for ( i = 0 ; i < 3 ; i++ )
|
|
{
|
|
mins[i] = floor( mins[i] );
|
|
maxs[i] = ceil( maxs[i] );
|
|
}
|
|
|
|
width = maxs[0] - mins[0];
|
|
height = maxs[2] - mins[2];
|
|
|
|
|
|
scWidth = ( ScaleWidth / 2 ) * SCALE_ADJUST_FACTOR;
|
|
scHeight = ScaleHeight * SCALE_ADJUST_FACTOR;
|
|
|
|
scale = scWidth / width;
|
|
|
|
if ( height * scale >= scHeight ) {
|
|
scale = scHeight / height;
|
|
}
|
|
|
|
iwidth = ceil( width * scale ) + 4;
|
|
iheight = ceil( height * scale ) + 4;
|
|
|
|
s_scale = (float)( iwidth - 4 ) / width;
|
|
t_scale = (float)( iheight - 4 ) / height;
|
|
t_scale = s_scale;
|
|
|
|
if ( DrawSkin ) {
|
|
DrawScreen( s_scale, t_scale, iwidth, iheight );
|
|
}
|
|
|
|
|
|
/* if (!g_fixedwidth)
|
|
{ // old style
|
|
scale = 8;
|
|
if (width*scale >= 150)
|
|
scale = 150.0 / width;
|
|
if (height*scale >= 190)
|
|
scale = 190.0 / height;
|
|
|
|
s_scale = t_scale = scale;
|
|
|
|
iwidth = ceil(width*s_scale);
|
|
iheight = ceil(height*t_scale);
|
|
|
|
iwidth += 4;
|
|
iheight += 4;
|
|
}
|
|
else
|
|
{ // new style
|
|
iwidth = g_fixedwidth / 2;
|
|
iheight = g_fixedheight;
|
|
|
|
s_scale = (float)(iwidth-4) / width;
|
|
t_scale = (float)(iheight-4) / height;
|
|
}*/
|
|
|
|
//
|
|
// determine which side of each triangle to map the texture to
|
|
//
|
|
for ( i = 0 ; i < numtri ; i++ )
|
|
{
|
|
if ( ptri[i].HasUV ) {
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
triangle_st[i][j][0] = Q_rint( ptri[i].uv[j][0] * iwidth );
|
|
triangle_st[i][j][1] = Q_rint( ( 1.0f - ptri[i].uv[j][1] ) * iheight );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
|
|
VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
|
|
CrossProduct( vtemp1, vtemp2, normal );
|
|
|
|
if ( normal[1] > 0 ) {
|
|
basex = iwidth + 2;
|
|
}
|
|
else
|
|
{
|
|
basex = 2;
|
|
}
|
|
basey = 2;
|
|
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
pbasevert = ptri[i].verts[j];
|
|
|
|
triangle_st[i][j][0] = Q_rint( ( pbasevert[0] - mins[0] ) * s_scale + basex );
|
|
triangle_st[i][j][1] = Q_rint( ( maxs[2] - pbasevert[2] ) * t_scale + basey );
|
|
}
|
|
}
|
|
|
|
DrawLine( triangle_st[i][0][0], triangle_st[i][0][1],
|
|
triangle_st[i][1][0], triangle_st[i][1][1] );
|
|
DrawLine( triangle_st[i][1][0], triangle_st[i][1][1],
|
|
triangle_st[i][2][0], triangle_st[i][2][1] );
|
|
DrawLine( triangle_st[i][2][0], triangle_st[i][2][1],
|
|
triangle_st[i][0][0], triangle_st[i][0][1] );
|
|
}
|
|
|
|
// make the width a multiple of 4; some hardware requires this, and it ensures
|
|
// dword alignment for each scan
|
|
|
|
swidth = iwidth * 2;
|
|
model.skinwidth = ( swidth + 3 ) & ~3;
|
|
model.skinheight = iheight;
|
|
}
|
|
|
|
|
|
static void ReplaceClusterIndex( int newIndex, int oldindex, int **clusters,
|
|
IntListNode_t **vertLists, int *num_verts, int *new_num_verts ){
|
|
int i, j;
|
|
IntListNode_t *next;
|
|
|
|
for ( j = 0; j < num_verts[0]; ++j )
|
|
{
|
|
for ( i = 0; i < num_verts[j + 1]; ++i )
|
|
{
|
|
if ( clusters[j][i] == oldindex ) {
|
|
++new_num_verts[j + 1];
|
|
|
|
next = vertLists[j];
|
|
|
|
vertLists[j] = (IntListNode_t *) SafeMalloc( sizeof( IntListNode_t ), "ReplaceClusterIndex" );
|
|
// Currently freed in WriteJointedModelFile only
|
|
|
|
vertLists[j]->data = newIndex;
|
|
vertLists[j]->next = next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Cmd_Base
|
|
=================
|
|
*/
|
|
void Cmd_Base( void ){
|
|
vec3_t base_xyz[MAX_VERTS];
|
|
triangle_t *ptri;
|
|
int i, j, k;
|
|
#if 1
|
|
#else
|
|
int time1;
|
|
#endif
|
|
char file1[1024];
|
|
char file2[1024];
|
|
|
|
GetScriptToken( false );
|
|
|
|
if ( g_skipmodel || g_release || g_archive ) {
|
|
return;
|
|
}
|
|
|
|
printf( "---------------------\n" );
|
|
#if 1
|
|
sprintf( file1, "%s/%s", cdpartial, token );
|
|
printf( "%s ", file1 );
|
|
|
|
ExpandPathAndArchive( file1 );
|
|
|
|
sprintf( file1, "%s/%s", cddir, token );
|
|
#else
|
|
sprintf( file1, "%s/%s.%s", cdarchive, token, trifileext );
|
|
printf( "%s\n", file1 );
|
|
|
|
ExpandPathAndArchive( file1 );
|
|
|
|
sprintf( file1, "%s/%s.%s", cddir, token, trifileext );
|
|
|
|
time1 = FileTime( file1 );
|
|
if ( time1 == -1 ) {
|
|
Error( "%s doesn't exist", file1 );
|
|
}
|
|
#endif
|
|
//
|
|
// load the base triangles
|
|
//
|
|
if ( do3ds ) {
|
|
Load3DSTriangleList( file1, &ptri, &model.num_tris, NULL, NULL );
|
|
}
|
|
else{
|
|
LoadTriangleList( file1, &ptri, &model.num_tris, NULL, NULL );
|
|
}
|
|
|
|
|
|
GetScriptToken( false );
|
|
sprintf( file2, "%s/%s.pcx", cddir, token );
|
|
// sprintf (trans_file, "%s/!%s_a.pcx", cddir, token);
|
|
|
|
printf( "skin: %s\n", file2 );
|
|
Load256Image( file2, &BasePixels, &BasePalette, &BaseWidth, &BaseHeight );
|
|
|
|
if ( BaseWidth != SKINPAGE_WIDTH || BaseHeight != SKINPAGE_HEIGHT ) {
|
|
if ( g_allow_newskin ) {
|
|
ScaleWidth = BaseWidth;
|
|
ScaleHeight = BaseHeight;
|
|
}
|
|
else
|
|
{
|
|
Error( "Invalid skin page size: (%d,%d) should be (%d,%d)",
|
|
BaseWidth,BaseHeight,SKINPAGE_WIDTH,SKINPAGE_HEIGHT );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ScaleWidth = (float)ExtractNumber( BasePixels, ENCODED_WIDTH_X,
|
|
ENCODED_WIDTH_Y );
|
|
ScaleHeight = (float)ExtractNumber( BasePixels, ENCODED_HEIGHT_X,
|
|
ENCODED_HEIGHT_Y );
|
|
}
|
|
|
|
//
|
|
// get the ST values
|
|
//
|
|
BuildST( ptri, model.num_tris,false );
|
|
|
|
//
|
|
// run through all the base triangles, storing each unique vertex in the
|
|
// base vertex list and setting the indirect triangles to point to the base
|
|
// vertices
|
|
//
|
|
for ( i = 0 ; i < model.num_tris ; i++ )
|
|
{
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
// get the xyz index
|
|
for ( k = 0 ; k < model.num_xyz ; k++ )
|
|
if ( VectorCompare( ptri[i].verts[j], base_xyz[k] ) ) {
|
|
break;
|
|
} // this vertex is already in the base vertex list
|
|
|
|
if ( k == model.num_xyz ) { // new index
|
|
VectorCopy( ptri[i].verts[j], base_xyz[model.num_xyz] );
|
|
|
|
if ( clustered ) {
|
|
ReplaceClusterIndex( k, ptri[i].indicies[j], (int **)&clusters, (IntListNode_t **)&vertLists, (int *)&num_verts, (int *)&new_num_verts );
|
|
}
|
|
|
|
model.num_xyz++;
|
|
}
|
|
|
|
triangles[i].index_xyz[j] = k;
|
|
|
|
// get the st index
|
|
for ( k = 0 ; k < model.num_st ; k++ )
|
|
if ( triangle_st[i][j][0] == base_st[k].s
|
|
&& triangle_st[i][j][1] == base_st[k].t ) {
|
|
break;
|
|
} // this vertex is already in the base vertex list
|
|
|
|
if ( k == model.num_st ) { // new index
|
|
base_st[model.num_st].s = triangle_st[i][j][0];
|
|
base_st[model.num_st].t = triangle_st[i][j][1];
|
|
model.num_st++;
|
|
}
|
|
|
|
triangles[i].index_st[j] = k;
|
|
}
|
|
}
|
|
|
|
// build triangle strips / fans
|
|
BuildGlCmds();
|
|
}
|
|
|
|
//===============================================================
|
|
|
|
char *FindFrameFile( char *frame ){
|
|
int time1;
|
|
char file1[1024];
|
|
static char retname[1024];
|
|
char base[32];
|
|
char suffix[32];
|
|
char *s;
|
|
|
|
if ( strstr( frame, "." ) ) {
|
|
return frame; // allready in dot format
|
|
|
|
}
|
|
// split 'run1' into 'run' and '1'
|
|
s = frame + strlen( frame ) - 1;
|
|
|
|
while ( s != frame && *s >= '0' && *s <= '9' )
|
|
s--;
|
|
|
|
strcpy( suffix, s + 1 );
|
|
strcpy( base, frame );
|
|
base[s - frame + 1] = 0;
|
|
|
|
sprintf( file1, "%s/%s%s.%s",cddir, base, suffix, "hrc" );
|
|
time1 = FileTime( file1 );
|
|
if ( time1 != -1 ) {
|
|
sprintf( retname, "%s%s.%s", base, suffix, "hrc" );
|
|
return retname;
|
|
}
|
|
|
|
sprintf( file1, "%s/%s%s.%s",cddir, base, suffix, "asc" );
|
|
time1 = FileTime( file1 );
|
|
if ( time1 != -1 ) {
|
|
sprintf( retname, "%s%s.%s", base, suffix, "asc" );
|
|
return retname;
|
|
}
|
|
|
|
sprintf( file1, "%s/%s%s.%s",cddir, base, suffix, "tri" );
|
|
time1 = FileTime( file1 );
|
|
if ( time1 != -1 ) {
|
|
sprintf( retname, "%s%s.%s", base, suffix, "tri" );
|
|
return retname;
|
|
}
|
|
|
|
sprintf( file1, "%s/%s%s.%s",cddir, base, suffix, "3ds" );
|
|
time1 = FileTime( file1 );
|
|
if ( time1 != -1 ) {
|
|
sprintf( retname, "%s%s.%s", base, suffix, "3ds" );
|
|
return retname;
|
|
}
|
|
|
|
sprintf( file1, "%s/%s%s.%s",cddir, base, suffix, "htr" );
|
|
time1 = FileTime( file1 );
|
|
if ( time1 != -1 ) {
|
|
sprintf( retname, "%s%s.%s", base, suffix, "htr" );
|
|
return retname;
|
|
}
|
|
|
|
// check for 'run.1'
|
|
sprintf( file1, "%s/%s.%s",cddir, base, suffix );
|
|
time1 = FileTime( file1 );
|
|
if ( time1 != -1 ) {
|
|
sprintf( retname, "%s.%s", base, suffix );
|
|
return retname;
|
|
}
|
|
|
|
Error( "frame %s could not be found",frame );
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
GrabFrame
|
|
===============
|
|
*/
|
|
static void GrabFrame( char *frame ){
|
|
triangle_t *ptri;
|
|
int i, j;
|
|
trivert_t *ptrivert;
|
|
int num_tris;
|
|
char file1[1024];
|
|
frame_t *fr;
|
|
vertexnormals_t vnorms[MAX_VERTS];
|
|
int index_xyz;
|
|
char *framefile;
|
|
|
|
// the frame 'run1' will be looked for as either
|
|
// run.1 or run1.tri, so the new alias sequence save
|
|
// feature an be used
|
|
framefile = FindFrameFile( frame );
|
|
|
|
sprintf( file1, "%s/%s", cdarchive, framefile );
|
|
ExpandPathAndArchive( file1 );
|
|
|
|
sprintf( file1, "%s/%s",cddir, framefile );
|
|
|
|
printf( "grabbing %s ", file1 );
|
|
|
|
if ( model.num_frames >= MAX_FRAMES ) {
|
|
Error( "model.num_frames >= MAX_FRAMES" );
|
|
}
|
|
fr = &g_frames[model.num_frames];
|
|
model.num_frames++;
|
|
|
|
strcpy( fr->name, frame );
|
|
|
|
//
|
|
// load the frame
|
|
//
|
|
if ( do3ds ) {
|
|
Load3DSTriangleList( file1, &ptri, &num_tris, NULL, NULL );
|
|
}
|
|
else{
|
|
LoadTriangleList( file1, &ptri, &num_tris, NULL, NULL );
|
|
}
|
|
|
|
if ( num_tris != model.num_tris ) {
|
|
Error( "%s: number of triangles doesn't match base frame\n", file1 );
|
|
}
|
|
|
|
//
|
|
// allocate storage for the frame's vertices
|
|
//
|
|
ptrivert = fr->v;
|
|
|
|
for ( i = 0 ; i < model.num_xyz ; i++ )
|
|
{
|
|
vnorms[i].numnormals = 0;
|
|
VectorClear( vnorms[i].normalsum );
|
|
}
|
|
ClearBounds( fr->mins, fr->maxs );
|
|
|
|
//
|
|
// store the frame's vertices in the same order as the base. This assumes the
|
|
// triangles and vertices in this frame are in exactly the same order as in the
|
|
// base
|
|
//
|
|
for ( i = 0 ; i < num_tris ; i++ )
|
|
{
|
|
vec3_t vtemp1, vtemp2, normal;
|
|
float ftemp;
|
|
|
|
VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 );
|
|
VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 );
|
|
CrossProduct( vtemp1, vtemp2, normal );
|
|
|
|
VectorNormalize( normal, normal );
|
|
|
|
// rotate the normal so the model faces down the positive x axis
|
|
ftemp = normal[0];
|
|
normal[0] = -normal[1];
|
|
normal[1] = ftemp;
|
|
|
|
for ( j = 0 ; j < 3 ; j++ )
|
|
{
|
|
index_xyz = triangles[i].index_xyz[j];
|
|
|
|
// rotate the vertices so the model faces down the positive x axis
|
|
// also adjust the vertices to the desired origin
|
|
ptrivert[index_xyz].v[0] = ( ( -ptri[i].verts[j][1] ) * scale_up ) +
|
|
adjust[0];
|
|
ptrivert[index_xyz].v[1] = ( ptri[i].verts[j][0] * scale_up ) +
|
|
adjust[1];
|
|
ptrivert[index_xyz].v[2] = ( ptri[i].verts[j][2] * scale_up ) +
|
|
adjust[2];
|
|
|
|
AddPointToBounds( ptrivert[index_xyz].v, fr->mins, fr->maxs );
|
|
|
|
VectorAdd( vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum );
|
|
vnorms[index_xyz].numnormals++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// calculate the vertex normals, match them to the template list, and store the
|
|
// index of the best match
|
|
//
|
|
for ( i = 0 ; i < model.num_xyz ; i++ )
|
|
{
|
|
int j;
|
|
vec3_t v;
|
|
float maxdot;
|
|
int maxdotindex;
|
|
int c;
|
|
|
|
c = vnorms[i].numnormals;
|
|
if ( !c ) {
|
|
Error( "Vertex with no triangles attached" );
|
|
}
|
|
|
|
VectorScale( vnorms[i].normalsum, 1.0 / c, v );
|
|
VectorNormalize( v, v );
|
|
|
|
maxdot = -999999.0;
|
|
maxdotindex = -1;
|
|
|
|
for ( j = 0 ; j < NUMVERTEXNORMALS ; j++ )
|
|
{
|
|
float dot;
|
|
|
|
dot = DotProduct( v, avertexnormals[j] );
|
|
if ( dot > maxdot ) {
|
|
maxdot = dot;
|
|
maxdotindex = j;
|
|
}
|
|
}
|
|
|
|
ptrivert[i].lightnormalindex = maxdotindex;
|
|
}
|
|
|
|
free( ptri );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
GrabJointedFrame
|
|
===============
|
|
*/
|
|
void GrabJointedFrame( char *frame ){
|
|
char file1[1024];
|
|
char *framefile;
|
|
frame_t *fr;
|
|
|
|
framefile = FindFrameFile( frame );
|
|
|
|
sprintf( file1, "%s/%s", cdarchive, framefile );
|
|
ExpandPathAndArchive( file1 );
|
|
|
|
sprintf( file1, "%s/%s",cddir, framefile );
|
|
|
|
printf( "grabbing %s\n", file1 );
|
|
|
|
fr = &g_frames[model.num_frames - 1]; // last frame read in
|
|
|
|
LoadJointList( file1, fr->joints, jointed );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
GrabGlobals
|
|
===============
|
|
*/
|
|
void GrabGlobals( char *frame ){
|
|
char file1[1024];
|
|
char *framefile;
|
|
frame_t *fr;
|
|
|
|
framefile = FindFrameFile( frame );
|
|
|
|
sprintf( file1, "%s/%s", cdarchive, framefile );
|
|
ExpandPathAndArchive( file1 );
|
|
|
|
sprintf( file1, "%s/%s",cddir, framefile );
|
|
|
|
printf( "grabbing %s\n", file1 );
|
|
|
|
fr = &g_frames[model.num_frames - 1]; // last frame read in
|
|
|
|
LoadGlobals( file1 );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Cmd_Frame
|
|
===============
|
|
*/
|
|
void Cmd_Frame( void ){
|
|
while ( ScriptTokenAvailable() )
|
|
{
|
|
GetScriptToken( false );
|
|
if ( g_skipmodel ) {
|
|
continue;
|
|
}
|
|
if ( g_release || g_archive ) {
|
|
model.num_frames = 1; // don't skip the writeout
|
|
continue;
|
|
}
|
|
|
|
H_printf( "#define FRAME_%-16s\t%i\n", token, model.num_frames );
|
|
|
|
GrabFrame( token );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Cmd_Skin
|
|
|
|
Skins aren't actually stored in the file, only a reference
|
|
is saved out to the header file.
|
|
===============
|
|
*/
|
|
void Cmd_Skin( void ){
|
|
byte *palette;
|
|
byte *pixels;
|
|
int width, height;
|
|
byte *cropped;
|
|
int y;
|
|
char name[1024], savename[1024];
|
|
|
|
GetScriptToken( false );
|
|
|
|
if ( model.num_skins == MAX_MD2SKINS ) {
|
|
Error( "model.num_skins == MAX_MD2SKINS" );
|
|
}
|
|
|
|
if ( g_skipmodel ) {
|
|
return;
|
|
}
|
|
|
|
#if 1
|
|
sprintf( name, "%s/%s.pcx", cddir, token );
|
|
sprintf( savename, "%s/!%s.pcx", g_outputDir, token );
|
|
sprintf( g_skins[model.num_skins], "%s/!%s.pcx", cdpartial, token );
|
|
#else
|
|
sprintf( name, "%s/%s.lbm", cdarchive, token );
|
|
strcpy( name, ExpandPathAndArchive( name ) );
|
|
// sprintf (name, "%s/%s.lbm", cddir, token);
|
|
|
|
if ( ScriptTokenAvailable() ) {
|
|
GetScriptToken( false );
|
|
sprintf( g_skins[model.num_skins], "%s.pcx", token );
|
|
sprintf( savename, "%s%s.pcx", g_outputDir, g_skins[model.num_skins] );
|
|
}
|
|
else
|
|
{
|
|
sprintf( savename, "%s/%s.pcx", g_outputDir, token );
|
|
sprintf( g_skins[model.num_skins], "%s/%s.pcx", cdpartial, token );
|
|
}
|
|
#endif
|
|
|
|
model.num_skins++;
|
|
|
|
if ( g_skipmodel || g_release || g_archive ) {
|
|
return;
|
|
}
|
|
|
|
// load the image
|
|
printf( "loading %s\n", name );
|
|
Load256Image( name, &pixels, &palette, &width, &height );
|
|
// RemapZero (pixels, palette, width, height);
|
|
|
|
// crop it to the proper size
|
|
cropped = (byte *) SafeMalloc( model.skinwidth * model.skinheight, "Cmd_Skin" );
|
|
for ( y = 0 ; y < model.skinheight ; y++ )
|
|
{
|
|
memcpy( cropped + y * model.skinwidth,
|
|
pixels + y * width, model.skinwidth );
|
|
}
|
|
|
|
// save off the new image
|
|
printf( "saving %s\n", savename );
|
|
CreatePath( savename );
|
|
WritePCXfile( savename, cropped, model.skinwidth,
|
|
model.skinheight, palette );
|
|
|
|
free( pixels );
|
|
free( palette );
|
|
free( cropped );
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Cmd_Origin
|
|
=================
|
|
*/
|
|
void Cmd_Origin( void ){
|
|
// rotate points into frame of reference so model points down the
|
|
// positive x axis
|
|
GetScriptToken( false );
|
|
adjust[1] = -atof( token );
|
|
|
|
GetScriptToken( false );
|
|
adjust[0] = atof( token );
|
|
|
|
GetScriptToken( false );
|
|
adjust[2] = -atof( token );
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Cmd_ScaleUp
|
|
=================
|
|
*/
|
|
void Cmd_ScaleUp( void ){
|
|
GetScriptToken( false );
|
|
scale_up = atof( token );
|
|
if ( g_skipmodel || g_release || g_archive ) {
|
|
return;
|
|
}
|
|
|
|
printf( "Scale up: %f\n", scale_up );
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
Cmd_Skinsize
|
|
|
|
Set a skin size other than the default
|
|
=================
|
|
*/
|
|
void Cmd_Skinsize( void ){
|
|
GetScriptToken( false );
|
|
g_fixedwidth = atoi( token );
|
|
GetScriptToken( false );
|
|
g_fixedheight = atoi( token );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Cmd_Modelname
|
|
|
|
Gives a different name/location for the file, instead of the cddir
|
|
=================
|
|
*/
|
|
void Cmd_Modelname( void ){
|
|
GetScriptToken( false );
|
|
strcpy( modelname, token );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
Cmd_Cd
|
|
===============
|
|
*/
|
|
void Cmd_Cd( void ){
|
|
char temp[256];
|
|
|
|
FinishModel();
|
|
ClearModel();
|
|
|
|
GetScriptToken( false );
|
|
|
|
// this is a silly mess...
|
|
sprintf( cdpartial, "models/%s", token );
|
|
sprintf( cdarchive, "%smodels/%s", gamedir + strlen( qdir ), token );
|
|
sprintf( cddir, "%s%s", gamedir, cdpartial );
|
|
|
|
// Since we also changed directories on the output side (for mirror) make sure the outputdir is set properly too.
|
|
sprintf( temp, "%s%s", g_outputDir, cdpartial );
|
|
strcpy( g_outputDir, temp );
|
|
|
|
// if -only was specified and this cd doesn't match,
|
|
// skip the model (you only need to match leading chars,
|
|
// so you could regrab all monsters with -only monsters)
|
|
if ( !g_only[0] ) {
|
|
return;
|
|
}
|
|
if ( strncmp( token, g_only, strlen( g_only ) ) ) {
|
|
g_skipmodel = true;
|
|
printf( "skipping %s\n", cdpartial );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Cmd_Cluster
|
|
=================
|
|
*/
|
|
void Cmd_Cluster(){
|
|
char file1[1024];
|
|
|
|
GetScriptToken( false );
|
|
|
|
printf( "---------------------\n" );
|
|
sprintf( file1, "%s/%s", cdpartial, token );
|
|
printf( "%s\n", file1 );
|
|
|
|
ExpandPathAndArchive( file1 );
|
|
|
|
sprintf( file1, "%s/%s", cddir, token );
|
|
|
|
LoadClusters( file1, (int **)&clusters, (int *)&num_verts, jointed );
|
|
|
|
new_num_verts[0] = num_verts[0];
|
|
|
|
clustered = 1;
|
|
}
|
|
|
|
// Model construction cover functions.
|
|
void MODELCMD_Modelname( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
Cmd_Modelname();
|
|
/*
|
|
switch(modeltype)
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Modelname ();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMModelname ();
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void MODELCMD_Cd( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Cd();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMCd();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_Origin( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
Cmd_Origin();
|
|
/* switch(modeltype)
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Origin ();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMOrigin ();
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void MODELCMD_Cluster( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Cluster();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMCluster();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_Base( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Base();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMBase( false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_BaseST( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Base();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMBase( true );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_ScaleUp( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
Cmd_ScaleUp();
|
|
/* switch(modeltype)
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_ScaleUp ();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMScaleUp ();
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void MODELCMD_Frame( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Frame();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMFrame();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_Skin( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Skin();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMSkin();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_Skinsize( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
Cmd_Skinsize();
|
|
/*
|
|
switch(modeltype)
|
|
{
|
|
case MODEL_MD2:
|
|
Cmd_Skinsize ();
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMSkinsize ();
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void MODELCMD_Skeleton( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMSkeleton();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_BeginGroup( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMBeginGroup();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_EndGroup( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMEndGroup();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_Referenced( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMReferenced();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MODELCMD_NodeOrder( int modeltype ){
|
|
if ( g_forcemodel ) {
|
|
modeltype = g_forcemodel;
|
|
}
|
|
|
|
switch ( modeltype )
|
|
{
|
|
case MODEL_MD2:
|
|
break;
|
|
case MODEL_FM:
|
|
Cmd_FMNodeOrder();
|
|
break;
|
|
}
|
|
}
|