📄 smdlexp.cpp
字号:
/***
*
* Copyright (c) 1998, Valve LLC. All rights reserved.
*
****/
#include "MAX.H"
#include "DECOMP.H"
#include "STDMAT.H"
#include "ANIMTBL.H"
#include "istdplug.h"
#include "phyexp.h"
#include "BonesPro.h"
#include "smexprc.h"
#include "smedefs.h"
//===================================================================
// Prototype declarations
//
int GetIndexOfINode(INode *pnode,BOOL fAssertPropExists = TRUE);
void SetIndexOfINode(INode *pnode, int inode);
BOOL FUndesirableNode(INode *pnode);
BOOL FNodeMarkedToSkip(INode *pnode);
float FlReduceRotation(float fl);
//===================================================================
// Global variable definitions
//
// Save for use with dialogs
static HINSTANCE hInstance;
// We just need one of these to hand off to 3DSMAX.
static SmdExportClassDesc SmdExportCD;
// For OutputDebugString and misc sprintf's
static char st_szDBG[300];
// INode mapping table
static int g_inmMac = 0;
//===================================================================
// Utility functions
//
static int AssertFailedFunc(char *sz)
{
MessageBox(GetActiveWindow(), sz, "Assert failure", MB_OK);
int Set_Your_Breakpoint_Here = 1;
return 1;
}
#define ASSERT_MBOX(f, sz) ((f) ? 1 : AssertFailedFunc(sz))
//===================================================================
// 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 1;
}
EXPORT_THIS ClassDesc *LibClassDesc(int iWhichClass)
{
switch(iWhichClass)
{
case 0: return &SmdExportCD;
default: return 0;
}
}
EXPORT_THIS const TCHAR *LibDescription()
{
return _T("Valve SMD Plug-in.");
}
EXPORT_THIS ULONG LibVersion()
{
return VERSION_3DSMAX;
}
//=====================================================================
// Methods for SmdExportClass
//
CONSTRUCTOR SmdExportClass::SmdExportClass(void)
{
m_rgmaxnode = NULL;
}
DESTRUCTOR SmdExportClass::~SmdExportClass(void)
{
if (m_rgmaxnode)
delete[] m_rgmaxnode;
}
int SmdExportClass::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
{
ExpInterface *pexpiface = ei; // Hungarian
Interface *piface = i; // Hungarian
// Reset the name-map property manager
g_inmMac = 0;
if (!suppressPrompts)
{
// Present the user with the Export Options dialog
if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORTOPTIONS), GetActiveWindow(),
ExportOptionsDlgProc, (LPARAM)this) <= 0)
return 0; // error or cancel
}
else
{
m_fReferenceFrame = 0;
}
// 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);
/*
if (m_fReferenceFrame)
sprintf(szFile, "%s\\%s_model.%s", (char*)strPath, (char*)strFile, DEFAULT_EXT);
*/
FILE *pFile;
if ((pFile = fopen(szFile, "w")) == NULL)
return FALSE/*failure*/;
fprintf( pFile, "version %d\n", 1 );
// Get animation metrics
m_intervalOfAnimation = piface->GetAnimRange();
m_tvStart = m_intervalOfAnimation.Start();
m_tvEnd = m_intervalOfAnimation.End();
m_tpf = ::GetTicksPerFrame();
// Count nodes, label them, collect into array
if (!CollectNodes(pexpiface))
return 0; /*fail*/
// Output nodes
if (!DumpBones(pFile, pexpiface))
{
fclose( pFile );
return 0; /*fail*/
}
// Output bone rotations, for each frame. Do only first frame if this is the reference frame MAX file
DumpRotations(pFile, pexpiface);
// Output triangle meshes (first frame/all frames), if this is the reference frame MAX file
if (m_fReferenceFrame)
{
DumpModel(pFile, pexpiface);
}
if (!suppressPrompts)
{
// 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);
}
fclose( pFile );
return 1/*success*/;
}
BOOL SmdExportClass::CollectNodes( ExpInterface *pexpiface)
{
// Count total nodes in the model, so I can alloc array
// Also "brands" each node with node index, or with "skip me" marker.
CountNodesTEP procCountNodes;
procCountNodes.m_cNodes = 0;
(void) pexpiface->theScene->EnumTree(&procCountNodes);
ASSERT_MBOX(procCountNodes.m_cNodes > 0, "No nodes!");
// Alloc and fill array
m_imaxnodeMac = procCountNodes.m_cNodes;
m_rgmaxnode = new MaxNode[m_imaxnodeMac];
ASSERT_MBOX(m_rgmaxnode != NULL, "new failed");
CollectNodesTEP procCollectNodes;
procCollectNodes.m_phec = this;
(void) pexpiface->theScene->EnumTree(&procCollectNodes);
return TRUE;
}
BOOL SmdExportClass::DumpBones(FILE *pFile, ExpInterface *pexpiface)
{
// Dump bone names
DumpNodesTEP procDumpNodes;
procDumpNodes.m_pfile = pFile;
procDumpNodes.m_phec = this;
fprintf(pFile, "nodes\n" );
(void) pexpiface->theScene->EnumTree(&procDumpNodes);
fprintf(pFile, "end\n" );
return TRUE;
}
BOOL SmdExportClass::DumpRotations(FILE *pFile, ExpInterface *pexpiface)
{
// Dump bone-rotation info, for each frame
// Also dumps root-node translation info (the model's world-position at each frame)
DumpFrameRotationsTEP procDumpFrameRotations;
procDumpFrameRotations.m_pfile = pFile;
procDumpFrameRotations.m_phec = this;
TimeValue m_tvTill = (m_fReferenceFrame) ? m_tvStart : m_tvEnd;
fprintf(pFile, "skeleton\n" );
for (TimeValue tv = m_tvStart; tv <= m_tvTill; tv += m_tpf)
{
fprintf(pFile, "time %d\n", tv / GetTicksPerFrame() );
procDumpFrameRotations.m_tvToDump = tv;
(void) pexpiface->theScene->EnumTree(&procDumpFrameRotations);
}
fprintf(pFile, "end\n" );
return TRUE;
}
BOOL SmdExportClass::DumpModel( FILE *pFile, ExpInterface *pexpiface)
{
// Dump mesh info: vertices, normals, UV texture map coords, bone assignments
DumpModelTEP procDumpModel;
procDumpModel.m_pfile = pFile;
procDumpModel.m_phec = this;
fprintf(pFile, "triangles\n" );
procDumpModel.m_tvToDump = m_tvStart;
(void) pexpiface->theScene->EnumTree(&procDumpModel);
fprintf(pFile, "end\n" );
return TRUE;
}
//=============================================================================
// TREE-ENUMERATION PROCEDURES
//=============================================================================
#define ASSERT_AND_ABORT(f, sz) \
if (!(f)) \
{ \
ASSERT_MBOX(FALSE, sz); \
cleanup( ); \
return TREE_ABORT; \
}
//=================================================================
// Methods for CountNodesTEP
//
int CountNodesTEP::callback( INode *node)
{
INode *pnode = node; // Hungarian
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
if (::FUndesirableNode(pnode))
{
// Mark as skippable
::SetIndexOfINode(pnode, SmdExportClass::UNDESIRABLE_NODE_MARKER);
return TREE_CONTINUE;
}
// Establish "node index"--just ascending ints
::SetIndexOfINode(pnode, m_cNodes);
m_cNodes++;
return TREE_CONTINUE;
}
//=================================================================
// Methods for CollectNodesTEP
//
int CollectNodesTEP::callback(INode *node)
{
INode *pnode = node; // Hungarian
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
if (::FNodeMarkedToSkip(pnode))
return TREE_CONTINUE;
// Get pre-stored "index"
int iNode = ::GetIndexOfINode(pnode);
ASSERT_MBOX(iNode >= 0 && iNode <= m_phec->m_imaxnodeMac-1, "Bogus iNode");
// Get name, store name in array
TSTR strNodeName(pnode->GetName());
strcpy(m_phec->m_rgmaxnode[iNode].szNodeName, (char*)strNodeName);
// Get Node's time-zero Transformation Matrices
m_phec->m_rgmaxnode[iNode].mat3NodeTM = pnode->GetNodeTM(0/*TimeValue*/);
m_phec->m_rgmaxnode[iNode].mat3ObjectTM = pnode->GetObjectTM(0/*TimeValue*/);
// I'll calculate this later
m_phec->m_rgmaxnode[iNode].imaxnodeParent = SmdExportClass::UNDESIRABLE_NODE_MARKER;
return TREE_CONTINUE;
}
//=================================================================
// Methods for DumpNodesTEP
//
int DumpNodesTEP::callback(INode *pnode)
{
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
if (::FNodeMarkedToSkip(pnode))
return TREE_CONTINUE;
// Get node's parent
INode *pnodeParent;
pnodeParent = pnode->GetParentNode();
// The model's root is a child of the real "scene root"
TSTR strNodeName(pnode->GetName());
BOOL fNodeIsRoot = pnodeParent->IsRootNode( );
int iNode = ::GetIndexOfINode(pnode);
int iNodeParent = ::GetIndexOfINode(pnodeParent, !fNodeIsRoot/*fAssertPropExists*/);
// Convenient time to cache this
m_phec->m_rgmaxnode[iNode].imaxnodeParent = fNodeIsRoot ? SmdExportClass::UNDESIRABLE_NODE_MARKER : iNodeParent;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -