tge/lib/dtsSDKPlus/DTSUtil.cpp
2017-04-17 06:17:10 -06:00

395 lines
12 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(disable : 4786 4018)
#endif
#include "DTSUtil.h"
#include "appConfig.h"
#include "decomp/Decompose.h"
#include <stdarg.h>
#define TEST_DTS_MATH
namespace DTS
{
const char* avar(const char *message, ...)
{
static char buffer[4096];
va_list args;
va_start(args, message);
vsprintf(buffer, message, args);
return( buffer );
}
bool isEqualQ16(const Quaternion & a, const Quaternion &b)
{
U16 MAX_VAL = 0x7fff;
// convert components to 16 bit, then test for equality
S16 x, y, z, w;
x = ((S16)(a.x() * F32(MAX_VAL))) - ((S16)(b.x() * F32(MAX_VAL)));
y = ((S16)(a.y() * F32(MAX_VAL))) - ((S16)(b.y() * F32(MAX_VAL)));
z = ((S16)(a.z() * F32(MAX_VAL))) - ((S16)(b.z() * F32(MAX_VAL)));
w = ((S16)(a.w() * F32(MAX_VAL))) - ((S16)(b.w() * F32(MAX_VAL)));
return (x==0) && (y==0) && (z==0) && (w==0);
}
void crossProduct(const Point3D & a, const Point3D & b, Point3D * c)
{
c->x(a.y() * b.z() - a.z() * b.y());
c->y(a.z() * b.x() - a.x() * b.z());
c->z(a.x() * b.y() - a.y() * b.x());
}
void convertToTransform(Matrix<4,4,F32> & mat, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
{
AffineParts parts;
decomp_affine(mat,&parts);
trans = parts.trans;
rot = parts.rot;
srot = parts.scaleRot;
scale = parts.scale;
}
void decomp_affine(const Matrix<4,4,F32> & mat, AffineParts * parts)
{
GraphicGems::HMatrix ggMat;
GraphicGems::AffineParts ggParts;
for (S32 i=0; i<4; i++)
{
for (S32 j=0; j<4; j++)
ggMat[i][j] = mat[i][j];
}
GraphicGems::decomp_affine(ggMat,&ggParts);
parts->rot = Quaternion(F32(ggParts.q.x),F32(ggParts.q.y),F32(ggParts.q.z),F32(ggParts.q.w));
parts->scale = Point3D(F32(ggParts.k.x),F32(ggParts.k.y),F32(ggParts.k.z));
parts->scaleRot = Quaternion(F32(ggParts.u.x),F32(ggParts.u.y),F32(ggParts.u.z),F32(ggParts.u.w));
parts->trans = Point3D(F32(ggParts.t.x),F32(ggParts.t.y),F32(ggParts.t.z));
parts->sign = F32(ggParts.f);
#ifdef TEST_DTS_MATH
// Test math (but only in the unscaled case
if (isEqual(parts->scale.x(),1.0f,0.01f) && isEqual(parts->scale.y(),1.0f,0.01f) && isEqual(parts->scale.z(),1.0f,0.01f))
{
Matrix<4,4,F32> mat2 = parts->rot.toMatrix();
Vector<F32,4> col;
col[0] = parts->trans.x();
col[1] = parts->trans.y();
col[2] = parts->trans.z();
col[3] = 1;
mat2.setCol(3,col);
for (S32 i=0; i<4; i++)
{
for (S32 ii=0; ii<4; ii++)
{
if (!isEqual(mat[i][ii],mat2[i][ii],0.01f))
AppConfig::PrintDump(-1,"Doh!");
}
}
}
#endif
}
void zapScale(Matrix<4,4,F32> & mat)
{
AffineParts parts;
decomp_affine(mat,&parts);
// now put the matrix back together again without the scale:
// mat = mat.rot * mat.pos
Vector<F32,4> trans;
trans[0] = parts.trans.x();
trans[1] = parts.trans.y();
trans[2] = parts.trans.z();
trans[3] = 1;
mat = parts.rot.toMatrix();
mat.setCol(3,trans);
#ifdef TEST_DTS_MATH
{
// A test...will get rid of once we know it works...
Matrix<4,4,F32> mat2;
decomp_affine(mat,&parts);
trans[0] = parts.trans.x();
trans[1] = parts.trans.y();
trans[2] = parts.trans.z();
trans[3] = 1;
mat2 = parts.rot.toMatrix();
mat2.setCol(3,trans);
for (S32 i=0; i<4; i++)
{
for (S32 j=0; j<4; j++)
{
assert(isZero(mat[i][j]-mat2[i][j],0.01f));
}
}
}
#endif
}
Matrix<4,4,F32> & getLocalNodeMatrix(AppNode * node, AppNode * parent, const AppTime & time, Matrix<4,4,F32> & matrix, AffineParts & a10, AffineParts & a20)
{
// Here's the story: the default transforms have no scale. In order to account for scale, the
// scale at the default time is folded into the object offset (which is multiplied into the points
// before exporting). Because of this, the local transform at a given time must take into account
// the scale of the parent and child node at time 0 in addition to the current time. In particular,
// the world transform at a given time is WT(time) = T(time) * inverse(Tscale(0))
// in order to avoid recomputing matrix at default time over and over, we assume that the first request
// for the matrix will be at the default time, and thereafter, we will pass that matrix in and reuse it...
Matrix<4,4,F32> m1 = node->getNodeTransform(time);
Matrix<4,4,F32> m2;
if (parent)
m2 = parent->getNodeTransform(time);
else
m2 = Matrix<4,4,F32>::identity();
if (time == AppTime::DefaultTime())
{
decomp_affine(m1,&a10);
decomp_affine(m2,&a20);
}
// build the inverse scale matrices
Matrix<4,4,F32> stretchRot10,stretchRot20;
Matrix<4,4,F32> scaleMat10,scaleMat20;
Matrix<4,4,F32> invScale10, invScale20;
Point3D sfactor10, sfactor20;
stretchRot10 = a10.scaleRot.toMatrix();
stretchRot20 = a20.scaleRot.toMatrix();
sfactor10 = Point3D(a10.sign/a10.scale.x(),a10.sign/a10.scale.y(),a10.sign/a10.scale.z());
sfactor20 = Point3D(a20.sign/a20.scale.x(),a20.sign/a20.scale.y(),a20.sign/a20.scale.z());
scaleMat10 = Matrix<4,4,F32>::identity();
scaleMat10[0][0] = sfactor10.x();
scaleMat10[1][1] = sfactor10.y();
scaleMat10[2][2] = sfactor10.z();
scaleMat20 = Matrix<4,4,F32>::identity();
scaleMat20[0][0] = sfactor20.x();
scaleMat20[1][1] = sfactor20.y();
scaleMat20[2][2] = sfactor20.z();
invScale10 = stretchRot10 * scaleMat10 * stretchRot10.inverse();
invScale20 = stretchRot20 * scaleMat20 * stretchRot20.inverse();
// build world transforms
m1 = m1 * invScale10;
m2 = m2 * invScale20;
// build local transform
matrix = m2.inverse() * m1;
#ifdef TEST_DTS_MATH
{
Matrix<4,4,F32> testMat;
Matrix<4,4,F32> m2inv = m2.inverse();
testMat = m2inv * m2;
{
for (S32 i=0; i<4; i++)
for (S32 j=0; j<4; j++)
{
F32 val = i==j ? 1.0f : 0.0f;
assert(isEqual(testMat[i][j],val,0.01f) && "assertion failed");
}
}
testMat = m2 * m2inv;
{
for (S32 i=0; i<4; i++)
for (S32 j=0; j<4; j++)
{
F32 val = i==j ? 1.0f : 0.0f;
assert(isEqual(testMat[i][j],val,0.01f) && "assertion failed");
}
}
}
#endif
return matrix;
}
void getLocalNodeTransform(AppNode * node, AppNode * parent, AffineParts & child0, AffineParts & parent0, const AppTime & time, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
{
Matrix<4,4,F32> localMat;
getLocalNodeMatrix(node,parent,time,localMat,child0,parent0);
convertToTransform(localMat,rot,trans,srot,scale);
}
void getBlendNodeTransform(AppNode * node, AppNode * parent, AffineParts & child0, AffineParts & parent0, const AppTime & time, const AppTime & referenceTime, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
{
Matrix<4,4,F32> m1, invm1, m2, retMat;
getLocalNodeMatrix(node, parent, referenceTime, m1, child0, parent0);
getLocalNodeMatrix(node, parent, time, m2, child0, parent0);
invm1 = m1.inverse();
retMat = invm1 * m2;
convertToTransform(retMat, rot,trans,srot,scale);
}
void getDeltaTransform(AppNode * node, const AppTime & time1, const AppTime & time2, Quaternion & rot, Point3D & trans, Quaternion & srot, Point3D & scale)
{
Matrix<4,4,F32> m1 = node->getNodeTransform(time1);
Matrix<4,4,F32> m2 = node->getNodeTransform(time2);
zapScale(m1);
zapScale(m2);
convertToTransform(m1.inverse() * m2,rot,trans,srot,scale);
}
bool neverAnimateNode(AppNode*)
{
return false;
}
char * chopNum(char * s)
{
if (s==NULL)
return NULL;
char * p = s + strlen(s);
if (p==s)
return s;
p--;
// trim spaces from the end
while (p!=s && *p==' ')
p--;
// back up until we reach a non-digit
// gotta be better way than this...
if (isdigit(*p))
do
{
if (p--==s)
return p+1;
} while (isdigit(*p));
// allow negative numbers, treat _ as - for Maya
if (*p=='-' || *p=='_')
p--;
// trim spaces separating name and number
while (*p==' ')
{
p--;
if (p==s)
return p;
}
// return first space if there was one,
// o.w. return first digit
return p+1;
}
char * chopTrailingNumber(const char * fullName, S32 & size)
{
if (!fullName)
{
size = -1;
return NULL;
}
char * buffer = strnew(fullName);
char * p = chopNum(buffer);
if (*p=='\0')
{
size = -1;
return buffer;
}
size = 1;
if (*p=='_')
size = -atoi(p+1);
else
size = atoi(p);
*p='\0'; // terminate string
return buffer;
}
S32 getTrailingNumber(const char * fullName)
{
S32 size;
delete [] chopTrailingNumber(fullName,size);
return size;
}
void tweakName(const char ** name)
{
char * pre[] = { "BB::", "BBZ::", "SORT::", "SEQUENCE::",
"BB_" , "BBZ_" , "SORT_" , "SEQUENCE_" , "" };
for (S32 i=0; strlen(pre[i]) != 0; i++)
{
if (!_strnicmp(*name,pre[i],strlen(pre[i])))
{
// found prefix...now skip the prefix and return
*name += strlen(pre[i]);
break;
}
}
}
const char * getFileBase(const char * str)
{
const char * ret = strrchr(str,'/');
if (!ret)
ret = strrchr(str,'\\');
if (!ret)
ret = strrchr(str,':');
if (!ret)
ret = str;
else
++ret;
return ret;
}
char * getFileBase(char * str)
{
return (char*)getFileBase((const char*)str);
}
char * getFilePath(const char * str, S32 pad)
{
const char * slash = getFileBase(str);
S32 len = slash-str;
char * ret = new char[len+1+pad];
strncpy(ret,str,len);
ret[len]='\0';
return ret;
}
// perform string compare with wildcards (in s2 only) and case insensitivity
bool stringEqual(const char * s1, const char * s2)
{
if (*s1=='\0' && *s2=='\0')
return true;
if (*s2=='*')
{
if (stringEqual(s1,s2+1))
return true;
if (*s1=='\0')
return false;
return stringEqual(s1+1,s2);
}
if (toupper(*s1)==toupper(*s2))
return stringEqual(s1+1,s2+1);
return false;
}
char * removeExt(char * str)
{
char * tmp = _strdup(str);
char * ext = strrchr(tmp,'.');
if (ext)
*ext = 0;
return tmp;
}
};