fakk2-sdk/source/utils/max2tan/models.c
2000-09-17 00:00:00 +00:00

1075 lines
30 KiB
C

#include <assert.h>
#include "cmdlib.h"
#include "mathlib.h"
#include "qfiles.h"
#include "models.h"
#include "tikidata.h"
#include "calclod.h"
//=================================================================
void OrderMesh( int input[][3], int output[][3], int numTris );
static void OrderSurfaces( void );
static void LoadBase( const char *filename );
q3data g_data;
qboolean g_verbose;
qboolean g_optimize;
qboolean g_lod = qtrue;
qboolean g_clampz;
qboolean g_noorigin;
qboolean g_zeroz;
qboolean g_clearz;
qboolean g_clearxy;
// the command list holds counts, the count * 3 xyz, st, normal indexes
// that are valid for every frame
char g_cddir[1024];
char g_modelname[1024];
/*
** TIKI_ComputeTagFromTri
*/
void TIKI_ComputeTagFromTri( tikiTagData_t *pTag, const float pTri[3][3], const float pbaseTri[3][3] )
{
float len[3];
float origlen[3];
vec3_t axes[3], sides[3];
int longestSide, shortestSide, hypotSide;
int origin;
int j;
float d;
longestSide = 0;
shortestSide = 0;
hypotSide = 0;
origin = 0;
memset( axes, 0, sizeof( axes ) );
memset( sides, 0, sizeof( sides ) );
//
// compute sides
//
for ( j = 0; j < 3; j++ )
{
#if 1
sides[j][0] = pbaseTri[(j+1)%3][0] - pbaseTri[j][0];
sides[j][1] = pbaseTri[(j+1)%3][1] - pbaseTri[j][1];
sides[j][2] = pbaseTri[(j+1)%3][2] - pbaseTri[j][2];
#else
sides[j][0] = pTri[(j+1)%3][0] - pTri[j][0];
sides[j][1] = pTri[(j+1)%3][1] - pTri[j][1];
sides[j][2] = pTri[(j+1)%3][2] - pTri[j][2];
#endif
origlen[ j ] = len[ j ] = ( float ) sqrt( DotProduct( sides[j], sides[j] ) );
}
#if 0
if ( len[0] > len[1] && len[0] > len[2] )
{
longestSide = 0; shortestSide = 1; origin = 2;
}
else if ( len[1] > len[0] && len[1] > len[2] )
{
longestSide = 1; shortestSide = 2; origin = 0;
}
else if ( len[2] > len[0] && len[2] > len[1] )
{
longestSide = 2; shortestSide = 0; origin = 1;
}
else
{
Error( "invalid tag triangle, must be a right triangle with unequal length sides" );
}
#endif
if ( len[0] > len[1] && len[0] > len[2] ) {
hypotSide = 0;
origin = 2;
} else if ( len[1] > len[0] && len[1] > len[2] ) {
hypotSide = 1;
origin = 0;
} else if ( len[2] > len[0] && len[2] > len[1] ) {
hypotSide = 2;
origin = 1;
}
len[hypotSide] = -1;
if ( len[0] > len[1] && len[0] > len[2] ) {
longestSide = 0;
} else if ( len[1] > len[0] && len[1] > len[2] ) {
longestSide = 1;
} else if ( len[2] > len[0] && len[2] > len[1] ) {
longestSide = 2;
}
len[longestSide] = -1;
if ( len[0] > len[1] && len[0] > len[2] ) {
shortestSide = 0;
} else if ( len[1] > len[0] && len[1] > len[2] ) {
shortestSide = 1;
} else if ( len[2] > len[0] && len[2] > len[1] ) {
shortestSide = 2;
}
len[shortestSide] = -1;
//
// compute sides
//
for ( j = 0; j < 3; j++ )
{
sides[j][0] = pTri[(j+1)%3][0] - pTri[j][0];
sides[j][1] = pTri[(j+1)%3][1] - pTri[j][1];
sides[j][2] = pTri[(j+1)%3][2] - pTri[j][2];
}
if ( longestSide == 0 && shortestSide == 1 )
{
VectorNegate( sides[ longestSide ] );
VectorNegate( sides[ shortestSide ] );
}
else if ( longestSide == 0 && shortestSide == 2 )
{
}
else if ( longestSide == 1 && shortestSide == 0 )
{
}
else if ( longestSide == 1 && shortestSide == 2 )
{
VectorNegate( sides[ longestSide ] );
VectorNegate( sides[ shortestSide ] );
}
else if ( longestSide == 2 && shortestSide == 0 )
{
VectorNegate( sides[ longestSide ] );
VectorNegate( sides[ shortestSide ] );
}
else if ( longestSide == 2 && shortestSide == 1 )
{
}
VectorNormalize( sides[longestSide], axes[0] );
VectorNormalize( sides[shortestSide], axes[1] );
// project shortest side so that it is exactly 90 degrees to the longer side
d = DotProduct( axes[1], axes[0] );
VectorMA( axes[1], -d, axes[0], axes[1] );
VectorNormalize( axes[1], axes[1] );
CrossProduct( sides[longestSide], sides[shortestSide], axes[2] );
VectorNormalize( axes[2], axes[2] );
pTag->origin[0] = pTri[origin][0];
pTag->origin[1] = pTri[origin][1];
pTag->origin[2] = pTri[origin][2];
VectorCopy( axes[0], pTag->axis[0] );
VectorCopy( axes[1], pTag->axis[1] );
VectorCopy( axes[2], pTag->axis[2] );
}
//==============================================================
/*
** MyNormalToLatLong
**
** Lat = 0 at (1,0,0) to 360 (-1,0,0), encoded in 8-bit sine table format
** Lng = 0 at (0,0,1) to 180 (0,0,-1), encoded in 8-bit sine table format
**
** Latitude is encoded in high 8 bits
*/
static unsigned short MyNormalToLatLong( vec3_t normal )
{
unsigned short ll;
// check for singularities
if ( normal[0] == 0 && normal[1] == 0 )
{
if ( normal[2] > 0 )
{
ll = 0; // lat = 0, long = 0
}
else
{
ll = 128; // lat = 0, long = 128
}
}
else
{
unsigned short a, b;
a = RAD2DEG( atan2( normal[1], normal[0] ) ) * (255.0f / 360.0f );
a &= 0xff;
b = RAD2DEG( acos( normal[2] ) ) * ( 255.0f / 360.0f );
b &= 0xff;
ll = ( a << 8 ) | b;
}
return ll;
}
/*
===============
ClearModel
===============
*/
void ClearModel (void)
{
int i;
g_data.type = 0;
for ( i = 0; i < TIKI_MAX_SURFACES * 2; i++ )
{
memset( &g_data.surfData[i].header, 0, sizeof( g_data.surfData[i].header ) );
memset( &g_data.surfData[i].shaders, 0, sizeof( g_data.surfData[i].shaders ) );
memset( &g_data.surfData[i].verts, 0, sizeof( g_data.surfData[i].verts ) );
}
memset( g_data.tags, 0, sizeof( g_data.tags ) );
memset( g_data.tagdata, 0, sizeof( g_data.tagdata ) );
memset( g_data.origins, 0, sizeof( g_data.origins ) );
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
int j;
for ( j = 0; j < g_data.model.numFrames; j++ )
{
if ( g_data.surfData[i].verts[j] )
{
free( g_data.surfData[i].verts[j] );
g_data.surfData[i].verts[j] = NULL;
}
}
}
memset (&g_data.model, 0, sizeof(g_data.model));
memset (g_cddir, 0, sizeof(g_cddir));
g_modelname[0] = 0;
g_data.scale_up = 1.0;
memset( &g_data.model, 0, sizeof( g_data.model ) );
VectorCopy (vec3_origin, g_data.adjust);
g_data.fixedwidth = g_data.fixedheight = 0;
g_data.originname[ 0 ] = 0;
}
/*
** void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
**
** This routine assumes that the file position has been adjusted
** properly prior to entry to point at the beginning of the surface.
**
** Since surface header information is completely relative, we can't
** just randomly seek to an arbitrary surface location right now. Is
** this something we should add?
*/
void WriteModelSurface( FILE *modelouthandle, md3SurfaceData_t *pSurfData )
{
tikiSurface_t *pSurf = &pSurfData->header;
md3Shader_t *pShader = pSurfData->shaders;
baseVertex_t *pBaseVertex = pSurfData->baseVertexes;
float **verts = pSurfData->verts;
short xyznormals[TIKI_MAX_VERTS][4];
float base_st[TIKI_MAX_VERTS][2];
tikiSurface_t surftemp;
int f, i, j, k, index;
if (
( strstr( pSurf->name, "tag_" ) == pSurf->name ) ||
( !strcmpi( g_data.originname, pSurf->name ) )
)
return;
//
// write out the header
//
surftemp = *pSurf;
surftemp.ident = LittleLong( TIKI_ANIM_IDENT );
surftemp.numFrames = LittleLong( pSurf->numFrames );
surftemp.minLod = LittleLong( pSurf->minLod );
surftemp.ofsTriangles = LittleLong( pSurf->ofsTriangles );
surftemp.numTriangles = LittleLong( pSurf->numTriangles );
surftemp.ofsCollapseMap = LittleLong( pSurf->ofsCollapseMap );
surftemp.ofsSt = LittleLong( pSurf->ofsSt );
surftemp.ofsXyzNormals = LittleLong( pSurf->ofsXyzNormals );
surftemp.ofsEnd = LittleLong( pSurf->ofsEnd );
SafeWrite( modelouthandle, &surftemp, sizeof( surftemp ) );
if ( g_verbose )
{
printf( "surface '%s'\n", pSurf->name );
}
//
// write out the triangles
//
for ( i = 0 ; i < pSurf->numTriangles ; i++ )
{
for (j = 0 ; j < 3 ; j++)
{
if ( g_lod )
{
int ivalue = LittleLong( pSurfData->permutationMap[ pSurfData->orderedTriangles[i][j] ] );
pSurfData->orderedTriangles[i][j] = ivalue;
}
else
{
int ivalue = LittleLong( pSurfData->orderedTriangles[i][j] );
pSurfData->orderedTriangles[i][j] = ivalue;
}
}
}
SafeWrite( modelouthandle, pSurfData->orderedTriangles, pSurf->numTriangles * sizeof( g_data.surfData[0].orderedTriangles[0] ) );
if ( g_verbose )
{
printf( "\n...num verts: %d\n", pSurf->numVerts );
printf( "...COLLAPSE MAP\n" );
}
//
// write out the collapse map
//
for ( i = 0 ; i < pSurf->numVerts; i++ )
{
{
if ( g_lod )
{
int ivalue = LittleLong( pSurfData->collapseMap[i] );
pSurfData->collapseMap[i] = ivalue;
}
else
{
pSurfData->collapseMap[i] = LittleLong( i );;
}
}
}
SafeWrite( modelouthandle, pSurfData->collapseMap, pSurf->numVerts * sizeof( g_data.surfData[0].collapseMap[0] ) );
if ( g_verbose )
{
printf( "...TEX COORDINATES\n" );
}
//
// write out the texture coordinates
//
for ( i = 0; i < pSurf->numVerts ; i++) {
if ( g_lod )
{
// reorder the verts for lod as we write them out
index = pSurfData->permutationMap[ i ];
base_st[index][0] = LittleFloat( pBaseVertex[i].st[0] );
base_st[index][1] = LittleFloat( pBaseVertex[i].st[1] );
if ( g_verbose )
printf( "......%d: %f,%f\n", i, base_st[index][0], base_st[index][1] );
}
else
{
base_st[i][0] = LittleFloat( pBaseVertex[i].st[0] );
base_st[i][1] = LittleFloat( pBaseVertex[i].st[1] );
if ( g_verbose )
printf( "......%d: %f,%f\n", i, base_st[i][0], base_st[i][1] );
}
}
SafeWrite( modelouthandle, base_st, pSurf->numVerts * sizeof(base_st[0]));
//
// write the xyz_normal
//
if ( g_verbose )
printf( "...XYZNORMALS\n" );
for ( f = 0; f < g_data.model.numFrames; f++ )
{
for (j=0 ; j< pSurf->numVerts; j++)
{
float fvalue;
short value;
if ( g_lod )
{
// reorder the verts for lod as we write them out
index = pSurfData->permutationMap[ j ];
}
else
{
index = j;
}
for (k=0 ; k < 3 ; k++)
{
fvalue = verts[f][j*6+k] - g_data.frames[ f ].offset[ k ];
value = ( short ) ( fvalue / g_data.frames[ f ].scale[ k ] );
xyznormals[index][k] = LittleShort( value );
}
value = MyNormalToLatLong( &verts[f][j*6+3] );
xyznormals[index][3] = LittleShort( value );
}
SafeWrite( modelouthandle, xyznormals, pSurf->numVerts * sizeof( short ) * 4 );
}
}
int numRealSurfaces;
/*
** void WriteModelFile( FILE *modelouthandle )
**
** CHUNK SIZE
** header sizeof( md3Header_t )
** frames sizeof( tikiFrame_t ) * numFrames
** tags sizeof( tikiTag_t ) * numTags + sizeof( tikiTagData_t ) * numFrames * numTags
** surfaces surfaceSum
*/
void WriteModelFile( FILE *modelouthandle )
{
int f, t;
int i, j;
tikiHeader_t modeltemp;
long surfaceSum = 0;
int numFrames = g_data.model.numFrames;
int ofstemp;
vec3_t totaldelta;
numRealSurfaces = 0 ;
// compute offsets for all surfaces, sum their total size
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
if (
( strstr( g_data.surfData[i].header.name, "tag_" ) != g_data.surfData[i].header.name ) &&
( strcmpi( g_data.originname, g_data.surfData[i].header.name ) )
)
{
tikiSurface_t *psurf = &g_data.surfData[i].header;
if ( psurf->numTriangles == 0 || psurf->numVerts == 0 )
continue;
//
// the triangle and vertex split threshold is controlled by a parameter
// to $base, a la $base blah.3ds 1900, where "1900" determines the number
// of triangles to split on
//
else if ( psurf->numVerts > MAX_SURFACE_VERTS )
{
Error( "too many vertices\n" );
}
psurf->numFrames = numFrames;
if ( psurf->numTriangles > MAX_SURFACE_TRIS )
{
Error( "too many faces\n" );
}
psurf->ofsTriangles = sizeof( tikiSurface_t );
psurf->ofsCollapseMap = psurf->ofsTriangles + psurf->numTriangles * sizeof( md3Triangle_t );
psurf->ofsSt = psurf->ofsCollapseMap + psurf->numVerts * sizeof( int );
psurf->ofsXyzNormals = psurf->ofsSt + psurf->numVerts * sizeof( md3St_t );
psurf->ofsEnd = psurf->ofsXyzNormals + psurf->numFrames * psurf->numVerts * ( sizeof( short ) * 4 );
surfaceSum += psurf->ofsEnd;
numRealSurfaces++;
}
}
g_data.model.ident = TIKI_ANIM_IDENT;
g_data.model.version = TIKI_ANIM_VERSION;
g_data.model.ofsFrames = sizeof(tikiHeader_t);
ofstemp = g_data.model.ofsFrames + numFrames*sizeof(tikiFrame_t);
for ( t = 0; t < g_data.model.numTags; t++ )
{
g_data.model.ofsTags[ t ] = ofstemp;
ofstemp += sizeof(tikiTag_t);
ofstemp += numFrames*sizeof(tikiTagData_t);
}
g_data.model.ofsSurfaces = ofstemp;
g_data.model.ofsEnd = g_data.model.ofsSurfaces + surfaceSum;
// was it optimized
//g_data.model.optimized = g_optimize;
// calculate total delta
VectorSubtract( g_data.origins[ numFrames - 1 ], g_data.origins[ 0 ], g_data.model.totaldelta );
// calculate total time
g_data.model.totaltime = ( float )numFrames / g_data.framerate;
//
// write out the model header
//
modeltemp = g_data.model;
modeltemp.ident = LittleLong( modeltemp.ident );
modeltemp.version = LittleLong( modeltemp.version );
modeltemp.numFrames = LittleLong( modeltemp.numFrames );
modeltemp.numTags = LittleLong( modeltemp.numTags );
modeltemp.numSurfaces = LittleLong( numRealSurfaces );
modeltemp.ofsFrames = LittleLong( modeltemp.ofsFrames );
for ( t = 0; t < g_data.model.numTags; t++ )
{
modeltemp.ofsTags[ t ] = LittleLong( modeltemp.ofsTags[ t ] );
}
modeltemp.ofsSurfaces = LittleLong( modeltemp.ofsSurfaces );
modeltemp.ofsEnd = LittleLong( modeltemp.ofsEnd );
modeltemp.totaltime = LittleFloat( modeltemp.totaltime );
for ( i = 0; i < 3; i++ )
{
modeltemp.totaldelta[ i ] = LittleFloat( modeltemp.totaldelta[ i ] );
}
SafeWrite (modelouthandle, &modeltemp, sizeof(modeltemp));
// clear out totaldelta
VectorClear( totaldelta );
//
// write out the frames
//
for (i=0 ; i < numFrames ; i++)
{
vec3_t tmpVec;
float maxRadius = 0;
//
// compute localOrigin and radius
//
for ( j = 0; j < 8; j++ )
{
tmpVec[0] = g_data.frames[i].bounds[(j&1)!=0][0];
tmpVec[1] = g_data.frames[i].bounds[(j&2)!=0][1];
tmpVec[2] = g_data.frames[i].bounds[(j&4)!=0][2];
if ( VectorLength( tmpVec ) > maxRadius )
maxRadius = VectorLength( tmpVec );
}
g_data.frames[i].radius = LittleFloat( maxRadius );
g_data.frames[i].frametime = LittleFloat( g_data.frames[i].frametime );
for (j=0 ; j<3 ; j++)
{
g_data.frames[i].scale[j] = ( g_data.frames[i].bounds[1][j] - g_data.frames[i].bounds[0][j] ) / 65535.0f;
g_data.frames[i].offset[j] = g_data.frames[i].bounds[0][j];
}
// calculate deltas
if ( i < ( numFrames - 1 ) )
{
for (j=0 ; j<3 ; j++)
{
g_data.frames[i].delta[j] = g_data.origins[ i + 1 ][ j ] - g_data.origins[ i ][ j ];
}
VectorAdd( totaldelta, g_data.frames[ i ].delta, totaldelta );
}
else
{
// fudge the last frame
if ( numFrames > 1 )
{
VectorScale( totaldelta, 1.0f / ( numFrames - 1 ), g_data.frames[i].delta );
}
else
{
VectorClear( totaldelta );
}
}
// swap
for (j=0 ; j<3 ; j++) {
g_data.frames[i].scale[j] = LittleFloat( g_data.frames[i].scale[j] );
g_data.frames[i].offset[j] = LittleFloat( g_data.frames[i].offset[j] );
g_data.frames[i].delta[j] = LittleFloat( g_data.frames[i].delta[j] );
g_data.frames[i].bounds[0][j] = LittleFloat( g_data.frames[i].bounds[0][j] );
g_data.frames[i].bounds[1][j] = LittleFloat( g_data.frames[i].bounds[1][j] );
}
}
fseek (modelouthandle, g_data.model.ofsFrames, SEEK_SET);
SafeWrite( modelouthandle, g_data.frames, numFrames * sizeof(g_data.frames[0]) );
//
// write out the tags
//
for ( t = 0; t < g_data.model.numTags; t++ )
{
for (f=0 ; f<g_data.model.numFrames; f++)
{
g_data.tagdata[t][f].origin[0] = LittleFloat(g_data.tagdata[t][f].origin[0]);
g_data.tagdata[t][f].origin[1] = LittleFloat(g_data.tagdata[t][f].origin[1]);
g_data.tagdata[t][f].origin[2] = LittleFloat(g_data.tagdata[t][f].origin[2]);
for (j=0 ; j<3 ; j++)
{
g_data.tagdata[t][f].axis[0][j] = LittleFloat(g_data.tagdata[t][f].axis[0][j]);
g_data.tagdata[t][f].axis[1][j] = LittleFloat(g_data.tagdata[t][f].axis[1][j]);
g_data.tagdata[t][f].axis[2][j] = LittleFloat(g_data.tagdata[t][f].axis[2][j]);
}
}
fseek( modelouthandle, g_data.model.ofsTags[ t ], SEEK_SET );
SafeWrite( modelouthandle, &g_data.tags[t], sizeof(tikiTag_t) );
SafeWrite( modelouthandle, g_data.tagdata[t], g_data.model.numFrames * sizeof(tikiTagData_t) );
}
//
// fixup the scale and offset parameters
//
for (i=0 ; i < numFrames ; i++)
{
// swap
for (j=0 ; j<3 ; j++)
{
g_data.frames[i].scale[j] = LittleFloat( g_data.frames[i].scale[j] );
g_data.frames[i].offset[j] = LittleFloat( g_data.frames[i].offset[j] );
}
}
//
// write out the surfaces
//
fseek( modelouthandle, g_data.model.ofsSurfaces, SEEK_SET );
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
WriteModelSurface( modelouthandle, &g_data.surfData[i] );
}
}
/*
===============
FinishModel
===============
*/
void FinishModel ( void )
{
FILE *modelouthandle;
char name[1024];
if (!g_data.model.numFrames)
return;
//
// build generalized triangle strips
//
OrderSurfaces();
sprintf (name, "%s%s", writedir, g_modelname);
//
// write the model output file
//
if ( g_verbose )
printf ("saving to %s\n", name);
CreatePath (name);
modelouthandle = SafeOpenWrite (name);
WriteModelFile (modelouthandle);
if ( g_verbose )
{
printf ("%4d surfaces\n", numRealSurfaces);
printf ("%4d frames\n", g_data.model.numFrames);
printf ("%4d tags\n", g_data.model.numTags);
printf ("file size: %d\n", (int)ftell (modelouthandle) );
printf ("---------------------\n");
}
fclose (modelouthandle);
}
/*
** OrderSurfaces
**
** Reorders triangles in all the surfaces.
*/
static void OrderSurfaces( void )
{
int s;
// go through each surface and find best strip/fans possible
for ( s = 0; s < g_data.model.numSurfaces; s++ )
{
int mesh[TIKI_MAX_TRIANGLES][3];
int i;
if (
( strstr( g_data.surfData[ s ].header.name, "tag_" ) == g_data.surfData[ s ].header.name ) ||
( !strcmpi( g_data.originname, g_data.surfData[ s ].header.name ) )
)
continue;
if ( g_verbose )
{
printf( "\nstripifying surface %16s %d/%d with %d tris\n", g_data.surfData[ s ].header.name, s+1, g_data.model.numSurfaces, g_data.surfData[s].header.numTriangles );
}
for ( i = 0; i < g_data.surfData[s].header.numTriangles; i++ )
{
mesh[i][0] = g_data.surfData[s].lodTriangles[i][0];
mesh[i][1] = g_data.surfData[s].lodTriangles[i][1];
mesh[i][2] = g_data.surfData[s].lodTriangles[i][2];
}
if ( g_optimize )
{
OrderMesh( mesh, // input
g_data.surfData[s].orderedTriangles, // output
g_data.surfData[s].header.numTriangles );
}
else
{
memcpy( g_data.surfData[s].orderedTriangles, mesh, sizeof( int ) * 3 * g_data.surfData[s].header.numTriangles );
}
}
}
/*
===============================================================
BASE FRAME SETUP
===============================================================
*/
static void BuildBaseFrame( const char *filename, baseframe_t * base )
{
int i, j, t;
// calculate the base triangles
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
for ( t = 0; t < base->surfaces[ i ].numtriangles; t++ )
{
for( j = 0; j < 3; j++ )
{
g_data.surfData[ i ].baseTriangles[ t ].v[ j ].index = base->surfaces[ i ].triangles[ t ].verts[ j ];
g_data.surfData[ i ].lodTriangles[ t ][ j ] = base->surfaces[ i ].triangles[ t ].verts[ j ];
}
}
strcpy( g_data.surfData[i].header.name, base->surfaces[ i ].name );
g_data.surfData[i].header.numTriangles = base->surfaces[ i ].numtriangles;
g_data.surfData[i].header.numVerts = base->surfaces[ i ].numvertices;
for( j = 0; j < base->surfaces[ i ].numvertices; j++ )
{
g_data.surfData[ i ].baseVertexes[ j ].index = base->surfaces[ i ].triverts[ j ].map;
g_data.surfData[ i ].baseVertexes[ j ].st[ 0 ] = base->surfaces[ i ].triverts[ j ].tex[ 0 ];
g_data.surfData[ i ].baseVertexes[ j ].st[ 1 ] = base->surfaces[ i ].triverts[ j ].tex[ 1 ];
VectorCopy( base->surfaces[ i ].triverts[ j ].xyz.xyz, g_data.surfData[ i ].baseVertexes[ j ].xyz );
VectorCopy( base->surfaces[ i ].triverts[ j ].xyz.normal, g_data.surfData[ i ].baseVertexes[ j ].normal );
}
CalculateLOD( &g_data.surfData[i] );
}
//
// find tags
//
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
if (
( strcmpi( g_data.originname, g_data.surfData[ i ].header.name ) ) &&
( strstr( g_data.surfData[ i ].header.name, "tag_" ) == g_data.surfData[ i ].header.name )
)
{
if ( g_data.surfData[ i ].header.numTriangles != 1 )
{
Error( "tag polysets must consist of only one triangle, tag %s", g_data.surfData[ i ].header.name );
}
if ( g_verbose )
{
printf( "found tag '%s'\n", g_data.surfData[ i ].header.name );
}
g_data.model.numTags++;
}
}
}
static void BuildAnimation
(
const char *filename,
baseframe_t * base,
animframe_t * anim,
int numFrames,
float framerate
)
{
int f, i, j, t, tagcount, index;
float *frameXyz;
float *frameNormals;
g_data.model.numSurfaces = base->numSurfaces;
g_data.model.numFrames = numFrames;
if ( g_data.model.numFrames >= TIKI_MAX_FRAMES)
Error ("model.numFrames >= TIKI_MAX_FRAMES");
// build base frame
BuildBaseFrame( filename, base );
// build animation frames
for ( f = 0; f < numFrames; f++ )
{
animframe_t *panim = &anim[f];
qboolean parentTagExists = qfalse;
tikiTagData_t tagParent;
int numtags = 0;
tikiFrame_t *fr;
fr = &g_data.frames[f];
// set frame time
fr->frametime = 1.0f / framerate;
VectorClear( g_data.origins[ f ] );
//
// find and count tags, locate parent tag
//
for ( i = 0; i < g_data.model.numSurfaces; i++ )
{
if (
( !strcmpi( g_data.originname, g_data.surfData[ i ].header.name ) ) ||
( strstr( g_data.surfData[ i ].header.name, "tag_" ) == g_data.surfData[ i ].header.name )
)
{
// store off origin info
if ( !strcmpi( g_data.originname, g_data.surfData[ i ].header.name ) )
{
float basetri[3][3];
float tri[3][3];
if ( parentTagExists )
Error( "Multiple parent tags not allowed" );
for( j = 0; j < 3; j++ )
{
index = g_data.surfData[ i ].baseVertexes[ g_data.surfData[ i ].baseTriangles[ 0 ].v[ j ].index ].index;
VectorCopy( panim->verts[ index ].xyz, tri[ j ] );
}
for( j = 0; j < 3; j++ )
{
VectorCopy( g_data.surfData[ i ].baseVertexes[ g_data.surfData[ i ].baseTriangles[ 0 ].v[ j ].index ].xyz, basetri[ j ] );
}
TIKI_ComputeTagFromTri( &tagParent, tri, basetri );
//strcpy( tagParent.name, "origin" );
//g_data.tags[f][numtags] = tagParent;
parentTagExists = qtrue;
if ( g_clampz && tagParent.origin[ 2 ] < 0 )
{
tagParent.origin[ 2 ] = 0;
}
if ( g_zeroz )
{
tagParent.origin[ 2 ] = 0;
}
if ( g_noorigin )
{
parentTagExists = qfalse;
VectorClear( tagParent.origin );
}
VectorCopy( tagParent.origin, g_data.origins[ f ] );
if ( g_clearxy )
{
// subtract forward and side movement from deltas
g_data.origins[ f ][ 0 ] = 0;
g_data.origins[ f ][ 1 ] = 0;
}
if ( g_clearz )
{
g_data.origins[ f ][ 2 ] = 0;
}
}
else
{
float basetri[3][3];
float tri[3][3];
for( j = 0; j < 3; j++ )
{
index = g_data.surfData[ i ].baseVertexes[ g_data.surfData[ i ].baseTriangles[ 0 ].v[ j ].index ].index;
VectorCopy( panim->verts[ index ].xyz, tri[ j ] );
}
for( j = 0; j < 3; j++ )
{
VectorCopy( g_data.surfData[ i ].baseVertexes[ g_data.surfData[ i ].baseTriangles[ 0 ].v[ j ].index ].xyz, basetri[ j ] );
}
TIKI_ComputeTagFromTri( &g_data.tagdata[numtags][f], tri, basetri );
strcpy( g_data.tags[numtags].name, g_data.surfData[ i ].header.name );
numtags++;
}
}
}
if ( numtags != g_data.model.numTags )
{
Error( "mismatched number of tags in frame(%d) vs. base(%d)", numtags, g_data.model.numTags );
}
//
// prepare to accumulate bounds and normals
//
ClearBounds( fr->bounds[0], fr->bounds[1] );
//
// 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
//
//
// parent tag adjust
//
if ( parentTagExists )
{
for ( t = 0; t < panim->numverts; t++ )
{
vec3_t tmp;
#if 1
VectorSubtract( panim->verts[ t ].xyz, tagParent.origin, tmp );
panim->verts[ t ].xyz[0] = DotProduct( tmp, tagParent.axis[0] );
panim->verts[ t ].xyz[1] = DotProduct( tmp, tagParent.axis[1] );
panim->verts[ t ].xyz[2] = DotProduct( tmp, tagParent.axis[2] );
#else
VectorCopy( panim->verts[ t ].xyz, tmp );
panim->verts[ t ].xyz[0] = DotProduct( tmp, tagParent.axis[0] );
panim->verts[ t ].xyz[1] = DotProduct( tmp, tagParent.axis[1] );
panim->verts[ t ].xyz[2] = DotProduct( tmp, tagParent.axis[2] );
VectorSubtract( panim->verts[ t ].xyz, tagParent.origin, panim->verts[ t ].xyz );
#endif
VectorCopy( panim->verts[ t ].normal, tmp );
panim->verts[ t ].normal[0] = DotProduct( tmp, tagParent.axis[0] );
panim->verts[ t ].normal[1] = DotProduct( tmp, tagParent.axis[1] );
panim->verts[ t ].normal[2] = DotProduct( tmp, tagParent.axis[2] );
}
}
for ( i = 0, tagcount = 0; i < g_data.model.numSurfaces; i++ )
{
int t;
//
// compute tag data
//
if (
( strcmpi( g_data.originname, g_data.surfData[ i ].header.name ) ) &&
( strstr( g_data.surfData[ i ].header.name, "tag_" ) == g_data.surfData[ i ].header.name )
)
{
tikiTag_t *pTag = &g_data.tags[tagcount];
tikiTagData_t *pTagData = &g_data.tagdata[tagcount][f];
float tri[3][3];
float basetri[3][3];
strcpy( pTag->name, g_data.surfData[ i ].header.name );
for( j = 0; j < 3; j++ )
{
index = g_data.surfData[ i ].baseVertexes[ g_data.surfData[ i ].baseTriangles[ 0 ].v[ j ].index ].index;
VectorCopy( panim->verts[ index ].xyz, tri[ j ] );
}
for( j = 0; j < 3; j++ )
{
VectorCopy( g_data.surfData[ i ].baseVertexes[ g_data.surfData[ i ].baseTriangles[ 0 ].v[ j ].index ].xyz, basetri[ j ] );
}
TIKI_ComputeTagFromTri( pTagData, tri, basetri );
tagcount++;
}
else
{
if ( g_data.surfData[i].verts[f] )
free( g_data.surfData[i].verts[f] );
frameXyz = g_data.surfData[i].verts[f] = calloc( 1, sizeof( float ) * 6 * g_data.surfData[i].header.numVerts );
frameNormals = frameXyz + 3;
for ( t = 0; t < g_data.surfData[ i ].header.numVerts; t++ )
{
int index;
index = g_data.surfData[ i ].baseVertexes[ t ].index;
frameXyz[t*6+0] = panim->verts[ index ].xyz[0];
frameXyz[t*6+1] = panim->verts[ index ].xyz[1];
frameXyz[t*6+2] = panim->verts[ index ].xyz[2];
frameNormals[t*6+0] = panim->verts[ index ].normal[0];
frameNormals[t*6+1] = panim->verts[ index ].normal[1];
frameNormals[t*6+2] = panim->verts[ index ].normal[2];
AddPointToBounds (&frameXyz[t*6], fr->bounds[0], fr->bounds[1] );
}
}
}
}
// set name
strcpy (g_modelname, filename);
strcpy (g_data.model.name, g_modelname);
FinishModel();
ClearModel();
}
void ConvertAnim
(
const char *filename,
int numFrames,
float framerate,
qboolean optimize,
baseframe_t * base,
animframe_t * anim,
const char *originname,
qboolean dolod,
qboolean clampz,
qboolean zeroz,
qboolean clearz,
qboolean clearxy,
qboolean noorigin,
qboolean verbose
)
{
char outfilename[1024];
ClearModel();
strcpy( g_data.originname, originname );
g_lod = dolod;
g_optimize = optimize;
g_verbose = verbose;
g_clampz = clampz;
g_zeroz = zeroz;
g_clearz = clearz;
g_clearxy = clearxy;
g_noorigin = noorigin;
g_data.framerate = framerate;
strcpy( outfilename, filename );
// create destination filename
StripExtension( outfilename );
strcat( outfilename, ".tan" );
BuildAnimation( outfilename, base, anim, numFrames, framerate );
}