mirror of
https://github.com/celisej567/source-engine.git
synced 2026-01-04 18:09:53 +03:00
1
This commit is contained in:
436
utils/vweightexp/vweight.cpp
Normal file
436
utils/vweightexp/vweight.cpp
Normal 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);
|
||||
}
|
||||
|
||||
356
utils/vweightexp/vweightexp.cpp
Normal file
356
utils/vweightexp/vweightexp.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
8
utils/vweightexp/vweightexp.def
Normal file
8
utils/vweightexp/vweightexp.def
Normal file
@@ -0,0 +1,8 @@
|
||||
LIBRARY vweightexp
|
||||
EXPORTS
|
||||
LibDescription @1
|
||||
LibNumberClasses @2
|
||||
LibClassDesc @3
|
||||
LibVersion @4
|
||||
SECTIONS
|
||||
.data READ WRITE
|
||||
183
utils/vweightexp/vweightexp.h
Normal file
183
utils/vweightexp/vweightexp.h
Normal 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);
|
||||
|
||||
189
utils/vweightexp/vweightexp.vcproj
Normal file
189
utils/vweightexp/vweightexp.vcproj
Normal 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>
|
||||
28
utils/vweightexp/vweightexprc.h
Normal file
28
utils/vweightexp/vweightexprc.h
Normal 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
|
||||
354
utils/vweightexp/vweightimp.cpp
Normal file
354
utils/vweightexp/vweightimp.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
130
utils/vweightexp/vweightimp.h
Normal file
130
utils/vweightexp/vweightimp.h
Normal 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 );
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user