2017-04-17 06:17:10 -06:00

749 lines
22 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "max2dtsExporter/maxUtil.h"
#pragma pack(push,8)
#include <decomp.h>
#include <dummy.h>
#include <ISkin.h>
#include <modstack.h>
#pragma pack(pop)
#include "Core/tVector.h"
// Special for biped stuff -- class id in no header, so we just def it here
// NOTE/WARNING: This could change with Character Studio updates
#define BipedObjectClassID Class_ID(37157,0)
Point3F & Point3ToPoint3F(Point3 & p3, Point3F & p3f)
{
p3f.x = p3.x;
p3f.y = p3.y;
p3f.z = p3.z;
return p3f;
}
void convertToTransform(Matrix3 & mat, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
AffineParts parts;
decomp_affine(mat,&parts);
Point3ToPoint3F(parts.t,trans);
rot.set(QuatF(parts.q[0],parts.q[1],parts.q[2],parts.q[3]));
srot.set(QuatF(parts.u[0],parts.u[1],parts.u[2],parts.u[3]));
Point3ToPoint3F(parts.k,scale);
}
MatrixF & convertToMatrixF(Matrix3 & mat3,MatrixF &matf)
{
matf.identity();
Point3F x,y,z,p;
Point3 x3 = mat3 * Point3(1.0f,0.0f,0.0f);
Point3 y3 = mat3 * Point3(0.0f,1.0f,0.0f);
Point3 z3 = mat3 * Point3(0.0f,0.0f,1.0f);
Point3 p3 = mat3 * Point3(0.0f,0.0f,0.0f);
x = Point3ToPoint3F(x3,x);
y = Point3ToPoint3F(y3,y);
z = Point3ToPoint3F(z3,z);
p = Point3ToPoint3F(p3,p);
x -= p;
y -= p;
z -= p;
matf.setColumn(0,x);
matf.setColumn(1,y);
matf.setColumn(2,z);
matf.setColumn(3,p);
return matf;
}
void getNodeTransform(INode *pNode, S32 time, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 nodeMat = pNode->GetNodeTM( time );
convertToTransform(nodeMat,rot,trans,srot,scale);
}
TriObject * getTriObject( INode *pNode, S32 time, S32 multiResVerts, bool & deleteIt)
{
TriObject * tri = NULL;
IParamBlock * paramBlock = NULL;
// if we're a multiRes object, dial us down
if (multiResVerts>0)
{
Animatable * obj = (Animatable*)pNode->GetObjectRef();
for (S32 i=0; i<obj->NumSubs(); i++)
{
if (!dStrcmp(obj->SubAnimName(i),"MultiRes"))
{
paramBlock = (IParamBlock*)obj->SubAnim(i)->SubAnim(0);
Interval range = pNode->GetTimeRange(TIMERANGE_ALL|TIMERANGE_CHILDNODES|TIMERANGE_CHILDANIMS);
paramBlock->SetValue(0,time,multiResVerts);
break;
}
}
}
// if the object can't convert to a tri-mesh, eval world state to
// get an object that can:
const ObjectState &os = pNode->EvalWorldState(time);
if ( os.obj->CanConvertToType(triObjectClassID) )
tri = (TriObject *)os.obj->ConvertToType( time, triObjectClassID );
deleteIt = (tri && (tri != os.obj));
return tri;
}
F32 findVolume(INode * pNode, S32 & polyCount)
{
bool deleteTri;
TriObject * tri = getTriObject(pNode,DEFAULT_TIME,-1,deleteTri);
if (!tri)
{
polyCount=0;
return 0.0f;
}
Mesh & mesh = tri->mesh;
polyCount = mesh.getNumFaces();
Box3F bounds;
bounds.min.set( 10E30f, 10E30f, 10E30f);
bounds.max.set(-10E30f,-10E30f,-10E30f);
for (S32 i=0; i<mesh.numVerts; i++)
{
Point3F v = Point3ToPoint3F(mesh.verts[i],v);
bounds.min.setMin(v);
bounds.max.setMax(v);
}
if (deleteTri)
delete tri;
return (bounds.max.x-bounds.min.x)*(bounds.max.y-bounds.min.y)*(bounds.max.z-bounds.min.z);
}
void findSkinData(INode * pNode, ISkin **skin, ISkinContextData ** skinData)
{
// till proven otherwise...
*skin = NULL;
*skinData = NULL;
// Get object from node. Abort if no object.
Object* obj = pNode->GetObjectRef();
if (!obj)
return;
Modifier * mod = NULL;
// Is derived object ?
S32 i;
while (obj->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
IDerivedObject* dobj = (IDerivedObject*) obj;
// Iterate over all entries of the modifier stack.
for (i=0;i<dobj->NumModifiers();i++)
if (dobj->GetModifier(i)->ClassID() == SKIN_CLASSID)
break;
if (i!=dobj->NumModifiers())
{
mod = dobj->GetModifier(i);
break;
}
obj = dobj->GetObjRef();
}
if (!mod)
return;
*skin = (ISkin*) mod->GetInterface(I_SKIN);
if (!*skin)
return;
*skinData = (*skin)->GetContextInterface(pNode);
if (!*skinData)
*skin=NULL; // return both or neither
}
bool hasSkin(INode * pNode)
{
ISkin * skin;
ISkinContextData * skinData;
findSkinData(pNode,&skin,&skinData);
return skin != NULL;
}
bool hasMesh(INode * pNode)
{
ObjectState os = pNode->EvalWorldState(0);
return( os.obj->CanConvertToType(triObjectClassID) && !(os.obj->ClassID() == BipedObjectClassID) );
}
void zapScale(Matrix3 & mat)
{
AffineParts parts;
decomp_affine(mat,&parts);
// now put the matrix back together again without the scale:
// mat = mat.rot * mat.pos
mat.IdentityMatrix();
mat.PreTranslate(parts.t);
PreRotateMatrix(mat,parts.q);
}
Matrix3 & getLocalNodeMatrix(INode *pNode, INode *parent, S32 time, Matrix3 & 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) = inverse(Tscale(0)) * T(time)
// Note: above formula is max style. Torque style would have the order reveresed. :(
// 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...
Matrix3 m1 = pNode->GetNodeTM(time);
Matrix3 m2 = parent ? parent->GetNodeTM(time) : Matrix3(true);
if (time==DEFAULT_TIME)
{
decomp_affine(m1,&a10);
decomp_affine(m2,&a20);
}
// build the inverse scale matrices
Matrix3 stretchRot10,stretchRot20;
Point3 sfactor10, sfactor20;
Matrix3 invScale10, invScale20;
a10.u.MakeMatrix(stretchRot10);
a20.u.MakeMatrix(stretchRot20);
sfactor10 = Point3(a10.f/a10.k.x,a10.f/a10.k.y,a10.f/a10.k.z);
sfactor20 = Point3(a20.f/a20.k.x,a20.f/a20.k.y,a20.f/a20.k.z);
invScale10 = Inverse(stretchRot10) * ScaleMatrix(sfactor10) * stretchRot10;
invScale20 = Inverse(stretchRot20) * ScaleMatrix(sfactor20) * stretchRot20;
// build world transforms
m1 = invScale10 * m1;
m2 = invScale20 * m2;
// build local transform
matrix = m1 * Inverse(m2);
return matrix;
}
void getLocalNodeTransform(INode *pNode, INode *parent, AffineParts & child0, AffineParts & parent0, S32 time, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 local;
getLocalNodeMatrix(pNode,parent,time,local,parent0,child0);
convertToTransform(local,rot,trans,srot,scale);
}
void getBlendNodeTransform(INode *pNode, INode *parent, AffineParts & child0, AffineParts & parent0, S32 time, S32 referenceTime, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 m1;
Matrix3 m2;
getLocalNodeMatrix(pNode, parent, referenceTime, m1, child0, parent0);
getLocalNodeMatrix(pNode, parent, time, m2, child0, parent0);
m1 = Inverse(m1);
m2 *= m1;
convertToTransform(m2, rot,trans,srot,scale);
}
void computeObjectOffset(INode * pNode, Matrix3 & mat)
{
// compute the object offset transform...
// this will be applied to all the verts of the meshes...
// we grab this straight from the node, but we also
// multiply in any scaling on the node transform because
// the scaling will be stripped out when making a tsshape
Matrix3 nodeMat = pNode->GetNodeTM(DEFAULT_TIME);
zapScale(nodeMat);
nodeMat = Inverse(nodeMat);
mat = pNode->GetObjTMAfterWSM(DEFAULT_TIME);
mat *= nodeMat;
}
void getDeltaTransform(INode * pNode, S32 time1, S32 time2, Quat16 & rot, Point3F & trans, Quat16 & srot, Point3F & scale)
{
Matrix3 m1 = pNode->GetNodeTM(time2);
Matrix3 m2 = pNode->GetNodeTM(time1);
m2 = Inverse(m2);
m1 *= m2;
convertToTransform(m1,rot,trans,srot,scale);
}
bool isVis(INode * pNode, S32 time)
{
return pNode->GetVisibility(time) > 0.0000001f;
}
F32 getVisValue(INode * pNode, S32 time)
{
return pNode->GetVisibility(time);
}
bool animatesVis(INode * pNode, const Interval & range, bool & error)
{
// running error...exit if already encountered an error
if (error)
return false;
F32 startVis = getVisValue(pNode,range.Start());
for (S32 i=range.Start()+1; i<=range.End(); i++)
if (mFabs(startVis-getVisValue(pNode,i))>0.01f)
return true;
return false;
}
bool animatesFrame(INode * pNode, const Interval & range, bool & error)
{
// running error...exit if already encountered an error
if (error)
return false;
bool deleteIt;
TriObject * tri = getTriObject(pNode,range.Start(),-1,deleteIt);
if (!tri)
{
error = true;
return false;
}
Interval ivalid;
ivalid = tri->ChannelValidity(range.Start(), GEOM_CHAN_NUM);
if (deleteIt)
tri->DeleteMe();
return (ivalid.Start() > range.Start() || ivalid.End() < range.End());
}
bool animatesMatFrame(INode * pNode, const Interval & range, bool & error)
{
// running error...exit if already encountered an error
if (error)
return false;
bool deleteIt;
TriObject * tri = getTriObject(pNode,range.Start(),-1,deleteIt);
if (!tri)
{
error = true;
return false;
}
Interval ivalid;
ivalid = tri->ChannelValidity(range.Start(), TEXMAP_CHAN_NUM);
if (deleteIt)
tri->DeleteMe();
return (ivalid.Start() > range.Start() || ivalid.End() < range.End());
}
bool isdigit(char ch)
{
return (ch>='0' && ch<='9');
}
//----------------------------------------------------------------------------
// If 's' ends in a number, chopNum returns
// a pointer to first digit in this number.
// If not, then returns pointer to '\0'
// at end of name or first of any trailing
// spaces.
char * chopNum(char * s)
{
if (s==NULL)
return NULL;
char * p = s + dStrlen(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
if (*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;
}
//----------------------------------------------------------------------------
// separates nodes names into base name
// and detail number
char * chopTrailingNumber(const char * fullName, S32 & size)
{
if (!fullName)
{
size = -1;
return NULL;
}
char * buffer = dStrdup(fullName);
char * p = chopNum(buffer);
if (*p=='\0')
{
size = -1;
return buffer;
}
size = dAtoi(p);
*p='\0'; // terminate string
return buffer;
}
//----------------------------------------------------------------------------
// returns a list of detail levels of this shape...grabs meshes off shape
// and finds meshes on root level with same name but different trailing
// detail number (i.e., head32 on shape, head16 at root, leads to detail
// levels 32 and 16).
void findDetails(INode * base, INode * root, Vector<S32> & details)
{
S32 i,j,k;
// run through all the descendents of this node
// and look for numbers indicating detail levels
Vector<INode*> nodeStack;
nodeStack.push_back(base);
while (!nodeStack.empty())
{
INode * node = nodeStack.last();
nodeStack.pop_back();
if (hasMesh(node))
{
// have a mesh...a little obscure below:
// add # of this mesh + any mesh w/ same
// name on the root level to details vector
S32 detailSize;
char * baseName = chopTrailingNumber(node->GetName(),detailSize);
INode * meshNode = node;
for (j=-1;j<root->NumberOfChildren();j++)
{
if (j>=0)
meshNode = root->GetChildNode(j);
if (!hasMesh(meshNode))
continue;
char * meshName = chopTrailingNumber(meshNode->GetName(),detailSize);
if (!dStricmp(baseName,meshName))
{
for (k=0; k<details.size(); k++)
if (detailSize==details[k])
break;
if (k==details.size())
details.push_back(detailSize);
}
dFree(meshName);
}
dFree(baseName);
}
for (j=0; j<node->NumberOfChildren();j++)
nodeStack.push_back(node->GetChildNode(j));
}
}
//----------------------------------------------------------------------------
// find selected subtrees (nodes on root level only) and embed in subtree of
// the style the exporter looks for:
// Root
// |
// |-base
// |-start
// | |
// | |-<selected node 1>
// | | .
// | | .
// | |-<selected node N>
// |
// |-detail 1
// | .
// | .
// |-detail N
void embedSubtree(Interface * ip)
{
S32 i,j,k;
// make dummy node named baseName
TSTR baseName("base");
ip->MakeNameUnique(baseName);
INode * base = ip->CreateObjectNode(new DummyObject);
base->SetName(baseName);
// make dummy node named startName
TSTR startName("start");
ip->MakeNameUnique(startName);
INode * start = ip->CreateObjectNode(new DummyObject);
start->SetName(startName);
// link later to former
base->AttachChild(start);
Vector<S32> details;
// loop through selection set, link to start (only if child of root)
S32 count = ip->GetSelNodeCount();
INode * root = ip->GetRootNode();
for (i=0; i<count; i++)
{
INode * node = ip->GetSelNode(i);
if (!node->GetParentNode()->IsRootNode())
continue;
start->AttachChild(node);
findDetails(node,root,details);
}
// now create detail markers
for (i=0; i<details.size();i++)
{
char detailName[20];
INode * detailNode = ip->CreateObjectNode(new DummyObject);
dSprintf(detailName,20,"detail%i",details[i]);
detailNode->SetName(detailName);
base->AttachChild(detailNode);
}
}
//----------------------------------------------------------------------------
void registerDetails(Interface * ip)
{
S32 i,j,k;
// loop through selection set, only care about nodes off root
S32 count = ip->GetSelNodeCount();
INode * root = ip->GetRootNode();
for (i=0; i<count; i++)
{
INode * node = ip->GetSelNode(i);
if (!node->GetParentNode()->IsRootNode())
continue;
Vector<S32> details;
// search branches of tree for meshes and find their details
for (j=0; j<node->NumberOfChildren(); j++)
{
INode * child = node->GetChildNode(j);
if (child->NumberOfChildren()>0)
findDetails(child,root,details);
}
// go through children of base node and cull detail numbers
// that don't need to be added (because detail marker already
// present)
for (j=0; j<node->NumberOfChildren(); j++)
{
INode * child = node->GetChildNode(j);
if (child->NumberOfChildren()==0)
{
// look for #
S32 detailSize;
char * baseName = chopTrailingNumber(child->GetName(),detailSize);
dFree(baseName);
for (k=0;k<details.size();k++)
{
if (details[k]==detailSize)
{
// found it
details.erase(k);
break;
}
}
}
}
// items left in details list are unique -- add markers
for (j=0;j<details.size();j++)
{
char detailName[20];
INode * detailNode = ip->CreateObjectNode(new DummyObject);
dSprintf(detailName,20,"detail%i",details[j]);
detailNode->SetName(detailName);
node->AttachChild(detailNode);
}
}
}
//----------------------------------------------------------------------------
void renumberNodes(Interface * ip, S32 newSize)
{
S32 i, count = ip->GetSelNodeCount();
char newName[128];
for (i=0; i<count; i++)
{
INode * node = ip->GetSelNode(i);
S32 oldSize;
char * baseName = chopTrailingNumber(node->GetName(),oldSize);
dSprintf(newName,128,"%s%i",baseName,newSize);
node->SetName(newName);
dFree(baseName);
}
}
// this version of doDot used by m_pointInPoly
inline float doDot(F32 v1x, F32 v1y, F32 v2x, F32 v2y, F32 px, F32 py)
{
return (v1x-px) * (v1y-v2y) +
(v1y-py) * (v2x-v1x);
}
//------------------------------------------------------------------------------------
// pointInPoly returns true if point is inside poly defined by verts on plane w/
// normal "normal" -- based on m_pointInTriangle.
//------------------------------------------------------------------------------------
bool pointInPoly(const Point3F & point,
const Point3F & normal,
const Point3F * verts,
U32 n)
{
F32 thisDot, lastDot=0;
U32 i;
// we can ignore one of the dimensions because all points are on the same plane...
if (mFabs(normal.y)>mFabs(normal.x)&&mFabs(normal.y)>mFabs(normal.z))
{
// drop y coord
thisDot = doDot(verts[n-1].x,verts[n-1].z,verts[0].x,verts[0].z,point.x,point.z);
if (thisDot*lastDot<0)
return false;
lastDot = thisDot;
for (i=0;i<n-1;i++)
{
thisDot = doDot(verts[i].x,verts[i].z,verts[i+1].x,verts[i+1].z,point.x,point.z);
if (thisDot*lastDot<0)
return false; // different sign, point outside one of the edges
lastDot = thisDot;
}
}
else if (mFabs(normal.x)>mFabs(normal.y)&&mFabs(normal.x)>mFabs(normal.z))
{
// drop x coord
thisDot = doDot(verts[n-1].y,verts[n-1].z,verts[0].y,verts[0].z,point.y,point.z);
if (thisDot*lastDot<0)
return false;
lastDot = thisDot;
for (i=0;i<n-1;i++)
{
thisDot = doDot(verts[i].y,verts[i].z,verts[i+1].y,verts[i+1].z,point.y,point.z);
if (thisDot*lastDot<0)
return false; // different sign, point outside one of the edges
lastDot = thisDot;
}
}
else
{
// drop z coord
thisDot = doDot(verts[n-1].x,verts[n-1].y,verts[0].x,verts[0].y,point.x,point.y);
if (thisDot*lastDot<0)
return false;
lastDot = thisDot;
for (i=0;i<n-1;i++)
{
thisDot = doDot(verts[i].x,verts[i].y,verts[i+1].x,verts[i+1].y,point.x,point.y);
if (thisDot*lastDot<0)
return false; // different sign, point outside one of the edges
lastDot = thisDot;
}
}
return true;
}
// deal with some unresolved externals...
#include "platform/event.h"
#include "core/bitStream.h"
void GamePostEvent(struct Event const &) {}
void GameHandleInfoPacket(struct NetAddress const *, BitStream *){}
void GameHandleDataPacket(S32, BitStream *){}
void GameConnectionAccepted(S32, BitStream *){}
void GameConnectionRejected(S32, BitStream *){}
void GameConnectionDisconnected(S32, BitStream *){}
void GameConnectionRequested(struct NetAddress const *, BitStream *){}
void GameConnectionEstablished(S32){}
void GameHandleNotify(S32,bool){}
void GameConnectionTimedOut(S32){}
void GameDeactivate(bool) {}
void GameReactivate() {}
S32 GameMain(S32,char const * *){ return 0; }
U32 getTickCount() { return 0; }
void (*terrMipBlit)(U16 *dest, U32 destStride, U32 squareSize, const U8 *sourcePtr, U32 sourceStep, U32 sourceRowAdd);
bool gEditingMission;
class SimGroup;
SimGroup * gDataBlockGroup;
SimGroup * gActionMapGroup;
SimGroup * gClientGroup;
SimGroup * gGuiGroup;
SimGroup * gGuiDataGroup;
SimGroup * gTCPGroup;
class SimSet;
SimSet * gActiveActionMapSet;
SimSet * gGhostAlwaysSet;
SimSet * gLightSet;
//------------------------------------------------------
// These routines aren't currently used, but could prove
// useful at a later time...
//------------------------------------------------------
/*
MatrixF & getNodeMatrix(INode *pNode, S32 time, MatrixF & matrix)
{
Matrix3 nodeMat = pNode->GetNodeTM( time );
convertToMatrixF(nodeMat,matrix);
return matrix;
}
MatrixF & getLocalNodeMatrix(INode *pNode, INode *parent, S32 time, MatrixF & matrix)
{
if (!parent)
return getNodeMatrix(pNode,time,matrix);
MatrixF m1,m2;
getNodeMatrix(pNode,time,m1);
getNodeMatrix(parent,time ,m2);
m2.inverse();
matrix.mul(m1,m2);
return matrix;
}
*/