You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
9.3 KiB
437 lines
9.3 KiB
5 years ago
|
//========= 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);
|
||
|
}
|
||
|
|