You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3272 lines
85 KiB
3272 lines
85 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <sys/stat.h> |
|
#include <math.h> |
|
#include "filesystem_tools.h" |
|
#include "cmdlib.h" |
|
#include "scriplib.h" |
|
#include "mathlib/mathlib.h" |
|
#define EXTERN |
|
#include "studio.h" |
|
#include "motionmapper.h" |
|
#include "tier1/strtools.h" |
|
#include "tier0/icommandline.h" |
|
#include "utldict.h" |
|
#include <windows.h> |
|
#include "UtlBuffer.h" |
|
#include "utlsymbol.h" |
|
|
|
bool g_quiet = false; |
|
bool g_verbose = false; |
|
char g_outfile[1024]; |
|
bool uselogfile = false; |
|
|
|
char g_szFilename[1024]; |
|
FILE *g_fpInput; |
|
char g_szLine[4096]; |
|
int g_iLinecount; |
|
|
|
bool g_bZBrush = false; |
|
bool g_bGaveMissingBoneWarning = false; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : depth - |
|
// *fmt - |
|
// ... - |
|
//----------------------------------------------------------------------------- |
|
void vprint( int depth, const char *fmt, ... ) |
|
{ |
|
char string[ 8192 ]; |
|
va_list va; |
|
va_start( va, fmt ); |
|
V_vsprintf_safe( string, fmt, va ); |
|
va_end( va ); |
|
|
|
FILE *fp = NULL; |
|
|
|
if ( uselogfile ) |
|
{ |
|
fp = fopen( "log.txt", "ab" ); |
|
} |
|
|
|
while ( depth-- > 0 ) |
|
{ |
|
vprint( 0, " " ); |
|
OutputDebugString( " " ); |
|
if ( fp ) |
|
{ |
|
fprintf( fp, " " ); |
|
} |
|
} |
|
|
|
::printf( "%s", string ); |
|
OutputDebugString( string ); |
|
|
|
if ( fp ) |
|
{ |
|
char *p = string; |
|
while ( *p ) |
|
{ |
|
if ( *p == '\n' ) |
|
{ |
|
fputc( '\r', fp ); |
|
} |
|
fputc( *p, fp ); |
|
p++; |
|
} |
|
fclose( fp ); |
|
} |
|
} |
|
|
|
|
|
int k_memtotal; |
|
void *kalloc( int num, int size ) |
|
{ |
|
// vprint( 0, "calloc( %d, %d )\n", num, size ); |
|
// vprint( 0, "%d ", num * size ); |
|
k_memtotal += num * size; |
|
return calloc( num, size ); |
|
} |
|
|
|
void kmemset( void *ptr, int value, int size ) |
|
{ |
|
// vprint( 0, "kmemset( %x, %d, %d )\n", ptr, value, size ); |
|
memset( ptr, value, size ); |
|
return; |
|
} |
|
|
|
static bool g_bFirstWarning = true; |
|
|
|
void MdlWarning( const char *fmt, ... ) |
|
{ |
|
va_list args; |
|
static char output[1024]; |
|
|
|
if (g_quiet) |
|
{ |
|
if (g_bFirstWarning) |
|
{ |
|
vprint( 0, "%s :\n", fullpath ); |
|
g_bFirstWarning = false; |
|
} |
|
vprint( 0, "\t"); |
|
} |
|
|
|
vprint( 0, "WARNING: "); |
|
va_start( args, fmt ); |
|
vprint( 0, fmt, args ); |
|
} |
|
|
|
|
|
void MdlError( char const *fmt, ... ) |
|
{ |
|
va_list args; |
|
|
|
if (g_quiet) |
|
{ |
|
if (g_bFirstWarning) |
|
{ |
|
vprint( 0, "%s :\n", fullpath ); |
|
g_bFirstWarning = false; |
|
} |
|
vprint( 0, "\t"); |
|
} |
|
|
|
vprint( 0, "ERROR: "); |
|
va_start( args, fmt ); |
|
vprint( 0, fmt, args ); |
|
|
|
exit( -1 ); |
|
} |
|
|
|
int OpenGlobalFile( char *src ) |
|
{ |
|
int time1; |
|
char filename[1024]; |
|
|
|
// local copy of string |
|
strcpy( filename, ExpandPath( src ) ); |
|
|
|
// Ummm, path sanity checking |
|
int pathLength; |
|
int numBasePaths = CmdLib_GetNumBasePaths(); |
|
// This is kinda gross. . . doing the same work in cmdlib on SafeOpenRead. |
|
if( CmdLib_HasBasePath( filename, pathLength ) ) |
|
{ |
|
char tmp[1024]; |
|
int i; |
|
for( i = 0; i < numBasePaths; i++ ) |
|
{ |
|
strcpy( tmp, CmdLib_GetBasePath( i ) ); |
|
strcat( tmp, filename + pathLength ); |
|
|
|
time1 = FileTime( tmp ); |
|
if( time1 != -1 ) |
|
{ |
|
if ((g_fpInput = fopen(tmp, "r")) == 0) |
|
{ |
|
MdlWarning( "reader: could not open file '%s'\n", src ); |
|
return 0; |
|
} |
|
else |
|
{ |
|
return 1; |
|
} |
|
} |
|
} |
|
return 0; |
|
} |
|
else |
|
{ |
|
time1 = FileTime (filename); |
|
if (time1 == -1) |
|
return 0; |
|
|
|
// Whoohooo, FOPEN! |
|
if ((g_fpInput = fopen(filename, "r")) == 0) |
|
{ |
|
MdlWarning( "reader: could not open file '%s'\n", src ); |
|
return 0; |
|
} |
|
|
|
return 1; |
|
} |
|
} |
|
|
|
bool IsEnd( char const* pLine ) |
|
{ |
|
if (strncmp( "end", pLine, 3 ) != 0) |
|
return false; |
|
return (pLine[3] == '\0') || (pLine[3] == '\n'); |
|
} |
|
|
|
|
|
//Wrong name for the use of it. |
|
void scale_vertex( Vector &org ) |
|
{ |
|
org[0] = org[0] * g_currentscale; |
|
org[1] = org[1] * g_currentscale; |
|
org[2] = org[2] * g_currentscale; |
|
} |
|
|
|
|
|
void clip_rotations( RadianEuler& rot ) |
|
{ |
|
int j; |
|
// clip everything to : -M_PI <= x < M_PI |
|
|
|
for (j = 0; j < 3; j++) { |
|
while (rot[j] >= M_PI) |
|
rot[j] -= M_PI*2; |
|
while (rot[j] < -M_PI) |
|
rot[j] += M_PI*2; |
|
} |
|
} |
|
|
|
|
|
void clip_rotations( Vector& rot ) |
|
{ |
|
int j; |
|
// clip everything to : -180 <= x < 180 |
|
|
|
for (j = 0; j < 3; j++) { |
|
while (rot[j] >= 180) |
|
rot[j] -= 180*2; |
|
while (rot[j] < -180) |
|
rot[j] += 180*2; |
|
} |
|
} |
|
|
|
|
|
void Build_Reference( s_source_t *psource) |
|
{ |
|
int i, parent; |
|
Vector angle; |
|
|
|
for (i = 0; i < psource->numbones; i++) |
|
{ |
|
matrix3x4_t m; |
|
AngleMatrix( psource->rawanim[0][i].rot, m ); |
|
m[0][3] = psource->rawanim[0][i].pos[0]; |
|
m[1][3] = psource->rawanim[0][i].pos[1]; |
|
m[2][3] = psource->rawanim[0][i].pos[2]; |
|
|
|
parent = psource->localBone[i].parent; |
|
if (parent == -1) |
|
{ |
|
// scale the done pos. |
|
// calc rotational matrices |
|
MatrixCopy( m, psource->boneToPose[i] ); |
|
} |
|
else |
|
{ |
|
// calc compound rotational matrices |
|
// FIXME : Hey, it's orthogical so inv(A) == transpose(A) |
|
ConcatTransforms( psource->boneToPose[parent], m, psource->boneToPose[i] ); |
|
} |
|
// vprint( 0, "%3d %f %f %f\n", i, psource->bonefixup[i].worldorg[0], psource->bonefixup[i].worldorg[1], psource->bonefixup[i].worldorg[2] ); |
|
/* |
|
AngleMatrix( angle, m ); |
|
vprint( 0, "%8.4f %8.4f %8.4f\n", m[0][0], m[1][0], m[2][0] ); |
|
vprint( 0, "%8.4f %8.4f %8.4f\n", m[0][1], m[1][1], m[2][1] ); |
|
vprint( 0, "%8.4f %8.4f %8.4f\n", m[0][2], m[1][2], m[2][2] ); |
|
*/ |
|
} |
|
} |
|
|
|
int Grab_Nodes( s_node_t *pnodes ) |
|
{ |
|
// |
|
// s_node_t structure: index is index!! |
|
// |
|
int index; |
|
char name[1024]; |
|
int parent; |
|
int numbones = 0; |
|
|
|
// Init parent to none |
|
for (index = 0; index < MAXSTUDIOSRCBONES; index++) |
|
{ |
|
pnodes[index].parent = -1; |
|
} |
|
|
|
// March through nodes lines |
|
while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) |
|
{ |
|
g_iLinecount++; |
|
// get tokens |
|
if (sscanf( g_szLine, "%d \"%[^\"]\" %d", &index, name, &parent ) == 3) |
|
{ |
|
// check for duplicated bones |
|
/* |
|
if (strlen(pnodes[index].name) != 0) |
|
{ |
|
MdlError( "bone \"%s\" exists more than once\n", name ); |
|
} |
|
*/ |
|
// copy name to struct array |
|
V_strcpy_safe( pnodes[index].name, name ); |
|
// set parent into struct array |
|
pnodes[index].parent = parent; |
|
// increment numbones |
|
if (index > numbones) |
|
{ |
|
numbones = index; |
|
} |
|
} |
|
else |
|
{ |
|
return numbones + 1; |
|
} |
|
} |
|
MdlError( "Unexpected EOF at line %d\n", g_iLinecount ); |
|
return 0; |
|
} |
|
|
|
void Grab_Vertexanimation( s_source_t *psource ) |
|
{ |
|
char cmd[1024]; |
|
int index; |
|
Vector pos; |
|
Vector normal; |
|
int t = -1; |
|
int count = 0; |
|
static s_vertanim_t tmpvanim[MAXSTUDIOVERTS*4]; |
|
|
|
while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) |
|
{ |
|
g_iLinecount++; |
|
if (sscanf( g_szLine, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &normal[0], &normal[1], &normal[2] ) == 7) |
|
{ |
|
if (psource->startframe < 0) |
|
{ |
|
MdlError( "Missing frame start(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
|
|
if (t < 0) |
|
{ |
|
MdlError( "VTA Frame Sync (%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
|
|
tmpvanim[count].vertex = index; |
|
VectorCopy( pos, tmpvanim[count].pos ); |
|
VectorCopy( normal, tmpvanim[count].normal ); |
|
count++; |
|
|
|
if (index >= psource->numvertices) |
|
psource->numvertices = index + 1; |
|
} |
|
else |
|
{ |
|
// flush data |
|
|
|
if (count) |
|
{ |
|
psource->numvanims[t] = count; |
|
|
|
psource->vanim[t] = (s_vertanim_t *)kalloc( count, sizeof( s_vertanim_t ) ); |
|
|
|
memcpy( psource->vanim[t], tmpvanim, count * sizeof( s_vertanim_t ) ); |
|
} |
|
else if (t > 0) |
|
{ |
|
psource->numvanims[t] = 0; |
|
} |
|
|
|
// next command |
|
if (sscanf( g_szLine, "%1023s %d", cmd, &index )) |
|
{ |
|
if (strcmp( cmd, "time" ) == 0) |
|
{ |
|
t = index; |
|
count = 0; |
|
|
|
if (t < psource->startframe) |
|
{ |
|
MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
if (t > psource->endframe) |
|
{ |
|
MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
|
|
t -= psource->startframe; |
|
} |
|
else if (strcmp( cmd, "end") == 0) |
|
{ |
|
psource->numframes = psource->endframe - psource->startframe + 1; |
|
return; |
|
} |
|
else |
|
{ |
|
MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
|
|
} |
|
else |
|
{ |
|
MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
} |
|
} |
|
MdlError( "unexpected EOF: %s\n", psource->filename ); |
|
} |
|
|
|
void Grab_Animation( s_source_t *psource ) |
|
{ |
|
Vector pos; |
|
RadianEuler rot; |
|
char cmd[1024]; |
|
int index; |
|
int t = -99999999; |
|
int size; |
|
|
|
// Init startframe |
|
psource->startframe = -1; |
|
|
|
// size per frame |
|
size = psource->numbones * sizeof( s_bone_t ); |
|
|
|
// march through animation |
|
while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) |
|
{ |
|
// linecount |
|
g_iLinecount++; |
|
// split if big enoough |
|
if (sscanf( g_szLine, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &rot[0], &rot[1], &rot[2] ) == 7) |
|
{ |
|
// startframe is sanity check for having determined time |
|
if (psource->startframe < 0) |
|
{ |
|
MdlError( "Missing frame start(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
|
|
// scale if pertinent |
|
scale_vertex( pos ); |
|
VectorCopy( pos, psource->rawanim[t][index].pos ); |
|
VectorCopy( rot, psource->rawanim[t][index].rot ); |
|
|
|
clip_rotations( rot ); // !!! |
|
} |
|
else if (sscanf( g_szLine, "%1023s %d", cmd, &index )) |
|
{ |
|
// get time |
|
if (strcmp( cmd, "time" ) == 0) |
|
{ |
|
// again time IS an index |
|
t = index; |
|
if (psource->startframe == -1) |
|
{ |
|
psource->startframe = t; |
|
} |
|
// sanity check time (little funny logic here, see previous IF) |
|
if (t < psource->startframe) |
|
{ |
|
MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
// bump up endframe? |
|
if (t > psource->endframe) |
|
{ |
|
psource->endframe = t; |
|
} |
|
// make t into pure index |
|
t -= psource->startframe; |
|
|
|
// check for memory allocation |
|
if (psource->rawanim[t] == NULL) |
|
{ |
|
// Allocate 1 frame of full bonecount |
|
psource->rawanim[t] = (s_bone_t *)kalloc( 1, size ); |
|
|
|
// duplicate previous frames keys?? preventative sanity? |
|
if (t > 0 && psource->rawanim[t-1]) |
|
{ |
|
for (int j = 0; j < psource->numbones; j++) |
|
{ |
|
VectorCopy( psource->rawanim[t-1][j].pos, psource->rawanim[t][j].pos ); |
|
VectorCopy( psource->rawanim[t-1][j].rot, psource->rawanim[t][j].rot ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// MdlError( "%s has duplicated frame %d\n", psource->filename, t ); |
|
} |
|
} |
|
else if (strcmp( cmd, "end") == 0) |
|
{ |
|
psource->numframes = psource->endframe - psource->startframe + 1; |
|
|
|
for (t = 0; t < psource->numframes; t++) |
|
{ |
|
if (psource->rawanim[t] == NULL) |
|
{ |
|
MdlError( "%s is missing frame %d\n", psource->filename, t + psource->startframe ); |
|
} |
|
} |
|
|
|
Build_Reference( psource ); |
|
return; |
|
} |
|
else |
|
{ |
|
MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
} |
|
else |
|
{ |
|
MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine ); |
|
} |
|
} |
|
|
|
MdlError( "unexpected EOF: %s\n", psource->filename ); |
|
} |
|
|
|
int lookup_index( s_source_t *psource, int material, Vector& vertex, Vector& normal, Vector2D texcoord ) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < numvlist; i++) |
|
{ |
|
if (v_listdata[i].m == material |
|
&& DotProduct( g_normal[i], normal ) > normal_blend |
|
&& VectorCompare( g_vertex[i], vertex ) |
|
&& g_texcoord[i][0] == texcoord[0] |
|
&& g_texcoord[i][1] == texcoord[1]) |
|
{ |
|
v_listdata[i].lastref = numvlist; |
|
return i; |
|
} |
|
} |
|
if (i >= MAXSTUDIOVERTS) { |
|
MdlError( "too many indices in source: \"%s\"\n", psource->filename); |
|
} |
|
|
|
VectorCopy( vertex, g_vertex[i] ); |
|
VectorCopy( normal, g_normal[i] ); |
|
Vector2Copy( texcoord, g_texcoord[i] ); |
|
|
|
v_listdata[i].v = i; |
|
v_listdata[i].m = material; |
|
v_listdata[i].n = i; |
|
v_listdata[i].t = i; |
|
|
|
v_listdata[i].firstref = numvlist; |
|
v_listdata[i].lastref = numvlist; |
|
|
|
numvlist = i + 1; |
|
return i; |
|
} |
|
|
|
|
|
void ParseFaceData( s_source_t *psource, int material, s_face_t *pFace ) |
|
{ |
|
int index[3]; |
|
int i, j; |
|
Vector p; |
|
Vector normal; |
|
Vector2D t; |
|
int iCount, bones[MAXSTUDIOSRCBONES]; |
|
float weights[MAXSTUDIOSRCBONES]; |
|
int bone; |
|
|
|
for (j = 0; j < 3; j++) |
|
{ |
|
memset( g_szLine, 0, sizeof( g_szLine ) ); |
|
|
|
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) == NULL) |
|
{ |
|
MdlError("%s: error on g_szLine %d: %s", g_szFilename, g_iLinecount, g_szLine ); |
|
} |
|
|
|
iCount = 0; |
|
|
|
g_iLinecount++; |
|
i = sscanf( g_szLine, "%d %f %f %f %f %f %f %f %f %d %d %f %d %f %d %f %d %f", |
|
&bone, |
|
&p[0], &p[1], &p[2], |
|
&normal[0], &normal[1], &normal[2], |
|
&t[0], &t[1], |
|
&iCount, |
|
&bones[0], &weights[0], &bones[1], &weights[1], &bones[2], &weights[2], &bones[3], &weights[3] ); |
|
|
|
if (i < 9) |
|
continue; |
|
|
|
if (bone < 0 || bone >= psource->numbones) |
|
{ |
|
MdlError("bogus bone index\n%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine ); |
|
} |
|
|
|
//Scale face pos |
|
scale_vertex( p ); |
|
|
|
// continue parsing more bones. |
|
// FIXME: don't we have a built in parser that'll do this? |
|
if (iCount > 4) |
|
{ |
|
int k; |
|
int ctr = 0; |
|
char *token; |
|
for (k = 0; k < 18; k++) |
|
{ |
|
while (g_szLine[ctr] == ' ') |
|
{ |
|
ctr++; |
|
} |
|
token = strtok( &g_szLine[ctr], " " ); |
|
ctr += strlen( token ) + 1; |
|
} |
|
for (k = 4; k < iCount && k < MAXSTUDIOSRCBONES; k++) |
|
{ |
|
while (g_szLine[ctr] == ' ') |
|
{ |
|
ctr++; |
|
} |
|
token = strtok( &g_szLine[ctr], " " ); |
|
ctr += strlen( token ) + 1; |
|
|
|
bones[k] = atoi(token); |
|
|
|
token = strtok( &g_szLine[ctr], " " ); |
|
ctr += strlen( token ) + 1; |
|
|
|
weights[k] = atof(token); |
|
} |
|
// vprint( 0, "%d ", iCount ); |
|
|
|
//vprint( 0, "\n"); |
|
//exit(1); |
|
} |
|
|
|
// adjust_vertex( p ); |
|
// scale_vertex( p ); |
|
|
|
// move vertex position to object space. |
|
// VectorSubtract( p, psource->bonefixup[bone].worldorg, tmp ); |
|
// VectorTransform(tmp, psource->bonefixup[bone].im, p ); |
|
|
|
// move normal to object space. |
|
// VectorCopy( normal, tmp ); |
|
// VectorTransform(tmp, psource->bonefixup[bone].im, normal ); |
|
// VectorNormalize( normal ); |
|
|
|
// invert v |
|
t[1] = 1.0 - t[1]; |
|
|
|
index[j] = lookup_index( psource, material, p, normal, t ); |
|
|
|
if (i == 9 || iCount == 0) |
|
{ |
|
g_bone[index[j]].numbones = 1; |
|
g_bone[index[j]].bone[0] = bone; |
|
g_bone[index[j]].weight[0] = 1.0; |
|
} |
|
else |
|
{ |
|
iCount = SortAndBalanceBones( iCount, MAXSTUDIOBONEWEIGHTS, bones, weights ); |
|
|
|
g_bone[index[j]].numbones = iCount; |
|
for (i = 0; i < iCount; i++) |
|
{ |
|
g_bone[index[j]].bone[i] = bones[i]; |
|
g_bone[index[j]].weight[i] = weights[i]; |
|
} |
|
} |
|
} |
|
|
|
// pFace->material = material; // BUG |
|
pFace->a = index[0]; |
|
pFace->b = index[1]; |
|
pFace->c = index[2]; |
|
Assert( ((pFace->a & 0xF0000000) == 0) && ((pFace->b & 0xF0000000) == 0) && |
|
((pFace->c & 0xF0000000) == 0) ); |
|
|
|
if (flip_triangles) |
|
{ |
|
j = pFace->b; pFace->b = pFace->c; pFace->c = j; |
|
} |
|
} |
|
|
|
int use_texture_as_material( int textureindex ) |
|
{ |
|
if (g_texture[textureindex].material == -1) |
|
{ |
|
// vprint( 0, "%d %d %s\n", textureindex, g_nummaterials, g_texture[textureindex].name ); |
|
g_material[g_nummaterials] = textureindex; |
|
g_texture[textureindex].material = g_nummaterials++; |
|
} |
|
|
|
return g_texture[textureindex].material; |
|
} |
|
|
|
int material_to_texture( int material ) |
|
{ |
|
int i; |
|
for (i = 0; i < g_numtextures; i++) |
|
{ |
|
if (g_texture[i].material == material) |
|
{ |
|
return i; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
int lookup_texture( char *texturename, int maxlen ) |
|
{ |
|
int i; |
|
|
|
Q_StripExtension( texturename, texturename, maxlen ); |
|
|
|
for (i = 0; i < g_numtextures; i++) |
|
{ |
|
if (stricmp( g_texture[i].name, texturename ) == 0) |
|
{ |
|
return i; |
|
} |
|
} |
|
|
|
if (i >= MAXSTUDIOSKINS) |
|
MdlError("Too many materials used, max %d\n", ( int )MAXSTUDIOSKINS ); |
|
|
|
// vprint( 0, "texture %d = %s\n", i, texturename ); |
|
V_strcpy_safe( g_texture[i].name, texturename ); |
|
|
|
g_texture[i].material = -1; |
|
/* |
|
if (stristr( texturename, "chrome" ) != NULL) { |
|
texture[i].flags = STUDIO_NF_FLATSHADE | STUDIO_NF_CHROME; |
|
} |
|
else { |
|
texture[i].flags = 0; |
|
} |
|
*/ |
|
g_numtextures++; |
|
return i; |
|
} |
|
|
|
int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] ) |
|
{ |
|
int i; |
|
|
|
// collapse duplicate bone weights |
|
for (i = 0; i < iCount-1; i++) |
|
{ |
|
int j; |
|
for (j = i + 1; j < iCount; j++) |
|
{ |
|
if (bones[i] == bones[j]) |
|
{ |
|
weights[i] += weights[j]; |
|
weights[j] = 0.0; |
|
} |
|
} |
|
} |
|
|
|
// do sleazy bubble sort |
|
int bShouldSort; |
|
do { |
|
bShouldSort = false; |
|
for (i = 0; i < iCount-1; i++) |
|
{ |
|
if (weights[i+1] > weights[i]) |
|
{ |
|
int j = bones[i+1]; bones[i+1] = bones[i]; bones[i] = j; |
|
float w = weights[i+1]; weights[i+1] = weights[i]; weights[i] = w; |
|
bShouldSort = true; |
|
} |
|
} |
|
} while (bShouldSort); |
|
|
|
// throw away all weights less than 1/20th |
|
while (iCount > 1 && weights[iCount-1] < 0.05) |
|
{ |
|
iCount--; |
|
} |
|
|
|
// clip to the top iMaxCount bones |
|
if (iCount > iMaxCount) |
|
{ |
|
iCount = iMaxCount; |
|
} |
|
|
|
float t = 0; |
|
for (i = 0; i < iCount; i++) |
|
{ |
|
t += weights[i]; |
|
} |
|
|
|
if (t <= 0.0) |
|
{ |
|
// missing weights?, go ahead and evenly share? |
|
// FIXME: shouldn't this error out? |
|
t = 1.0 / iCount; |
|
|
|
for (i = 0; i < iCount; i++) |
|
{ |
|
weights[i] = t; |
|
} |
|
} |
|
else |
|
{ |
|
// scale to sum to 1.0 |
|
t = 1.0 / t; |
|
|
|
for (i = 0; i < iCount; i++) |
|
{ |
|
weights[i] = weights[i] * t; |
|
} |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
int vlistCompare( const void *elem1, const void *elem2 ) |
|
{ |
|
v_unify_t *u1 = &v_listdata[*(int *)elem1]; |
|
v_unify_t *u2 = &v_listdata[*(int *)elem2]; |
|
|
|
// sort by material |
|
if (u1->m < u2->m) |
|
return -1; |
|
if (u1->m > u2->m) |
|
return 1; |
|
|
|
// sort by last used |
|
if (u1->lastref < u2->lastref) |
|
return -1; |
|
if (u1->lastref > u2->lastref) |
|
return 1; |
|
|
|
return 0; |
|
} |
|
|
|
int faceCompare( const void *elem1, const void *elem2 ) |
|
{ |
|
int i1 = *(int *)elem1; |
|
int i2 = *(int *)elem2; |
|
|
|
// sort by material |
|
if (g_face[i1].material < g_face[i2].material) |
|
return -1; |
|
if (g_face[i1].material > g_face[i2].material) |
|
return 1; |
|
|
|
// sort by original usage |
|
if (i1 < i2) |
|
return -1; |
|
if (i1 > i2) |
|
return 1; |
|
|
|
return 0; |
|
} |
|
|
|
#define SMALL_FLOAT 1e-12 |
|
|
|
// NOTE: This routine was taken (and modified) from NVidia's BlinnReflection demo |
|
// Creates basis vectors, based on a vertex and index list. |
|
// See the NVidia white paper 'GDC2K PerPixel Lighting' for a description |
|
// of how this computation works |
|
static void CalcTriangleTangentSpace( s_source_t *pSrc, int v1, int v2, int v3, |
|
Vector &sVect, Vector &tVect ) |
|
{ |
|
/* |
|
static bool firstTime = true; |
|
static FILE *fp = NULL; |
|
if( firstTime ) |
|
{ |
|
firstTime = false; |
|
fp = fopen( "crap.out", "w" ); |
|
} |
|
*/ |
|
|
|
/* Compute the partial derivatives of X, Y, and Z with respect to S and T. */ |
|
Vector2D t0( pSrc->texcoord[v1][0], pSrc->texcoord[v1][1] ); |
|
Vector2D t1( pSrc->texcoord[v2][0], pSrc->texcoord[v2][1] ); |
|
Vector2D t2( pSrc->texcoord[v3][0], pSrc->texcoord[v3][1] ); |
|
Vector p0( pSrc->vertex[v1][0], pSrc->vertex[v1][1], pSrc->vertex[v1][2] ); |
|
Vector p1( pSrc->vertex[v2][0], pSrc->vertex[v2][1], pSrc->vertex[v2][2] ); |
|
Vector p2( pSrc->vertex[v3][0], pSrc->vertex[v3][1], pSrc->vertex[v3][2] ); |
|
|
|
sVect.Init( 0.0f, 0.0f, 0.0f ); |
|
tVect.Init( 0.0f, 0.0f, 0.0f ); |
|
|
|
// x, s, t |
|
Vector edge01 = Vector( p1.x - p0.x, t1.x - t0.x, t1.y - t0.y ); |
|
Vector edge02 = Vector( p2.x - p0.x, t2.x - t0.x, t2.y - t0.y ); |
|
|
|
Vector cross; |
|
CrossProduct( edge01, edge02, cross ); |
|
if( fabs( cross.x ) > SMALL_FLOAT ) |
|
{ |
|
sVect.x += -cross.y / cross.x; |
|
tVect.x += -cross.z / cross.x; |
|
} |
|
|
|
// y, s, t |
|
edge01 = Vector( p1.y - p0.y, t1.x - t0.x, t1.y - t0.y ); |
|
edge02 = Vector( p2.y - p0.y, t2.x - t0.x, t2.y - t0.y ); |
|
|
|
CrossProduct( edge01, edge02, cross ); |
|
if( fabs( cross.x ) > SMALL_FLOAT ) |
|
{ |
|
sVect.y += -cross.y / cross.x; |
|
tVect.y += -cross.z / cross.x; |
|
} |
|
|
|
// z, s, t |
|
edge01 = Vector( p1.z - p0.z, t1.x - t0.x, t1.y - t0.y ); |
|
edge02 = Vector( p2.z - p0.z, t2.x - t0.x, t2.y - t0.y ); |
|
|
|
CrossProduct( edge01, edge02, cross ); |
|
if( fabs( cross.x ) > SMALL_FLOAT ) |
|
{ |
|
sVect.z += -cross.y / cross.x; |
|
tVect.z += -cross.z / cross.x; |
|
} |
|
|
|
// Normalize sVect and tVect |
|
VectorNormalize( sVect ); |
|
VectorNormalize( tVect ); |
|
|
|
/* |
|
// Calculate flat normal |
|
Vector flatNormal; |
|
edge01 = p1 - p0; |
|
edge02 = p2 - p0; |
|
CrossProduct( edge02, edge01, flatNormal ); |
|
VectorNormalize( flatNormal ); |
|
|
|
// Get the average position |
|
Vector avgPos = ( p0 + p1 + p2 ) / 3.0f; |
|
|
|
// Draw the svect |
|
Vector endS = avgPos + sVect * .2f; |
|
fvprint( 0, fp, "2\n" ); |
|
fvprint( 0, fp, "%f %f %f 1.0 0.0 0.0\n", endS[0], endS[1], endS[2] ); |
|
fvprint( 0, fp, "%f %f %f 1.0 0.0 0.0\n", avgPos[0], avgPos[1], avgPos[2] ); |
|
|
|
// Draw the tvect |
|
Vector endT = avgPos + tVect * .2f; |
|
fvprint( 0, fp, "2\n" ); |
|
fvprint( 0, fp, "%f %f %f 0.0 1.0 0.0\n", endT[0], endT[1], endT[2] ); |
|
fvprint( 0, fp, "%f %f %f 0.0 1.0 0.0\n", avgPos[0], avgPos[1], avgPos[2] ); |
|
|
|
// Draw the normal |
|
Vector endN = avgPos + flatNormal * .2f; |
|
fvprint( 0, fp, "2\n" ); |
|
fvprint( 0, fp, "%f %f %f 0.0 0.0 1.0\n", endN[0], endN[1], endN[2] ); |
|
fvprint( 0, fp, "%f %f %f 0.0 0.0 1.0\n", avgPos[0], avgPos[1], avgPos[2] ); |
|
|
|
// Draw the wireframe of the triangle in white. |
|
fvprint( 0, fp, "2\n" ); |
|
fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p0[0], p0[1], p0[2] ); |
|
fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p1[0], p1[1], p1[2] ); |
|
fvprint( 0, fp, "2\n" ); |
|
fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p1[0], p1[1], p1[2] ); |
|
fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p2[0], p2[1], p2[2] ); |
|
fvprint( 0, fp, "2\n" ); |
|
fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p2[0], p2[1], p2[2] ); |
|
fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p0[0], p0[1], p0[2] ); |
|
|
|
// Draw a slightly shrunken version of the geometry to hide surfaces |
|
Vector tmp0 = p0 - flatNormal * .1f; |
|
Vector tmp1 = p1 - flatNormal * .1f; |
|
Vector tmp2 = p2 - flatNormal * .1f; |
|
fvprint( 0, fp, "3\n" ); |
|
fvprint( 0, fp, "%f %f %f 0.1 0.1 0.1\n", tmp0[0], tmp0[1], tmp0[2] ); |
|
fvprint( 0, fp, "%f %f %f 0.1 0.1 0.1\n", tmp1[0], tmp1[1], tmp1[2] ); |
|
fvprint( 0, fp, "%f %f %f 0.1 0.1 0.1\n", tmp2[0], tmp2[1], tmp2[2] ); |
|
|
|
fflush( fp ); |
|
*/ |
|
} |
|
|
|
typedef CUtlVector<int> CIntVector; |
|
|
|
void CalcModelTangentSpaces( s_source_t *pSrc ) |
|
{ |
|
// Build a map from vertex to a list of triangles that share the vert. |
|
int meshID; |
|
for( meshID = 0; meshID < pSrc->nummeshes; meshID++ ) |
|
{ |
|
s_mesh_t *pMesh = &pSrc->mesh[pSrc->meshindex[meshID]]; |
|
CUtlVector<CIntVector> vertToTriMap; |
|
vertToTriMap.AddMultipleToTail( pMesh->numvertices ); |
|
int triID; |
|
for( triID = 0; triID < pMesh->numfaces; triID++ ) |
|
{ |
|
s_face_t *pFace = &pSrc->face[triID + pMesh->faceoffset]; |
|
vertToTriMap[pFace->a].AddToTail( triID ); |
|
vertToTriMap[pFace->b].AddToTail( triID ); |
|
vertToTriMap[pFace->c].AddToTail( triID ); |
|
} |
|
|
|
// Calculate the tangent space for each triangle. |
|
CUtlVector<Vector> triSVect; |
|
CUtlVector<Vector> triTVect; |
|
triSVect.AddMultipleToTail( pMesh->numfaces ); |
|
triTVect.AddMultipleToTail( pMesh->numfaces ); |
|
for( triID = 0; triID < pMesh->numfaces; triID++ ) |
|
{ |
|
s_face_t *pFace = &pSrc->face[triID + pMesh->faceoffset]; |
|
CalcTriangleTangentSpace( pSrc, |
|
pMesh->vertexoffset + pFace->a, |
|
pMesh->vertexoffset + pFace->b, |
|
pMesh->vertexoffset + pFace->c, |
|
triSVect[triID], triTVect[triID] ); |
|
} |
|
|
|
// calculate an average tangent space for each vertex. |
|
int vertID; |
|
for( vertID = 0; vertID < pMesh->numvertices; vertID++ ) |
|
{ |
|
const Vector &normal = pSrc->normal[vertID+pMesh->vertexoffset]; |
|
Vector4D &finalSVect = pSrc->tangentS[vertID+pMesh->vertexoffset]; |
|
Vector sVect, tVect; |
|
|
|
sVect.Init( 0.0f, 0.0f, 0.0f ); |
|
tVect.Init( 0.0f, 0.0f, 0.0f ); |
|
for( triID = 0; triID < vertToTriMap[vertID].Size(); triID++ ) |
|
{ |
|
sVect += triSVect[vertToTriMap[vertID][triID]]; |
|
tVect += triTVect[vertToTriMap[vertID][triID]]; |
|
} |
|
|
|
// In the case of zbrush, everything needs to be treated as smooth. |
|
if( g_bZBrush ) |
|
{ |
|
int vertID2; |
|
Vector vertPos1( pSrc->vertex[vertID][0], pSrc->vertex[vertID][1], pSrc->vertex[vertID][2] ); |
|
for( vertID2 = 0; vertID2 < pMesh->numvertices; vertID2++ ) |
|
{ |
|
if( vertID2 == vertID ) |
|
{ |
|
continue; |
|
} |
|
Vector vertPos2( pSrc->vertex[vertID2][0], pSrc->vertex[vertID2][1], pSrc->vertex[vertID2][2] ); |
|
if( vertPos1 == vertPos2 ) |
|
{ |
|
int triID2; |
|
for( triID2 = 0; triID2 < vertToTriMap[vertID2].Size(); triID2++ ) |
|
{ |
|
sVect += triSVect[vertToTriMap[vertID2][triID2]]; |
|
tVect += triTVect[vertToTriMap[vertID2][triID2]]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// make an orthonormal system. |
|
// need to check if we are left or right handed. |
|
Vector tmpVect; |
|
CrossProduct( sVect, tVect, tmpVect ); |
|
bool leftHanded = DotProduct( tmpVect, normal ) < 0.0f; |
|
if( !leftHanded ) |
|
{ |
|
CrossProduct( normal, sVect, tVect ); |
|
CrossProduct( tVect, normal, sVect ); |
|
VectorNormalize( sVect ); |
|
VectorNormalize( tVect ); |
|
finalSVect[0] = sVect[0]; |
|
finalSVect[1] = sVect[1]; |
|
finalSVect[2] = sVect[2]; |
|
finalSVect[3] = 1.0f; |
|
} |
|
else |
|
{ |
|
CrossProduct( sVect, normal, tVect ); |
|
CrossProduct( normal, tVect, sVect ); |
|
VectorNormalize( sVect ); |
|
VectorNormalize( tVect ); |
|
finalSVect[0] = sVect[0]; |
|
finalSVect[1] = sVect[1]; |
|
finalSVect[2] = sVect[2]; |
|
finalSVect[3] = -1.0f; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void BuildIndividualMeshes( s_source_t *psource ) |
|
{ |
|
int i, j, k; |
|
|
|
// sort new vertices by materials, last used |
|
static int v_listsort[MAXSTUDIOVERTS]; // map desired order to vlist entry |
|
static int v_ilistsort[MAXSTUDIOVERTS]; // map vlist entry to desired order |
|
|
|
for (i = 0; i < numvlist; i++) |
|
{ |
|
v_listsort[i] = i; |
|
} |
|
qsort( v_listsort, numvlist, sizeof( int ), vlistCompare ); |
|
for (i = 0; i < numvlist; i++) |
|
{ |
|
v_ilistsort[v_listsort[i]] = i; |
|
} |
|
|
|
|
|
// allocate memory |
|
psource->numvertices = numvlist; |
|
psource->localBoneweight = (s_boneweight_t *)kalloc( psource->numvertices, sizeof( s_boneweight_t ) ); |
|
psource->globalBoneweight = NULL; |
|
psource->vertexInfo = (s_vertexinfo_t *)kalloc( psource->numvertices, sizeof( s_vertexinfo_t ) ); |
|
psource->vertex = new Vector[psource->numvertices]; |
|
psource->normal = new Vector[psource->numvertices]; |
|
psource->tangentS = new Vector4D[psource->numvertices]; |
|
psource->texcoord = (Vector2D *)kalloc( psource->numvertices, sizeof( Vector2D ) ); |
|
|
|
// create arrays of unique vertexes, normals, texcoords. |
|
for (i = 0; i < psource->numvertices; i++) |
|
{ |
|
j = v_listsort[i]; |
|
|
|
VectorCopy( g_vertex[v_listdata[j].v], psource->vertex[i] ); |
|
VectorCopy( g_normal[v_listdata[j].n], psource->normal[i] ); |
|
Vector2Copy( g_texcoord[v_listdata[j].t], psource->texcoord[i] ); |
|
|
|
psource->localBoneweight[i].numbones = g_bone[v_listdata[j].v].numbones; |
|
int k; |
|
for( k = 0; k < MAXSTUDIOBONEWEIGHTS; k++ ) |
|
{ |
|
psource->localBoneweight[i].bone[k] = g_bone[v_listdata[j].v].bone[k]; |
|
psource->localBoneweight[i].weight[k] = g_bone[v_listdata[j].v].weight[k]; |
|
} |
|
|
|
// store a bunch of other info |
|
psource->vertexInfo[i].material = v_listdata[j].m; |
|
|
|
psource->vertexInfo[i].firstref = v_listdata[j].firstref; |
|
psource->vertexInfo[i].lastref = v_listdata[j].lastref; |
|
// vprint( 0, "%4d : %2d : %6.2f %6.2f %6.2f\n", i, psource->boneweight[i].bone[0], psource->vertex[i][0], psource->vertex[i][1], psource->vertex[i][2] ); |
|
} |
|
|
|
// sort faces by materials, last used. |
|
static int facesort[MAXSTUDIOTRIANGLES]; // map desired order to src_face entry |
|
static int ifacesort[MAXSTUDIOTRIANGLES]; // map src_face entry to desired order |
|
|
|
for (i = 0; i < g_numfaces; i++) |
|
{ |
|
facesort[i] = i; |
|
} |
|
qsort( facesort, g_numfaces, sizeof( int ), faceCompare ); |
|
for (i = 0; i < g_numfaces; i++) |
|
{ |
|
ifacesort[facesort[i]] = i; |
|
} |
|
|
|
psource->numfaces = g_numfaces; |
|
// find first occurance for each material |
|
for (k = 0; k < MAXSTUDIOSKINS; k++) |
|
{ |
|
psource->mesh[k].numvertices = 0; |
|
psource->mesh[k].vertexoffset = psource->numvertices; |
|
|
|
psource->mesh[k].numfaces = 0; |
|
psource->mesh[k].faceoffset = g_numfaces; |
|
} |
|
|
|
// find first and count of indices per material |
|
for (i = 0; i < psource->numvertices; i++) |
|
{ |
|
k = psource->vertexInfo[i].material; |
|
psource->mesh[k].numvertices++; |
|
if (psource->mesh[k].vertexoffset > i) |
|
psource->mesh[k].vertexoffset = i; |
|
} |
|
|
|
// find first and count of faces per material |
|
for (i = 0; i < psource->numfaces; i++) |
|
{ |
|
k = g_face[facesort[i]].material; |
|
|
|
psource->mesh[k].numfaces++; |
|
if (psource->mesh[k].faceoffset > i) |
|
psource->mesh[k].faceoffset = i; |
|
} |
|
|
|
/* |
|
for (k = 0; k < MAXSTUDIOSKINS; k++) |
|
{ |
|
vprint( 0, "%d : %d:%d %d:%d\n", k, psource->mesh[k].numvertices, psource->mesh[k].vertexoffset, psource->mesh[k].numfaces, psource->mesh[k].faceoffset ); |
|
} |
|
*/ |
|
|
|
// create remapped faces |
|
psource->face = (s_face_t *)kalloc( psource->numfaces, sizeof( s_face_t )); |
|
for (k = 0; k < MAXSTUDIOSKINS; k++) |
|
{ |
|
if (psource->mesh[k].numfaces) |
|
{ |
|
psource->meshindex[psource->nummeshes] = k; |
|
|
|
for (i = psource->mesh[k].faceoffset; i < psource->mesh[k].numfaces + psource->mesh[k].faceoffset; i++) |
|
{ |
|
j = facesort[i]; |
|
|
|
psource->face[i].a = v_ilistsort[g_src_uface[j].a] - psource->mesh[k].vertexoffset; |
|
psource->face[i].b = v_ilistsort[g_src_uface[j].b] - psource->mesh[k].vertexoffset; |
|
psource->face[i].c = v_ilistsort[g_src_uface[j].c] - psource->mesh[k].vertexoffset; |
|
Assert( ((psource->face[i].a & 0xF0000000) == 0) && ((psource->face[i].b & 0xF0000000) == 0) && |
|
((psource->face[i].c & 0xF0000000) == 0) ); |
|
// vprint( 0, "%3d : %4d %4d %4d\n", i, psource->face[i].a, psource->face[i].b, psource->face[i].c ); |
|
} |
|
|
|
psource->nummeshes++; |
|
} |
|
} |
|
|
|
CalcModelTangentSpaces( psource ); |
|
} |
|
|
|
void Grab_Triangles( s_source_t *psource ) |
|
{ |
|
int i; |
|
Vector vmin, vmax; |
|
|
|
vmin[0] = vmin[1] = vmin[2] = 99999; |
|
vmax[0] = vmax[1] = vmax[2] = -99999; |
|
|
|
g_numfaces = 0; |
|
numvlist = 0; |
|
|
|
// |
|
// load the base triangles |
|
// |
|
int texture; |
|
int material; |
|
char texturename[64]; |
|
|
|
while (1) |
|
{ |
|
if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) == NULL) |
|
break; |
|
|
|
g_iLinecount++; |
|
|
|
// check for end |
|
if (IsEnd( g_szLine )) |
|
break; |
|
|
|
// Look for extra junk that we may want to avoid... |
|
int nLineLength = strlen( g_szLine ); |
|
if (nLineLength >= 64) |
|
{ |
|
MdlWarning("Unexpected data at line %d, (need a texture name) ignoring...\n", g_iLinecount ); |
|
continue; |
|
} |
|
|
|
// strip off trailing smag |
|
V_strcpy_safe( texturename, g_szLine ); |
|
for (i = strlen( texturename ) - 1; i >= 0 && ! isgraph( texturename[i] ); i--) |
|
{ |
|
} |
|
texturename[i + 1] = '\0'; |
|
|
|
// funky texture overrides |
|
for (i = 0; i < numrep; i++) |
|
{ |
|
if (sourcetexture[i][0] == '\0') |
|
{ |
|
strcpy( texturename, defaulttexture[i] ); |
|
break; |
|
} |
|
if (stricmp( texturename, sourcetexture[i]) == 0) |
|
{ |
|
strcpy( texturename, defaulttexture[i] ); |
|
break; |
|
} |
|
} |
|
|
|
if (texturename[0] == '\0') |
|
{ |
|
// weird source problem, skip them |
|
fgets( g_szLine, sizeof( g_szLine ), g_fpInput ); |
|
fgets( g_szLine, sizeof( g_szLine ), g_fpInput ); |
|
fgets( g_szLine, sizeof( g_szLine ), g_fpInput ); |
|
g_iLinecount += 3; |
|
continue; |
|
} |
|
|
|
if (stricmp( texturename, "null.bmp") == 0 || stricmp( texturename, "null.tga") == 0) |
|
{ |
|
// skip all faces with the null texture on them. |
|
fgets( g_szLine, sizeof( g_szLine ), g_fpInput ); |
|
fgets( g_szLine, sizeof( g_szLine ), g_fpInput ); |
|
fgets( g_szLine, sizeof( g_szLine ), g_fpInput ); |
|
g_iLinecount += 3; |
|
continue; |
|
} |
|
|
|
texture = lookup_texture( texturename, sizeof( texturename ) ); |
|
psource->texmap[texture] = texture; // hack, make it 1:1 |
|
material = use_texture_as_material( texture ); |
|
|
|
s_face_t f; |
|
ParseFaceData( psource, material, &f ); |
|
|
|
g_src_uface[g_numfaces] = f; |
|
g_face[g_numfaces].material = material; |
|
g_numfaces++; |
|
} |
|
|
|
BuildIndividualMeshes( psource ); |
|
} |
|
|
|
//-------------------------------------------------------------------- |
|
// Load a SMD file |
|
//-------------------------------------------------------------------- |
|
int Load_SMD ( s_source_t *psource ) |
|
{ |
|
char cmd[1024]; |
|
int option; |
|
|
|
// Open file |
|
if (!OpenGlobalFile( psource->filename )) |
|
return 0; |
|
|
|
// verbose |
|
if( !g_quiet ) |
|
{ |
|
printf ("SMD MODEL %s\n", psource->filename); |
|
} |
|
|
|
//March through lines |
|
g_iLinecount = 0; |
|
while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) |
|
{ |
|
g_iLinecount++; |
|
int numRead = sscanf( g_szLine, "%s %d", cmd, &option ); |
|
|
|
// Blank line |
|
if ((numRead == EOF) || (numRead == 0)) |
|
continue; |
|
|
|
if (strcmp( cmd, "version" ) == 0) |
|
{ |
|
if (option != 1) |
|
{ |
|
MdlError("bad version\n"); |
|
} |
|
} |
|
// Get hierarchy? |
|
else if (strcmp( cmd, "nodes" ) == 0) |
|
{ |
|
psource->numbones = Grab_Nodes( psource->localBone ); |
|
} |
|
// Get animation?? |
|
else if (strcmp( cmd, "skeleton" ) == 0) |
|
{ |
|
Grab_Animation( psource ); |
|
} |
|
// Geo? |
|
else if (strcmp( cmd, "triangles" ) == 0) |
|
{ |
|
Grab_Triangles( psource ); |
|
} |
|
// Geo animation |
|
else if (strcmp( cmd, "vertexanimation" ) == 0) |
|
{ |
|
Grab_Vertexanimation( psource ); |
|
} |
|
else |
|
{ |
|
MdlWarning("unknown studio command\n" ); |
|
} |
|
} |
|
fclose( g_fpInput ); |
|
|
|
is_v1support = true; |
|
|
|
return 1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Checks to see if the model source was already loaded |
|
//----------------------------------------------------------------------------- |
|
static s_source_t *FindCachedSource( char const* name, char const* xext ) |
|
{ |
|
int i; |
|
|
|
if( xext[0] ) |
|
{ |
|
// we know what extension is necessary. . look for it. |
|
sprintf (g_szFilename, "%s%s.%s", cddir[numdirs], name, xext ); |
|
for (i = 0; i < g_numsources; i++) |
|
{ |
|
if (stricmp( g_szFilename, g_source[i]->filename ) == 0) |
|
return g_source[i]; |
|
} |
|
} |
|
else |
|
{ |
|
// we don't know what extension to use, so look for all of 'em. |
|
sprintf (g_szFilename, "%s%s.vrm", cddir[numdirs], name ); |
|
for (i = 0; i < g_numsources; i++) |
|
{ |
|
if (stricmp( g_szFilename, g_source[i]->filename ) == 0) |
|
return g_source[i]; |
|
} |
|
sprintf (g_szFilename, "%s%s.smd", cddir[numdirs], name ); |
|
for (i = 0; i < g_numsources; i++) |
|
{ |
|
if (stricmp( g_szFilename, g_source[i]->filename ) == 0) |
|
return g_source[i]; |
|
} |
|
/* |
|
sprintf (g_szFilename, "%s%s.vta", cddir[numdirs], name ); |
|
for (i = 0; i < g_numsources; i++) |
|
{ |
|
if (stricmp( g_szFilename, g_source[i]->filename ) == 0) |
|
return g_source[i]; |
|
} |
|
*/ |
|
} |
|
|
|
// Not found |
|
return 0; |
|
} |
|
|
|
static void FlipFacing( s_source_t *pSrc ) |
|
{ |
|
unsigned short tmp; |
|
|
|
int i, j; |
|
for( i = 0; i < pSrc->nummeshes; i++ ) |
|
{ |
|
s_mesh_t *pMesh = &pSrc->mesh[i]; |
|
for( j = 0; j < pMesh->numfaces; j++ ) |
|
{ |
|
s_face_t &f = pSrc->face[pMesh->faceoffset + j]; |
|
tmp = f.b; f.b = f.c; f.c = tmp; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Loads an animation source |
|
//----------------------------------------------------------------------------- |
|
|
|
s_source_t *Load_Source( char const *name, const char *ext, bool reverse, bool isActiveModel ) |
|
{ |
|
// Sanity check number of source files |
|
if ( g_numsources >= MAXSTUDIOSEQUENCES ) |
|
MdlError( "Load_Source( %s ) - overflowed g_numsources.", name ); |
|
|
|
// Sanity check file and init |
|
Assert(name); |
|
int namelen = strlen(name) + 1; |
|
char* pTempName = (char*)_alloca( namelen ); |
|
char xext[32]; |
|
int result = false; |
|
|
|
// Local copy of filename |
|
strcpy( pTempName, name ); |
|
|
|
// Sanity check file extension? |
|
Q_ExtractFileExtension( pTempName, xext, sizeof( xext ) ); |
|
if (xext[0] == '\0') |
|
{ |
|
V_strcpy_safe( xext, ext ); |
|
} |
|
else |
|
{ |
|
Q_StripExtension( pTempName, pTempName, namelen ); |
|
} |
|
|
|
// Cached source, ie: already loaded model, legacy |
|
// s_source_t* pSource = FindCachedSource( pTempName, xext ); |
|
// if (pSource) |
|
// { |
|
// if (isActiveModel) |
|
// pSource->isActiveModel = true; |
|
// return pSource; |
|
// } |
|
|
|
// allocate space and whatnot |
|
g_source[g_numsources] = (s_source_t *)kalloc( 1, sizeof( s_source_t ) ); |
|
V_strcpy_safe( g_source[g_numsources]->filename, g_szFilename ); |
|
|
|
// legacy stuff |
|
if (isActiveModel) |
|
{ |
|
g_source[g_numsources]->isActiveModel = true; |
|
} |
|
|
|
// more ext sanity check |
|
if ( ( !result && xext[0] == '\0' ) || stricmp( xext, "smd" ) == 0) |
|
{ |
|
Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.smd", cddir[numdirs], pTempName ); |
|
V_strcpy_safe( g_source[g_numsources]->filename, g_szFilename ); |
|
|
|
// Import part, load smd file |
|
result = Load_SMD( g_source[g_numsources] ); |
|
} |
|
|
|
/* |
|
if ( ( !result && xext[0] == '\0' ) || stricmp( xext, "dmx" ) == 0) |
|
{ |
|
Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.dmx", cddir[numdirs], pTempName ); |
|
V_strcpy_safe( g_source[g_numsources]->filename, g_szFilename ); |
|
|
|
// Import part, load smd file |
|
result = Load_DMX( g_source[g_numsources] ); |
|
} |
|
*/ |
|
|
|
// Oops |
|
if ( !result) |
|
{ |
|
MdlError( "could not load file '%s'\n", g_source[g_numsources]->filename ); |
|
} |
|
|
|
// bump up number of sources |
|
g_numsources++; |
|
if( reverse ) |
|
{ |
|
FlipFacing( g_source[g_numsources-1] ); |
|
} |
|
return g_source[g_numsources-1]; |
|
} |
|
|
|
void SaveNodes( s_source_t *source, CUtlBuffer& buf ) |
|
{ |
|
if ( source->numbones <= 0 ) |
|
return; |
|
|
|
buf.Printf( "nodes\n" ); |
|
|
|
for ( int i = 0; i < source->numbones; ++i ) |
|
{ |
|
s_node_t *bone = &source->localBone[ i ]; |
|
|
|
buf.Printf( "%d \"%s\" %d\n", i, bone->name, bone->parent ); |
|
} |
|
|
|
buf.Printf( "end\n" ); |
|
} |
|
|
|
// FIXME: since we don't us a .qc, we could have problems with scaling, etc.??? |
|
void descale_vertex( Vector &org ) |
|
{ |
|
float invscale = 1.0f / g_currentscale; |
|
|
|
org[0] = org[0] * invscale; |
|
org[1] = org[1] * invscale; |
|
org[2] = org[2] * invscale; |
|
} |
|
|
|
void SaveAnimation( s_source_t *source, CUtlBuffer& buf ) |
|
{ |
|
if ( source->numbones <= 0 ) |
|
return; |
|
|
|
buf.Printf( "skeleton\n" ); |
|
|
|
for ( int frame = 0; frame < source->numframes; ++frame ) |
|
{ |
|
buf.Printf( "time %i\n", frame + source->startframe ); |
|
|
|
for ( int i = 0; i < source->numbones; ++i ) |
|
{ |
|
s_bone_t *prev = NULL; |
|
if ( frame > 0 ) |
|
{ |
|
if ( source->rawanim[ frame - 1 ] ) |
|
{ |
|
prev = &source->rawanim[ frame - 1 ][ i ]; |
|
} |
|
} |
|
|
|
Vector pos = source->rawanim[ frame ][ i ].pos; |
|
descale_vertex( pos ); |
|
RadianEuler rot = source->rawanim[ frame ][ i ].rot; |
|
|
|
// If this is enabled, then we delta this pos vs the prev frame and don't write out a sample if it's the same value... |
|
#if 0 |
|
if ( prev ) |
|
{ |
|
Vector ppos = source->rawanim[ frame -1 ][ i ].pos; |
|
descale_vertex( pos ); |
|
RadianEuler prot = source->rawanim[ frame -1 ][ i ].rot; |
|
|
|
// Only output it if there's a delta |
|
if ( ( ppos != pos ) || |
|
Q_memcmp( &prot, &rot, sizeof( prot ) ) ) |
|
{ |
|
buf.Printf |
|
( "%d %f %f %f %f %f %f\n", |
|
i, // bone index |
|
pos[ 0 ], |
|
pos[ 1 ], |
|
pos[ 2 ], |
|
rot[ 0 ], |
|
rot[ 1 ], |
|
rot[ 2 ] |
|
); |
|
} |
|
} |
|
else |
|
#endif |
|
{ |
|
buf.Printf |
|
( "%d %f %f %f %f %f %f\n", |
|
i, // bone index |
|
pos[ 0 ], |
|
pos[ 1 ], |
|
pos[ 2 ], |
|
rot[ 0 ], |
|
rot[ 1 ], |
|
rot[ 2 ] |
|
); |
|
} |
|
} |
|
} |
|
|
|
buf.Printf( "end\n" ); |
|
} |
|
|
|
void Save_SMD( char const *filename, s_source_t *source ) |
|
{ |
|
// Text buffer |
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); |
|
|
|
buf.Printf( "version 1\n" ); |
|
|
|
SaveNodes( source, buf ); |
|
SaveAnimation( source, buf ); |
|
|
|
FileHandle_t fh = g_pFileSystem->Open( filename, "wb" ); |
|
if ( FILESYSTEM_INVALID_HANDLE != fh ) |
|
{ |
|
g_pFileSystem->Write( buf.Base(), buf.TellPut(), fh ); |
|
g_pFileSystem->Close( fh ); |
|
} |
|
} |
|
|
|
//-------------------------------------------------------------------- |
|
// mikes right handed row based linear algebra |
|
//-------------------------------------------------------------------- |
|
struct M_matrix4x4_t |
|
{ |
|
M_matrix4x4_t() { |
|
|
|
m_flMatVal[0][0] = 1.0; m_flMatVal[0][1] = 0.0; m_flMatVal[0][2] = 0.0; m_flMatVal[0][3] = 0.0; |
|
m_flMatVal[1][0] = 0.0; m_flMatVal[1][1] = 1.0; m_flMatVal[1][2] = 0.0; m_flMatVal[1][3] = 0.0; |
|
m_flMatVal[2][0] = 0.0; m_flMatVal[2][1] = 0.0; m_flMatVal[2][2] = 1.0; m_flMatVal[2][3] = 0.0; |
|
m_flMatVal[3][0] = 0.0; m_flMatVal[3][1] = 0.0; m_flMatVal[3][2] = 0.0; m_flMatVal[3][3] = 1.0; |
|
|
|
} |
|
// M_matrix3x4_t( |
|
// float m00, float m01, float m02, |
|
// float m10, float m11, float m12, |
|
// float m20, float m21, float m22, |
|
// float m30, float m31, float m32) |
|
// { |
|
// m_flMatVal[0][0] = m00; m_flMatVal[0][1] = m01; m_flMatVal[0][2] = m02; |
|
// m_flMatVal[1][0] = m10; m_flMatVal[1][1] = m11; m_flMatVal[1][2] = m12; |
|
// m_flMatVal[2][0] = m20; m_flMatVal[2][1] = m21; m_flMatVal[2][2] = m22; |
|
// m_flMatVal[3][0] = m30; m_flMatVal[3][1] = m31; m_flMatVal[3][2] = m32; |
|
|
|
// } |
|
|
|
float *operator[]( int i ) { Assert(( i >= 0 ) && ( i < 4 )); return m_flMatVal[i]; } |
|
const float *operator[]( int i ) const { Assert(( i >= 0 ) && ( i < 4 )); return m_flMatVal[i]; } |
|
float *Base() { return &m_flMatVal[0][0]; } |
|
const float *Base() const { return &m_flMatVal[0][0]; } |
|
|
|
float m_flMatVal[4][4]; |
|
}; |
|
|
|
void M_MatrixAngles( const M_matrix4x4_t& matrix, RadianEuler &angles, Vector &position) |
|
{ |
|
float cX, sX, cY, sY, cZ, sZ; |
|
|
|
sY = -matrix[0][2]; |
|
cY = sqrtf(1.0-(sY*sY)); |
|
|
|
if (cY != 0.0) |
|
{ |
|
sX = matrix[1][2]; |
|
cX = matrix[2][2]; |
|
sZ = matrix[0][1]; |
|
cZ = matrix[0][0]; |
|
} |
|
else |
|
{ |
|
sX = -matrix[2][1]; |
|
cX = matrix[1][1]; |
|
sZ = 0.0; |
|
cZ = 1.0; |
|
} |
|
|
|
angles[0] = atan2f( sX, cX ); |
|
angles[2] = atan2f( sZ, cZ ); |
|
|
|
sX = sinf(angles[0]); |
|
cX = cosf(angles[0]); |
|
|
|
if (sX > cX) |
|
cY = matrix[1][2] / sX; |
|
else |
|
cY = matrix[2][2] / cX; |
|
|
|
angles[1] = atan2f( sY, cY ); |
|
|
|
|
|
position.x = matrix[3][0]; |
|
position.y = matrix[3][1]; |
|
position.z = matrix[3][2]; |
|
|
|
} |
|
|
|
// void M_MatrixAngles( const M_matrix4x4_t& matrix, RadianEuler &angles, Vector &position) |
|
// { |
|
|
|
// float cX, sX, cY, sY, cZ, sZ; |
|
|
|
// sY = matrix[2][0]; |
|
// cY = sqrtf(1.0-(sY*sY)); |
|
|
|
// if (cY != 0.0) |
|
// { |
|
// sX = -matrix[2][1]; |
|
// cX = matrix[2][2]; |
|
// sZ = -matrix[1][0]; |
|
// cZ = matrix[0][0]; |
|
// } |
|
// else |
|
// { |
|
// sX = matrix[0][1]; |
|
// cX = matrix[1][1]; |
|
// sZ = 0.0; |
|
// cZ = 1.0; |
|
// } |
|
|
|
// angles[0] = atan2f( sX, cX ); |
|
// angles[2] = atan2f( sZ, cZ ); |
|
|
|
// sX = sinf(angles[0]); |
|
// cX = cosf(angles[0]); |
|
|
|
// if (sX > cX) |
|
// cY = -matrix[2][1] / sX; |
|
// else |
|
// cY = matrix[2][2] / cX; |
|
|
|
// angles[1] = atan2f( sY, cY ); |
|
|
|
// angles[0] = angles[0]; |
|
// angles[1] = angles[1]; |
|
// angles[2] = angles[2]; |
|
|
|
// position.x = matrix[3][0]; |
|
// position.y = matrix[3][1]; |
|
// position.z = matrix[3][2]; |
|
// } |
|
|
|
void M_MatrixCopy( const M_matrix4x4_t& in, M_matrix4x4_t& out ) |
|
{ |
|
// Assert( s_bMathlibInitialized ); |
|
memcpy( out.Base(), in.Base(), sizeof( float ) * 4 * 4 ); |
|
} |
|
void M_RotateZMatrix(float radian, M_matrix4x4_t &resultMatrix) |
|
{ |
|
|
|
resultMatrix[0][0] = cosf(radian); |
|
resultMatrix[0][1] = sin(radian); |
|
resultMatrix[0][2] = 0.0; |
|
resultMatrix[1][0] =-sin(radian); |
|
resultMatrix[1][1] = cos(radian); |
|
resultMatrix[1][2] = 0.0; |
|
resultMatrix[2][0] = 0.0; |
|
resultMatrix[2][1] = 0.0; |
|
resultMatrix[2][2] = 1.0; |
|
} |
|
|
|
// !!! THIS SHIT DOESN'T WORK!! WHY? HAS I EVER? |
|
void M_AngleAboutAxis(Vector &axis, float radianAngle, M_matrix4x4_t &result) |
|
{ |
|
float c = cosf(radianAngle); |
|
float s = sinf(radianAngle); |
|
float t = 1.0 - c; |
|
// axis.normalize(); |
|
|
|
result[0][0] = t * axis[0] * axis[0] + c; |
|
result[0][1] = t * axis[0] * axis[1] - s * axis[2]; |
|
result[0][2] = t * axis[0] * axis[2] + s * axis[1]; |
|
result[1][0] = t * axis[0] * axis[1] + s * axis[2]; |
|
result[1][1] = t * axis[1] * axis[1] + c; |
|
result[1][2] = t * axis[1] * axis[2] - s * axis[0]; |
|
result[2][0] = t * axis[1] * axis[2] - s; |
|
result[2][1] = t * axis[1] * axis[2] + s * axis[1]; |
|
result[2][2] = t * axis[2] * axis[2] + c * axis[0]; |
|
|
|
} |
|
|
|
|
|
void M_MatrixInvert( const M_matrix4x4_t& in, M_matrix4x4_t& out ) |
|
{ |
|
// Assert( s_bMathlibInitialized ); |
|
if ( &in == &out ) |
|
{ |
|
M_matrix4x4_t in2; |
|
M_MatrixCopy( in, in2 ); |
|
M_MatrixInvert( in2, out ); |
|
return; |
|
} |
|
float tmp[3]; |
|
|
|
// I'm guessing this only works on a 3x4 orthonormal matrix |
|
out[0][0] = in[0][0]; |
|
out[1][0] = in[0][1]; |
|
out[2][0] = in[0][2]; |
|
|
|
out[0][1] = in[1][0]; |
|
out[1][1] = in[1][1]; |
|
out[2][1] = in[1][2]; |
|
|
|
out[0][2] = in[2][0]; |
|
out[1][2] = in[2][1]; |
|
out[2][2] = in[2][2]; |
|
|
|
tmp[0] = in[3][0]; |
|
tmp[1] = in[3][1]; |
|
tmp[2] = in[3][2]; |
|
|
|
float v1[3], v2[3], v3[3]; |
|
v1[0] = out[0][0]; |
|
v1[1] = out[1][0]; |
|
v1[2] = out[2][0]; |
|
v2[0] = out[0][1]; |
|
v2[1] = out[1][1]; |
|
v2[2] = out[2][1]; |
|
v3[0] = out[0][2]; |
|
v3[1] = out[1][2]; |
|
v3[2] = out[2][2]; |
|
|
|
out[3][0] = -DotProduct( tmp, v1 ); |
|
out[3][1] = -DotProduct( tmp, v2 ); |
|
out[3][2] = -DotProduct( tmp, v3 ); |
|
|
|
// Trivial case |
|
// if (IS_IDENTITY(matrix)) |
|
// return SbMatrix::identity(); |
|
|
|
// // Affine case... |
|
// // SbMatrix affineAnswer; |
|
// // if ( affine_inverse( SbMatrix(matrix), affineAnswer ) ) |
|
// // return affineAnswer; |
|
|
|
// int index[4]; |
|
// float d, invmat[4][4], temp; |
|
// SbMatrix inverse = *this; |
|
|
|
// if(inverse.LUDecomposition(index, d)) { |
|
|
|
// invmat[0][0] = 1.0; |
|
// invmat[0][1] = 0.0; |
|
// invmat[0][2] = 0.0; |
|
// invmat[0][3] = 0.0; |
|
// inverse.LUBackSubstitution(index, invmat[0]); |
|
// invmat[1][0] = 0.0; |
|
// invmat[1][1] = 1.0; |
|
// invmat[1][2] = 0.0; |
|
// invmat[1][3] = 0.0; |
|
// inverse.LUBackSubstitution(index, invmat[1]); |
|
// invmat[2][0] = 0.0; |
|
// invmat[2][1] = 0.0; |
|
// invmat[2][2] = 1.0; |
|
// invmat[2][3] = 0.0; |
|
// inverse.LUBackSubstitution(index, invmat[2]); |
|
// invmat[3][0] = 0.0; |
|
// invmat[3][1] = 0.0; |
|
// invmat[3][2] = 0.0; |
|
// invmat[3][3] = 1.0; |
|
// inverse.LUBackSubstitution(index, invmat[3]); |
|
|
|
// #define SWAP(i,j) \ |
|
// temp = invmat[i][j]; \ |
|
// invmat[i][j] = invmat[j][i]; \ |
|
// invmat[j][i] = temp; |
|
|
|
// SWAP(1,0); |
|
|
|
// SWAP(2,0); |
|
// SWAP(2,1); |
|
|
|
// SWAP(3,0); |
|
// SWAP(3,1); |
|
// SWAP(3,2); |
|
// #undef SWAP |
|
// } |
|
} |
|
|
|
/* |
|
================ |
|
M_ConcatTransforms |
|
================ |
|
*/ |
|
void M_ConcatTransforms (const M_matrix4x4_t &in1, const M_matrix4x4_t &in2, M_matrix4x4_t &out) |
|
{ |
|
|
|
// Assert( s_bMathlibInitialized ); |
|
// if ( &in1 == &out ) |
|
// { |
|
// matrix3x4_t in1b; |
|
// MatrixCopy( in1, in1b ); |
|
// ConcatTransforms( in1b, in2, out ); |
|
// return; |
|
// } |
|
// if ( &in2 == &out ) |
|
// { |
|
// matrix3x4_t in2b; |
|
// MatrixCopy( in2, in2b ); |
|
// ConcatTransforms( in1, in2b, out ); |
|
// return; |
|
// } |
|
|
|
#define MULT(i,j) (in1[i][0]*in2[0][j] + \ |
|
in1[i][1]*in2[1][j] + \ |
|
in1[i][2]*in2[2][j] + \ |
|
in1[i][3]*in2[3][j]) |
|
|
|
out[0][0] = MULT(0,0); |
|
out[0][1] = MULT(0,1); |
|
out[0][2] = MULT(0,2); |
|
out[0][3] = MULT(0,3); |
|
out[1][0] = MULT(1,0); |
|
out[1][1] = MULT(1,1); |
|
out[1][2] = MULT(1,2); |
|
out[1][3] = MULT(1,3); |
|
out[2][0] = MULT(2,0); |
|
out[2][1] = MULT(2,1); |
|
out[2][2] = MULT(2,2); |
|
out[2][3] = MULT(2,3); |
|
out[3][0] = MULT(3,0); |
|
out[3][1] = MULT(3,1); |
|
out[3][2] = MULT(3,2); |
|
out[3][3] = MULT(3,3); |
|
|
|
#undef MULT |
|
|
|
} |
|
|
|
void M_AngleMatrix( RadianEuler const &angles, const Vector &position, M_matrix4x4_t& matrix ) |
|
{ |
|
// Assert( s_bMathlibInitialized ); |
|
float sx, sy, sz, cx, cy, cz; |
|
|
|
|
|
sx = sinf(angles[0]); |
|
cx = cosf(angles[0]); |
|
sy = sinf(angles[1]); |
|
cy = cosf(angles[1]); |
|
sz = sinf(angles[2]); |
|
cz = cosf(angles[2]); |
|
|
|
// SinCos( angles[0], &sx, &cx ); // 2 |
|
// SinCos( angles[1], &sy, &cy ); // 1 |
|
// SinCos( angles[2], &sz, &cz ); // 0 |
|
|
|
M_matrix4x4_t mx, my, mz, temp1; |
|
|
|
// rotation about x |
|
mx[1][1] = cx; |
|
mx[1][2] = sx; |
|
mx[2][1] = -sx; |
|
mx[2][2] = cx; |
|
|
|
// rotation about y |
|
my[0][0] = cy; |
|
my[0][2] = -sy; |
|
my[2][0] = sy; |
|
my[2][2] = cy; |
|
|
|
// rotation about z |
|
mz[0][0] = cz; |
|
mz[0][1] = sz; |
|
mz[1][0] = -sz; |
|
mz[1][1] = cz; |
|
|
|
// z * y * x |
|
M_ConcatTransforms(mx, my, temp1); |
|
M_ConcatTransforms(temp1, mz, matrix); |
|
|
|
// put position in |
|
matrix[3][0] = position.x; |
|
matrix[3][1] = position.y; |
|
matrix[3][2] = position.z; |
|
|
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Motion mapper functions |
|
//----------------------------------------------------------------------------- |
|
#define BONEAXIS 0 |
|
#define BONEDIR 0 |
|
#define BONESIDE 1 |
|
#define BONEUP 2 |
|
#define WORLDUP 2 |
|
#define PRINTMAT(m) \ |
|
printf("\n%f %f %f %f\n", m[0][0], m[0][1], m[0][2], m[0][3]); \ |
|
printf("%f %f %f %f\n", m[1][0], m[1][1], m[1][2], m[1][3]); \ |
|
printf("%f %f %f %f\n", m[2][0], m[2][1], m[2][2], m[2][3]); \ |
|
printf("%f %f %f %f\n", m[3][0], m[3][1], m[3][2], m[3][3]); |
|
|
|
struct s_planeConstraint_t |
|
{ |
|
char jointNameString[1024]; |
|
float floor; |
|
int axis; |
|
|
|
}; |
|
|
|
struct s_iksolve_t |
|
{ |
|
char jointNameString[1024]; |
|
int reverseSolve; |
|
float extremityScale; |
|
Vector limbRootOffsetScale; |
|
int doRelativeLock; |
|
char relativeLockNameString[1024]; |
|
float relativeLockScale; |
|
|
|
}; |
|
|
|
struct s_jointScale_t |
|
{ |
|
char jointNameString[1024]; |
|
float scale; |
|
}; |
|
|
|
struct s_template_t |
|
{ |
|
char rootScaleJoint[1024]; |
|
float rootScaleAmount; |
|
int numIKSolves; |
|
s_iksolve_t *ikSolves[128]; |
|
int numJointScales; |
|
s_jointScale_t *jointScales[128]; |
|
int numPlaneConstraints; |
|
s_planeConstraint_t *planeConstraints[128]; |
|
float toeFloorZ; |
|
int doSkeletonScale; |
|
float skeletonScale; |
|
|
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Load a template file into structure |
|
//----------------------------------------------------------------------------- |
|
s_template_t *New_Template() |
|
{ |
|
s_template_t *pTemplate = (s_template_t *)kalloc(1, sizeof(s_template_t)); |
|
pTemplate->rootScaleAmount = 1.0; |
|
pTemplate->numIKSolves = 0; |
|
pTemplate->numJointScales = 0; |
|
pTemplate->toeFloorZ = 2.802277; |
|
pTemplate->numPlaneConstraints = 0; |
|
pTemplate->doSkeletonScale = 0; |
|
pTemplate->skeletonScale = 1.0; |
|
return pTemplate; |
|
} |
|
s_iksolve_t *New_IKSolve() |
|
{ |
|
s_iksolve_t *pIKSolve = (s_iksolve_t *)kalloc(1, sizeof(s_iksolve_t)); |
|
pIKSolve->reverseSolve = 0; |
|
pIKSolve->extremityScale = 1.0; |
|
pIKSolve->limbRootOffsetScale[0] = pIKSolve->limbRootOffsetScale[1] = pIKSolve->limbRootOffsetScale[2] = 0.0; |
|
pIKSolve->doRelativeLock = 0; |
|
pIKSolve->relativeLockScale = 1.0; |
|
return pIKSolve; |
|
} |
|
|
|
s_planeConstraint_t *New_planeConstraint(float floor) |
|
{ |
|
s_planeConstraint_t *pConstraint = (s_planeConstraint_t *)kalloc(1, sizeof(s_planeConstraint_t)); |
|
pConstraint->floor = floor; |
|
pConstraint->axis = 2; |
|
|
|
return pConstraint; |
|
} |
|
|
|
void Set_DefaultTemplate(s_template_t *pTemplate) |
|
{ |
|
pTemplate->numJointScales = 0; |
|
|
|
strcpy(pTemplate->rootScaleJoint, "ValveBiped.Bip01_L_Foot"); |
|
pTemplate->rootScaleAmount = 1.0; |
|
|
|
pTemplate->numIKSolves = 4; |
|
pTemplate->ikSolves[0] = New_IKSolve(); |
|
pTemplate->ikSolves[1] = New_IKSolve(); |
|
pTemplate->ikSolves[2] = New_IKSolve(); |
|
pTemplate->ikSolves[3] = New_IKSolve(); |
|
|
|
|
|
pTemplate->numPlaneConstraints = 2; |
|
pTemplate->planeConstraints[0] = New_planeConstraint(pTemplate->toeFloorZ); |
|
strcpy(pTemplate->planeConstraints[0]->jointNameString, "ValveBiped.Bip01_L_Toe0"); |
|
pTemplate->planeConstraints[1] = New_planeConstraint(pTemplate->toeFloorZ); |
|
strcpy(pTemplate->planeConstraints[1]->jointNameString, "ValveBiped.Bip01_R_Toe0"); |
|
|
|
strcpy(pTemplate->ikSolves[0]->jointNameString, "ValveBiped.Bip01_L_Foot"); |
|
pTemplate->ikSolves[0]->reverseSolve = 0; |
|
pTemplate->ikSolves[0]->extremityScale = 1.0; |
|
pTemplate->ikSolves[0]->limbRootOffsetScale[0] = 1.0; |
|
pTemplate->ikSolves[0]->limbRootOffsetScale[1] = 1.0; |
|
pTemplate->ikSolves[0]->limbRootOffsetScale[2] = 0.0; |
|
|
|
strcpy(pTemplate->ikSolves[1]->jointNameString, "ValveBiped.Bip01_R_Foot"); |
|
pTemplate->ikSolves[1]->reverseSolve = 0; |
|
pTemplate->ikSolves[1]->extremityScale = 1.0; |
|
pTemplate->ikSolves[1]->limbRootOffsetScale[0] = 1.0; |
|
pTemplate->ikSolves[1]->limbRootOffsetScale[1] = 1.0; |
|
pTemplate->ikSolves[1]->limbRootOffsetScale[2] = 0.0; |
|
|
|
strcpy(pTemplate->ikSolves[2]->jointNameString, "ValveBiped.Bip01_R_Hand"); |
|
pTemplate->ikSolves[2]->reverseSolve = 1; |
|
pTemplate->ikSolves[2]->extremityScale = 1.0; |
|
pTemplate->ikSolves[2]->limbRootOffsetScale[0] = 0.0; |
|
pTemplate->ikSolves[2]->limbRootOffsetScale[1] = 0.0; |
|
pTemplate->ikSolves[2]->limbRootOffsetScale[2] = 1.0; |
|
|
|
strcpy(pTemplate->ikSolves[3]->jointNameString, "ValveBiped.Bip01_L_Hand"); |
|
pTemplate->ikSolves[3]->reverseSolve = 1; |
|
pTemplate->ikSolves[3]->extremityScale = 1.0; |
|
pTemplate->ikSolves[3]->limbRootOffsetScale[0] = 0.0; |
|
pTemplate->ikSolves[3]->limbRootOffsetScale[1] = 0.0; |
|
pTemplate->ikSolves[3]->limbRootOffsetScale[2] = 1.0; |
|
// pTemplate->ikSolves[3]->doRelativeLock = 1; |
|
// strcpy(pTemplate->ikSolves[3]->relativeLockNameString, "ValveBiped.Bip01_R_Hand"); |
|
// pTemplate->ikSolves[3]->relativeLockScale = 1.0; |
|
|
|
} |
|
|
|
void split(char *str, char *sep, char **sp) |
|
{ |
|
char *r = strtok(str, sep); |
|
while(r != NULL) |
|
{ |
|
*sp = r; |
|
sp++; |
|
r = strtok(NULL, sep); |
|
} |
|
*sp = NULL; |
|
} |
|
|
|
|
|
int checkCommand(char *str, char *cmd, int numOptions, int numSplit) |
|
{ |
|
if(strcmp(str, cmd) == 0) |
|
{ |
|
if(numOptions <= numSplit) |
|
return 1; |
|
else |
|
{ |
|
printf("Error: Number or argument mismatch in template file cmd %s, requires %i, found %i\n", cmd, numOptions, numSplit); |
|
return 0; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
s_template_t *Load_Template(char *name ) |
|
{ |
|
|
|
// Sanity check file and init |
|
Assert(name); |
|
|
|
s_template_t *pTemplate = New_Template(); |
|
|
|
|
|
// Open file |
|
if (!OpenGlobalFile( name )) |
|
return 0; |
|
|
|
|
|
//March through lines |
|
g_iLinecount = 0; |
|
while(fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL) |
|
{ |
|
g_iLinecount++; |
|
if(g_szLine[0] == '#') |
|
continue; |
|
|
|
char *endP = strrchr(g_szLine, '\n'); |
|
if(endP != NULL) |
|
*endP = '\0'; |
|
|
|
|
|
char *sp[128]; |
|
char **spp = sp; |
|
|
|
char sep[] = " "; |
|
split(g_szLine, sep, sp); |
|
int numSplit = 0; |
|
|
|
while(*spp != NULL) |
|
{ |
|
spp++; |
|
numSplit++; |
|
|
|
} |
|
if(numSplit < 1 || |
|
*sp[0] == '\n') |
|
continue; |
|
|
|
|
|
// int numRead = sscanf( g_szLine, "%s %s %s", cmd, &option, &option2 ); |
|
|
|
// // Blank line |
|
// if ((numRead == EOF) || (numRead == 0)) |
|
// continue; |
|
|
|
// commands |
|
char *cmd; |
|
int numOptions = numSplit - 1; |
|
|
|
cmd = sp[0]; |
|
if(checkCommand(cmd, "twoJointIKSolve", 1, numOptions)) |
|
{ |
|
printf("\nCreating two joint IK solve %s\n", sp[1]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves] = New_IKSolve(); |
|
strcpy(pTemplate->ikSolves[pTemplate->numIKSolves]->jointNameString, sp[1]); |
|
pTemplate->numIKSolves++; |
|
|
|
} |
|
else if(checkCommand(cmd, "oneJointPlaneConstraint", 1, numOptions)) |
|
{ |
|
printf("\nCreating one joint plane constraint %s\n", sp[1]); |
|
pTemplate->planeConstraints[pTemplate->numPlaneConstraints] = New_planeConstraint(pTemplate->toeFloorZ); |
|
strcpy(pTemplate->planeConstraints[pTemplate->numPlaneConstraints]->jointNameString, sp[1]); |
|
pTemplate->numPlaneConstraints++; |
|
|
|
} |
|
else if(checkCommand(cmd, "reverseSolve", 1, numOptions)) |
|
{ |
|
printf("reverseSolve: %s\n", sp[1]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->reverseSolve = atoi(sp[1]); |
|
} |
|
else if(checkCommand(cmd, "extremityScale", 1, numOptions)) |
|
{ |
|
printf("extremityScale: %s\n", sp[1]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->extremityScale = atof(sp[1]); |
|
} |
|
else if(checkCommand(cmd, "limbRootOffsetScale", 3, numOptions)) |
|
{ |
|
printf("limbRootOffsetScale: %s %s %s\n", sp[1], sp[2], sp[3]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->limbRootOffsetScale[0] = atof(sp[1]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->limbRootOffsetScale[1] = atof(sp[2]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->limbRootOffsetScale[2] = atof(sp[3]); |
|
} |
|
else if(checkCommand(cmd, "toeFloorZ", 1, numOptions)) |
|
{ |
|
printf("toeFloorZ: %s\n", sp[1]); |
|
pTemplate->toeFloorZ = atof(sp[1]); |
|
} |
|
else if(checkCommand(cmd, "relativeLock", 2, numOptions)) |
|
{ |
|
printf("relativeLock: %s\n", sp[1]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->doRelativeLock = 1; |
|
strcpy(pTemplate->ikSolves[pTemplate->numIKSolves - 1]->relativeLockNameString, sp[1]); |
|
pTemplate->ikSolves[pTemplate->numIKSolves - 1]->relativeLockScale = atof(sp[2]); |
|
|
|
} |
|
else if(checkCommand(cmd, "rootScaleJoint", 1, numOptions)) |
|
{ |
|
printf("\nrootScaleJoint: %s\n", sp[1]); |
|
strcpy(pTemplate->rootScaleJoint, sp[1]); |
|
} |
|
else if(checkCommand(cmd, "rootScaleAmount", 1, numOptions)) |
|
{ |
|
printf("rootScaleAmount: %s\n", sp[1]); |
|
pTemplate->rootScaleAmount = atof(sp[1]); |
|
} |
|
else if(checkCommand(cmd, "jointScale", 2, numOptions)) |
|
{ |
|
printf("\nCreating joint scale %s of %s\n", sp[1], sp[2]); |
|
pTemplate->jointScales[pTemplate->numJointScales] = (s_jointScale_t *)kalloc(1, sizeof(s_jointScale_t)); |
|
strcpy(pTemplate->jointScales[pTemplate->numJointScales]->jointNameString, sp[1]); |
|
pTemplate->jointScales[pTemplate->numJointScales]->scale = atof(sp[2]); |
|
pTemplate->numJointScales++; |
|
} |
|
else if(checkCommand(cmd, "skeletonScale", 2, numOptions)) |
|
{ |
|
printf("\nCreating skeleton scale of %s\n", sp[1]); |
|
pTemplate->doSkeletonScale = 1; |
|
pTemplate->skeletonScale = atof(sp[1]); |
|
} |
|
else |
|
{ |
|
MdlWarning("unknown studio command\n" ); |
|
} |
|
} |
|
fclose( g_fpInput ); |
|
return pTemplate; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// get node index from node string name |
|
//----------------------------------------------------------------------------- |
|
int GetNodeIndex(s_source_t *psource, char *nodeName) |
|
{ |
|
for(int i = 0; i < psource->numbones; i++) |
|
{ |
|
if(strcmp(nodeName, psource->localBone[i].name) == 0) |
|
{ |
|
return i; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// get node index from node string name |
|
//----------------------------------------------------------------------------- |
|
void GetNodePath(s_source_t *psource, int startIndex, int endIndex, int *path) |
|
{ |
|
*path = endIndex; |
|
|
|
s_node_t *nodes; |
|
nodes = psource->localBone; |
|
while(*path != startIndex) |
|
{ |
|
int parent = nodes[*path].parent; |
|
path++; |
|
*path = parent; |
|
} |
|
path++; |
|
*path = -1; |
|
} |
|
|
|
void SumBonePathTranslations(int *indexPath, s_bone_t *boneArray, Vector &resultVector, int rootOffset = 0) |
|
{ |
|
|
|
// walk the path |
|
int *pathPtr = indexPath; |
|
// M_matrix4x4_t matrixCum; |
|
|
|
// find length of path |
|
int length = 0; |
|
while(*pathPtr != -1) |
|
{ |
|
length++; |
|
pathPtr++; |
|
} |
|
|
|
int l = length - (1 + rootOffset); |
|
|
|
resultVector[0] = 0.0; |
|
resultVector[1] = 0.0; |
|
resultVector[2] = 0.0; |
|
|
|
for(int i = l; i > -1; i--) |
|
{ |
|
s_bone_t *thisBone = boneArray + indexPath[i]; |
|
resultVector += thisBone->pos; |
|
} |
|
} |
|
|
|
void CatBonePath(int *indexPath, s_bone_t *boneArray, M_matrix4x4_t &resultMatrix, int rootOffset = 0) |
|
{ |
|
|
|
// walk the path |
|
int *pathPtr = indexPath; |
|
// M_matrix4x4_t matrixCum; |
|
|
|
// find length of path |
|
int length = 0; |
|
while(*pathPtr != -1) |
|
{ |
|
length++; |
|
pathPtr++; |
|
} |
|
|
|
int l = length - (1 + rootOffset); |
|
|
|
for(int i = l; i > -1; i--) |
|
{ |
|
s_bone_t *thisBone = boneArray + indexPath[i]; |
|
// printf("bone index: %i %i\n", i, indexPath[i]); |
|
// printf("pos: %f %f %f, rot: %f %f %f\n", thisBone->pos.x, thisBone->pos.y, thisBone->pos.z, thisBone->rot.x, thisBone->rot.y, thisBone->rot.z); |
|
M_matrix4x4_t thisMatrix; |
|
M_AngleMatrix(thisBone->rot, thisBone->pos, thisMatrix); |
|
// PRINTMAT(thisMatrix) |
|
M_matrix4x4_t tempCum; |
|
M_MatrixCopy(resultMatrix, tempCum); |
|
M_ConcatTransforms(thisMatrix, tempCum, resultMatrix); |
|
} |
|
// PRINTMAT(matrixCum); |
|
// M_MatrixAngles(matrixCum, resultBone.rot, resultBone.pos); |
|
|
|
// printf("pos: %f %f %f, rot: %f %f %f\n", resultBone.pos.x,resultBone.pos.y, resultBone.pos.z, RAD2DEG(resultBone.rot.x),RAD2DEG(resultBone.rot.y),RAD2DEG(resultBone.rot.z)); |
|
|
|
} |
|
// int ConformSources(s_source_t *pSource, s_source_t *pTarget) |
|
// { |
|
// if(pSource->numbones != *pTarget->numbones) |
|
// { |
|
// printf("ERROR: The number of bones in the target file must match the source file."); |
|
// return 1; |
|
// } |
|
// if(pSource->numframes != pTarget->numframes) |
|
// { |
|
// printf("Note: Source and target frame lengths do not match"); |
|
// for(int t = 0; t < pTarget->numframes; t++) |
|
// { |
|
// free(pTarget->rawanim[t]); |
|
// } |
|
// pTarget->numframes = pSource->numframes; |
|
// int size = pTarget->numbones * sizeof( s_bone_t ); |
|
// for(t = 0; t < pTarget->numframes; t++) |
|
// { |
|
// pTarget->rawanim[t] = (s_bone_t *) kalloc(1, size); |
|
// memcpy((void *) pSource->rawanim[t], (void *) pTarget->rawanim[t], size |
|
// } |
|
// } |
|
// pTarget->startframe = pSource->startframe; |
|
// pTarget->endframe = pSource->endframe; |
|
|
|
|
|
|
|
|
|
void ScaleJointsFrame(s_source_t *pSkeleton, s_jointScale_t *jointScale, int t) |
|
{ |
|
int numBones = pSkeleton->numbones; |
|
|
|
for(int i = 0; i < numBones; i++) |
|
{ |
|
s_node_t pNode = pSkeleton->localBone[i]; |
|
s_bone_t *pSkelBone = &pSkeleton->rawanim[t][i]; |
|
if(strcmp(jointScale->jointNameString, pNode.name) == 0) |
|
{ |
|
// printf("Scaling joint %s\n", pNode.name); |
|
pSkelBone->pos = pSkelBone->pos * jointScale->scale; |
|
} |
|
|
|
} |
|
} |
|
void ScaleJoints(s_source_t *pSkeleton, s_jointScale_t *jointScale) |
|
{ |
|
int numFrames = pSkeleton->numframes; |
|
for(int t = 0; t < numFrames; t++) |
|
{ |
|
ScaleJointsFrame(pSkeleton, jointScale, t); |
|
} |
|
} |
|
|
|
void ScaleSkeletonFrame(s_source_t *pSkeleton, float scale, int t) |
|
{ |
|
int numBones = pSkeleton->numbones; |
|
|
|
for(int i = 0; i < numBones; i++) |
|
{ |
|
s_bone_t *pSkelBone = &pSkeleton->rawanim[t][i]; |
|
pSkelBone->pos = pSkelBone->pos * scale; |
|
|
|
} |
|
} |
|
void ScaleSkeleton(s_source_t *pSkeleton, float scale) |
|
{ |
|
int numFrames = pSkeleton->numframes; |
|
for(int t = 0; t < numFrames; t++) |
|
{ |
|
ScaleSkeletonFrame(pSkeleton, scale, t); |
|
} |
|
} |
|
|
|
void CombineSkeletonAnimationFrame(s_source_t *pSkeleton, s_source_t *pAnimation, s_bone_t **ppAnim, int t) |
|
{ |
|
int numBones = pAnimation->numbones; |
|
int size = numBones * sizeof( s_bone_t ); |
|
ppAnim[t] = (s_bone_t *) kalloc(1, size); |
|
for(int i = 0; i < numBones; i++) |
|
{ |
|
s_node_t pNode = pAnimation->localBone[i]; |
|
s_bone_t pAnimBone = pAnimation->rawanim[t][i]; |
|
|
|
if(pNode.parent > -1) |
|
{ |
|
if ( i < pSkeleton->numbones ) |
|
{ |
|
s_bone_t pSkelBone = pSkeleton->rawanim[0][i]; |
|
ppAnim[t][i].pos = pSkelBone.pos; |
|
} |
|
else |
|
{ |
|
if ( !g_bGaveMissingBoneWarning ) |
|
{ |
|
g_bGaveMissingBoneWarning = true; |
|
Warning( "Warning: Target skeleton has less bones than source animation. Reverting to source data for extra bones.\n" ); |
|
} |
|
|
|
ppAnim[t][i].pos = pAnimBone.pos; |
|
} |
|
} |
|
else |
|
{ |
|
ppAnim[t][i].pos = pAnimBone.pos; |
|
} |
|
|
|
ppAnim[t][i].rot = pAnimBone.rot; |
|
} |
|
} |
|
void CombineSkeletonAnimation(s_source_t *pSkeleton, s_source_t *pAnimation, s_bone_t **ppAnim) |
|
{ |
|
int numFrames = pAnimation->numframes; |
|
for(int t = 0; t < numFrames; t++) |
|
{ |
|
CombineSkeletonAnimationFrame(pSkeleton, pAnimation, ppAnim, t); |
|
} |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------- |
|
// MotionMap |
|
//-------------------------------------------------------------------- |
|
s_source_t *MotionMap( s_source_t *pSource, s_source_t *pTarget, s_template_t *pTemplate ) |
|
{ |
|
|
|
// scale skeleton |
|
if(pTemplate->doSkeletonScale) |
|
{ |
|
ScaleSkeleton(pTarget, pTemplate->skeletonScale); |
|
} |
|
|
|
// scale joints |
|
for(int j = 0; j < pTemplate->numJointScales; j++) |
|
{ |
|
s_jointScale_t *pJointScale = pTemplate->jointScales[j]; |
|
ScaleJoints(pTarget, pJointScale); |
|
} |
|
|
|
|
|
// root stuff |
|
char rootString[128] = "ValveBiped.Bip01"; |
|
|
|
// !!! PARAMETER |
|
int rootIndex = GetNodeIndex(pSource, rootString); |
|
int rootScaleIndex = GetNodeIndex(pSource, pTemplate->rootScaleJoint); |
|
int rootScalePath[512]; |
|
if(rootScaleIndex > -1) |
|
{ |
|
GetNodePath(pSource, rootIndex, rootScaleIndex, rootScalePath); |
|
} |
|
else |
|
{ |
|
printf("Error: Can't find node\n"); |
|
exit(0); |
|
} |
|
float rootScaleLengthSrc = pSource->rawanim[0][rootScaleIndex].pos[BONEDIR]; |
|
float rootScaleParentLengthSrc = pSource->rawanim[0][rootScalePath[1]].pos[BONEDIR]; |
|
float rootScaleSrc = rootScaleLengthSrc + rootScaleParentLengthSrc; |
|
float rootScaleLengthTgt = pTarget->rawanim[0][rootScaleIndex].pos[BONEDIR]; |
|
float rootScaleParentLengthTgt = pTarget->rawanim[0][rootScalePath[1]].pos[BONEDIR]; |
|
float rootScaleTgt = rootScaleLengthTgt + rootScaleParentLengthTgt; |
|
float rootScaleFactor = rootScaleTgt / rootScaleSrc; |
|
|
|
if(g_verbose) |
|
printf("Root Scale Factor: %f\n", rootScaleFactor); |
|
|
|
|
|
// root scale origin |
|
float toeFloorZ = pTemplate->toeFloorZ; |
|
Vector rootScaleOrigin = pSource->rawanim[0][rootIndex].pos; |
|
rootScaleOrigin[2] = toeFloorZ; |
|
|
|
|
|
// setup workspace |
|
s_bone_t *combinedRefAnimation[MAXSTUDIOANIMFRAMES]; |
|
s_bone_t *combinedAnimation[MAXSTUDIOANIMFRAMES]; |
|
s_bone_t *sourceAnimation[MAXSTUDIOANIMFRAMES]; |
|
CombineSkeletonAnimation(pTarget, pSource, combinedAnimation); |
|
CombineSkeletonAnimation(pTarget, pSource, combinedRefAnimation); |
|
|
|
|
|
// do source and target sanity checking |
|
int sourceNumFrames = pSource->numframes; |
|
|
|
|
|
// iterate through limb solves |
|
for(int t = 0; t < sourceNumFrames; t++) |
|
{ |
|
// setup pTarget for skeleton comparison |
|
pTarget->rawanim[t] = combinedRefAnimation[t]; |
|
|
|
printf("Note: Processing frame: %i\n", t); |
|
for(int ii = 0; ii < pTemplate->numIKSolves; ii++) |
|
{ |
|
s_iksolve_t *thisSolve = pTemplate->ikSolves[ii]; |
|
|
|
char *thisJointNameString = thisSolve->jointNameString; |
|
int thisJointIndex = GetNodeIndex(pSource, thisJointNameString); |
|
|
|
// init paths to feet |
|
int thisJointPathInRoot[512]; |
|
|
|
// get paths to feet |
|
if(thisJointIndex > -1) |
|
{ |
|
GetNodePath(pSource, rootIndex, thisJointIndex, thisJointPathInRoot); |
|
} |
|
else |
|
{ |
|
printf("Error: Can't find node: %s\n" , thisJointNameString); |
|
exit(0); |
|
} |
|
|
|
// leg "root" or thigh pointers |
|
//int gParentIndex = thisJointPathInRoot[2]; |
|
int *gParentPath = thisJointPathInRoot + 2; |
|
|
|
//---------------------------------------------------------------- |
|
// get limb lengths |
|
//---------------------------------------------------------------- |
|
float thisJointLengthSrc = pSource->rawanim[0][thisJointIndex].pos[BONEDIR]; |
|
float parentJointLengthSrc = pSource->rawanim[0][thisJointPathInRoot[1]].pos[BONEDIR]; |
|
|
|
float thisLimbLengthSrc = thisJointLengthSrc + parentJointLengthSrc; |
|
|
|
float thisJointLengthTgt = pTarget->rawanim[0][thisJointIndex].pos[BONEDIR]; |
|
float parentJointLengthTgt = pTarget->rawanim[0][thisJointPathInRoot[1]].pos[BONEDIR]; |
|
|
|
float thisLimbLengthTgt = thisJointLengthTgt + parentJointLengthTgt; |
|
|
|
// Factor leg length delta |
|
float thisLimbLength = thisLimbLengthSrc - thisLimbLengthTgt; |
|
float thisLimbLengthFactor = thisLimbLengthTgt / thisLimbLengthSrc; |
|
|
|
if(g_verbose) |
|
printf("limb length %s: %i: %f, factor %f\n", thisJointNameString, thisJointIndex, thisLimbLength, thisLimbLengthFactor); |
|
|
|
// calculate joint grandparent offset |
|
// Note: because there's no reference pose this doesn't take rotation into account. |
|
// This only works because of the assumption that joint translations aren't animated. |
|
M_matrix4x4_t gParentGlobalMatSrc, gParentGlobalMatTgt; |
|
Vector gParentGlobalSrc, gParentGlobalTgt; |
|
|
|
// SumBonePathTranslations(gParentPath, pSource->rawanim[t], gParentGlobalSrc, 1); |
|
// SumBonePathTranslations(gParentPath, pTarget->rawanim[t], gParentGlobalTgt, 1); |
|
|
|
// get root path to source parent |
|
CatBonePath(gParentPath, pSource->rawanim[t], gParentGlobalMatSrc, 1); |
|
// check against reference animation |
|
CatBonePath(gParentPath, pTarget->rawanim[t], gParentGlobalMatTgt, 1); |
|
|
|
gParentGlobalSrc[0] = gParentGlobalMatSrc[3][0]; |
|
gParentGlobalSrc[1] = gParentGlobalMatSrc[3][1]; |
|
gParentGlobalSrc[2] = gParentGlobalMatSrc[3][2]; |
|
|
|
gParentGlobalTgt[0] = gParentGlobalMatTgt[3][0]; |
|
gParentGlobalTgt[1] = gParentGlobalMatTgt[3][1]; |
|
gParentGlobalTgt[2] = gParentGlobalMatTgt[3][2]; |
|
|
|
|
|
Vector gParentDelta(gParentGlobalTgt - gParentGlobalSrc); |
|
|
|
if(g_verbose) |
|
printf("Grand parent delta: %f %f %f\n", gParentDelta[0], gParentDelta[1], gParentDelta[2]); |
|
|
|
gParentDelta *= thisSolve->limbRootOffsetScale; |
|
|
|
|
|
//---------------------------------------------------------------- |
|
// time takes effect here |
|
// above waste is unavoidable? |
|
//---------------------------------------------------------------- |
|
M_matrix4x4_t rootMat; |
|
M_AngleMatrix(pSource->rawanim[t][rootIndex].rot, pSource->rawanim[t][rootIndex].pos, rootMat); |
|
|
|
|
|
// OK, time to get it together |
|
// 1) scale foot by legLengthFactor in the non-translated thigh space |
|
// 2) translate foot by legRootDelta in the space of the root |
|
// do we leave everything in the space of the root then? PROBABLY!! |
|
|
|
M_matrix4x4_t thisJointMat, parentJointMat, thisJointInGParentMat; |
|
M_AngleMatrix(pSource->rawanim[t][thisJointPathInRoot[0]].rot, pSource->rawanim[t][thisJointPathInRoot[0]].pos, thisJointMat); |
|
M_AngleMatrix(pSource->rawanim[t][thisJointPathInRoot[1]].rot, pSource->rawanim[t][thisJointPathInRoot[1]].pos, parentJointMat); |
|
M_ConcatTransforms(thisJointMat, parentJointMat, thisJointInGParentMat); |
|
|
|
if(!thisSolve->doRelativeLock) |
|
{ |
|
// scale around grand parent |
|
float effectiveScaleFactor = ((thisLimbLengthFactor - 1.0) * thisSolve->extremityScale ) + 1.0; |
|
thisJointInGParentMat[3][0] *= effectiveScaleFactor; |
|
thisJointInGParentMat[3][1] *= effectiveScaleFactor; |
|
thisJointInGParentMat[3][2] *= effectiveScaleFactor; |
|
} |
|
|
|
// adjust into source root space |
|
M_matrix4x4_t gParentInRootMat, thisJointInRootMat; |
|
CatBonePath(gParentPath, pSource->rawanim[t], gParentInRootMat, 1); |
|
M_ConcatTransforms(thisJointInGParentMat, gParentInRootMat, thisJointInRootMat); |
|
|
|
if(!thisSolve->doRelativeLock) |
|
{ |
|
// adjust by difference of local root |
|
thisJointInRootMat[3][0] += gParentDelta[0]; |
|
thisJointInRootMat[3][1] += gParentDelta[1]; |
|
thisJointInRootMat[3][2] += gParentDelta[2]; |
|
} |
|
else |
|
{ |
|
char *relativeJointNameString = thisSolve->relativeLockNameString; |
|
int relativeJointIndex = GetNodeIndex(pSource, relativeJointNameString); |
|
|
|
// init paths to feet |
|
int relativeJointPathInRoot[512]; |
|
|
|
// get paths to feet |
|
if(relativeJointIndex > -1) |
|
{ |
|
GetNodePath(pSource, rootIndex, relativeJointIndex, relativeJointPathInRoot); |
|
} |
|
else |
|
{ |
|
printf("Error: Can't find node: %s\n" , relativeJointNameString); |
|
exit(0); |
|
} |
|
// get the source relative joint |
|
M_matrix4x4_t relativeJointInRootMatSrc, relativeJointInRootMatSrcInverse, thisJointInRelativeSrcMat; |
|
CatBonePath(relativeJointPathInRoot, pSource->rawanim[t], relativeJointInRootMatSrc, 1); |
|
M_MatrixInvert(relativeJointInRootMatSrc, relativeJointInRootMatSrcInverse); |
|
M_ConcatTransforms(thisJointInRootMat, relativeJointInRootMatSrcInverse, thisJointInRelativeSrcMat); |
|
if(thisSolve->relativeLockScale != 1.0) |
|
{ |
|
thisJointInRelativeSrcMat[3][0] *= thisSolve->relativeLockScale; |
|
thisJointInRelativeSrcMat[3][1] *= thisSolve->relativeLockScale; |
|
thisJointInRelativeSrcMat[3][2] *= thisSolve->relativeLockScale; |
|
} |
|
|
|
// swap momentarily to get new destination |
|
// NOTE: the relative lock must have already been solved |
|
sourceAnimation[t] = pSource->rawanim[t]; |
|
pSource->rawanim[t] = combinedAnimation[t]; |
|
|
|
// get new relative location |
|
M_matrix4x4_t relativeJointInRootMatTgt; |
|
CatBonePath(relativeJointPathInRoot, pSource->rawanim[t], relativeJointInRootMatTgt, 1); |
|
M_ConcatTransforms(thisJointInRelativeSrcMat, relativeJointInRootMatTgt, thisJointInRootMat); |
|
|
|
// swap back just for cleanliness |
|
// a little overkill as it's just swapped |
|
// just leaving it here for clarity |
|
combinedAnimation[t] = pSource->rawanim[t]; |
|
pSource->rawanim[t] = sourceAnimation[t]; |
|
|
|
} |
|
|
|
//---------------------------------------------------------------- |
|
// swap animation |
|
//---------------------------------------------------------------- |
|
sourceAnimation[t] = pSource->rawanim[t]; |
|
pSource->rawanim[t] = combinedAnimation[t]; |
|
|
|
//---------------------------------------------------------------- |
|
// make thigh data global based on new skeleton |
|
//---------------------------------------------------------------- |
|
// get thigh in global space |
|
M_matrix4x4_t gParentInTgtRootMat, ggParentInTgtRootMat; |
|
// int *gParentPath = thisJointPathInRoot + 2; |
|
CatBonePath(gParentPath, pSource->rawanim[t], gParentInTgtRootMat, 1); |
|
CatBonePath(gParentPath+1, pSource->rawanim[t], ggParentInTgtRootMat, 1); |
|
|
|
|
|
//---------------------------------------------------------------- |
|
// Calculate IK for legs |
|
//---------------------------------------------------------------- |
|
float parentJointLength = pSource->rawanim[t][*(thisJointPathInRoot + 1)].pos[BONEDIR]; |
|
float thisJointLength = pSource->rawanim[t][thisJointIndex].pos[BONEDIR]; |
|
|
|
Vector thisLimbHypot; |
|
thisLimbHypot[0] = thisJointInRootMat[3][0] - gParentInTgtRootMat[3][0]; |
|
thisLimbHypot[1] = thisJointInRootMat[3][1] - gParentInTgtRootMat[3][1]; |
|
thisLimbHypot[2] = thisJointInRootMat[3][2] - gParentInTgtRootMat[3][2]; |
|
|
|
float thisLimbHypotLength = thisLimbHypot.Length(); |
|
|
|
// law of cosines! |
|
float gParentCos = (thisLimbHypotLength*thisLimbHypotLength + parentJointLength*parentJointLength - thisJointLength*thisJointLength) / (2*parentJointLength*thisLimbHypotLength); |
|
float parentCos = (parentJointLength*parentJointLength + thisJointLength*thisJointLength - thisLimbHypotLength*thisLimbHypotLength) / (2*parentJointLength*thisJointLength); |
|
|
|
VectorNormalize(thisLimbHypot); |
|
|
|
Vector thisLimbHypotUnit = thisLimbHypot; |
|
|
|
M_matrix4x4_t gParentJointIKMat; |
|
Vector gParentJointIKRot, gParentJointIKOrth; |
|
|
|
gParentJointIKRot[0] = gParentInTgtRootMat[BONEUP][0]; |
|
gParentJointIKRot[1] = gParentInTgtRootMat[BONEUP][1]; |
|
gParentJointIKRot[2] = gParentInTgtRootMat[BONEUP][2]; |
|
|
|
VectorNormalize(gParentJointIKRot); |
|
gParentJointIKOrth = gParentJointIKRot.Cross(thisLimbHypotUnit); |
|
VectorNormalize(gParentJointIKOrth); |
|
gParentJointIKRot = thisLimbHypotUnit.Cross(gParentJointIKOrth); |
|
VectorNormalize(gParentJointIKRot); |
|
|
|
M_MatrixCopy(gParentInTgtRootMat, gParentJointIKMat); |
|
|
|
gParentJointIKMat[0][0] = thisLimbHypotUnit[0]; |
|
gParentJointIKMat[0][1] = thisLimbHypotUnit[1]; |
|
gParentJointIKMat[0][2] = thisLimbHypotUnit[2]; |
|
|
|
gParentJointIKMat[1][0] = gParentJointIKOrth[0]; |
|
gParentJointIKMat[1][1] = gParentJointIKOrth[1]; |
|
gParentJointIKMat[1][2] = gParentJointIKOrth[2]; |
|
|
|
gParentJointIKMat[2][0] = gParentJointIKRot[0]; |
|
gParentJointIKMat[2][1] = gParentJointIKRot[1]; |
|
gParentJointIKMat[2][2] = gParentJointIKRot[2]; |
|
|
|
|
|
M_matrix4x4_t gParentJointIKRotMat, gParentJointResultMat; |
|
float gParentDeg; |
|
if(thisSolve->reverseSolve) |
|
{ |
|
gParentDeg = acos(gParentCos); |
|
} |
|
else |
|
{ |
|
gParentDeg = -acos(gParentCos); |
|
} |
|
|
|
// sanity check limb length |
|
if(thisLimbHypotLength < thisLimbLengthTgt) |
|
{ |
|
M_RotateZMatrix(gParentDeg, gParentJointIKRotMat); |
|
} |
|
|
|
M_ConcatTransforms(gParentJointIKRotMat, gParentJointIKMat, gParentJointResultMat); |
|
|
|
M_matrix4x4_t parentJointIKRotMat; |
|
//!!! shouldn't need the 180 degree addition, something in the law of cosines!!! |
|
float parentDeg; |
|
if(thisSolve->reverseSolve) |
|
{ |
|
parentDeg = acos(parentCos)+M_PI; |
|
} |
|
else |
|
{ |
|
parentDeg = -acos(parentCos)+M_PI; |
|
} |
|
|
|
// sanity check limb length |
|
if(thisLimbHypotLength < thisLimbLengthTgt) |
|
{ |
|
M_RotateZMatrix(parentDeg, parentJointIKRotMat); |
|
} |
|
|
|
|
|
// Thighs |
|
M_matrix4x4_t ggParentInTgtRootMatInverse, gParentJointLocalMat; |
|
M_MatrixInvert(ggParentInTgtRootMat, ggParentInTgtRootMatInverse); |
|
M_ConcatTransforms(gParentJointResultMat, ggParentInTgtRootMatInverse, gParentJointLocalMat); |
|
|
|
s_bone_t resultBone; |
|
|
|
// temp test stuff |
|
// M_MatrixAngles(thisJointInRootMat, resultBone.rot, resultBone.pos); |
|
// pSource->rawanim[t][thisJointIndex].rot = resultBone.rot; |
|
// pSource->rawanim[t][thisJointIndex].pos = resultBone.pos; |
|
|
|
// M_MatrixAngles(gParentInTgtRootMat, resultBone.rot, resultBone.pos); |
|
// pSource->rawanim[t][gParentIndex].rot = resultBone.rot; |
|
// pSource->rawanim[t][gParentIndex].pos = resultBone.pos; |
|
|
|
|
|
M_MatrixAngles(gParentJointLocalMat, resultBone.rot, resultBone.pos); |
|
pSource->rawanim[t][*gParentPath].pos = resultBone.pos; |
|
pSource->rawanim[t][*gParentPath].rot = resultBone.rot; |
|
|
|
M_MatrixAngles(parentJointIKRotMat, resultBone.rot, resultBone.pos); |
|
pSource->rawanim[t][*(thisJointPathInRoot+1)].rot = resultBone.rot; |
|
|
|
M_matrix4x4_t parentJointGlobalMat, parentJointGlobalMatInverse, thisJointLocalMat; |
|
CatBonePath(thisJointPathInRoot+1, pSource->rawanim[t], parentJointGlobalMat, 1); |
|
|
|
|
|
M_MatrixInvert(parentJointGlobalMat, parentJointGlobalMatInverse); |
|
M_ConcatTransforms(thisJointInRootMat, parentJointGlobalMatInverse, thisJointLocalMat); |
|
|
|
M_MatrixAngles(thisJointLocalMat, resultBone.rot, resultBone.pos); |
|
pSource->rawanim[t][thisJointIndex].rot = resultBone.rot; |
|
|
|
|
|
// swap animation back for next solve |
|
combinedAnimation[t] = pSource->rawanim[t]; |
|
pSource->rawanim[t] = sourceAnimation[t]; |
|
|
|
} |
|
// swap animation |
|
sourceAnimation[t] = pSource->rawanim[t]; |
|
pSource->rawanim[t] = combinedAnimation[t]; |
|
|
|
//---------------------------------------------------------------- |
|
// adjust root |
|
//---------------------------------------------------------------- |
|
Vector originBonePos = pSource->rawanim[t][rootIndex].pos; |
|
Vector rootInScaleOrigin = originBonePos - rootScaleOrigin; |
|
float effectiveRootScale = ((rootScaleFactor - 1.0) * pTemplate->rootScaleAmount) + 1.0; |
|
Vector scaledRoot = rootInScaleOrigin * effectiveRootScale; |
|
pSource->rawanim[t][rootIndex].pos = rootScaleOrigin + scaledRoot; |
|
|
|
//------------------------------------------------------------ |
|
// plane constraints |
|
//------------------------------------------------------------ |
|
for(int ii = 0; ii < pTemplate->numPlaneConstraints; ii++) |
|
{ |
|
s_planeConstraint_t *thisSolve = pTemplate->planeConstraints[ii]; |
|
|
|
char *thisJointNameString = thisSolve->jointNameString; |
|
if(g_verbose) |
|
printf("Executing plane constraint: %s\n", thisJointNameString); |
|
|
|
int thisJointIndex = GetNodeIndex(pSource, thisJointNameString); |
|
|
|
// init paths to feet |
|
int thisJointPath[512]; |
|
|
|
// get paths to feet |
|
if(thisJointIndex > -1) |
|
{ |
|
GetNodePath(pSource, -1, thisJointIndex, thisJointPath); |
|
} |
|
else |
|
{ |
|
printf("Error: Can't find node: %s\n" , thisJointNameString); |
|
exit(0); |
|
} |
|
int parentIndex = thisJointPath[1]; |
|
int *parentPath = thisJointPath + 1; |
|
|
|
M_matrix4x4_t thisJointGlobalMat, parentJointGlobalMat, gParentJointGlobalMat, gParentJointGlobalMatInverse; |
|
CatBonePath(thisJointPath, pSource->rawanim[t], thisJointGlobalMat, 0); |
|
CatBonePath(parentPath, pSource->rawanim[t], parentJointGlobalMat, 0); |
|
CatBonePath(parentPath+1, pSource->rawanim[t], gParentJointGlobalMat, 0); |
|
M_MatrixInvert(gParentJointGlobalMat, gParentJointGlobalMatInverse); |
|
|
|
if(thisJointGlobalMat[3][thisSolve->axis] < thisSolve->floor) |
|
{ |
|
// printf("-- broken plane: %f\n", thisJointGlobalMat[3][thisSolve->axis]); |
|
if(parentJointGlobalMat[3][thisSolve->axis] < thisSolve->floor) |
|
{ |
|
printf("Error: Constraint parent has broken the plane, this frame's plane constraint unsolvable!\n"); |
|
} |
|
else |
|
{ |
|
Vector parentJointAtPlane(parentJointGlobalMat[3][0], parentJointGlobalMat[3][1], parentJointGlobalMat[3][2]); |
|
Vector parentPos(parentJointGlobalMat[3][0], parentJointGlobalMat[3][1], parentJointGlobalMat[3][2]); |
|
Vector thisJointAtPlane(thisJointGlobalMat[3][0], thisJointGlobalMat[3][1], thisJointGlobalMat[3][2]); |
|
Vector thisJointPos(thisJointGlobalMat[3][0], thisJointGlobalMat[3][1], thisJointGlobalMat[3][2]); |
|
|
|
thisJointAtPlane[thisSolve->axis] = thisSolve->floor; |
|
parentJointAtPlane[thisSolve->axis] = thisSolve->floor; |
|
|
|
float thisJointLength = pSource->rawanim[t][thisJointIndex].pos[BONEAXIS]; |
|
float parentLengthToPlane = parentPos[thisSolve->axis] - thisSolve->floor; |
|
float adjacent = sqrtf((thisJointLength * thisJointLength) - (parentLengthToPlane * parentLengthToPlane)); |
|
Vector parentDirection = thisJointAtPlane - parentJointAtPlane; |
|
VectorNormalize(parentDirection); |
|
|
|
Vector newJointPos = parentJointAtPlane + (parentDirection * adjacent); |
|
|
|
Vector newParentDir = newJointPos - parentPos; |
|
Vector parentUp(parentJointGlobalMat[BONEUP][0], parentJointGlobalMat[BONEUP][1], parentJointGlobalMat[BONEUP][2]); |
|
|
|
VectorNormalize(newParentDir); |
|
VectorNormalize(parentUp); |
|
// Vector parentSide = newParentDir.Cross(parentUp); |
|
Vector parentSide = parentUp.Cross(newParentDir); |
|
VectorNormalize(parentSide); |
|
parentUp = newParentDir.Cross(parentSide); |
|
// parentUp = parentSide.Cross(newParentDir); |
|
VectorNormalize(parentUp); |
|
parentJointGlobalMat[BONEDIR][0] = newParentDir[0]; |
|
parentJointGlobalMat[BONEDIR][1] = newParentDir[1]; |
|
parentJointGlobalMat[BONEDIR][2] = newParentDir[2]; |
|
parentJointGlobalMat[BONEUP][0] = parentUp[0]; |
|
parentJointGlobalMat[BONEUP][1] = parentUp[1]; |
|
parentJointGlobalMat[BONEUP][2] = parentUp[2]; |
|
parentJointGlobalMat[BONESIDE][0] = parentSide[0]; |
|
parentJointGlobalMat[BONESIDE][1] = parentSide[1]; |
|
parentJointGlobalMat[BONESIDE][2] = parentSide[2]; |
|
|
|
|
|
M_matrix4x4_t newParentJointMat; |
|
|
|
M_ConcatTransforms(parentJointGlobalMat, gParentJointGlobalMatInverse, newParentJointMat); |
|
|
|
s_bone_t resultBone; |
|
M_MatrixAngles(newParentJointMat, resultBone.rot, resultBone.pos); |
|
pSource->rawanim[t][parentIndex].rot = resultBone.rot; |
|
} |
|
} |
|
} |
|
|
|
// swap animation back for next solve |
|
combinedAnimation[t] = pSource->rawanim[t]; |
|
pSource->rawanim[t] = sourceAnimation[t]; |
|
} |
|
for(int t = 0; t < sourceNumFrames; t++) |
|
{ |
|
pTarget->rawanim[t] = combinedAnimation[t]; |
|
} |
|
pTarget->numframes = sourceNumFrames; |
|
|
|
|
|
|
|
|
|
|
|
#if 0 |
|
// Process motion mapping into out and return that |
|
s_source_t *out = new s_source_t; |
|
|
|
return out; |
|
#else |
|
// Just returns the start animation, to test the Save_SMD API. |
|
return pTarget; |
|
#endif |
|
} |
|
|
|
char templates[] = |
|
"\n\ |
|
#\n\ |
|
# default template file is analogus to not specifying a template file at all\n\ |
|
#\n\ |
|
\n\ |
|
rootScaleJoint ValveBiped.Bip01_L_Foot\n\ |
|
rootScaleAmount 1.0\n\ |
|
toeFloorZ 2.7777\n\ |
|
\n\ |
|
twoJointIKSolve ValveBiped.Bip01_L_Foot\n\ |
|
reverseSolve 0\n\ |
|
extremityScale 1.0\n\ |
|
limbRootOffsetScale 1.0 1.0 0.0\n\ |
|
\n\ |
|
twoJointIKSolve ValveBiped.Bip01_R_Foot\n\ |
|
reverseSolve 0\n\ |
|
extremityScale 1.0\n\ |
|
limbRootOffsetScale 1.0 1.0 0.0\n\ |
|
\n\ |
|
oneJointPlaneConstraint ValveBiped.Bip01_L_Toe0\n\ |
|
\n\ |
|
oneJointPlaneConstraint ValveBiped.Bip01_R_Toe0\n\ |
|
\n\ |
|
twoJointIKSolve ValveBiped.Bip01_R_Hand\n\ |
|
reverseSolve 1\n\ |
|
extremityScale 1.0\n\ |
|
limbRootOffsetScale 0.0 0.0 1.0\n\ |
|
\n\ |
|
twoJointIKSolve ValveBiped.Bip01_L_Hand\n\ |
|
reverseSolve 1\n\ |
|
extremityScale 1.0\n\ |
|
limbRootOffsetScale 0.0 0.0 1.0\n\ |
|
\n\ |
|
"; |
|
|
|
|
|
void UsageAndExit() |
|
{ |
|
MdlError( "usage: motionmapper [-quiet] [-verbose] [-templateFile filename] [-printTemplates] sourceanim.smd targetskeleton.smd output.smd\n\ |
|
\tsourceanim: should contain ref pose and animation data\n\ |
|
\ttargetsekeleton: should contain new ref pose, animation data ignored/can be absent\n\ |
|
\toutput: animation from source mapped onto target skeleton (contains new ref pose)\n\ |
|
\t-templateFile filename : specifies a template file for guiding the mapping of motion\n\ |
|
\t-printTemplate: Causes motionmapper to output the contents of an example template file, which can be used in conjunction with the -templateFile argument to create various motion effects.\n\ |
|
\n"); |
|
} |
|
|
|
void PrintHeader() |
|
{ |
|
vprint( 0, "Valve Software - motionmapper.exe ((c) Valve Coroporation %s)\n", __DATE__ ); |
|
vprint( 0, "--- Maps motion from one animation/skeleton onto another skeleton ---\n" ); |
|
} |
|
|
|
|
|
|
|
/* |
|
============== |
|
main |
|
============== |
|
*/ |
|
int main (int argc, char **argv) |
|
{ |
|
int i; |
|
|
|
int useTemplate = 0; |
|
char templateFileName[1024]; |
|
|
|
// Header |
|
PrintHeader(); |
|
|
|
// Init command line stuff |
|
CommandLine()->CreateCmdLine( argc, argv ); |
|
InstallSpewFunction(); |
|
|
|
// init math stuff |
|
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false ); |
|
g_currentscale = g_defaultscale = 1.0; |
|
g_defaultrotation = RadianEuler( 0, 0, M_PI / 2 ); |
|
|
|
// No args? |
|
if (argc == 1) |
|
{ |
|
UsageAndExit(); |
|
} |
|
|
|
// Init variable |
|
g_quiet = false; |
|
|
|
// list template hooey |
|
CUtlVector< CUtlSymbol > filenames; |
|
|
|
// Get args |
|
for (i = 1; i < argc; i++) |
|
{ |
|
// Switches |
|
if (argv[i][0] == '-') |
|
{ |
|
if (!stricmp(argv[i], "-allowdebug")) |
|
{ |
|
// Ignore, used by interface system to catch debug builds checked into release tree |
|
continue; |
|
} |
|
|
|
if (!stricmp(argv[i], "-quiet")) |
|
{ |
|
g_quiet = true; |
|
g_verbose = false; |
|
continue; |
|
} |
|
|
|
if (!stricmp(argv[i], "-verbose")) |
|
{ |
|
g_quiet = false; |
|
g_verbose = true; |
|
continue; |
|
} |
|
if (!stricmp(argv[i], "-printTemplate")) |
|
{ |
|
printf("%s\n", templates); |
|
exit(0); |
|
|
|
} |
|
if (!stricmp(argv[i], "-templateFile")) |
|
{ |
|
if(i + 1 < argc) |
|
{ |
|
strcpy( templateFileName, argv[i+1]); |
|
useTemplate = 1; |
|
printf("Note: %s passed as template file", templateFileName); |
|
} |
|
else |
|
{ |
|
printf("Error: -templateFile requires an argument, none found!"); |
|
UsageAndExit(); |
|
|
|
} |
|
i++; |
|
continue; |
|
} |
|
} |
|
else |
|
{ |
|
// more template stuff |
|
CUtlSymbol sym = argv[ i ]; |
|
filenames.AddToTail( sym ); |
|
} |
|
} |
|
|
|
// Enough file args? |
|
if ( filenames.Count() != 3 ) |
|
{ |
|
// misformed arguments |
|
// otherwise generating unintended results |
|
printf("Error: 3 file arguments required, %i found!", filenames.Count()); |
|
UsageAndExit(); |
|
} |
|
|
|
// Filename arg indexes |
|
int sourceanim = 0; |
|
int targetskel = 1; |
|
int outputanim = 2; |
|
|
|
// Copy arg string to global variable |
|
strcpy( g_outfile, filenames[ outputanim ].String() ); |
|
|
|
// Init filesystem hooey |
|
CmdLib_InitFileSystem( g_outfile ); |
|
// ?? |
|
Q_FileBase( g_outfile, g_outfile, sizeof( g_outfile ) ); |
|
|
|
// Verbose stuff |
|
if (!g_quiet) |
|
{ |
|
vprint( 0, "%s, %s, %s, path %s\n", qdir, gamedir, g_outfile ); |
|
} |
|
// ?? |
|
Q_DefaultExtension(g_outfile, ".smd", sizeof( g_outfile ) ); |
|
|
|
// Verbose stuff |
|
if (!g_quiet) |
|
{ |
|
vprint( 0, "Source animation: %s\n", filenames[ sourceanim ].String() ); |
|
vprint( 0, "Target skeleton: %s\n", filenames[ targetskel ].String() ); |
|
|
|
vprint( 0, "Creating on \"%s\"\n", g_outfile); |
|
} |
|
// fullpath = EXTERNAL GLOBAL!!!??? |
|
strcpy( fullpath, g_outfile ); |
|
strcpy( fullpath, ExpandPath( fullpath ) ); |
|
strcpy( fullpath, ExpandArg( fullpath ) ); |
|
|
|
// Load source and target data |
|
s_source_t *pSource = Load_Source( filenames[sourceanim].String(), "smd", false, false ); |
|
s_source_t *pTarget = Load_Source( filenames[targetskel].String(), "smd", false, false ); |
|
|
|
|
|
// |
|
s_template_t *pTemplate = NULL; |
|
if(useTemplate) |
|
{ |
|
pTemplate = Load_Template(templateFileName); |
|
} |
|
else |
|
{ |
|
printf("Note: No template file specified, using defaults settings.\n"); |
|
|
|
pTemplate = New_Template(); |
|
Set_DefaultTemplate(pTemplate); |
|
} |
|
|
|
|
|
// Process skeleton |
|
s_source_t *pMappedAnimation = MotionMap( pSource, pTarget, pTemplate ); |
|
|
|
|
|
// Save output (ref skeleton & animation data); |
|
Save_SMD( fullpath, pMappedAnimation ); |
|
|
|
Q_StripExtension( filenames[outputanim].String(), outname, sizeof( outname ) ); |
|
|
|
// Verbose stuff |
|
if (!g_quiet) |
|
{ |
|
vprint( 0, "\nCompleted \"%s\"\n", g_outfile); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|