2007-11-04 03:34:51 +00:00
/*
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 "qd_fmodel.h"
# include "animcomp.h"
# include "qd_skeletons.h"
# include "skeletons.h"
# include "qdata.h"
# include "flex.h"
# include "reference.h"
# include <assert.h>
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
. FM triangle flexible model file format
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
//=================================================================
# define NUMVERTEXNORMALS 162
extern float avertexnormals [ NUMVERTEXNORMALS ] [ 3 ] ;
# define MAX_GROUPS 128
typedef struct
{
triangle_t triangle ;
int group ;
} trigroup_t ;
# define TRIVERT_DIST .1
typedef struct
{
int start_frame ;
int num_frames ;
int degrees ;
char * mat ;
char * ccomp ;
char * cbase ;
float * cscale ;
float * coffset ;
float trans [ 3 ] ;
float scale [ 3 ] ;
float bmin [ 3 ] ;
float bmax [ 3 ] ;
} fmgroup_t ;
//================================================================
// Initial
fmheader_t fmheader ;
// Skin
extern char g_skins [ MAX_FM_SKINS ] [ 64 ] ;
// ST Coord
extern fmstvert_t base_st [ MAX_FM_VERTS ] ;
// Triangles
extern fmtriangle_t triangles [ MAX_FM_TRIANGLES ] ;
// Frames
fmframe_t g_frames [ MAX_FM_FRAMES ] ;
//fmframe_t *g_FMframes;
// GL Commands
extern int commands [ 16384 ] ;
extern int numcommands ;
//
// varibles set by commands
//
extern float scale_up ; // set by $scale
extern vec3_t adjust ; // set by $origin
extern int g_fixedwidth , g_fixedheight ; // set by $skinsize
extern char modelname [ 64 ] ; // set by $modelname
extern char * g_outputDir ;
// Mesh Nodes
mesh_node_t * pmnodes = NULL ;
fmmeshnode_t mesh_nodes [ MAX_FM_MESH_NODES ] ;
fmgroup_t groups [ MAX_GROUPS ] ;
int num_groups ;
int frame_to_group [ MAX_FM_FRAMES ] ;
//
// variables set by command line arguments
//
qboolean g_no_opimizations = false ;
//
// base frame info
//
static int triangle_st [ MAX_FM_TRIANGLES ] [ 3 ] [ 2 ] ;
// number of gl vertices
extern int numglverts ;
// indicates if a triangle has already been used in a glcmd
extern int used [ MAX_FM_TRIANGLES ] ;
// indicates if a triangle has translucency in it or not
static qboolean translucent [ MAX_FM_TRIANGLES ] ;
// main output file handle
extern FILE * headerouthandle ;
// output sizes of buildst()
static int skin_width , skin_height ;
// statistics
static int total_skin_pixels ;
static int skin_pixels_used ;
int ShareVertex ( trigroup_t trione , trigroup_t tritwo ) ;
float DistBetween ( vec3_t point1 , vec3_t point2 ) ;
int GetNumTris ( trigroup_t * tris , int group ) ;
void GetOneGroup ( trigroup_t * tris , int grp , triangle_t * triangles ) ;
void ScaleTris ( vec3_t min , vec3_t max , int Width , int Height , float * u , float * v , int verts ) ;
void NewDrawLine ( int x1 , int y1 , int x2 , int y2 , unsigned char * picture , int width , int height ) ;
# ifndef _WIN32
void strupr ( char * string )
{
int i ;
for ( i = 0 ; i < strlen ( string ) ; i + + )
toupper ( string [ i ] ) ;
return ;
}
# endif
//==============================================================
/*
= = = = = = = = = = = = = = =
ClearModel
= = = = = = = = = = = = = = =
*/
static void ClearModel ( void )
{
memset ( & fmheader , 0 , sizeof ( fmheader ) ) ;
modelname [ 0 ] = 0 ;
scale_up = 1.0 ;
VectorCopy ( vec3_origin , adjust ) ;
g_fixedwidth = g_fixedheight = 0 ;
g_skipmodel = false ;
num_groups = 0 ;
if ( pmnodes )
{
free ( pmnodes ) ;
pmnodes = NULL ;
}
ClearSkeletalModel ( ) ;
}
extern void H_printf ( char * fmt , . . . ) ;
void WriteHeader ( FILE * FH , char * Ident , int Version , int Size , void * Data )
{
header_t header ;
static long pos = - 1 ;
long CurrentPos ;
if ( Size = = 0 )
{ // Don't write out empty packets
return ;
}
if ( pos ! = - 1 )
{
CurrentPos = ftell ( FH ) ;
Size = CurrentPos - pos + sizeof ( header_t ) ;
fseek ( FH , pos , SEEK_SET ) ;
pos = - 2 ;
}
else if ( Size = = - 1 )
{
pos = ftell ( FH ) ;
}
memset ( & header , 0 , sizeof ( header ) ) ;
strcpy ( header . ident , Ident ) ;
header . version = Version ;
header . size = Size ;
SafeWrite ( FH , & header , sizeof ( header ) ) ;
if ( Data )
{
SafeWrite ( FH , Data , Size ) ;
}
if ( pos = = - 2 )
{
pos = - 1 ;
fseek ( FH , 0 , SEEK_END ) ;
}
}
/*
= = = = = = = = = = = =
WriteModelFile
= = = = = = = = = = = =
*/
static void WriteModelFile ( FILE * modelouthandle )
{
int i ;
int j , k ;
fmframe_t * in ;
fmaliasframe_t * out ;
byte buffer [ MAX_FM_VERTS * 4 + 128 ] ;
float v ;
int c_on , c_off ;
IntListNode_t * current , * toFree ;
qboolean framesWritten = false ;
size_t temp , size = 0 ;
// probably should do this dynamically one of these days
struct
{
float scale [ 3 ] ; // multiply byte verts by this
float translate [ 3 ] ; // then add this
} outFrames [ MAX_FM_FRAMES ] ;
# define DATA_SIZE 0x60000 // 384K had better be enough, particularly for the reference points
byte data [ DATA_SIZE ] ;
byte data2 [ DATA_SIZE ] ;
fmheader . num_glcmds = numcommands ;
fmheader . framesize = ( int ) & ( ( fmaliasframe_t * ) 0 ) - > verts [ fmheader . num_xyz ] ;
WriteHeader ( modelouthandle , FM_HEADER_NAME , FM_HEADER_VER , sizeof ( fmheader ) , & fmheader ) ;
//
// write out the skin names
//
WriteHeader ( modelouthandle , FM_SKIN_NAME , FM_SKIN_VER , fmheader . num_skins * MAX_FM_SKINNAME , g_skins ) ;
//
// write out the texture coordinates
//
c_on = c_off = 0 ;
for ( i = 0 ; i < fmheader . num_st ; i + + )
{
base_st [ i ] . s = LittleShort ( base_st [ i ] . s ) ;
base_st [ i ] . t = LittleShort ( base_st [ i ] . t ) ;
}
WriteHeader ( modelouthandle , FM_ST_NAME , FM_ST_VER , fmheader . num_st * sizeof ( base_st [ 0 ] ) , base_st ) ;
//
// write out the triangles
//
WriteHeader ( modelouthandle , FM_TRI_NAME , FM_TRI_VER , fmheader . num_tris * sizeof ( fmtriangle_t ) , NULL ) ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
int j ;
fmtriangle_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 ) ) ;
}
if ( ! num_groups )
{
//
// write out the frames
//
WriteHeader ( modelouthandle , FM_FRAME_NAME , FM_FRAME_VER , fmheader . num_frames * fmheader . framesize , NULL ) ;
// WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
for ( i = 0 ; i < fmheader . num_frames ; i + + )
{
in = & g_frames [ i ] ;
out = ( fmaliasframe_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 ] ;
outFrames [ i ] . scale [ j ] = out - > scale [ j ] ;
outFrames [ i ] . translate [ j ] = out - > translate [ j ] ;
}
for ( j = 0 ; j < fmheader . 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 , fmheader . framesize ) ;
}
// Go back and finish the header
// WriteHeader(modelouthandle, FM_FRAME_NAME, FM_FRAME_VER, -1, NULL);
}
else
{
WriteHeader ( modelouthandle , FM_SHORT_FRAME_NAME , FM_SHORT_FRAME_VER , FRAME_NAME_LEN * fmheader . num_frames , NULL ) ;
for ( i = 0 ; i < fmheader . num_frames ; i + + )
{
in = & g_frames [ i ] ;
SafeWrite ( modelouthandle , in - > name , FRAME_NAME_LEN ) ;
}
WriteHeader ( modelouthandle , FM_NORMAL_NAME , FM_NORMAL_VER , fmheader . num_xyz , NULL ) ;
in = & g_frames [ 0 ] ;
for ( j = 0 ; j < fmheader . num_xyz ; j + + )
SafeWrite ( modelouthandle , & in - > v [ j ] . lightnormalindex , 1 ) ;
}
//
// write out glcmds
//
WriteHeader ( modelouthandle , FM_GLCMDS_NAME , FM_GLCMDS_VER , numcommands * 4 , commands ) ;
//
// write out mesh nodes
//
for ( i = 0 ; i < fmheader . num_mesh_nodes ; i + + )
{
memcpy ( mesh_nodes [ i ] . tris , pmnodes [ i ] . tris , sizeof ( mesh_nodes [ i ] . tris ) ) ;
memcpy ( mesh_nodes [ i ] . verts , pmnodes [ i ] . verts , sizeof ( mesh_nodes [ i ] . verts ) ) ;
mesh_nodes [ i ] . start_glcmds = LittleShort ( ( short ) pmnodes [ i ] . start_glcmds ) ;
mesh_nodes [ i ] . num_glcmds = LittleShort ( ( short ) pmnodes [ i ] . num_glcmds ) ;
}
WriteHeader ( modelouthandle , FM_MESH_NAME , FM_MESH_VER , sizeof ( fmmeshnode_t ) * fmheader . num_mesh_nodes , mesh_nodes ) ;
if ( num_groups )
{
/*
typedef struct
{
int start_frame ;
int num_frames ;
int degrees ;
char * mat ; fmheader . num_xyz * 3 * g - > degrees * sizeof ( char )
char * ccomp ; g - > num_frames * g - > degrees * sizeof ( char )
char * cbase ; fmheader . num_xyz * 3 * sizeof ( unsigned char )
float * cscale ; g - > degrees * sizeof ( float )
float * coffset ; g - > degrees * sizeof ( float )
float trans [ 3 ] ; 3 * sizeof ( float )
float scale [ 3 ] ; 3 * sizeof ( float )
} fmgroup_t ;
*/
int tmp , k ;
fmgroup_t * g ;
size = sizeof ( int ) + fmheader . num_frames * sizeof ( int ) ;
for ( k = 0 ; k < num_groups ; k + + )
{
g = & groups [ k ] ;
size + = sizeof ( int ) * 3 ;
size + = fmheader . num_xyz * 3 * g - > degrees * sizeof ( char ) ;
size + = g - > num_frames * g - > degrees * sizeof ( char ) ;
size + = fmheader . num_xyz * 3 * sizeof ( unsigned char ) ;
size + = g - > degrees * sizeof ( float ) ;
size + = g - > degrees * sizeof ( float ) ;
size + = 12 * sizeof ( float ) ;
}
WriteHeader ( modelouthandle , FM_COMP_NAME , FM_COMP_VER , size , NULL ) ;
SafeWrite ( modelouthandle , & num_groups , sizeof ( int ) ) ;
SafeWrite ( modelouthandle , frame_to_group , sizeof ( int ) * fmheader . num_frames ) ;
for ( k = 0 ; k < num_groups ; k + + )
{
g = & groups [ k ] ;
tmp = LittleLong ( g - > start_frame ) ;
SafeWrite ( modelouthandle , & tmp , sizeof ( int ) ) ;
tmp = LittleLong ( g - > num_frames ) ;
SafeWrite ( modelouthandle , & tmp , sizeof ( int ) ) ;
tmp = LittleLong ( g - > degrees ) ;
SafeWrite ( modelouthandle , & tmp , sizeof ( int ) ) ;
SafeWrite ( modelouthandle , g - > mat , fmheader . num_xyz * 3 * g - > degrees * sizeof ( char ) ) ;
SafeWrite ( modelouthandle , g - > ccomp , g - > num_frames * g - > degrees * sizeof ( char ) ) ;
SafeWrite ( modelouthandle , g - > cbase , fmheader . num_xyz * 3 * sizeof ( unsigned char ) ) ;
SafeWrite ( modelouthandle , g - > cscale , g - > degrees * sizeof ( float ) ) ;
SafeWrite ( modelouthandle , g - > coffset , g - > degrees * sizeof ( float ) ) ;
SafeWrite ( modelouthandle , g - > trans , 3 * sizeof ( float ) ) ;
SafeWrite ( modelouthandle , g - > scale , 3 * sizeof ( float ) ) ;
SafeWrite ( modelouthandle , g - > bmin , 3 * sizeof ( float ) ) ;
SafeWrite ( modelouthandle , g - > bmax , 3 * sizeof ( float ) ) ;
free ( g - > mat ) ;
free ( g - > ccomp ) ;
free ( g - > cbase ) ;
free ( g - > cscale ) ;
free ( g - > coffset ) ;
}
}
// write the skeletal info
if ( g_skelModel . type ! = SKEL_NULL )
{
size = 0 ;
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data + size , & g_skelModel . type , temp ) ;
size + = temp ;
// number of joints
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data + size , & numJointsInSkeleton [ g_skelModel . type ] , temp ) ;
size + = temp ;
// number of verts in each joint cluster
temp = sizeof ( int ) * numJointsInSkeleton [ g_skelModel . type ] ; // change this to shorts
memcpy ( data + size , & g_skelModel . new_num_verts [ 1 ] , temp ) ;
size + = temp ;
// cluster verts
for ( i = 0 ; i < numJointsInSkeleton [ g_skelModel . type ] ; + + i )
{
current = g_skelModel . vertLists [ i ] ;
while ( current )
{
temp = sizeof ( int ) ; // change this to a short
memcpy ( data + size , & current - > data , temp ) ;
size + = temp ;
toFree = current ;
current = current - > next ;
free ( toFree ) ; // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
}
}
if ( ! num_groups ) // joints are stored with regular verts for compressed models
{
framesWritten = true ;
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data + size , & framesWritten , temp ) ;
size + = temp ;
for ( i = 0 ; i < fmheader . num_frames ; + + i )
{
in = & g_frames [ i ] ;
for ( j = 0 ; j < numJointsInSkeleton [ g_skelModel . type ] ; + + 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 ] ) ;
// write out origin as a float since they arn't clamped
temp = sizeof ( float ) ; // change this to a short
assert ( size + temp < DATA_SIZE ) ;
memcpy ( data + size , & v , temp ) ;
size + = temp ;
}
for ( k = 0 ; k < 3 ; k + + )
{
v = Q_rint ( ( in - > joints [ j ] . placement . direction [ k ] - outFrames [ i ] . translate [ k ] ) / outFrames [ i ] . scale [ k ] ) ;
// write out origin as a float since they arn't clamped
temp = sizeof ( float ) ; // change this to a short
assert ( size + temp < DATA_SIZE ) ;
memcpy ( data + size , & v , temp ) ;
size + = temp ;
}
for ( k = 0 ; k < 3 ; k + + )
{
v = Q_rint ( ( in - > joints [ j ] . placement . up [ k ] - outFrames [ i ] . translate [ k ] ) / outFrames [ i ] . scale [ k ] ) ;
// write out origin as a float since they arn't clamped
temp = sizeof ( float ) ; // change this to a short
assert ( size + temp < DATA_SIZE ) ;
memcpy ( data + size , & v , temp ) ;
size + = temp ;
}
}
}
}
else
{
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data + size , & framesWritten , temp ) ;
size + = temp ;
}
WriteHeader ( modelouthandle , FM_SKELETON_NAME , FM_SKELETON_VER , size , data ) ;
}
if ( g_skelModel . references ! = REF_NULL )
{
int refnum ;
size = 0 ;
if ( RefPointNum < = 0 )
{ // Hard-coded labels
refnum = numReferences [ g_skelModel . references ] ;
}
else
{ // Labels indicated in QDT
refnum = RefPointNum ;
}
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data2 + size , & g_skelModel . references , temp ) ;
size + = temp ;
if ( ! num_groups )
{
framesWritten = true ;
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data2 + size , & framesWritten , temp ) ;
size + = temp ;
for ( i = 0 ; i < fmheader . num_frames ; + + i )
{
in = & g_frames [ i ] ;
for ( j = 0 ; j < refnum ; + + j )
{
for ( k = 0 ; k < 3 ; k + + )
{
// scale to byte values & min/max check
v = Q_rint ( ( in - > references [ j ] . placement . origin [ k ] - outFrames [ i ] . translate [ k ] ) / outFrames [ i ] . scale [ k ] ) ;
// write out origin as a float since they arn't clamped
temp = sizeof ( float ) ; // change this to a short
assert ( size + temp < DATA_SIZE ) ;
memcpy ( data2 + size , & v , temp ) ;
size + = temp ;
}
for ( k = 0 ; k < 3 ; k + + )
{
v = Q_rint ( ( in - > references [ j ] . placement . direction [ k ] - outFrames [ i ] . translate [ k ] ) / outFrames [ i ] . scale [ k ] ) ;
// write out origin as a float since they arn't clamped
temp = sizeof ( float ) ; // change this to a short
assert ( size + temp < DATA_SIZE ) ;
memcpy ( data2 + size , & v , temp ) ;
size + = temp ;
}
for ( k = 0 ; k < 3 ; k + + )
{
v = Q_rint ( ( in - > references [ j ] . placement . up [ k ] - outFrames [ i ] . translate [ k ] ) / outFrames [ i ] . scale [ k ] ) ;
// write out origin as a float since they arn't clamped
temp = sizeof ( float ) ; // change this to a short
assert ( size + temp < DATA_SIZE ) ;
memcpy ( data2 + size , & v , temp ) ;
size + = temp ;
}
}
}
}
else // FINISH ME: references need to be stored with regular verts for compressed models
{
framesWritten = false ;
temp = sizeof ( int ) ; // change this to a byte
memcpy ( data2 + size , & framesWritten , temp ) ;
size + = temp ;
}
WriteHeader ( modelouthandle , FM_REFERENCES_NAME , FM_REFERENCES_VER , size , data2 ) ;
}
}
static void CompressFrames ( )
{
fmgroup_t * g ;
int i , j , k ;
fmframe_t * in ;
j = 0 ;
for ( i = 0 ; i < fmheader . num_frames ; i + + )
{
while ( i > = groups [ j ] . start_frame + groups [ j ] . num_frames & & j < num_groups - 1 )
j + + ;
frame_to_group [ i ] = j ;
}
for ( k = 0 ; k < num_groups ; k + + )
{
g = & groups [ k ] ;
printf ( " \n Compressing Frames for group %i... \n " , k ) ;
AnimCompressInit ( g - > num_frames , fmheader . num_xyz , g - > degrees ) ;
for ( i = 0 ; i < g - > num_frames ; i + + )
{
in = & g_frames [ i + g - > start_frame ] ;
for ( j = 0 ; j < fmheader . num_xyz ; j + + )
AnimSetFrame ( i , j , in - > v [ j ] . v [ 0 ] , in - > v [ j ] . v [ 1 ] , in - > v [ j ] . v [ 2 ] ) ;
}
AnimCompressDoit ( ) ;
g - > mat = ( char * ) SafeMalloc ( fmheader . num_xyz * 3 * g - > degrees * sizeof ( char ) , " CompressFrames " ) ;
g - > ccomp = ( char * ) SafeMalloc ( g - > num_frames * g - > degrees * sizeof ( char ) , " CompressFrames " ) ;
g - > cbase = ( char * ) SafeMalloc ( fmheader . num_xyz * 3 * sizeof ( unsigned char ) , " CompressFrames " ) ;
g - > cscale = ( float * ) SafeMalloc ( g - > degrees * sizeof ( float ) , " CompressFrames " ) ;
g - > coffset = ( float * ) SafeMalloc ( g - > degrees * sizeof ( float ) , " CompressFrames " ) ;
AnimCompressToBytes ( g - > trans , g - > scale , g - > mat , g - > ccomp , g - > cbase , g - > cscale , g - > coffset , g - > bmin , g - > bmax ) ;
AnimCompressEnd ( ) ;
}
}
static void OptimizeVertices ( void )
{
qboolean vert_used [ MAX_FM_VERTS ] ;
short vert_replacement [ MAX_FM_VERTS ] ;
int i , j , k , l , pos , bit , set_pos , set_bit ;
fmframe_t * in ;
qboolean Found ;
int num_unique ;
static IntListNode_t * newVertLists [ NUM_CLUSTERS ] ;
static int newNum_verts [ NUM_CLUSTERS ] ;
IntListNode_t * current , * next ;
printf ( " Optimizing vertices... " ) ;
memset ( vert_used , 0 , sizeof ( vert_used ) ) ;
if ( g_skelModel . clustered = = true )
{
memset ( newNum_verts , 0 , sizeof ( newNum_verts ) ) ;
memset ( newVertLists , 0 , sizeof ( newVertLists ) ) ;
}
num_unique = 0 ;
// search for common points among all the frames
for ( i = 0 ; i < fmheader . num_frames ; i + + )
{
in = & g_frames [ i ] ;
for ( j = 0 ; j < fmheader . num_xyz ; j + + )
{
for ( k = 0 , Found = false ; k < j ; k + + )
{ // starting from the beginning always ensures vert_replacement points to the first point in the array
if ( in - > v [ j ] . v [ 0 ] = = in - > v [ k ] . v [ 0 ] & &
in - > v [ j ] . v [ 1 ] = = in - > v [ k ] . v [ 1 ] & &
in - > v [ j ] . v [ 2 ] = = in - > v [ k ] . v [ 2 ] )
{
Found = true ;
vert_replacement [ j ] = k ;
break ;
}
}
if ( ! Found )
{
if ( ! vert_used [ j ] )
{
num_unique + + ;
}
vert_used [ j ] = true ;
}
}
}
// recompute the light normals
for ( i = 0 ; i < fmheader . num_frames ; i + + )
{
in = & g_frames [ i ] ;
for ( j = 0 ; j < fmheader . num_xyz ; j + + )
{
if ( ! vert_used [ j ] )
{
k = vert_replacement [ j ] ;
VectorAdd ( in - > v [ j ] . vnorm . normalsum , in - > v [ k ] . vnorm . normalsum , in - > v [ k ] . vnorm . normalsum ) ;
in - > v [ k ] . vnorm . numnormals + = in - > v [ j ] . vnorm . numnormals + + ;
}
}
for ( j = 0 ; j < fmheader . num_xyz ; j + + )
{
vec3_t v ;
float maxdot ;
int maxdotindex ;
int c ;
c = in - > v [ j ] . vnorm . numnormals ;
if ( ! c )
Error ( " Vertex with no triangles attached " ) ;
VectorScale ( in - > v [ j ] . vnorm . normalsum , 1.0 / c , v ) ;
VectorNormalize ( v , v ) ;
maxdot = - 999999.0 ;
maxdotindex = - 1 ;
for ( k = 0 ; k < NUMVERTEXNORMALS ; k + + )
{
float dot ;
dot = DotProduct ( v , avertexnormals [ k ] ) ;
if ( dot > maxdot )
{
maxdot = dot ;
maxdotindex = k ;
}
}
in - > v [ j ] . lightnormalindex = maxdotindex ;
}
}
// create substitution list
num_unique = 0 ;
for ( i = 0 ; i < fmheader . num_xyz ; i + + )
{
if ( vert_used [ i ] )
{
vert_replacement [ i ] = num_unique ;
num_unique + + ;
}
else
{
vert_replacement [ i ] = vert_replacement [ vert_replacement [ i ] ] ;
}
// vert_replacement[i] is the new index, i is the old index
// need to add the new index to the cluster list if old index was in it
if ( g_skelModel . clustered = = true )
{
for ( k = 0 ; k < numJointsInSkeleton [ g_skelModel . type ] ; + + k )
{
for ( l = 0 , current = g_skelModel . vertLists [ k ] ;
l < g_skelModel . new_num_verts [ k + 1 ] ; + + l , current = current - > next )
{
if ( current - > data = = i )
{
IntListNode_t * current2 ;
int m ;
qboolean added = false ;
for ( m = 0 , current2 = newVertLists [ k ] ; m < newNum_verts [ k + 1 ] ;
+ + m , current2 = current2 - > next )
{
if ( current2 - > data = = vert_replacement [ i ] )
{
added = true ;
break ;
}
}
if ( ! added )
{
+ + newNum_verts [ k + 1 ] ;
next = newVertLists [ k ] ;
newVertLists [ k ] = ( IntListNode_t * ) SafeMalloc ( sizeof ( IntListNode_t ) , " OptimizeVertices " ) ;
// freed after model write out
newVertLists [ k ] - > data = vert_replacement [ i ] ;
newVertLists [ k ] - > next = next ;
}
break ;
}
}
}
}
}
// substitute
for ( i = 0 ; i < fmheader . num_frames ; i + + )
{
in = & g_frames [ i ] ;
for ( j = 0 ; j < fmheader . num_xyz ; j + + )
{
in - > v [ vert_replacement [ j ] ] = in - > v [ j ] ;
}
}
for ( i = 0 ; i < numJointsInSkeleton [ g_skelModel . type ] ; + + i )
{
IntListNode_t * toFree ;
current = g_skelModel . vertLists [ i ] ;
while ( current )
{
toFree = current ;
current = current - > next ;
free ( toFree ) ; // freeing of memory allocated in ReplaceClusterIndex called in Cmd_Base
}
g_skelModel . vertLists [ i ] = newVertLists [ i ] ;
g_skelModel . new_num_verts [ i + 1 ] = newNum_verts [ i + 1 ] ;
}
# ifndef NDEBUG
for ( k = 0 ; k < numJointsInSkeleton [ g_skelModel . type ] ; + + k )
{
for ( l = 0 , current = g_skelModel . vertLists [ k ] ;
l < g_skelModel . new_num_verts [ k + 1 ] ; + + l , current = current - > next )
{
IntListNode_t * current2 ;
int m ;
for ( m = l + 1 , current2 = current - > next ; m < newNum_verts [ k + 1 ] ;
+ + m , current2 = current2 - > next )
{
if ( current - > data = = current2 - > data )
{
printf ( " Warning duplicate vertex: %d \n " , current - > data ) ;
break ;
}
}
}
}
# endif
for ( i = 0 ; i < fmheader . num_mesh_nodes ; i + + )
{ // reset the vert bits
memset ( pmnodes [ i ] . verts , 0 , sizeof ( pmnodes [ i ] . verts ) ) ;
}
// repleace the master triangle list vertex indexes and update the vert bits for each mesh node
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
pos = i > > 3 ;
bit = 1 < < ( i & 7 ) ;
for ( j = 0 ; j < 3 ; j + + )
{
set_bit = set_pos = triangles [ i ] . index_xyz [ j ] = vert_replacement [ triangles [ i ] . index_xyz [ j ] ] ;
set_pos > > = 3 ;
set_bit = 1 < < ( set_bit & 7 ) ;
for ( k = 0 ; k < fmheader . num_mesh_nodes ; k + + )
{
if ( ! ( pmnodes [ k ] . tris [ pos ] & bit ) )
{
continue ;
}
pmnodes [ k ] . verts [ set_pos ] | = set_bit ;
}
}
}
for ( i = 0 ; i < numcommands ; i + + )
{
j = commands [ i ] ;
if ( ! j ) continue ;
j = abs ( j ) ;
for ( i + + ; j ; j - - , i + = 3 )
{
commands [ i + 2 ] = vert_replacement [ commands [ i + 2 ] ] ;
}
i - - ;
}
printf ( " Reduced by %d \n " , fmheader . num_xyz - num_unique ) ;
fmheader . num_xyz = num_unique ;
if ( num_groups )
{
// tack on the reference verts to the regular verts
if ( g_skelModel . references ! = REF_NULL )
{
fmframe_t * in ;
int index ;
int refnum ;
if ( RefPointNum < = 0 )
{ // Hard-coded labels
refnum = numReferences [ g_skelModel . references ] ;
}
else
{ // Labels indicated in QDT
refnum = RefPointNum ;
}
for ( i = 0 ; i < fmheader . num_frames ; + + i )
{
in = & g_frames [ i ] ;
index = fmheader . num_xyz ;
for ( j = 0 ; j < refnum ; + + j )
{
VectorCopy ( in - > references [ j ] . placement . origin , in - > v [ index ] . v ) ;
index + + ;
VectorCopy ( in - > references [ j ] . placement . direction , in - > v [ index ] . v ) ;
index + + ;
VectorCopy ( in - > references [ j ] . placement . up , in - > v [ index ] . v ) ;
index + + ;
}
}
fmheader . num_xyz + = refnum * 3 ;
}
// tack on the skeletal joint verts to the regular verts
if ( g_skelModel . type ! = SKEL_NULL )
{
fmframe_t * in ;
int index ;
for ( i = 0 ; i < fmheader . num_frames ; + + i )
{
in = & g_frames [ i ] ;
index = fmheader . num_xyz ;
for ( j = 0 ; j < numJointsInSkeleton [ g_skelModel . type ] ; + + j )
{
VectorCopy ( in - > joints [ j ] . placement . origin , in - > v [ index ] . v ) ;
index + + ;
VectorCopy ( in - > joints [ j ] . placement . direction , in - > v [ index ] . v ) ;
index + + ;
VectorCopy ( in - > joints [ j ] . placement . up , in - > v [ index ] . v ) ;
index + + ;
}
}
fmheader . num_xyz + = numJointsInSkeleton [ g_skelModel . type ] * 3 ;
}
CompressFrames ( ) ;
}
}
/*
= = = = = = = = = = = = = = =
FinishModel
= = = = = = = = = = = = = = =
*/
void FMFinishModel ( void )
{
FILE * modelouthandle ;
int i , j , length , tris , verts , bit , pos , total_tris , total_verts ;
char name [ 1024 ] ;
int trans_count ;
if ( ! fmheader . 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.fm " , cdpartial ) ;
ReleaseFile ( name ) ;
for ( i = 0 ; i < fmheader . num_skins ; i + + )
{
ReleaseFile ( g_skins [ i ] ) ;
}
fmheader . num_frames = 0 ;
return ;
}
printf ( " \n " ) ;
trans_count = 0 ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
if ( translucent [ i ] )
trans_count + + ;
if ( ! g_no_opimizations )
{
OptimizeVertices ( ) ;
}
//
// write the model output file
//
if ( modelname [ 0 ] )
sprintf ( name , " %s%s " , g_outputDir , modelname ) ;
else
sprintf ( name , " %s/tris.fm " , g_outputDir ) ;
printf ( " saving to %s \n " , name ) ;
CreatePath ( name ) ;
modelouthandle = SafeOpenWrite ( name ) ;
WriteModelFile ( modelouthandle ) ;
printf ( " %3dx%3d skin \n " , fmheader . skinwidth , fmheader . 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 " , fmheader . num_xyz ) ;
printf ( " %4d triangles, %4d of them translucent \n " , fmheader . num_tris , trans_count ) ;
printf ( " %4d frame \n " , fmheader . num_frames ) ;
printf ( " %4d glverts \n " , numglverts ) ;
printf ( " %4d glcmd \n " , fmheader . num_glcmds ) ;
printf ( " %4d skins \n " , fmheader . num_skins ) ;
printf ( " %4d mesh nodes \n " , fmheader . num_mesh_nodes ) ;
printf ( " wasted pixels: %d / %d (%5.2f Percent) \n " , total_skin_pixels - skin_pixels_used ,
total_skin_pixels , ( double ) ( total_skin_pixels - skin_pixels_used ) / ( double ) total_skin_pixels * 100.0 ) ;
printf ( " file size: %d \n " , ( int ) ftell ( modelouthandle ) ) ;
printf ( " --------------------- \n " ) ;
if ( g_verbose )
{
if ( fmheader . num_mesh_nodes )
{
total_tris = total_verts = 0 ;
printf ( " Node Name Tris Verts \n " ) ;
printf ( " --------------------------------- ---- ----- \n " ) ;
for ( i = 0 ; i < fmheader . num_mesh_nodes ; i + + )
{
tris = 0 ;
verts = 0 ;
for ( j = 0 ; j < MAXTRIANGLES ; j + + )
{
pos = ( j ) > > 3 ;
bit = 1 < < ( ( j ) & 7 ) ;
if ( pmnodes [ i ] . tris [ pos ] & bit )
{
tris + + ;
}
}
for ( j = 0 ; j < MAX_FM_VERTS ; j + + )
{
pos = ( j ) > > 3 ;
bit = 1 < < ( ( j ) & 7 ) ;
if ( pmnodes [ i ] . verts [ pos ] & bit )
{
verts + + ;
}
}
printf ( " %-33s %4d %5d \n " , pmnodes [ i ] . name , tris , verts ) ;
total_tris + = tris ;
total_verts + = verts ;
}
printf ( " --------------------------------- ---- ----- \n " ) ;
printf ( " %-33s %4d %5d \n " , " TOTALS " , total_tris , total_verts ) ;
}
}
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 ) ;
// mesh nodes
if ( fmheader . num_mesh_nodes )
{
H_printf ( " \n " ) ;
H_printf ( " #define NUM_MESH_NODES \t \t %d \n \n " , fmheader . num_mesh_nodes ) ;
for ( i = 0 ; i < fmheader . num_mesh_nodes ; i + + )
{
strcpy ( name , pmnodes [ i ] . name ) ;
strupr ( name ) ;
length = strlen ( name ) ;
for ( j = 0 ; j < length ; j + + )
{
if ( name [ j ] = = ' ' )
{
name [ j ] = ' _ ' ;
}
}
H_printf ( " #define MESH_%s \t \t %d \n " , name , i ) ;
}
}
fclose ( headerouthandle ) ;
headerouthandle = NULL ;
free ( pmnodes ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
ALIAS MODEL DISPLAY LIST GENERATION
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
extern int strip_xyz [ 128 ] ;
extern int strip_st [ 128 ] ;
extern int strip_tris [ 128 ] ;
extern int stripcount ;
/*
= = = = = = = = = = = = = = = =
StripLength
= = = = = = = = = = = = = = = =
*/
static int StripLength ( int starttri , int startv , int num_tris , int node )
{
int m1 , m2 ;
int st1 , st2 ;
int j ;
fmtriangle_t * last , * check ;
int k ;
int pos , bit ;
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 < num_tris ; j + + , check + + )
{
pos = j > > 3 ;
bit = 1 < < ( j & 7 ) ;
if ( ! ( pmnodes [ node ] . tris [ pos ] & bit ) )
{
continue ;
}
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 ] | | translucent [ j ] ! = translucent [ starttri ] )
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 < num_tris ; j + + )
if ( used [ j ] = = 2 )
used [ j ] = 0 ;
return stripcount ;
}
/*
= = = = = = = = = = =
FanLength
= = = = = = = = = = =
*/
static int FanLength ( int starttri , int startv , int num_tris , int node )
{
int m1 , m2 ;
int st1 , st2 ;
int j ;
fmtriangle_t * last , * check ;
int k ;
int pos , bit ;
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 < num_tris ; j + + , check + + )
{
pos = j > > 3 ;
bit = 1 < < ( j & 7 ) ;
if ( ! ( pmnodes [ node ] . tris [ pos ] & bit ) )
{
continue ;
}
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 ] | | translucent [ j ] ! = translucent [ starttri ] )
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 < 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 , l ;
int startv ;
float s , t ;
int len , bestlen , besttype ;
int best_xyz [ 1024 ] ;
int best_st [ 1024 ] ;
int best_tris [ 1024 ] ;
int type ;
int trans_check ;
int bit , pos ;
//
// build tristrips
//
numcommands = 0 ;
numglverts = 0 ;
for ( l = 0 ; l < fmheader . num_mesh_nodes ; l + + )
{
memset ( used , 0 , sizeof ( used ) ) ;
pmnodes [ l ] . start_glcmds = numcommands ;
for ( trans_check = 0 ; trans_check < 2 ; trans_check + + )
{
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
pos = i > > 3 ;
bit = 1 < < ( i & 7 ) ;
if ( ! ( pmnodes [ l ] . tris [ pos ] & bit ) )
{
continue ;
}
// pick an unused triangle and start the trifan
if ( used [ i ] | | trans_check ! = translucent [ 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 , fmheader . num_tris , l ) ;
else
len = FanLength ( i , startv , fmheader . num_tris , l ) ;
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 ) / fmheader . skinwidth ;
t = ( t ) / fmheader . skinheight ;
* ( float * ) & commands [ numcommands + + ] = s ;
* ( float * ) & commands [ numcommands + + ] = t ;
* ( int * ) & commands [ numcommands + + ] = best_xyz [ j ] ;
}
}
}
commands [ numcommands + + ] = 0 ; // end of list marker
pmnodes [ l ] . num_glcmds = numcommands - pmnodes [ l ] . start_glcmds ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
BASE FRAME SETUP
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# define LINE_NORMAL 1
# define LINE_FAT 2
# define LINE_DOTTED 3
# define ASCII_SPACE 32
int LineType = LINE_NORMAL ;
extern unsigned char pic [ SKINPAGE_HEIGHT * SKINPAGE_WIDTH ] , pic_palette [ 768 ] ;
unsigned char LineColor = 255 ;
int ScaleWidth , ScaleHeight ;
static char * CharDefs [ ] =
{
" ------------------------- " ,
" ------------------------- " , // !
" ------------------------- " , // "
" ------------------------- " , // #
" ------------------------- " , // $
" ------------------------- " , // %
" ------------------------- " , // &
" --*----*----------------- " , // '
" -*---*----*----*-----*--- " , // (
" *-----*----*----*---*---- " , // )
" -----*--*--**---**--*--*- " , // *
" ------------------------- " , // +
" ----------------**--**--- " , // ,
" ------------------------- " , // -
" ----------------**---**-- " , // .
" ------------------------- " , // /
" *** * *** * *** * *** " , // 0
" * ** * * * " ,
" **** * *** * ***** " ,
" **** * *** ***** " ,
" ** * * * * ***** * " ,
" **** * **** ***** " ,
" *** * **** * * *** " ,
" ***** * * * * " ,
" *** * * *** * * *** " ,
" *** * * **** * *** " , // 9
" -**---**--------**---**-- " , // :
" ------------------------- " , // ;
" ------------------------- " , // <
" ------------------------- " , // =
" ------------------------- " , // >
" ------------------------- " , // ?
" ------------------------- " , // @
" -***-*---*******---**---* " , // A
" ****-*---*****-*---*****- " ,
" -*****----*----*-----**** " ,
" ****-*---**---**---*****- " ,
" ******----****-*----***** " ,
" ******----****-*----*---- " ,
" -*****----*--***---*-**** " ,
" *---**---*******---**---* " ,
" -***---*----*----*---***- " ,
" ----*----*----**---*-***- " ,
" -*--*-*-*--**---*-*--*--* " ,
" -*----*----*----*----**** " ,
" *---***-***-*-**---**---* " ,
" *---***--**-*-**--***---* " ,
" -***-*---**---**---*-***- " ,
" ****-*---*****-*----*---- " ,
" -***-*---**---*-***----** " ,
" ****-*---*****-*-*--*--** " ,
" -*****-----***-----*****- " ,
" *****--*----*----*----*-- " ,
" *---**---**---**---****** " ,
" *---**---**---*-*-*---*-- " ,
" *---**---**-*-***-***---* " ,
" *---*-*-*---*---*-*-*---* " ,
" *---**---*-*-*---*----*-- " ,
" *****---*---*---*---***** " // Z
} ;
void DrawLine ( int x1 , int y1 , int x2 , int y2 )
{
int dx , dy ;
int adx , ady ;
int count ;
float xfrac , yfrac , xstep , ystep ;
unsigned sx , sy ;
float u , v ;
dx = x2 - x1 ;
dy = y2 - y1 ;
adx = abs ( dx ) ;
ady = abs ( dy ) ;
count = adx > ady ? adx : ady ;
count + + ;
if ( count > 300 )
{
printf ( " Bad count \n " ) ;
return ; // don't ever hang up on bad data
}
xfrac = x1 ;
yfrac = y1 ;
xstep = ( float ) dx / count ;
ystep = ( float ) dy / count ;
switch ( LineType )
{
case LINE_NORMAL :
do
{
if ( xfrac < SKINPAGE_WIDTH & & yfrac < SKINPAGE_HEIGHT )
{
pic [ ( int ) yfrac * SKINPAGE_WIDTH + ( int ) xfrac ] = LineColor ;
}
xfrac + = xstep ;
yfrac + = ystep ;
count - - ;
} while ( count > 0 ) ;
break ;
case LINE_FAT :
do
{
for ( u = - 0.1 ; u < = 0.9 ; u + = 0.999 )
{
for ( v = - 0.1 ; v < = 0.9 ; v + = 0.999 )
{
sx = xfrac + u ;
sy = yfrac + v ;
if ( sx < SKINPAGE_WIDTH & & sy < SKINPAGE_HEIGHT )
{
pic [ sy * SKINPAGE_WIDTH + sx ] = LineColor ;
}
}
}
xfrac + = xstep ;
yfrac + = ystep ;
count - - ;
} while ( count > 0 ) ;
break ;
case LINE_DOTTED :
do
{
if ( count & 1 & & xfrac < SKINPAGE_WIDTH & &
yfrac < SKINPAGE_HEIGHT )
{
pic [ ( int ) yfrac * SKINPAGE_WIDTH + ( int ) xfrac ] = LineColor ;
}
xfrac + = xstep ;
yfrac + = ystep ;
count - - ;
} while ( count > 0 ) ;
break ;
default :
Error ( " Unknown <linetype> %d. \n " , LineType ) ;
}
}
//==========================================================================
//
// DrawCharacter
//
//==========================================================================
static void DrawCharacter ( int x , int y , int character )
{
int r , c ;
char * def ;
character = toupper ( character ) ;
if ( character < ASCII_SPACE | | character > ' Z ' )
{
character = ASCII_SPACE ;
}
character - = ASCII_SPACE ;
for ( def = CharDefs [ character ] , r = 0 ; r < 5 ; r + + )
{
for ( c = 0 ; c < 5 ; c + + )
{
pic [ ( y + r ) * SKINPAGE_WIDTH + x + c ] = * def + + = = ' * ' ? 255 : 0 ;
}
}
}
//==========================================================================
//
// DrawTextChar
//
//==========================================================================
void DrawTextChar ( int x , int y , char * text )
{
int c ;
while ( ( c = * text + + ) ! = ' \0 ' )
{
DrawCharacter ( x , y , c ) ;
x + = 6 ;
}
}
extern void DrawScreen ( float s_scale , float t_scale , float iwidth , float iheight ) ;
//==========================================================================
// ExtractDigit
static int ExtractDigit ( byte * pic , int x , int y )
{
int i ;
int r , c ;
char digString [ 32 ] ;
char * buffer ;
byte backColor ;
char * * DigitDefs ;
backColor = pic [ ( SKINPAGE_HEIGHT - 1 ) * SKINPAGE_WIDTH ] ;
DigitDefs = & CharDefs [ ' 0 ' - ASCII_SPACE ] ;
buffer = digString ;
for ( r = 0 ; r < 5 ; r + + )
{
for ( c = 0 ; c < 5 ; c + + )
{
* buffer + + = ( pic [ ( y + r ) * SKINPAGE_WIDTH + x + c ] = = backColor ) ? ' ' : ' * ' ;
}
}
* buffer = ' \0 ' ;
for ( i = 0 ; i < 10 ; i + + )
{
if ( strcmp ( DigitDefs [ i ] , digString ) = = 0 )
{
return i ;
}
}
Error ( " Unable to extract scaling info from skin PCX. " ) ;
return 0 ;
}
//==========================================================================
// ExtractNumber
int ExtractNumber ( byte * pic , int x , int y )
{
return ExtractDigit ( pic , x , y ) * 100 + ExtractDigit ( pic , x + 6 , y ) * 10 + ExtractDigit ( pic , x + 12 , y ) ;
}
/*
= = = = = = = = = = = =
BuildST
Builds the triangle_st array for the base frame and
fmheader . skinwidth / fmheader . skinheight
FIXME : allow this to be loaded from a file for
arbitrary mappings
= = = = = = = = = = = =
*/
static void BuildST ( triangle_t * ptri , int numtri , qboolean DrawSkin )
{
int backface_flag ;
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 ;
int skinwidth ;
int skinheight ;
//
// find bounds of all the verts on the base frame
//
ClearBounds ( mins , maxs ) ;
backface_flag = false ;
if ( ptri [ 0 ] . HasUV ) // if we have the uv already, we don't want to double up or scale
{
iwidth = ScaleWidth ;
iheight = ScaleHeight ;
t_scale = s_scale = 1.0 ;
}
else
{
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 ] ;
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 )
{
backface_flag = true ;
break ;
}
}
scWidth = ScaleWidth * SCALE_ADJUST_FACTOR ;
if ( backface_flag ) //we are doubling
scWidth / = 2 ;
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 )
{
if ( backface_flag )
DrawScreen ( s_scale , t_scale , iwidth * 2 , iheight ) ;
else
DrawScreen ( s_scale , t_scale , iwidth , iheight ) ;
}
if ( backface_flag )
skinwidth = iwidth * 2 ;
else
skinwidth = iwidth ;
skinheight = 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
//
basey = 2 ;
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 ] * skinwidth ) ;
triangle_st [ i ] [ j ] [ 1 ] = Q_rint ( ( 1.0f - ptri [ i ] . uv [ j ] [ 1 ] ) * skinheight ) ;
}
}
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 ;
}
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 ) ;
}
}
if ( DrawSkin )
{
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 ;
if ( backface_flag )
swidth * = 2 ;
fmheader . skinwidth = ( swidth + 3 ) & ~ 3 ;
fmheader . skinheight = iheight ;
skin_width = iwidth ;
skin_height = iheight ;
}
static void BuildNewST ( triangle_t * ptri , int numtri , qboolean DrawSkin )
{
int i , j ;
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 ] * ( ScaleWidth - 1 ) ) ;
triangle_st [ i ] [ j ] [ 1 ] = Q_rint ( ( 1.0f - ptri [ i ] . uv [ j ] [ 1 ] ) * ( ScaleHeight - 1 ) ) ;
}
}
if ( DrawSkin )
{
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
fmheader . skinwidth = ( ScaleWidth + 3 ) & ~ 3 ;
fmheader . skinheight = ScaleHeight ;
skin_width = ScaleWidth ;
skin_height = ScaleHeight ;
}
byte * BasePalette ;
byte * BasePixels , * TransPixels ;
int BaseWidth , BaseHeight , TransWidth , TransHeight ;
qboolean BaseTrueColor ;
static qboolean SetPixel = false ;
int CheckTransRecursiveTri ( int * lp1 , int * lp2 , int * lp3 )
{
int * temp ;
int d ;
int new [ 2 ] ;
d = lp2 [ 0 ] - lp1 [ 0 ] ;
if ( d < - 1 | | d > 1 )
goto split ;
d = lp2 [ 1 ] - lp1 [ 1 ] ;
if ( d < - 1 | | d > 1 )
goto split ;
d = lp3 [ 0 ] - lp2 [ 0 ] ;
if ( d < - 1 | | d > 1 )
goto split2 ;
d = lp3 [ 1 ] - lp2 [ 1 ] ;
if ( d < - 1 | | d > 1 )
goto split2 ;
d = lp1 [ 0 ] - lp3 [ 0 ] ;
if ( d < - 1 | | d > 1 )
goto split3 ;
d = lp1 [ 1 ] - lp3 [ 1 ] ;
if ( d < - 1 | | d > 1 )
{
split3 :
temp = lp1 ;
lp1 = lp3 ;
lp3 = lp2 ;
lp2 = temp ;
goto split ;
}
return 0 ; // entire tri is filled
split2 :
temp = lp1 ;
lp1 = lp2 ;
lp2 = lp3 ;
lp3 = temp ;
split :
// split this edge
new [ 0 ] = ( lp1 [ 0 ] + lp2 [ 0 ] ) > > 1 ;
new [ 1 ] = ( lp1 [ 1 ] + lp2 [ 1 ] ) > > 1 ;
// draw the point if splitting a leading edge
if ( lp2 [ 1 ] > lp1 [ 1 ] )
goto nodraw ;
if ( ( lp2 [ 1 ] = = lp1 [ 1 ] ) & & ( lp2 [ 0 ] < lp1 [ 0 ] ) )
goto nodraw ;
if ( SetPixel )
{
assert ( ( new [ 1 ] * BaseWidth ) + new [ 0 ] < BaseWidth * BaseHeight ) ;
if ( BaseTrueColor )
{
BasePixels [ ( ( new [ 1 ] * BaseWidth ) + new [ 0 ] ) * 4 ] = 1 ;
}
else
{
BasePixels [ ( new [ 1 ] * BaseWidth ) + new [ 0 ] ] = 1 ;
}
}
else
{
if ( TransPixels )
{
if ( TransPixels [ ( new [ 1 ] * TransWidth ) + new [ 0 ] ] ! = 255 )
return 1 ;
}
else if ( BaseTrueColor )
{
if ( BasePixels [ ( ( ( new [ 1 ] * BaseWidth ) + new [ 0 ] ) * 4 ) + 3 ] ! = 255 )
return 1 ;
}
else
{
// pixel = BasePixels[(new[1]*BaseWidth) + new[0]];
}
}
nodraw :
// recursively continue
if ( CheckTransRecursiveTri ( lp3 , lp1 , new ) )
return 1 ;
return CheckTransRecursiveTri ( lp3 , new , lp2 ) ;
}
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 < numJointsInSkeleton [ g_skelModel . type ] ; + + j )
{
if ( ! clusters [ j ] )
{
continue ;
}
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 ;
}
}
}
}
# define FUDGE_EPSILON 0.002
qboolean VectorFudgeCompare ( vec3_t v1 , vec3_t v2 )
{
int i ;
for ( i = 0 ; i < 3 ; i + + )
if ( fabs ( v1 [ i ] - v2 [ i ] ) > FUDGE_EPSILON )
return false ;
return true ;
}
/*
= = = = = = = = = = = = = = = = =
Cmd_Base
= = = = = = = = = = = = = = = = =
*/
void Cmd_FMBase ( qboolean GetST )
{
triangle_t * ptri , * st_tri ;
int num_st_tris ;
int i , j , k , l ;
int x , y , z ;
// int time1;
char file1 [ 1024 ] , file2 [ 1024 ] , trans_file [ 1024 ] , stfile [ 1024 ] , extension [ 256 ] ;
vec3_t base_xyz [ MAX_FM_VERTS ] ;
FILE * FH ;
int pos , bit ;
qboolean NewSkin ;
GetScriptToken ( false ) ;
if ( g_skipmodel | | g_release | | g_archive )
return ;
printf ( " --------------------- \n " ) ;
sprintf ( file1 , " %s/%s.%s " , cdarchive , token , trifileext ) ;
printf ( " %s " , file1 ) ;
ExpandPathAndArchive ( file1 ) ;
// Use the input filepath for this one.
sprintf ( file1 , " %s/%s " , cddir , token ) ;
// time1 = FileTime (file1);
// if (time1 == -1)
// Error ("%s doesn't exist", file1);
//
// load the base triangles
//
if ( do3ds )
Load3DSTriangleList ( file1 , & ptri , & fmheader . num_tris , & pmnodes , & fmheader . num_mesh_nodes ) ;
else
LoadTriangleList ( file1 , & ptri , & fmheader . num_tris , & pmnodes , & fmheader . num_mesh_nodes ) ;
if ( g_ignoreTriUV )
{
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
ptri [ i ] . HasUV = 0 ;
}
}
GetScriptToken ( false ) ;
sprintf ( file2 , " %s/%s " , cddir , token ) ;
sprintf ( trans_file , " %s/!%s_a.pcx " , cddir , token ) ;
ExtractFileExtension ( file2 , extension ) ;
if ( extension [ 0 ] = = 0 )
{
strcat ( file2 , " .pcx " ) ;
}
printf ( " skin: %s \n " , file2 ) ;
BaseTrueColor = LoadAnyImage ( file2 , & BasePixels , & BasePalette , & BaseWidth , & BaseHeight ) ;
NewSkin = false ;
if ( BaseWidth ! = SKINPAGE_WIDTH | | BaseHeight ! = SKINPAGE_HEIGHT )
{
if ( g_allow_newskin )
{
ScaleWidth = BaseWidth ;
ScaleHeight = BaseHeight ;
NewSkin = true ;
}
else
{
Error ( " Invalid skin page size: (%d,%d) should be (%d,%d) " ,
BaseWidth , BaseHeight , SKINPAGE_WIDTH , SKINPAGE_HEIGHT ) ;
}
}
else if ( ! BaseTrueColor )
{
ScaleWidth = ( float ) ExtractNumber ( BasePixels , ENCODED_WIDTH_X ,
ENCODED_WIDTH_Y ) ;
ScaleHeight = ( float ) ExtractNumber ( BasePixels , ENCODED_HEIGHT_X ,
ENCODED_HEIGHT_Y ) ;
}
else
{
Error ( " Texture coordinates not supported on true color image " ) ;
}
if ( GetST )
{
GetScriptToken ( false ) ;
sprintf ( stfile , " %s/%s.%s " , cdarchive , token , trifileext ) ;
printf ( " ST: %s " , stfile ) ;
sprintf ( stfile , " %s/%s " , cddir , token ) ;
if ( do3ds )
Load3DSTriangleList ( stfile , & st_tri , & num_st_tris , NULL , NULL ) ;
else
LoadTriangleList ( stfile , & st_tri , & num_st_tris , NULL , NULL ) ;
if ( num_st_tris ! = fmheader . num_tris )
{
Error ( " num st tris mismatch: st %d / base %d " , num_st_tris , fmheader . num_tris ) ;
}
printf ( " matching triangles... \n " ) ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
k = - 1 ;
for ( j = 0 ; j < num_st_tris ; j + + )
{
for ( x = 0 ; x < 3 ; x + + )
{
for ( y = 0 ; y < 3 ; y + + )
{
if ( x = = y )
{
continue ;
}
for ( z = 0 ; z < 3 ; z + + )
{
if ( z = = x | | z = = y )
{
continue ;
}
if ( VectorFudgeCompare ( ptri [ i ] . verts [ 0 ] , st_tri [ j ] . verts [ x ] ) & &
VectorFudgeCompare ( ptri [ i ] . verts [ 1 ] , st_tri [ j ] . verts [ y ] ) & &
VectorFudgeCompare ( ptri [ i ] . verts [ 2 ] , st_tri [ j ] . verts [ z ] ) )
{
if ( k = = - 1 )
{
k = j ;
ptri [ i ] . HasUV = st_tri [ k ] . HasUV ;
ptri [ i ] . uv [ 0 ] [ 0 ] = st_tri [ k ] . uv [ x ] [ 0 ] ;
ptri [ i ] . uv [ 0 ] [ 1 ] = st_tri [ k ] . uv [ x ] [ 1 ] ;
ptri [ i ] . uv [ 1 ] [ 0 ] = st_tri [ k ] . uv [ y ] [ 0 ] ;
ptri [ i ] . uv [ 1 ] [ 1 ] = st_tri [ k ] . uv [ y ] [ 1 ] ;
ptri [ i ] . uv [ 2 ] [ 0 ] = st_tri [ k ] . uv [ z ] [ 0 ] ;
ptri [ i ] . uv [ 2 ] [ 1 ] = st_tri [ k ] . uv [ z ] [ 1 ] ;
x = y = z = 999 ;
}
else if ( k ! = j )
{
printf ( " Duplicate triangle %d found in st file: %d and %d \n " , i , k , j ) ;
printf ( " (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) \n " ,
ptri [ i ] . verts [ 0 ] [ 0 ] , ptri [ i ] . verts [ 0 ] [ 1 ] , ptri [ i ] . verts [ 0 ] [ 2 ] ,
ptri [ i ] . verts [ 1 ] [ 0 ] , ptri [ i ] . verts [ 1 ] [ 1 ] , ptri [ i ] . verts [ 1 ] [ 2 ] ,
ptri [ i ] . verts [ 2 ] [ 0 ] , ptri [ i ] . verts [ 2 ] [ 1 ] , ptri [ i ] . verts [ 2 ] [ 2 ] ) ;
printf ( " (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) \n " ,
st_tri [ k ] . verts [ 0 ] [ 0 ] , st_tri [ k ] . verts [ 0 ] [ 1 ] , st_tri [ k ] . verts [ 0 ] [ 2 ] ,
st_tri [ k ] . verts [ 1 ] [ 0 ] , st_tri [ k ] . verts [ 1 ] [ 1 ] , st_tri [ k ] . verts [ 1 ] [ 2 ] ,
st_tri [ k ] . verts [ 2 ] [ 0 ] , st_tri [ k ] . verts [ 2 ] [ 1 ] , st_tri [ k ] . verts [ 2 ] [ 2 ] ) ;
printf ( " (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) (%0.3f %0.3f %0.3f) \n " ,
st_tri [ j ] . verts [ 0 ] [ 0 ] , st_tri [ j ] . verts [ 0 ] [ 1 ] , st_tri [ j ] . verts [ 0 ] [ 2 ] ,
st_tri [ j ] . verts [ 1 ] [ 0 ] , st_tri [ j ] . verts [ 1 ] [ 1 ] , st_tri [ j ] . verts [ 1 ] [ 2 ] ,
st_tri [ j ] . verts [ 2 ] [ 0 ] , st_tri [ j ] . verts [ 2 ] [ 1 ] , st_tri [ j ] . verts [ 2 ] [ 2 ] ) ;
}
}
}
}
}
}
if ( k = = - 1 )
{
printf ( " No matching triangle %d \n " , i ) ;
}
}
free ( st_tri ) ;
}
//
// get the ST values
//
if ( ptri & & ptri [ 0 ] . HasUV )
{
if ( ! NewSkin )
{
Error ( " Base has UVs with old style skin page \n Maybe you want to use -ignoreUV " ) ;
}
else
{
BuildNewST ( ptri , fmheader . num_tris , false ) ;
}
}
else
{
if ( NewSkin )
{
Error ( " Base has new style skin without UVs " ) ;
}
else
{
BuildST ( ptri , fmheader . num_tris , false ) ;
}
}
TransPixels = NULL ;
if ( ! BaseTrueColor )
{
FH = fopen ( trans_file , " rb " ) ;
if ( FH )
{
fclose ( FH ) ;
Load256Image ( trans_file , & TransPixels , NULL , & TransWidth , & TransHeight ) ;
if ( TransWidth ! = fmheader . skinwidth | | TransHeight ! = fmheader . skinheight )
{
Error ( " source image %s dimensions (%d,%d) are not the same as alpha image (%d,%d) \n " , file2 , fmheader . skinwidth , fmheader . skinheight , TransWidth , TransHeight ) ;
}
}
}
//
// 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 ( l = 0 ; l < fmheader . num_mesh_nodes ; l + + )
{
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
pos = i > > 3 ;
bit = 1 < < ( i & 7 ) ;
if ( ! ( pmnodes [ l ] . tris [ pos ] & bit ) )
{
continue ;
}
for ( j = 0 ; j < 3 ; j + + )
{
// get the xyz index
for ( k = 0 ; k < fmheader . num_xyz ; k + + )
{
if ( VectorCompare ( ptri [ i ] . verts [ j ] , base_xyz [ k ] ) )
{
break ; // this vertex is already in the base vertex list
}
}
if ( k = = fmheader . num_xyz )
{ // new index
VectorCopy ( ptri [ i ] . verts [ j ] , base_xyz [ fmheader . num_xyz ] ) ;
if ( pmnodes [ l ] . clustered = = true )
{
ReplaceClusterIndex ( k , ptri [ i ] . indicies [ j ] , ( int * * ) & pmnodes [ l ] . clusters , ( IntListNode_t * * ) & g_skelModel . vertLists , ( int * ) & pmnodes [ l ] . num_verts , ( int * ) & g_skelModel . new_num_verts ) ;
}
fmheader . num_xyz + + ;
}
pos = k > > 3 ;
bit = 1 < < ( k & 7 ) ;
pmnodes [ l ] . verts [ pos ] | = bit ;
triangles [ i ] . index_xyz [ j ] = k ;
// get the st index
for ( k = 0 ; k < fmheader . 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 = = fmheader . num_st )
{ // new index
base_st [ fmheader . num_st ] . s = triangle_st [ i ] [ j ] [ 0 ] ;
base_st [ fmheader . num_st ] . t = triangle_st [ i ] [ j ] [ 1 ] ;
fmheader . num_st + + ;
}
triangles [ i ] . index_st [ j ] = k ;
}
if ( TransPixels | | BaseTrueColor )
{
translucent [ i ] = CheckTransRecursiveTri ( triangle_st [ i ] [ 0 ] , triangle_st [ i ] [ 1 ] , triangle_st [ i ] [ 2 ] ) ;
}
else
{
translucent [ i ] = false ;
}
}
}
if ( ! BaseTrueColor )
{
SetPixel = true ;
memset ( BasePixels , 0 , BaseWidth * BaseHeight ) ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
CheckTransRecursiveTri ( triangle_st [ i ] [ 0 ] , triangle_st [ i ] [ 1 ] , triangle_st [ i ] [ 2 ] ) ;
}
SetPixel = false ;
skin_pixels_used = 0 ;
for ( i = 0 ; i < fmheader . skinheight ; i + + )
{
for ( j = 0 ; j < fmheader . skinwidth ; j + + )
{
skin_pixels_used + = BasePixels [ ( i * BaseWidth ) + j ] ;
}
}
total_skin_pixels = fmheader . skinheight * fmheader . skinwidth ;
}
else
{
SetPixel = true ;
memset ( BasePixels , 0 , BaseWidth * BaseHeight * 4 ) ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
CheckTransRecursiveTri ( triangle_st [ i ] [ 0 ] , triangle_st [ i ] [ 1 ] , triangle_st [ i ] [ 2 ] ) ;
}
SetPixel = false ;
skin_pixels_used = 0 ;
for ( i = 0 ; i < fmheader . skinheight ; i + + )
{
for ( j = 0 ; j < fmheader . skinwidth ; j + + )
{
skin_pixels_used + = BasePixels [ ( ( i * BaseWidth ) + j ) * 4 ] ;
}
}
total_skin_pixels = fmheader . skinheight * fmheader . skinwidth ;
}
// build triangle strips / fans
BuildGlCmds ( ) ;
if ( TransPixels )
{
free ( TransPixels ) ;
}
free ( BasePixels ) ;
if ( BasePalette )
{
free ( BasePalette ) ;
}
free ( ptri ) ;
}
void Cmd_FMNodeOrder ( void )
{
mesh_node_t * newnodes , * pos ;
int i , j ;
if ( ! pmnodes )
{
Error ( " Base has not been established yet " ) ;
}
pos = newnodes = malloc ( sizeof ( mesh_node_t ) * fmheader . num_mesh_nodes ) ;
for ( i = 0 ; i < fmheader . num_mesh_nodes ; i + + )
{
GetScriptToken ( false ) ;
for ( j = 0 ; j < fmheader . num_mesh_nodes ; j + + )
{
if ( strcmpi ( pmnodes [ j ] . name , token ) = = 0 )
{
* pos = pmnodes [ j ] ;
pos + + ;
break ;
}
}
if ( j > = fmheader . num_mesh_nodes )
{
Error ( " Node '%s' not in base list! \n " , token ) ;
}
}
free ( pmnodes ) ;
pmnodes = newnodes ;
}
//===============================================================
extern char * FindFrameFile ( char * frame ) ;
/*
= = = = = = = = = = = = = = =
GrabFrame
= = = = = = = = = = = = = = =
*/
void GrabFrame ( char * frame )
{
triangle_t * ptri ;
int i , j ;
fmtrivert_t * ptrivert ;
int num_tris ;
char file1 [ 1024 ] ;
fmframe_t * fr ;
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 ( fmheader . num_frames > = MAX_FM_FRAMES )
Error ( " fmheader.num_frames >= MAX_FM_FRAMES " ) ;
fr = & g_frames [ fmheader . num_frames ] ;
fmheader . 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 ! = fmheader . num_tris )
Error ( " %s: number of triangles (%d) doesn't match base frame (%d) \n " , file1 , num_tris , fmheader . num_tris ) ;
//
// allocate storage for the frame's vertices
//
ptrivert = fr - > v ;
for ( i = 0 ; i < fmheader . num_xyz ; i + + )
{
ptrivert [ i ] . vnorm . numnormals = 0 ;
VectorClear ( ptrivert [ i ] . vnorm . 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 ( ptrivert [ index_xyz ] . vnorm . normalsum , normal , ptrivert [ index_xyz ] . vnorm . normalsum ) ;
ptrivert [ index_xyz ] . vnorm . numnormals + + ;
}
}
//
// calculate the vertex normals, match them to the template list, and store the
// index of the best match
//
for ( i = 0 ; i < fmheader . num_xyz ; i + + )
{
int j ;
vec3_t v ;
float maxdot ;
int maxdotindex ;
int c ;
c = ptrivert [ i ] . vnorm . numnormals ;
if ( ! c )
Error ( " Vertex with no triangles attached " ) ;
VectorScale ( ptrivert [ i ] . vnorm . 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_FMFrame ( void )
{
while ( ScriptTokenAvailable ( ) )
{
GetScriptToken ( false ) ;
if ( g_skipmodel )
continue ;
if ( g_release | | g_archive )
{
fmheader . num_frames = 1 ; // don't skip the writeout
continue ;
}
H_printf ( " #define FRAME_%-16s \t %i \n " , token , fmheader . num_frames ) ;
if ( ( g_skelModel . type ! = SKEL_NULL ) | | ( g_skelModel . references ! = REF_NULL ) )
{
GrabModelTransform ( token ) ;
}
GrabFrame ( token ) ;
if ( g_skelModel . type ! = SKEL_NULL )
{
GrabSkeletalFrame ( token ) ;
}
if ( g_skelModel . references ! = REF_NULL )
{
GrabReferencedFrame ( token ) ;
}
// need to add the up and dir points to the frame bounds here
// using AddPointToBounds (ptrivert[index_xyz].v, fr->mins, fr->maxs);
// then remove fudge in determining scale on frame write out
}
}
/*
= = = = = = = = = = = = = = =
Cmd_Skin
Skins aren ' t actually stored in the file , only a reference
is saved out to the header file .
= = = = = = = = = = = = = = =
*/
void Cmd_FMSkin ( void )
{
byte * palette ;
byte * pixels ;
int width , height ;
byte * cropped ;
int y ;
char name [ 1024 ] , savename [ 1024 ] , transname [ 1024 ] , extension [ 256 ] ;
miptex32_t * qtex32 ;
int size ;
FILE * FH ;
qboolean TrueColor ;
GetScriptToken ( false ) ;
if ( fmheader . num_skins = = MAX_FM_SKINS )
Error ( " fmheader.num_skins == MAX_FM_SKINS " ) ;
if ( g_skipmodel )
return ;
sprintf ( name , " %s/%s " , cdarchive , token ) ;
strcpy ( name , ExpandPathAndArchive ( name ) ) ;
// sprintf (name, "%s/%s.lbm", cddir, token);
if ( ScriptTokenAvailable ( ) )
{
GetScriptToken ( false ) ;
sprintf ( g_skins [ fmheader . num_skins ] , " !%s " , token ) ;
sprintf ( savename , " %s!%s " , g_outputDir , token ) ;
sprintf ( transname , " %s!%s_a.pcx " , gamedir , token ) ;
}
else
{
sprintf ( g_skins [ fmheader . num_skins ] , " %s/!%s " , cdpartial , token ) ;
sprintf ( savename , " %s/!%s " , g_outputDir , token ) ;
sprintf ( transname , " %s/!%s_a.pcx " , cddir , token ) ;
}
fmheader . num_skins + + ;
if ( g_skipmodel | | g_release | | g_archive )
return ;
// load the image
printf ( " loading %s \n " , name ) ;
ExtractFileExtension ( name , extension ) ;
if ( extension [ 0 ] = = 0 )
{
strcat ( name , " .pcx " ) ;
}
TrueColor = LoadAnyImage ( name , & pixels , & palette , & width , & height ) ;
// RemapZero (pixels, palette, width, height);
// crop it to the proper size
if ( ! TrueColor )
{
cropped = ( byte * ) SafeMalloc ( fmheader . skinwidth * fmheader . skinheight , " Cmd_FMSkin " ) ;
for ( y = 0 ; y < fmheader . skinheight ; y + + )
{
memcpy ( cropped + y * fmheader . skinwidth ,
pixels + y * width , fmheader . skinwidth ) ;
}
TransPixels = NULL ;
FH = fopen ( transname , " rb " ) ;
if ( FH )
{
fclose ( FH ) ;
strcat ( g_skins [ fmheader . num_skins - 1 ] , " .pcx " ) ;
strcat ( savename , " .pcx " ) ;
// save off the new image
printf ( " saving %s \n " , savename ) ;
CreatePath ( savename ) ;
WritePCXfile ( savename , cropped , fmheader . skinwidth , fmheader . skinheight , palette ) ;
}
else
{
# if 1
miptex_t * qtex ;
qtex = CreateMip ( cropped , fmheader . skinwidth , fmheader . skinheight , palette , & size , true ) ;
strcat ( g_skins [ fmheader . num_skins - 1 ] , " .m8 " ) ;
strcat ( savename , " .m8 " ) ;
printf ( " saving %s \n " , savename ) ;
CreatePath ( savename ) ;
SaveFile ( savename , ( byte * ) qtex , size ) ;
free ( qtex ) ;
# else
strcat ( g_skins [ fmheader . num_skins - 1 ] , " .pcx " ) ;
strcat ( savename , " .pcx " ) ;
// save off the new image
printf ( " saving %s \n " , savename ) ;
CreatePath ( savename ) ;
WritePCXfile ( savename , cropped , fmheader . skinwidth , fmheader . skinheight , palette ) ;
# endif
}
}
else
{
cropped = ( byte * ) SafeMalloc ( fmheader . skinwidth * fmheader . skinheight * 4 , " Cmd_FMSkin " ) ;
for ( y = 0 ; y < fmheader . skinheight ; y + + )
{
memcpy ( cropped + ( ( y * fmheader . skinwidth ) * 4 ) , pixels + ( y * width * 4 ) , fmheader . skinwidth * 4 ) ;
}
qtex32 = CreateMip32 ( ( unsigned * ) cropped , fmheader . skinwidth , fmheader . skinheight , & size , true ) ;
StripExtension ( g_skins [ fmheader . num_skins - 1 ] ) ;
strcat ( g_skins [ fmheader . num_skins - 1 ] , " .m32 " ) ;
StripExtension ( savename ) ;
strcat ( savename , " .m32 " ) ;
printf ( " saving %s \n " , savename ) ;
CreatePath ( savename ) ;
SaveFile ( savename , ( byte * ) qtex32 , size ) ;
}
free ( pixels ) ;
if ( palette )
{
free ( palette ) ;
}
free ( cropped ) ;
}
/*
= = = = = = = = = = = = = = =
Cmd_Cd
= = = = = = = = = = = = = = =
*/
void Cmd_FMCd ( 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 ) ;
}
}
/*
//=======================
// NEW GEN
//=======================
void NewGen ( char * ModelFile , char * OutputName , int width , int height )
{
trigroup_t * triangles ;
triangle_t * ptri ;
triangle_t * grouptris ;
mesh_node_t * pmnodes ;
vec3_t * vertices ;
vec3_t * uvs ;
vec3_t aveNorm , crossvect ;
vec3_t diffvect1 , diffvect2 ;
vec3_t v0 , v1 , v2 ;
vec3_t n , u , v ;
vec3_t base , zaxis , yaxis ;
vec3_t uvwMin , uvwMax ;
vec3_t groupMin , groupMax ;
vec3_t uvw ;
float * uFinal , * vFinal ;
unsigned char * newpic ;
int finalstart = 0 , finalcount = 0 ;
int xbase = 0 , xwidth = 0 , ywidth = 0 ;
int * todo , * done , finished ;
int i , j , k , l ; //counters
int groupnum , numtris , numverts , num ;
int count ;
FILE * grpfile ;
long datasize ;
for ( i = 0 ; i < 3 ; i + + )
{
aveNorm [ i ] = 0 ;
uvwMin [ i ] = 1e30 f ;
uvwMax [ i ] = - 1e30 f ;
}
pmnodes = NULL ;
ptri = NULL ;
triangles = NULL ;
zaxis [ 0 ] = 0 ;
zaxis [ 1 ] = 0 ;
zaxis [ 2 ] = 1 ;
yaxis [ 0 ] = 0 ;
yaxis [ 1 ] = 1 ;
yaxis [ 2 ] = 0 ;
LoadTriangleList ( ModelFile , & ptri , & fmheader . num_tris , & pmnodes , & fmheader . num_mesh_nodes ) ;
todo = ( int * ) SafeMalloc ( fmheader . num_tris * sizeof ( int ) , " NewGen " ) ;
done = ( int * ) SafeMalloc ( fmheader . num_tris * sizeof ( int ) , " NewGen " ) ;
triangles = ( trigroup_t * ) SafeMalloc ( fmheader . num_tris * sizeof ( trigroup_t ) , " NewGen " ) ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
todo [ i ] = false ;
done [ i ] = false ;
triangles [ i ] . triangle = ptri [ i ] ;
triangles [ i ] . group = 0 ;
}
groupnum = 0 ;
// transitive closure algorithm follows
// put all triangles who transitively share vertices into separate groups
while ( 1 )
{
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
if ( ! done [ i ] )
{
break ;
}
}
if ( i = = fmheader . num_tris )
{
break ;
}
finished = false ;
todo [ i ] = true ;
while ( ! finished )
{
finished = true ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
if ( todo [ i ] )
{
done [ i ] = true ;
triangles [ i ] . group = groupnum ;
todo [ i ] = false ;
for ( j = 0 ; j < fmheader . num_tris ; j + + )
{
if ( ( ! done [ j ] ) & & ( ShareVertex ( triangles [ i ] , triangles [ j ] ) ) )
{
todo [ j ] = true ;
finished = false ;
}
}
}
}
}
groupnum + + ;
}
uFinal = ( float * ) SafeMalloc ( 3 * fmheader . num_tris * sizeof ( float ) , " NewGen " ) ;
vFinal = ( float * ) SafeMalloc ( 3 * fmheader . num_tris * sizeof ( float ) , " NewGen " ) ;
grpfile = fopen ( " grpdebug.txt " , " w " ) ;
for ( i = 0 ; i < groupnum ; i + + )
{
fprintf ( grpfile , " Group Number: %d \n " , i ) ;
numtris = GetNumTris ( triangles , i ) ; // number of triangles in group i
numverts = numtris * 3 ;
fprintf ( grpfile , " %d triangles. \n " , numtris ) ;
vertices = ( vec3_t * ) SafeMalloc ( numverts * sizeof ( vec3_t ) , " NewGen " ) ;
uvs = ( vec3_t * ) SafeMalloc ( numverts * sizeof ( vec3_t ) , " NewGen " ) ;
grouptris = ( triangle_t * ) SafeMalloc ( numtris * sizeof ( triangle_t ) , " NewGen " ) ;
for ( count = 0 ; count < fmheader . num_tris ; count + + )
{
if ( triangles [ count ] . group = = i )
{
fprintf ( grpfile , " Triangle %d \n " , count ) ;
}
}
fprintf ( grpfile , " \n " ) ;
GetOneGroup ( triangles , i , grouptris ) ;
num = 0 ;
for ( j = 0 ; j < numtris ; j + + )
{
VectorCopy ( grouptris [ j ] . verts [ 0 ] , v0 ) ;
VectorCopy ( grouptris [ j ] . verts [ 1 ] , v1 ) ;
VectorCopy ( grouptris [ j ] . verts [ 2 ] , v2 ) ;
VectorSubtract ( v1 , v0 , diffvect1 ) ;
VectorSubtract ( v2 , v1 , diffvect2 ) ;
CrossProduct ( diffvect1 , diffvect2 , crossvect ) ;
VectorAdd ( aveNorm , crossvect , aveNorm ) ;
VectorCopy ( v0 , vertices [ num ] ) ;
num + + ; // FIXME
VectorCopy ( v1 , vertices [ num ] ) ;
num + + ; // add routine to add only verts that
VectorCopy ( v2 , vertices [ num ] ) ;
num + + ; // have not already been added
}
assert ( num > = 3 ) ;
// figure out the best plane projections
DOsvdPlane ( ( float * ) vertices , num , ( float * ) & n , ( float * ) & base ) ;
if ( DotProduct ( aveNorm , n ) < 0.0f )
{
VectorScale ( n , - 1.0f , n ) ;
}
VectorNormalize ( n , n ) ;
if ( fabs ( n [ 2 ] ) < .57 )
{
CrossProduct ( zaxis , n , crossvect ) ;
VectorCopy ( crossvect , u ) ;
}
else
{
CrossProduct ( yaxis , n , crossvect ) ;
VectorCopy ( crossvect , u ) ;
}
VectorNormalize ( u , u ) ;
CrossProduct ( n , u , crossvect ) ;
VectorCopy ( crossvect , v ) ;
VectorNormalize ( v , v ) ;
num = 0 ;
for ( j = 0 ; j < 3 ; j + + )
{
groupMin [ j ] = 1e30 f ;
groupMax [ j ] = - 1e30 f ;
}
for ( j = 0 ; j < numtris ; j + + )
{
for ( k = 0 ; k < 3 ; k + + )
{
VectorCopy ( grouptris [ j ] . verts [ k ] , v0 ) ;
VectorSubtract ( v0 , base , v0 ) ;
uvw [ 0 ] = DotProduct ( v0 , u ) ;
uvw [ 1 ] = DotProduct ( v0 , v ) ;
uvw [ 2 ] = DotProduct ( v0 , n ) ;
VectorCopy ( uvw , uvs [ num ] ) ;
num + + ;
for ( l = 0 ; l < 3 ; l + + )
{
if ( uvw [ l ] < groupMin [ l ] )
{
groupMin [ l ] = uvw [ l ] ;
}
if ( uvw [ l ] > groupMax [ l ] )
{
groupMax [ l ] = uvw [ l ] ;
}
}
}
}
xwidth = ceil ( 0 - groupMin [ 0 ] ) + 2 ; // move right of origin and avoid overlap
ywidth = ceil ( 0 - groupMin [ 1 ] ) + 2 ; // move "above" origin
for ( j = 0 ; j < numverts ; j + + )
{
uFinal [ finalcount ] = uvs [ j ] [ 0 ] + xwidth + xbase ;
vFinal [ finalcount ] = uvs [ j ] [ 1 ] + ywidth ;
if ( uFinal [ finalcount ] < uvwMin [ 0 ] )
{
uvwMin [ 0 ] = uFinal [ finalcount ] ;
}
if ( uFinal [ finalcount ] > uvwMax [ 0 ] )
{
uvwMax [ 0 ] = uFinal [ finalcount ] ;
}
if ( vFinal [ finalcount ] < uvwMin [ 1 ] )
{
uvwMin [ 1 ] = vFinal [ finalcount ] ;
}
if ( vFinal [ finalcount ] > uvwMax [ 1 ] )
{
uvwMax [ 1 ] = vFinal [ finalcount ] ;
}
finalcount + + ;
}
fprintf ( grpfile , " svdPlaned Group min: ( %f , %f ) \n " , groupMin [ 0 ] + xwidth + xbase , groupMin [ 1 ] + ywidth ) ;
fprintf ( grpfile , " svdPlaned Group max: ( %f , %f ) \n " , groupMax [ 0 ] + xwidth + xbase , groupMax [ 1 ] + ywidth ) ;
finalcount = finalstart ;
for ( count = 0 ; count < numverts ; count + + )
{
fprintf ( grpfile , " Vertex %d: ( %f , %f , %f ) \n " , count , vertices [ count ] [ 0 ] , vertices [ count ] [ 1 ] , vertices [ count ] [ 2 ] ) ;
fprintf ( grpfile , " svdPlaned: ( %f , %f ) \n " , uFinal [ finalcount ] , vFinal [ finalcount + + ] ) ;
}
finalstart = finalcount ;
fprintf ( grpfile , " \n " ) ;
free ( vertices ) ;
free ( uvs ) ;
free ( grouptris ) ;
xbase + = ceil ( groupMax [ 0 ] - groupMin [ 0 ] ) + 2 ;
}
fprintf ( grpfile , " Global Min ( %f , %f ) \n " , uvwMin [ 0 ] , uvwMin [ 1 ] ) ;
fprintf ( grpfile , " Global Max ( %f , %f ) \n " , uvwMax [ 0 ] , uvwMax [ 1 ] ) ;
ScaleTris ( uvwMin , uvwMax , width , height , uFinal , vFinal , finalcount ) ;
for ( k = 0 ; k < finalcount ; k + + )
{
fprintf ( grpfile , " scaled vertex %d: ( %f , %f ) \n " , k , uFinal [ k ] , vFinal [ k ] ) ;
}
// i've got the array of vertices in uFinal and vFinal. Now I need to write them and draw lines
datasize = width * height * sizeof ( unsigned char ) ;
newpic = ( unsigned char * ) SafeMalloc ( datasize , " NewGen " ) ;
memset ( newpic , 0 , datasize ) ;
memset ( pic_palette , 0 , sizeof ( pic_palette ) ) ;
pic_palette [ 767 ] = pic_palette [ 766 ] = pic_palette [ 765 ] = 255 ;
k = 0 ;
while ( k < finalcount )
{
NewDrawLine ( uFinal [ k ] , vFinal [ k ] , uFinal [ k + 1 ] , vFinal [ k + 1 ] , newpic , width , height ) ;
k + + ;
NewDrawLine ( uFinal [ k ] , vFinal [ k ] , uFinal [ k + 1 ] , vFinal [ k + 1 ] , newpic , width , height ) ;
k + + ;
NewDrawLine ( uFinal [ k ] , vFinal [ k ] , uFinal [ k - 2 ] , vFinal [ k - 2 ] , newpic , width , height ) ;
k + + ;
fprintf ( grpfile , " output tri with verts %d, %d, %d " , k - 2 , k - 1 , k ) ;
}
WritePCXfile ( OutputName , newpic , width , height , pic_palette ) ;
fclose ( grpfile ) ;
free ( todo ) ;
free ( done ) ;
free ( triangles ) ;
free ( newpic ) ;
return ;
}
void NewDrawLine ( int x1 , int y1 , int x2 , int y2 , unsigned char * picture , int width , int height )
{
long dx , dy ;
long adx , ady ;
long count ;
float xfrac , yfrac , xstep , ystep ;
unsigned long sx , sy ;
float u , v ;
dx = x2 - x1 ;
dy = y2 - y1 ;
adx = abs ( dx ) ;
ady = abs ( dy ) ;
count = adx > ady ? adx : ady ;
count + + ;
if ( count > 300 )
{
printf ( " Bad count \n " ) ;
return ; // don't ever hang up on bad data
}
xfrac = x1 ;
yfrac = y1 ;
xstep = ( float ) dx / count ;
ystep = ( float ) dy / count ;
switch ( LineType )
{
case LINE_NORMAL :
do
{
if ( xfrac < width & & yfrac < height )
{
picture [ ( long ) yfrac * width + ( long ) xfrac ] = LineColor ;
}
xfrac + = xstep ;
yfrac + = ystep ;
count - - ;
} while ( count > 0 ) ;
break ;
case LINE_FAT :
do
{
for ( u = - 0.1 ; u < = 0.9 ; u + = 0.999 )
{
for ( v = - 0.1 ; v < = 0.9 ; v + = 0.999 )
{
sx = xfrac + u ;
sy = yfrac + v ;
if ( sx < width & & sy < height )
{
picture [ sy * width + sx ] = LineColor ;
}
}
}
xfrac + = xstep ;
yfrac + = ystep ;
count - - ;
} while ( count > 0 ) ;
break ;
case LINE_DOTTED :
do
{
if ( count & 1 & & xfrac < width & &
yfrac < height )
{
picture [ ( long ) yfrac * width + ( long ) xfrac ] = LineColor ;
}
xfrac + = xstep ;
yfrac + = ystep ;
count - - ;
} while ( count > 0 ) ;
break ;
default :
Error ( " Unknown <linetype> %d. \n " , LineType ) ;
}
}
*/
void ScaleTris ( vec3_t min , vec3_t max , int Width , int Height , float * u , float * v , int verts )
{
int i ;
float hscale , vscale ;
float scale ;
hscale = max [ 0 ] ;
vscale = max [ 1 ] ;
hscale = ( Width - 2 ) / max [ 0 ] ;
vscale = ( Height - 2 ) / max [ 1 ] ;
scale = hscale ;
if ( scale > vscale )
{
scale = vscale ;
}
for ( i = 0 ; i < verts ; i + + )
{
u [ i ] * = scale ;
v [ i ] * = scale ;
}
return ;
}
void GetOneGroup ( trigroup_t * tris , int grp , triangle_t * triangles )
{
int i ;
int j ;
j = 0 ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
if ( tris [ i ] . group = = grp )
{
triangles [ j + + ] = tris [ i ] . triangle ;
}
}
return ;
}
int GetNumTris ( trigroup_t * tris , int grp )
{
int i ;
int verts ;
verts = 0 ;
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
if ( tris [ i ] . group = = grp )
{
verts + + ;
}
}
return verts ;
}
int ShareVertex ( trigroup_t trione , trigroup_t tritwo )
{
int i ;
int j ;
i = 1 ;
j = 1 ;
for ( i = 0 ; i < 3 ; i + + )
{
for ( j = 0 ; j < 3 ; j + + )
{
if ( DistBetween ( trione . triangle . verts [ i ] , tritwo . triangle . verts [ j ] ) < TRIVERT_DIST )
{
return true ;
}
}
}
return false ;
}
float DistBetween ( vec3_t point1 , vec3_t point2 )
{
float dist ;
dist = ( point1 [ 0 ] - point2 [ 0 ] ) ;
dist * = dist ;
dist + = ( point1 [ 1 ] - point2 [ 1 ] ) * ( point1 [ 1 ] - point2 [ 1 ] ) ;
dist + = ( point1 [ 2 ] - point2 [ 2 ] ) * ( point1 [ 2 ] - point2 [ 2 ] ) ;
dist = sqrt ( dist ) ;
return dist ;
}
void GenSkin ( char * ModelFile , char * OutputName , int Width , int Height )
{
triangle_t * ptri ;
mesh_node_t * pmnodes ;
int i ;
pmnodes = NULL ;
ptri = NULL ;
LoadTriangleList ( ModelFile , & ptri , & fmheader . num_tris , & pmnodes , & fmheader . num_mesh_nodes ) ;
if ( g_ignoreTriUV )
{
for ( i = 0 ; i < fmheader . num_tris ; i + + )
{
ptri [ i ] . HasUV = 0 ;
}
}
memset ( pic , 0 , sizeof ( pic ) ) ;
memset ( pic_palette , 0 , sizeof ( pic_palette ) ) ;
pic_palette [ 767 ] = pic_palette [ 766 ] = pic_palette [ 765 ] = 255 ;
ScaleWidth = Width ;
ScaleHeight = Height ;
BuildST ( ptri , fmheader . num_tris , true ) ;
WritePCXfile ( OutputName , pic , SKINPAGE_WIDTH , SKINPAGE_HEIGHT , pic_palette ) ;
printf ( " Gen Skin Stats: \n " ) ;
printf ( " Input Base: %s \n " , ModelFile ) ;
printf ( " Input Dimensions: %d,%d \n " , Width , Height ) ;
printf ( " \n " ) ;
printf ( " Output File: %s \n " , OutputName ) ;
printf ( " Output Dimensions: %d,%d \n " , ScaleWidth , ScaleHeight ) ;
if ( fmheader . num_mesh_nodes )
{
printf ( " \n Nodes: \n " ) ;
for ( i = 0 ; i < fmheader . num_mesh_nodes ; i + + )
{
printf ( " %s \n " , pmnodes [ i ] . name ) ;
}
}
free ( ptri ) ;
free ( pmnodes ) ;
}
void Cmd_FMBeginGroup ( void )
{
GetScriptToken ( false ) ;
g_no_opimizations = false ;
groups [ num_groups ] . start_frame = fmheader . num_frames ;
groups [ num_groups ] . num_frames = 0 ;
groups [ num_groups ] . degrees = atol ( token ) ;
if ( groups [ num_groups ] . degrees < 1 | | groups [ num_groups ] . degrees > 32 )
{
Error ( " Degrees of freedom out of range: %d " , groups [ num_groups ] . degrees ) ;
}
}
void Cmd_FMEndGroup ( void )
{
groups [ num_groups ] . num_frames = fmheader . num_frames - groups [ num_groups ] . start_frame ;
if ( num_groups < MAX_GROUPS - 1 )
{
num_groups + + ;
}
else
{
Error ( " Number of compression groups exceded: %i \n " , MAX_GROUPS ) ;
}
}