⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smdlexp.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/***
*
*	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 + -