This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View File

@@ -0,0 +1,436 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "MAX.H"
#include "DECOMP.H"
#include "STDMAT.H"
#include "ANIMTBL.H"
#include "istdplug.h"
#include "phyexp.h"
#include "BonesPro.h"
#include "vweightexprc.h"
#include "vweightexp.h"
#include "vweightimp.h"
// Save for use with dialogs
static HINSTANCE hInstance;
// We just need one of these to hand off to 3DSMAX.
static VWeightExportClassDesc VWeightExportCD;
static VWeightImportClassDesc VWeightImportCD;
//===================================================================
// Required plug-in export functions
//
BOOL WINAPI DllMain( HINSTANCE hinstDLL, ULONG fdwReason, LPVOID lpvReserved)
{
static int fFirstTimeHere = TRUE;
if (fFirstTimeHere)
{
fFirstTimeHere = FALSE;
hInstance = hinstDLL;
}
return TRUE;
}
EXPORT_THIS int LibNumberClasses(void)
{
return 2;
}
EXPORT_THIS ClassDesc *LibClassDesc(int iWhichClass)
{
switch(iWhichClass)
{
case 0: return &VWeightExportCD;
case 1: return &VWeightImportCD;
default: return 0;
}
}
EXPORT_THIS const TCHAR *LibDescription()
{
return _T("Valve VVW Plug-in.");
}
EXPORT_THIS ULONG LibVersion()
{
return VERSION_3DSMAX;
}
//===================================================================
// Utility functions
//
int AssertFailedFunc(char *sz)
{
MessageBox(GetActiveWindow(), sz, "Assert failure", MB_OK);
int Set_Your_Breakpoint_Here = 1;
return 1;
}
//=================================================================
// Methods for CollectModelTEP
//
Modifier *FindPhysiqueModifier (INode *nodePtr)
{
// Get object from node. Abort if no object.
Object *ObjectPtr = nodePtr->GetObjectRef();
if (!ObjectPtr) return NULL;
// Is derived object ?
if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
// Yes -> Cast.
IDerivedObject *DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);
// Iterate over all entries of the modifier stack.
int ModStackIndex = 0;
while (ModStackIndex < DerivedObjectPtr->NumModifiers())
{
// Get current modifier.
Modifier *ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
// Is this Physique ?
if (ModifierPtr->ClassID() == Class_ID( PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B) )
{
// Yes -> Exit.
return ModifierPtr;
}
// Next modifier stack entry.
ModStackIndex++;
}
}
// Not found.
return NULL;
}
Modifier *FindBonesProModifier (INode *nodePtr)
{
// Get object from node. Abort if no object.
Object *ObjectPtr = nodePtr->GetObjectRef();
if (!ObjectPtr) return NULL;
// Is derived object ?
if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
{
// Yes -> Cast.
IDerivedObject *DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);
// Iterate over all entries of the modifier stack.
int ModStackIndex = 0;
while (ModStackIndex < DerivedObjectPtr->NumModifiers())
{
// Get current modifier.
Modifier *ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
// Is this Bones Pro OSM?
if (ModifierPtr->ClassID() == BP_CLASS_ID_OSM )
{
// Yes -> Exit.
return ModifierPtr;
}
// Is this Bones Pro WSM?
if (ModifierPtr->ClassID() == BP_CLASS_ID_WSM )
{
// Yes -> Exit.
return ModifierPtr;
}
// Next modifier stack entry.
ModStackIndex++;
}
}
// Not found.
return NULL;
}
//========================================================================
// Utility functions for getting/setting the personal "node index" property.
// NOTE: I'm storing a string-property because I hit a 3DSMax bug in v1.2 when I
// NOTE: tried using an integer property.
// FURTHER NOTE: those properties seem to change randomly sometimes, so I'm
// implementing my own.
typedef struct
{
char szNodeName[MAX_NAME_CHARS];
} NAMEMAP;
const int MAX_NAMEMAP = 512;
static NAMEMAP g_NameMap[MAX_NAMEMAP];
static int g_cNameMap = 0;
void ResetINodeMap( void )
{
g_cNameMap = 0;
}
int BuildINodeMap(INode *pnode)
{
if (!FUndesirableNode(pnode))
{
AddINode(pnode);
}
// For each child of this node, we recurse into ourselves
// until no more children are found.
for (int c = 0; c < pnode->NumberOfChildren(); c++)
{
BuildINodeMap(pnode->GetChildNode(c));
}
return g_cNameMap;
}
int GetIndexOfNodeName(char *szNodeName, BOOL fAssertPropExists)
{
for (int inm = 0; inm < g_cNameMap; inm++)
{
if (FStrEq(g_NameMap[inm].szNodeName, szNodeName))
{
return inm;
}
}
return -1;
}
int GetIndexOfINode( INode *pnode, BOOL fAssertPropExists )
{
return GetIndexOfNodeName( pnode->GetName(), fAssertPropExists );
}
void AddINode( INode *pnode )
{
TSTR strNodeName(pnode->GetName());
for (int inm = 0; inm < g_cNameMap; inm++)
{
if (FStrEq(g_NameMap[inm].szNodeName, (char*)strNodeName))
{
return;
}
}
ASSERT_MBOX(g_cNameMap < MAX_NAMEMAP, "NAMEMAP is full");
strcpy(g_NameMap[g_cNameMap++].szNodeName, (char*)strNodeName);
}
//=============================================================
// Returns TRUE if a node should be ignored during tree traversal.
//
BOOL FUndesirableNode(INode *pnode)
{
// Get Node's underlying object, and object class name
Object *pobj = pnode->GetObjectRef();
if (!pobj)
return TRUE;
// Don't care about lights, dummies, and cameras
if (pobj->SuperClassID() == CAMERA_CLASS_ID)
return TRUE;
if (pobj->SuperClassID() == LIGHT_CLASS_ID)
return TRUE;
if (!strstr(pnode->GetName(), "Bip01" ))
return TRUE;
return FALSE;
// Actually, if it's not selected, pretend it doesn't exist!
//if (!pnode->Selected())
// return TRUE;
//return FALSE;
}
//=============================================================
// Returns TRUE if a node has been marked as skippable
//
BOOL FNodeMarkedToSkip(INode *pnode)
{
return (::GetIndexOfINode(pnode) == UNDESIRABLE_NODE_MARKER);
}
//=============================================================
// gets a weighted value for the current system of nodes
//
void GetUnifiedCoord( Point3 &p1, MaxVertWeight pweight[], MaxNode maxNode[], int cMaxNode )
{
float flMin = 1E20f;
for (int iNode = 0; iNode < cMaxNode; iNode++)
{
float f = (p1 - maxNode[iNode].mat3NodeTM.GetTrans()).LengthSquared();
if (f > 0 && f < flMin)
flMin = f;
pweight[iNode].flDist = f;
}
float flTotal = 0;
float flInvMin = 1.0 / flMin;
for (iNode = 0; iNode < cMaxNode; iNode++)
{
if (pweight[iNode].flDist > 0)
{
pweight[iNode].flDist = flInvMin / pweight[iNode].flDist;
}
else
{
pweight[iNode].flDist = 10.0;
}
flTotal += pweight[iNode].flDist;
}
float flInvTotal;
if (flTotal > 0)
{
flInvTotal = 1.0 / flTotal;
}
else
{
flInvTotal = 1.0;
}
for (iNode = 0; iNode < cMaxNode; iNode++)
{
pweight[iNode].flDist = pweight[iNode].flDist * flInvTotal;
// fprintf(m_pfile, "%8.4f ", pweight[iNode].flDist );
}
}
int GetBoneWeights( IPhyContextExport *mcExport, int iVertex, MaxVertWeight *pweight)
{
float fTotal = 0;
IPhyVertexExport *vtxExport = mcExport->GetVertexInterface(iVertex);
if (vtxExport)
{
if (vtxExport->GetVertexType() & BLENDED_TYPE)
{
IPhyBlendedRigidVertex *pBlend = ((IPhyBlendedRigidVertex *)vtxExport);
for (int i = 0; i < pBlend->GetNumberNodes(); i++)
{
int index = GetIndexOfINode( pBlend->GetNode( i ) );
if (index >= 0)
{
pweight[index].flWeight = pBlend->GetWeight( i );
fTotal += pweight[index].flWeight;
}
}
}
else
{
INode *Bone = ((IPhyRigidVertex *)vtxExport)->GetNode();
int index = GetIndexOfINode(Bone);
if (index >= 0)
{
pweight[index].flWeight = 100.0;
}
}
mcExport->ReleaseVertexInterface(vtxExport);
}
return (fTotal > 0);
}
int GetBoneWeights( Modifier * bonesProMod, int iVertex, MaxVertWeight *pweight)
{
int iTotal = 0;
int nb = bonesProMod->SetProperty( BP_PROPID_GET_N_BONES, NULL );
for ( int iBone = 0; iBone < nb; iBone++)
{
BonesPro_BoneVertex bv;
bv.bindex = iBone;
bv.vindex = iVertex;
bonesProMod->SetProperty( BP_PROPID_GET_BV, &bv );
if (bv.included > 0 && bv.forced_weight >= 0)
{
BonesPro_Bone bone;
bone.t = BP_TIME_ATTACHED;
bone.index = iBone;
bonesProMod->SetProperty( BP_PROPID_GET_BONE_STAT, &bone );
if (bone.node != NULL)
{
int index = GetIndexOfINode( bone.node );
if (index >= 0)
{
pweight[index].flWeight = bv.forced_weight;
iTotal++;
}
}
}
}
return iTotal;
}
void SetBoneWeights( Modifier * bonesProMod, int iVertex, MaxVertWeight *pweight)
{
int nb = bonesProMod->SetProperty( BP_PROPID_GET_N_BONES, NULL );
// FILE *fp = fopen("bone2.txt", "w");
for ( int iBone = 0; iBone < nb; iBone++)
{
BonesPro_Bone bone;
bone.t = BP_TIME_ATTACHED;
bone.index = iBone;
bonesProMod->SetProperty( BP_PROPID_GET_BONE_STAT, &bone );
/*
if (GetIndexOfINode( bone.node ) >= 0)
{
fprintf( fp, "\"%s\" %d\n", bone.name, GetIndexOfINode( bone.node ) );
}
else
{
fprintf( fp, "\"%s\"\n", bone.name );
}
*/
BonesPro_BoneVertex bv;
bv.bindex = iBone;
bv.vindex = iVertex;
bv.included = 0;
bv.forced_weight = -1;
if (bone.node != NULL)
{
int index = GetIndexOfINode( bone.node );
if (index >= 0 && pweight[index].flWeight >= 0)
{
bv.included = 1;
bv.forced_weight = pweight[index].flWeight;
}
}
bonesProMod->SetProperty( BP_PROPID_SET_BV, &bv );
}
//fclose(fp);
//exit(1);
}

View File

@@ -0,0 +1,356 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "MAX.H"
#include "DECOMP.H"
#include "STDMAT.H"
#include "ANIMTBL.H"
#include "istdplug.h"
#include "phyexp.h"
#include "BonesPro.h"
#include "vweightexprc.h"
#include "vweightexp.h"
//===================================================================
// Global variable definitions
//
// For OutputDebugString and misc sprintf's
static char st_szDBG[300];
//=====================================================================
// Methods for VWeightExportClass
//
CONSTRUCTOR VWeightExportClass::VWeightExportClass(void)
{
m_cMaxNode = 0;
m_cMaxVertex = 0;
}
DESTRUCTOR VWeightExportClass::~VWeightExportClass(void)
{
for (int i = 0; i < m_cMaxVertex; i++)
{
delete[] m_MaxVertex[i];
}
}
int VWeightExportClass::DoExport(const TCHAR *name, ExpInterface *ei, Interface *pi, BOOL suppressPrompts, DWORD options)
{
ExpInterface *pexpiface = ei; // Hungarian
Interface *piface = pi; // Hungarian
// Reset the name-map property manager
ResetINodeMap();
// Break up filename, re-assemble longer versions
TSTR strPath, strFile, strExt;
TCHAR szFile[MAX_PATH];
SplitFilename(TSTR(name), &strPath, &strFile, &strExt);
sprintf(szFile, "%s\\%s.%s", (char*)strPath, (char*)strFile, DEFAULT_EXT);
// Get animation metrics
m_intervalOfAnimation = piface->GetAnimRange();
m_tvStart = m_intervalOfAnimation.Start();
m_tvEnd = m_intervalOfAnimation.End();
m_tpf = ::GetTicksPerFrame();
Interface *ip = GetCOREInterface();
ResetINodeMap( );
m_cMaxNode = BuildINodeMap( ip->GetRootNode() );
// Count nodes, label them, collect into array
CollectNodes( ip->GetRootNode() );
CollectModel( pexpiface );
#if 1
FILE *pFile;
if ((pFile = fopen(szFile, "wb")) == NULL)
return FALSE/*failure*/;
int version = 1;
fwrite( &version, 1, sizeof( int ), pFile );
int i, j;
fwrite( &m_cMaxNode, 1, sizeof( int ), pFile );
fwrite( &m_cMaxVertex, 1, sizeof( int ), pFile );
for (i = 0; i < m_cMaxNode; i++)
{
fwrite( &m_MaxNode[i], 1, sizeof(m_MaxNode[i]), pFile );
}
for (j = 0; j < m_cMaxVertex; j++)
{
fwrite( m_MaxVertex[j], m_cMaxNode, sizeof(MaxVertWeight), pFile );
}
fclose( pFile );
#else
FILE *pFile;
if ((pFile = fopen(szFile, "w")) == NULL)
return FALSE/*failure*/;
fprintf( pFile, "version %d\n", 1 );
int i, j;
fprintf(pFile, "%d\n", m_cMaxNode );
fprintf(pFile, "%d\n", m_cMaxVertex );
for (i = 0; i < m_cMaxNode; i++)
{
fprintf(pFile, "%5d \"%s\" %3d\n",
i, m_MaxNode[i].szNodeName, m_MaxNode[i].imaxnodeParent );
}
for (j = 0; j < m_cMaxVertex; j++)
{
fprintf( pFile, "%d ", j );
for (int i = 0; i < m_cMaxNode; i++)
{
// if (strstr(m_MaxNode[i].szNodeName, "Bip01 R Finger"))
// if (m_MaxNode[i].szNodeName[0] == 'D')
{
fprintf(pFile, " %5.3f", m_MaxVertex[j][i].flDist );
fprintf(pFile, " %3.0f", m_MaxVertex[j][i].flWeight );
}
}
fprintf(pFile, "\n" );
}
fclose( pFile );
#endif
// Tell user that exporting is finished (it can take a while with no feedback)
char szExportComplete[300];
sprintf(szExportComplete, "Exported %s.", szFile);
MessageBox(GetActiveWindow(), szExportComplete, "Status", MB_OK);
return 1/*success*/;
}
void VWeightExportClass::CollectNodes( INode *pnode )
{
// Get pre-stored "index"
int index = ::GetIndexOfINode(pnode);
if (index >= 0)
{
// Get name, store name in array
TSTR strNodeName(pnode->GetName());
strcpy(m_MaxNode[index].szNodeName, (char*)strNodeName);
// Get Node's time-zero Transformation Matrices
m_MaxNode[index].mat3NodeTM = pnode->GetNodeTM(0);
m_MaxNode[index].mat3ObjectTM = pnode->GetObjectTM(0);
}
for (int c = 0; c < pnode->NumberOfChildren(); c++)
{
CollectNodes(pnode->GetChildNode(c));
}
return;
}
BOOL VWeightExportClass::CollectModel( ExpInterface *pexpiface)
{
// Dump mesh info: vertices, normals, UV texture map coords, bone assignments
CollectModelTEP procCollectModel;
// init data
m_cMaxVertex = 0;
procCollectModel.m_phec = this;
//fprintf(pFile, "triangles\n" );
procCollectModel.m_tvToDump = m_tvStart;
(void) pexpiface->theScene->EnumTree(&procCollectModel);
//fprintf(pFile, "end\n" );
return TRUE;
}
// #define DEBUG_MESH_DUMP
//=================================================================
// Methods for CollectModelTEP
//
int CollectModelTEP::callback(INode *pnode)
{
if (::FNodeMarkedToSkip(pnode))
return TREE_CONTINUE;
if ( !pnode->Selected())
return TREE_CONTINUE;
// clear physique export parameters
m_mcExport = NULL;
m_phyExport = NULL;
m_phyMod = NULL;
m_bonesProMod = NULL;
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
int iNode = ::GetIndexOfINode(pnode);
TSTR strNodeName(pnode->GetName());
// The Footsteps node apparently MUST have a dummy mesh attached! Ignore it explicitly.
if (FStrEq((char*)strNodeName, "Bip01 Footsteps"))
return TREE_CONTINUE;
// Helper nodes don't have meshes
Object *pobj = pnode->GetObjectRef();
if (pobj->SuperClassID() == HELPER_CLASS_ID)
return TREE_CONTINUE;
// Get Node's object, convert to a triangle-mesh object, so I can access the Faces
ObjectState os = pnode->EvalWorldState(m_tvToDump);
pobj = os.obj;
// Shouldn't have gotten this far if it's a helper object
if (pobj->SuperClassID() == HELPER_CLASS_ID)
{
sprintf(st_szDBG, "ERROR--Helper node %s has an attached mesh, and it shouldn't.", (char*)strNodeName);
ASSERT_AND_ABORT(FALSE, st_szDBG);
}
// convert mesh to triobject
if (!pobj->CanConvertToType(triObjectClassID))
return TREE_CONTINUE;
TriObject *ptriobj = (TriObject*)pobj->ConvertToType(m_tvToDump, triObjectClassID);
if (ptriobj == NULL)
return TREE_CONTINUE;
Mesh *pmesh = &ptriobj->mesh;
// We want the vertex coordinates in World-space, not object-space
Matrix3 mat3ObjectTM = pnode->GetObjectTM(m_tvToDump);
// initialize physique export parameters
m_phyMod = FindPhysiqueModifier(pnode);
if (m_phyMod)
{
// Physique Modifier exists for given Node
m_phyExport = (IPhysiqueExport *)m_phyMod->GetInterface(I_PHYINTERFACE);
if (m_phyExport)
{
// create a ModContext Export Interface for the specific node of the Physique Modifier
m_mcExport = (IPhyContextExport *)m_phyExport->GetContextInterface(pnode);
if (m_mcExport)
{
// convert all vertices to Rigid
m_mcExport->ConvertToRigid(TRUE);
}
}
}
// initialize bones pro export parameters
m_wa = NULL;
m_bonesProMod = FindBonesProModifier(pnode);
if (m_bonesProMod)
{
m_bonesProMod->SetProperty( BP_PROPID_GET_WEIGHTS, &m_wa );
}
int cVerts = pmesh->getNumVerts();
// Dump the triangle face info
int cFaces = pmesh->getNumFaces();
int *iUsed = new int[cVerts];
for (int iVert = 0; iVert < cVerts; iVert++)
{
iUsed[iVert] = 0;
}
for (int iFace = 0; iFace < cFaces; iFace++)
{
if (pmesh->faces[iFace].flags & HAS_TVERTS)
{
iUsed[pmesh->faces[iFace].getVert(0)] = 1;
iUsed[pmesh->faces[iFace].getVert(1)] = 1;
iUsed[pmesh->faces[iFace].getVert(2)] = 1;
}
}
for (iVert = 0; iVert < cVerts; iVert++)
{
MaxVertWeight *pweight = m_phec->m_MaxVertex[m_phec->m_cMaxVertex] = new MaxVertWeight [m_phec->m_cMaxNode];
Point3 pt3Vertex1 = pmesh->getVert(iVert);
Point3 v1 = pt3Vertex1 * mat3ObjectTM;
GetUnifiedCoord( v1, pweight, m_phec->m_MaxNode, m_phec->m_cMaxNode );
if (CollectWeights( iVert, pweight ))
{
m_phec->m_cMaxVertex++;
}
}
// fflush( m_pfile );
return TREE_CONTINUE;
}
int CollectModelTEP::CollectWeights(int iVertex, MaxVertWeight *pweight)
{
for (int index = 0; index < m_phec->m_cMaxNode; index++)
{
pweight[index].flWeight = -1;
}
if (m_mcExport)
{
return GetBoneWeights( m_mcExport, iVertex, pweight );
}
else
{
return GetBoneWeights( m_bonesProMod, iVertex, pweight );
}
}
void CollectModelTEP::cleanup(void)
{
if (m_phyMod && m_phyExport)
{
if (m_mcExport)
{
m_phyExport->ReleaseContextInterface(m_mcExport);
m_mcExport = NULL;
}
m_phyMod->ReleaseInterface(I_PHYINTERFACE, m_phyExport);
m_phyExport = NULL;
m_phyMod = NULL;
}
}

View File

@@ -0,0 +1,8 @@
LIBRARY vweightexp
EXPORTS
LibDescription @1
LibNumberClasses @2
LibClassDesc @3
LibVersion @4
SECTIONS
.data READ WRITE

View File

@@ -0,0 +1,183 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//===================================================================
// Useful macros
//
#define CONSTRUCTOR
#define DESTRUCTOR
#define EXPORT_THIS __declspec(dllexport)
#define DEFAULT_EXT _T("vvw")
#define FStrEq(sz1, sz2) (strcmp((sz1), (sz2)) == 0)
//=============================================================================
// TREE-ENUMERATION PROCEDURES
//=============================================================================
#define ASSERT_AND_ABORT(f, sz) \
if (!(f)) \
{ \
ASSERT_MBOX(FALSE, sz); \
cleanup( ); \
return TREE_ABORT; \
}
// Integer constants for this class
enum
{
MAX_NAME_CHARS = 70,
UNDESIRABLE_NODE_MARKER = -7777
};
// For keeping info about each (non-ignored) 3dsMax node in the tree
typedef struct
{
char szNodeName[MAX_NAME_CHARS]; // usefull for lookups
Matrix3 mat3NodeTM; // node's transformation matrix (at time zero)
Matrix3 mat3ObjectTM; // object-offset transformation matrix (at time zero)
int imaxnodeParent; // cached index of parent node
} MaxNode;
typedef struct
{
float flDist;
float flWeight;
} MaxVertWeight;
//===================================================================
// Class that implements the scene-export.
//
class VWeightExportClass : public SceneExport
{
friend BOOL CALLBACK ExportOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
friend class CollectModelTEP;
public:
CONSTRUCTOR VWeightExportClass (void);
DESTRUCTOR ~VWeightExportClass (void);
// Required by classes derived from SceneExport
virtual int ExtCount (void) { return 1; }
virtual const TCHAR* Ext (int i) { return DEFAULT_EXT; }
virtual const TCHAR* LongDesc (void) { return _T("Valve Skeletal Model Exporter for 3D Studio Max"); }
virtual const TCHAR* ShortDesc (void) { return _T("Valve VVW"); }
virtual const TCHAR* AuthorName (void) { return _T("Valve, LLC"); }
virtual const TCHAR* CopyrightMessage(void) { return _T("Copyright (c) 1998, Valve LLC"); }
virtual const TCHAR* OtherMessage1 (void) { return _T(""); }
virtual const TCHAR* OtherMessage2 (void) { return _T(""); }
virtual unsigned int Version (void) { return 201; }
virtual void ShowAbout (HWND hWnd) { return; }
// virtual int DoExport (const TCHAR *name, ExpInterface *ei, Interface *i);
virtual int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE,DWORD options=0); // Export file
MaxNode m_MaxNode[512]; // array of nodes
long m_cMaxNode; // # of nodes
MaxVertWeight *m_MaxVertex[10000];
long m_cMaxVertex;
// Animation metrics (gleaned from 3dsMax and cached for convenience)
Interval m_intervalOfAnimation;
TimeValue m_tvStart;
TimeValue m_tvEnd;
int m_tpf; // ticks-per-frame
private:
void CollectNodes( INode *pnode );
BOOL CollectModel( ExpInterface *pexpiface );
};
//===================================================================
// Basically just a ClassFactory for communicating with 3DSMAX.
//
class VWeightExportClassDesc : public ClassDesc
{
public:
int IsPublic (void) { return TRUE; }
void * Create (BOOL loading=FALSE) { return new VWeightExportClass; }
const TCHAR * ClassName (void) { return _T("VWeightExport"); }
SClass_ID SuperClassID (void) { return SCENE_EXPORT_CLASS_ID; }
Class_ID ClassID (void) { return Class_ID(0x554b1092, 0x206a444f); }
const TCHAR * Category (void) { return _T(""); }
};
//===================================================================
// Tree Enumeration Callback
// Just counts the nodes in the node tree
//
class CountNodesTEP : public ITreeEnumProc
{
public:
virtual int callback(INode *node);
int m_cNodes; // running count of nodes
};
//===================================================================
// Tree Enumeration Callback
// Collects the nodes in the tree into the global array
//
class CollectNodesTEP : public ITreeEnumProc
{
public:
virtual int callback(INode *node);
VWeightExportClass *m_phec;
};
//===================================================================
// Tree Enumeration Callback
// Dumps the triangle meshes to a file.
//
class CollectModelTEP : public ITreeEnumProc
{
public:
virtual int callback(INode *node);
void cleanup(void);
// FILE *m_pfile; // write to this file
TimeValue m_tvToDump; // dump snapshot at this frame time
VWeightExportClass *m_phec;
IPhyContextExport *m_mcExport;
IPhysiqueExport *m_phyExport;
Modifier *m_phyMod;
Modifier *m_bonesProMod;
BonesPro_WeightArray *m_wa;
private:
int CollectWeights( int iVertex, MaxVertWeight *pweight );
};
//===================================================================
// Prototype declarations
//
void ResetINodeMap( void );
int BuildINodeMap(INode *pnode);
void AddINode( INode *pnode );
int GetIndexOfNodeName( TCHAR *szNodeName, BOOL fAssertPropExists = TRUE );
int GetIndexOfINode( INode *pnode, BOOL fAssertPropExists = TRUE);
BOOL FUndesirableNode( INode *pnode);
BOOL FNodeMarkedToSkip( INode *pnode);
Modifier *FindPhysiqueModifier( INode *nodePtr );
Modifier *FindBonesProModifier( INode *nodePtr );
int AssertFailedFunc(char *sz);
#define ASSERT_MBOX(f, sz) ((f) ? 1 : AssertFailedFunc(sz))
void GetUnifiedCoord( Point3 &p1, MaxVertWeight pweight[], MaxNode maxNode[], int cMaxNode );
int GetBoneWeights( IPhyContextExport *mcExport, int iVertex, MaxVertWeight *pweight);
int GetBoneWeights( Modifier * bonesProMod, int iVertex, MaxVertWeight *pweight);
void SetBoneWeights( Modifier * bonesProMod, int iVertex, MaxVertWeight *pweight);

View File

@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="vweightexp"
ProjectGUID="{6233E118-9880-4484-8243-AB7BE7AF96A1}"
SccProjectName=""
SccLocalPath="">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="2"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\MAXSDK\INCLUDE"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;VWEIGHTEXP_EXPORTS"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Debug/vweightexp.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="4"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="maxutil.lib geom.lib mesh.lib core.lib odbc32.lib odbccp32.lib"
OutputFile="c:\3DSMAX3_1\plugins\VWEIGHTEXP.DLE"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\..\maxsdk\lib"
ModuleDefinitionFile=".\vweightexp.def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\Debug/VWEIGHTEXP.pdb"
ImportLibrary=".\Debug/VWEIGHTEXP.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="_DEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName=".\Debug/vweightexp.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="2"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
AdditionalIncludeDirectories="..\..\MAXSDK\INCLUDE"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;VWEIGHTEXP_EXPORTS"
StringPooling="TRUE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="TRUE"
UsePrecompiledHeader="2"
PrecompiledHeaderFile=".\Release/vweightexp.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="TRUE"
CompileAs="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="maxutil.lib geom.lib mesh.lib core.lib odbc32.lib odbccp32.lib"
OutputFile="c:\3DSMAX3_1\plugins\VWEIGHTEXP.DLE"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\..\maxsdk\lib"
ModuleDefinitionFile=".\vweightexp.def"
ProgramDatabaseFile=".\Release/VWEIGHTEXP.pdb"
ImportLibrary=".\Release/VWEIGHTEXP.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="NDEBUG"
MkTypLibCompatible="TRUE"
SuppressStartupBanner="TRUE"
TargetEnvironment="1"
TypeLibraryName=".\Release/vweightexp.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath="vweight.cpp">
</File>
<File
RelativePath="vweightexp.cpp">
</File>
<File
RelativePath="vweightexp.def">
</File>
<File
RelativePath="vweightimp.cpp">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl">
<File
RelativePath="vweightexp.h">
</File>
<File
RelativePath="vweightexprc.h">
</File>
<File
RelativePath="vweightimp.h">
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,28 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by smdlexp.rc
//
#define IDD_SMDLEXP_UI 101
#define IDD_EXPORTOPTIONS 101
#define IDC_CHECK_SKELETAL 1000
#define IDC_CHECK_DEFORM 1001
#define IDC_CHECK_REFFRAME 1002
#define IDC_CHECK_PHYSIQUE 1003
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1006
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,354 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "MAX.H"
#include "DECOMP.H"
#include "STDMAT.H"
#include "ANIMTBL.H"
#include "istdplug.h"
#include "phyexp.h"
#include "BonesPro.h"
#include "vweightexprc.h"
#include "vweightexp.h"
#include "vweightimp.h"
//===================================================================
// Global variable definitions
//
// For OutputDebugString and misc sprintf's
static char st_szDBG[8193];
//=====================================================================
// Methods for VWeightImportClass
//
CONSTRUCTOR VWeightImportClass::VWeightImportClass(void)
{
m_cSrcMaxNodes = 0;
m_cSrcMaxVertex = 0;
m_cMaxNodes = 0;
}
DESTRUCTOR VWeightImportClass::~VWeightImportClass(void)
{
for (int i = 0; i < m_cSrcMaxVertex; i++)
{
delete[] m_SrcMaxVertex[i];
}
}
int VWeightImportClass::DoImport(const TCHAR *name, ImpInterface *ii, Interface *pi, BOOL suppressPrompts)
{
ImpInterface *pimpiface = ii;
Interface *piface = pi;
// Break up filename, re-assemble longer versions
TSTR strPath, strFile, strExt;
TCHAR szFile[MAX_PATH];
SplitFilename(TSTR(name), &strPath, &strFile, &strExt);
sprintf(szFile, "%s\\%s.%s", (char*)strPath, (char*)strFile, DEFAULT_EXT);
// Get animation metrics
m_intervalOfAnimation = piface->GetAnimRange();
m_tvStart = m_intervalOfAnimation.Start();
m_tvEnd = m_intervalOfAnimation.End();
m_tpf = ::GetTicksPerFrame();
Interface *ip = GetCOREInterface();
ResetINodeMap( );
m_cMaxNodes = BuildINodeMap( ip->GetRootNode() );
CollectNodes( ip->GetRootNode() );
// read src data
FILE *pFile;
if ((pFile = fopen(szFile, "rb")) == NULL)
return FALSE/*failure*/;
int version = 1;
fread( &version, 1, sizeof( int ), pFile );
int i, j;
fread( &m_cSrcMaxNodes, 1, sizeof( int ), pFile );
fread( &m_cSrcMaxVertex, 1, sizeof( int ), pFile );
for (i = 0; i < m_cSrcMaxNodes; i++)
{
fread( &m_SrcMaxNode[i], 1, sizeof(m_SrcMaxNode[i]), pFile );
}
int cNodes = (m_cSrcMaxNodes > m_cMaxNodes) ? m_cSrcMaxNodes : m_cMaxNodes;
for (j = 0; j < m_cSrcMaxVertex; j++)
{
m_SrcMaxVertex[j] = new MaxVertWeight [cNodes];
fread( m_SrcMaxVertex[j], m_cSrcMaxNodes, sizeof(MaxVertWeight), pFile );
}
fclose( pFile );
RemapSrcWeights( );
NodeEnum( ip->GetSelNode( 0 ) );
// Tell user that exporting is finished (it can take a while with no feedback)
char szExportComplete[300];
sprintf(szExportComplete, "Imported %s.", szFile);
MessageBox(GetActiveWindow(), szExportComplete, "Status", MB_OK);
return 1/*success*/;
}
void VWeightImportClass::CollectNodes( INode *pnode )
{
// Get pre-stored "index"
int index = ::GetIndexOfINode(pnode);
if (index >= 0)
{
// Get name, store name in array
TSTR strNodeName(pnode->GetName());
strcpy(m_MaxNode[index].szNodeName, (char*)strNodeName);
// Get Node's time-zero Transformation Matrices
m_MaxNode[index].mat3NodeTM = pnode->GetNodeTM(0);
m_MaxNode[index].mat3ObjectTM = pnode->GetObjectTM(0);
}
for (int c = 0; c < pnode->NumberOfChildren(); c++)
{
CollectNodes(pnode->GetChildNode(c));
}
return;
}
void VWeightImportClass::NodeEnum( INode *node )
{
#if 0
// For each child of this node, we recurse into ourselves
// until no more children are found.
for (int c = 0; c < node->NumberOfChildren(); c++)
{
NodeEnum(node->GetChildNode(c));
}
#endif
UpdateModel( node );
}
void VWeightImportClass::RemapSrcWeights( )
{
int index;
for (index = 0; index < m_cMaxNodes; index++)
{
m_iToSrc[index] = -1;
}
for (int iSrc = 0; iSrc < m_cSrcMaxNodes; iSrc++)
{
index = GetIndexOfNodeName( (char*) m_SrcMaxNode[iSrc].szNodeName );
if (index >= 0)
{
m_iToSrc[index] = iSrc;
}
else
{
iSrc = iSrc;
}
}
MaxVertWeight flTmp[512];
for (index = 0; index < m_cMaxNodes; index++)
{
flTmp[index].flDist = 0.0;
flTmp[index].flWeight = 0.0;
}
for (int iVert = 0; iVert < m_cSrcMaxVertex; iVert++)
{
for (index = 0; index < m_cMaxNodes; index++)
{
if (m_iToSrc[index] >= 0)
{
flTmp[index] = m_SrcMaxVertex[iVert][m_iToSrc[index]];
}
}
for (index = 0; index < m_cMaxNodes; index++)
{
m_SrcMaxVertex[iVert][index] = flTmp[index];
}
}
}
int VWeightImportClass::UpdateModel(INode *pnode)
{
if (::FNodeMarkedToSkip(pnode))
return TREE_CONTINUE;
if ( !pnode->Selected())
return TREE_CONTINUE;
// clear physique export parameters
m_mcExport = NULL;
m_phyExport = NULL;
m_phyMod = NULL;
m_bonesProMod = NULL;
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
int iNode = ::GetIndexOfINode(pnode);
TSTR strNodeName(pnode->GetName());
// The Footsteps node apparently MUST have a dummy mesh attached! Ignore it explicitly.
if (FStrEq((char*)strNodeName, "Bip01 Footsteps"))
return TREE_CONTINUE;
// Helper nodes don't have meshes
Object *pobj = pnode->GetObjectRef();
if (pobj->SuperClassID() == HELPER_CLASS_ID)
return TREE_CONTINUE;
// Get Node's object, convert to a triangle-mesh object, so I can access the Faces
ObjectState os = pnode->EvalWorldState(m_tvStart);
pobj = os.obj;
// Shouldn't have gotten this far if it's a helper object
if (pobj->SuperClassID() == HELPER_CLASS_ID)
{
return TREE_CONTINUE;
}
// convert mesh to triobject
if (!pobj->CanConvertToType(triObjectClassID))
return TREE_CONTINUE;
TriObject *ptriobj = (TriObject*)pobj->ConvertToType(m_tvStart, triObjectClassID);
if (ptriobj == NULL)
return TREE_CONTINUE;
Mesh *pmesh = &ptriobj->mesh;
// We want the vertex coordinates in World-space, not object-space
Matrix3 mat3ObjectTM = pnode->GetObjectTM(m_tvStart);
// initialize physique export parameters
m_phyMod = FindPhysiqueModifier(pnode);
if (m_phyMod)
{
// Physique Modifier exists for given Node
m_phyExport = (IPhysiqueExport *)m_phyMod->GetInterface(I_PHYINTERFACE);
if (m_phyExport)
{
// create a ModContext Export Interface for the specific node of the Physique Modifier
m_mcExport = (IPhyContextExport *)m_phyExport->GetContextInterface(pnode);
if (m_mcExport)
{
// convert all vertices to Rigid
m_mcExport->ConvertToRigid(TRUE);
}
}
}
// initialize bones pro export parameters
m_wa = NULL;
m_bonesProMod = FindBonesProModifier(pnode);
if (m_bonesProMod)
{
m_bonesProMod->SetProperty( BP_PROPID_GET_WEIGHTS, &m_wa );
}
int cVerts = pmesh->getNumVerts();
// Dump the triangle face info
int cFaces = pmesh->getNumFaces();
MaxVertWeight mvTmp[512];
for (int iVert = 0; iVert < cVerts; iVert++)
{
Point3 pt3Vertex1 = pmesh->getVert(iVert);
Point3 v1 = pt3Vertex1 * mat3ObjectTM;
if (v1.x < -24.0)
{
v1 = v1;
}
GetUnifiedCoord( v1, mvTmp, m_MaxNode, m_cMaxNodes );
GetBoneWeights( m_bonesProMod, iVert, mvTmp );
int iClosest = -1;
float flMinDist = 1E30f;
for (int iVert2 = 0; iVert2 < m_cSrcMaxVertex; iVert2++)
{
float flDist = 0;
for (int index = 0; index < m_cMaxNodes && flDist < flMinDist; index++)
{
if (m_iToSrc[index]>=0)
{
float tmp = mvTmp[index].flDist - m_SrcMaxVertex[iVert2][index].flDist;
flDist += (tmp * tmp);
}
}
if (flDist < flMinDist)
{
iClosest = iVert2;
flMinDist = flDist;
}
}
if (iClosest != -1)
{
SetBoneWeights( m_bonesProMod, iVert, m_SrcMaxVertex[iClosest] );
}
}
if (m_bonesProMod)
{
m_bonesProMod->SetProperty ( BP_PROPID_REFRESH, NULL );
}
// fflush( m_pfile );
return TREE_CONTINUE;
}
void UpdateModelTEP::cleanup(void)
{
if (m_phyMod && m_phyExport)
{
if (m_mcExport)
{
m_phyExport->ReleaseContextInterface(m_mcExport);
m_mcExport = NULL;
}
m_phyMod->ReleaseInterface(I_PHYINTERFACE, m_phyExport);
m_phyExport = NULL;
m_phyMod = NULL;
}
}

View File

@@ -0,0 +1,130 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
//===================================================================
// Useful macros
//
#define CONSTRUCTOR
#define DESTRUCTOR
#define EXPORT_THIS __declspec(dllexport)
#define DEFAULT_EXT _T("vvw")
#define FStrEq(sz1, sz2) (strcmp((sz1), (sz2)) == 0)
//===================================================================
// Class that implements the scene-export.
//
class VWeightImportClass : public SceneImport
{
// friend BOOL CALLBACK ImportOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
// friend class DumpModelTEP;
// friend class DumpDeformsTEP;
public:
CONSTRUCTOR VWeightImportClass (void);
DESTRUCTOR ~VWeightImportClass (void);
// Required by classes derived from SceneExport
virtual int ExtCount (void) { return 1; }
virtual const TCHAR* Ext (int i) { return DEFAULT_EXT; }
virtual const TCHAR* LongDesc (void) { return _T("Valve Skeletal Model Exporter for 3D Studio Max"); }
virtual const TCHAR* ShortDesc (void) { return _T("Valve VVW"); }
virtual const TCHAR* AuthorName (void) { return _T("Valve, LLC"); }
virtual const TCHAR* CopyrightMessage(void) { return _T("Copyright (c) 1998, Valve LLC"); }
virtual const TCHAR* OtherMessage1 (void) { return _T(""); }
virtual const TCHAR* OtherMessage2 (void) { return _T(""); }
virtual unsigned int Version (void) { return 201; }
virtual void ShowAbout (HWND hWnd) { return; }
// virtual int DoExport (const TCHAR *name, ExpInterface *ei, Interface *i);
virtual int DoImport(const TCHAR *name,ImpInterface *ii,Interface *i, BOOL suppressPrompts=FALSE); // Export file
MaxNode m_SrcMaxNode[512]; // array of nodes
long m_cSrcMaxNodes; // # of nodes
MaxVertWeight *m_SrcMaxVertex[10000];
long m_cSrcMaxVertex;
MaxNode m_MaxNode[512]; // array of nodes
long m_cMaxNodes; // # of nodes
int m_iToSrc[512]; // translate src nodes to local bones
// Animation metrics (gleaned from 3dsMax and cached for convenience)
Interval m_intervalOfAnimation;
TimeValue m_tvStart;
TimeValue m_tvEnd;
int m_tpf; // ticks-per-frame
private:
void CollectNodes( INode *pnode );
void NodeEnum( INode *node );
int UpdateModel( INode *node );
void CollectWeights(int iVertex, MaxVertWeight *pweight);
void RemapSrcWeights( void );
IPhyContextExport *m_mcExport;
IPhysiqueExport *m_phyExport;
Modifier *m_phyMod;
Modifier *m_bonesProMod;
BonesPro_WeightArray *m_wa;
};
//===================================================================
// Basically just a ClassFactory for communicating with 3DSMAX.
//
class VWeightImportClassDesc : public ClassDesc
{
public:
int IsPublic (void) { return TRUE; }
void * Create (BOOL loading=FALSE) { return new VWeightImportClass; }
const TCHAR * ClassName (void) { return _T("VWeightImport"); }
SClass_ID SuperClassID (void) { return SCENE_IMPORT_CLASS_ID; }
Class_ID ClassID (void) { return Class_ID(0x554b1092, 0x206a444f); }
const TCHAR * Category (void) { return _T(""); }
};
//===================================================================
// Tree Enumeration Callback
// Collects the nodes in the tree into the global array
//
class UpdateNodesTEP : public ITreeEnumProc
{
public:
virtual int callback(INode *node);
VWeightImportClass *m_phec;
};
//===================================================================
// Tree Enumeration Callback
// Dumps the triangle meshes to a file.
//
class UpdateModelTEP : public ITreeEnumProc
{
public:
virtual int callback(INode *node);
void cleanup(void);
// FILE *m_pfile; // write to this file
TimeValue m_tvToDump; // dump snapshot at this frame time
VWeightImportClass *m_phec;
IPhyContextExport *m_mcExport;
IPhysiqueExport *m_phyExport;
Modifier *m_phyMod;
Modifier *m_bonesProMod;
BonesPro_WeightArray *m_wa;
private:
void CollectWeights( int iVertex, MaxVertWeight *pweight );
};