Moved modified HL SDK to trunk
This commit is contained in:
951
hlsdk/utils/smdlexp/smdlexp.cpp
Normal file
951
hlsdk/utils/smdlexp/smdlexp.cpp
Normal file
@ -0,0 +1,951 @@
|
||||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
****/
|
||||
|
||||
#include "MAX.H"
|
||||
#include "DECOMP.H"
|
||||
#include "STDMAT.H"
|
||||
#include "ANIMTBL.H"
|
||||
#include "istdplug.h"
|
||||
#include "phyexp.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)
|
||||
{
|
||||
ExpInterface *pexpiface = ei; // Hungarian
|
||||
Interface *piface = i; // Hungarian
|
||||
|
||||
// Reset the name-map property manager
|
||||
g_inmMac = 0;
|
||||
|
||||
// Present the user with the Export Options dialog
|
||||
if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORTOPTIONS), GetActiveWindow(),
|
||||
ExportOptionsDlgProc, (LPARAM)this) <= 0)
|
||||
return 0; // error or cancel
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Root node has no parent, thus no translation
|
||||
if (fNodeIsRoot)
|
||||
iNodeParent = -1;
|
||||
|
||||
// Dump node description
|
||||
fprintf(m_pfile, "%3d \"%s\" %3d\n",
|
||||
iNode,
|
||||
strNodeName,
|
||||
iNodeParent );
|
||||
|
||||
return TREE_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================================================================
|
||||
// Methods for DumpFrameRotationsTEP
|
||||
//
|
||||
int DumpFrameRotationsTEP::callback(INode *pnode)
|
||||
{
|
||||
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
|
||||
|
||||
if (::FNodeMarkedToSkip(pnode))
|
||||
return TREE_CONTINUE;
|
||||
|
||||
int iNode = ::GetIndexOfINode(pnode);
|
||||
|
||||
TSTR strNodeName(pnode->GetName());
|
||||
|
||||
// The model's root is a child of the real "scene root"
|
||||
INode *pnodeParent = pnode->GetParentNode();
|
||||
BOOL fNodeIsRoot = pnodeParent->IsRootNode( );
|
||||
|
||||
// Get Node's "Local" Transformation Matrix
|
||||
Matrix3 mat3NodeTM = pnode->GetNodeTM(m_tvToDump);
|
||||
Matrix3 mat3ParentTM = pnodeParent->GetNodeTM(m_tvToDump);
|
||||
mat3NodeTM.NoScale(); // Clear these out because they apparently
|
||||
mat3ParentTM.NoScale(); // screw up the following calculation.
|
||||
Matrix3 mat3NodeLocalTM = mat3NodeTM * Inverse(mat3ParentTM);
|
||||
Point3 rowTrans = mat3NodeLocalTM.GetTrans();
|
||||
|
||||
// Get the rotation (via decomposition into "affine parts", then quaternion-to-Euler)
|
||||
// Apparently the order of rotations returned by QuatToEuler() is X, then Y, then Z.
|
||||
AffineParts affparts;
|
||||
float rgflXYZRotations[3];
|
||||
|
||||
decomp_affine(mat3NodeLocalTM, &affparts);
|
||||
QuatToEuler(affparts.q, rgflXYZRotations);
|
||||
|
||||
float xRot = rgflXYZRotations[0]; // in radians
|
||||
float yRot = rgflXYZRotations[1]; // in radians
|
||||
float zRot = rgflXYZRotations[2]; // in radians
|
||||
|
||||
// Get rotations in the -2pi...2pi range
|
||||
xRot = ::FlReduceRotation(xRot);
|
||||
yRot = ::FlReduceRotation(yRot);
|
||||
zRot = ::FlReduceRotation(zRot);
|
||||
|
||||
// Print rotations
|
||||
//fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
|
||||
fprintf(m_pfile, "%3d %f %f %f %f %f %f\n",
|
||||
// Node:%-15s Rotation (x,y,z)\n",
|
||||
iNode, rowTrans.x, rowTrans.y, rowTrans.z, xRot, yRot, zRot);
|
||||
|
||||
return TREE_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================================================================
|
||||
// Methods for DumpModelTEP
|
||||
//
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// #define DEBUG_MESH_DUMP
|
||||
|
||||
//=================================================================
|
||||
// Methods for DumpModelTEP
|
||||
//
|
||||
int DumpModelTEP::callback(INode *pnode)
|
||||
{
|
||||
Object* pobj;
|
||||
int fHasMat = TRUE;
|
||||
|
||||
// clear physique export parameters
|
||||
m_mcExport = NULL;
|
||||
m_phyExport = NULL;
|
||||
m_phyMod = NULL;
|
||||
|
||||
ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
|
||||
|
||||
if (::FNodeMarkedToSkip(pnode))
|
||||
return TREE_CONTINUE;
|
||||
|
||||
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
|
||||
pobj = pnode->GetObjectRef();
|
||||
if (pobj->SuperClassID() == HELPER_CLASS_ID)
|
||||
return TREE_CONTINUE;
|
||||
|
||||
// The model's root is a child of the real "scene root"
|
||||
INode *pnodeParent = pnode->GetParentNode();
|
||||
BOOL fNodeIsRoot = pnodeParent->IsRootNode( );
|
||||
|
||||
// Get node's material: should be a multi/sub (if it has a material at all)
|
||||
Mtl *pmtlNode = pnode->GetMtl();
|
||||
if (pmtlNode == NULL)
|
||||
{
|
||||
return TREE_CONTINUE;
|
||||
fHasMat = FALSE;
|
||||
}
|
||||
else if (!(pmtlNode->ClassID() == Class_ID(MULTI_CLASS_ID, 0) && pmtlNode->IsMultiMtl()))
|
||||
{
|
||||
// sprintf(st_szDBG, "ERROR--Material on node %s isn't a Multi/Sub-Object", (char*)strNodeName);
|
||||
// ASSERT_AND_ABORT(FALSE, st_szDBG);
|
||||
fHasMat = FALSE;
|
||||
}
|
||||
|
||||
// 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;
|
||||
TriObject *ptriobj;
|
||||
BOOL fConvertedToTriObject =
|
||||
pobj->CanConvertToType(triObjectClassID) &&
|
||||
(ptriobj = (TriObject*)pobj->ConvertToType(m_tvToDump, triObjectClassID)) != NULL;
|
||||
if (!fConvertedToTriObject)
|
||||
return TREE_CONTINUE;
|
||||
Mesh *pmesh = &ptriobj->mesh;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Ensure that the vertex normals are up-to-date
|
||||
pmesh->buildNormals();
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dump the triangle face info
|
||||
int cFaces = pmesh->getNumFaces();
|
||||
for (int iFace = 0; iFace < cFaces; iFace++)
|
||||
{
|
||||
Face* pface = &pmesh->faces[iFace];
|
||||
TVFace* ptvface = &pmesh->tvFace[iFace];
|
||||
DWORD smGroupFace = pface->getSmGroup();
|
||||
|
||||
// Get face's 3 indexes into the Mesh's vertex array(s).
|
||||
DWORD iVertex0 = pface->getVert(0);
|
||||
DWORD iVertex1 = pface->getVert(1);
|
||||
DWORD iVertex2 = pface->getVert(2);
|
||||
ASSERT_AND_ABORT((int)iVertex0 < pmesh->getNumVerts(), "Bogus Vertex 0 index");
|
||||
ASSERT_AND_ABORT((int)iVertex1 < pmesh->getNumVerts(), "Bogus Vertex 1 index");
|
||||
ASSERT_AND_ABORT((int)iVertex2 < pmesh->getNumVerts(), "Bogus Vertex 2 index");
|
||||
|
||||
// Get the 3 Vertex's for this face
|
||||
Point3 pt3Vertex0 = pmesh->getVert(iVertex0);
|
||||
Point3 pt3Vertex1 = pmesh->getVert(iVertex1);
|
||||
Point3 pt3Vertex2 = pmesh->getVert(iVertex2);
|
||||
|
||||
// Get the 3 RVertex's for this face
|
||||
// NOTE: I'm using getRVertPtr instead of getRVert to work around a 3DSMax bug
|
||||
RVertex *prvertex0 = pmesh->getRVertPtr(iVertex0);
|
||||
RVertex *prvertex1 = pmesh->getRVertPtr(iVertex1);
|
||||
RVertex *prvertex2 = pmesh->getRVertPtr(iVertex2);
|
||||
|
||||
// Find appropriate normals for each RVertex
|
||||
// A vertex can be part of multiple faces, so the "smoothing group"
|
||||
// is used to locate the normal for this face's use of the vertex.
|
||||
Point3 pt3Vertex0Normal;
|
||||
Point3 pt3Vertex1Normal;
|
||||
Point3 pt3Vertex2Normal;
|
||||
if (smGroupFace)
|
||||
{
|
||||
pt3Vertex0Normal = Pt3GetRVertexNormal(prvertex0, smGroupFace);
|
||||
pt3Vertex1Normal = Pt3GetRVertexNormal(prvertex1, smGroupFace);
|
||||
pt3Vertex2Normal = Pt3GetRVertexNormal(prvertex2, smGroupFace);
|
||||
}
|
||||
else
|
||||
{
|
||||
pt3Vertex0Normal = pmesh->getFaceNormal( iFace );
|
||||
pt3Vertex1Normal = pmesh->getFaceNormal( iFace );
|
||||
pt3Vertex2Normal = pmesh->getFaceNormal( iFace );
|
||||
}
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus orig normal 0" );
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus orig normal 1" );
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus orig normal 2" );
|
||||
|
||||
// Get Face's sub-material from node's material, to get the bitmap name.
|
||||
// And no, there isn't a simpler way to get the bitmap name, you have to
|
||||
// dig down through all these levels.
|
||||
TCHAR szBitmapName[256] = "null.bmp";
|
||||
if (fHasMat)
|
||||
{
|
||||
MtlID mtlidFace = pface->getMatID();
|
||||
if (mtlidFace >= pmtlNode->NumSubMtls())
|
||||
{
|
||||
sprintf(st_szDBG, "ERROR--Bogus sub-material index %d in node %s; highest valid index is %d",
|
||||
mtlidFace, (char*)strNodeName, pmtlNode->NumSubMtls()-1);
|
||||
// ASSERT_AND_ABORT(FALSE, st_szDBG);
|
||||
mtlidFace = 0;
|
||||
}
|
||||
Mtl *pmtlFace = pmtlNode->GetSubMtl(mtlidFace);
|
||||
ASSERT_AND_ABORT(pmtlFace != NULL, "NULL Sub-material returned");
|
||||
|
||||
if ((pmtlFace->ClassID() == Class_ID(MULTI_CLASS_ID, 0) && pmtlFace->IsMultiMtl()))
|
||||
{
|
||||
// it's a sub-sub material. Gads.
|
||||
pmtlFace = pmtlFace->GetSubMtl(mtlidFace);
|
||||
ASSERT_AND_ABORT(pmtlFace != NULL, "NULL Sub-material returned");
|
||||
}
|
||||
|
||||
if (!(pmtlFace->ClassID() == Class_ID(DMTL_CLASS_ID, 0)))
|
||||
{
|
||||
|
||||
sprintf(st_szDBG,
|
||||
"ERROR--Sub-material with index %d (used in node %s) isn't a 'default/standard' material [%x].",
|
||||
mtlidFace, (char*)strNodeName, pmtlFace->ClassID());
|
||||
ASSERT_AND_ABORT(FALSE, st_szDBG);
|
||||
}
|
||||
StdMat *pstdmtlFace = (StdMat*)pmtlFace;
|
||||
Texmap *ptexmap = pstdmtlFace->GetSubTexmap(ID_DI);
|
||||
// ASSERT_AND_ABORT(ptexmap != NULL, "NULL diffuse texture")
|
||||
if (ptexmap != NULL)
|
||||
{
|
||||
if (!(ptexmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)))
|
||||
{
|
||||
sprintf(st_szDBG,
|
||||
"ERROR--Sub-material with index %d (used in node %s) doesn't have a bitmap as its diffuse texture.",
|
||||
mtlidFace, (char*)strNodeName);
|
||||
ASSERT_AND_ABORT(FALSE, st_szDBG);
|
||||
}
|
||||
BitmapTex *pbmptex = (BitmapTex*)ptexmap;
|
||||
strcpy(szBitmapName, pbmptex->GetMapName());
|
||||
TSTR strPath, strFile;
|
||||
SplitPathFile(TSTR(szBitmapName), &strPath, &strFile);
|
||||
strcpy(szBitmapName,strFile);
|
||||
}
|
||||
}
|
||||
|
||||
UVVert UVvertex0( 0, 0, 0 );
|
||||
UVVert UVvertex1( 1, 0, 0 );
|
||||
UVVert UVvertex2( 0, 1, 0 );
|
||||
|
||||
// All faces must have textures assigned to them
|
||||
if (pface->flags & HAS_TVERTS)
|
||||
{
|
||||
// Get TVface's 3 indexes into the Mesh's TVertex array(s).
|
||||
DWORD iTVertex0 = ptvface->getTVert(0);
|
||||
DWORD iTVertex1 = ptvface->getTVert(1);
|
||||
DWORD iTVertex2 = ptvface->getTVert(2);
|
||||
ASSERT_AND_ABORT((int)iTVertex0 < pmesh->getNumTVerts(), "Bogus TVertex 0 index");
|
||||
ASSERT_AND_ABORT((int)iTVertex1 < pmesh->getNumTVerts(), "Bogus TVertex 1 index");
|
||||
ASSERT_AND_ABORT((int)iTVertex2 < pmesh->getNumTVerts(), "Bogus TVertex 2 index");
|
||||
|
||||
// Get the 3 TVertex's for this TVFace
|
||||
// NOTE: I'm using getRVertPtr instead of getRVert to work around a 3DSMax bug
|
||||
UVvertex0 = pmesh->getTVert(iTVertex0);
|
||||
UVvertex1 = pmesh->getTVert(iTVertex1);
|
||||
UVvertex2 = pmesh->getTVert(iTVertex2);
|
||||
}
|
||||
else
|
||||
{
|
||||
//sprintf(st_szDBG, "ERROR--Node %s has a textureless face. All faces must have an applied texture.", (char*)strNodeName);
|
||||
//ASSERT_AND_ABORT(FALSE, st_szDBG);
|
||||
}
|
||||
|
||||
/*
|
||||
const char *szExpectedExtension = ".bmp";
|
||||
if (stricmp(szBitmapName+strlen(szBitmapName)-strlen(szExpectedExtension), szExpectedExtension) != 0)
|
||||
{
|
||||
sprintf(st_szDBG, "Node %s uses %s, which is not a %s file", (char*)strNodeName, szBitmapName, szExpectedExtension);
|
||||
ASSERT_AND_ABORT(FALSE, st_szDBG);
|
||||
}
|
||||
*/
|
||||
|
||||
// Determine owning bones for the vertices.
|
||||
int iNodeV0, iNodeV1, iNodeV2;
|
||||
if (m_mcExport)
|
||||
{
|
||||
// The Physique add-in allows vertices to be assigned to bones arbitrarily
|
||||
iNodeV0 = InodeOfPhyVectex( iVertex0 );
|
||||
iNodeV1 = InodeOfPhyVectex( iVertex1 );
|
||||
iNodeV2 = InodeOfPhyVectex( iVertex2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple 3dsMax model: the vertices are owned by the object, and hence the node
|
||||
iNodeV0 = iNode;
|
||||
iNodeV1 = iNode;
|
||||
iNodeV2 = iNode;
|
||||
}
|
||||
|
||||
// Rotate the face vertices out of object-space, and into world-space space
|
||||
Point3 v0 = pt3Vertex0 * mat3ObjectTM;
|
||||
Point3 v1 = pt3Vertex1 * mat3ObjectTM;
|
||||
Point3 v2 = pt3Vertex2 * mat3ObjectTM;
|
||||
|
||||
|
||||
Matrix3 mat3ObjectNTM = mat3ObjectTM;
|
||||
mat3ObjectNTM.NoScale( );
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus pre normal 0" );
|
||||
pt3Vertex0Normal = VectorTransform(mat3ObjectNTM, pt3Vertex0Normal);
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus post normal 0" );
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus pre normal 1" );
|
||||
pt3Vertex1Normal = VectorTransform(mat3ObjectNTM, pt3Vertex1Normal);
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus post normal 1" );
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus pre normal 2" );
|
||||
pt3Vertex2Normal = VectorTransform(mat3ObjectNTM, pt3Vertex2Normal);
|
||||
ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus post normal 2" );
|
||||
|
||||
// Finally dump the bitmap name and 3 lines of face info
|
||||
fprintf(m_pfile, "%s\n", szBitmapName);
|
||||
fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
|
||||
iNodeV0, v0.x, v0.y, v0.z,
|
||||
pt3Vertex0Normal.x, pt3Vertex0Normal.y, pt3Vertex0Normal.z,
|
||||
UVvertex0.x, UVvertex0.y);
|
||||
fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
|
||||
iNodeV1, v1.x, v1.y, v1.z,
|
||||
pt3Vertex1Normal.x, pt3Vertex1Normal.y, pt3Vertex1Normal.z,
|
||||
UVvertex1.x, UVvertex1.y);
|
||||
fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
|
||||
iNodeV2, v2.x, v2.y, v2.z,
|
||||
pt3Vertex2Normal.x, pt3Vertex2Normal.y, pt3Vertex2Normal.z,
|
||||
UVvertex2.x, UVvertex2.y);
|
||||
}
|
||||
|
||||
cleanup( );
|
||||
return TREE_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
void DumpModelTEP::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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int DumpModelTEP::InodeOfPhyVectex(int iVertex)
|
||||
{
|
||||
int iNode = 0;
|
||||
|
||||
IPhyVertexExport *vtxExport = m_mcExport->GetVertexInterface(iVertex);
|
||||
|
||||
if (vtxExport)
|
||||
{
|
||||
//need to check if vertex has blending
|
||||
if (vtxExport->GetVertexType() & BLENDED_TYPE)
|
||||
{
|
||||
//
|
||||
}
|
||||
else
|
||||
{
|
||||
INode *Bone = ((IPhyRigidVertex *)vtxExport)->GetNode();
|
||||
|
||||
iNode = GetIndexOfINode(Bone);
|
||||
}
|
||||
m_mcExport->ReleaseVertexInterface(vtxExport);
|
||||
}
|
||||
|
||||
return iNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Point3 DumpModelTEP::Pt3GetRVertexNormal(RVertex *prvertex, DWORD smGroupFace)
|
||||
{
|
||||
// Lookup the appropriate vertex normal, based on smoothing group.
|
||||
int cNormals = prvertex->rFlags & NORCT_MASK;
|
||||
|
||||
ASSERT_MBOX((cNormals == 1 && prvertex->ern == NULL) ||
|
||||
(cNormals > 1 && prvertex->ern != NULL), "BOGUS RVERTEX");
|
||||
|
||||
if (cNormals == 1)
|
||||
return prvertex->rn.getNormal();
|
||||
else
|
||||
{
|
||||
for (int irn = 0; irn < cNormals; irn++)
|
||||
if (prvertex->ern[irn].getSmGroup() & smGroupFace)
|
||||
break;
|
||||
|
||||
if (irn >= cNormals)
|
||||
{
|
||||
irn = 0;
|
||||
// ASSERT_MBOX(irn < cNormals, "unknown smoothing group\n");
|
||||
}
|
||||
return prvertex->ern[irn].getNormal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========================================================
|
||||
// Dialog proc for export options
|
||||
//
|
||||
static BOOL CALLBACK ExportOptionsDlgProc(
|
||||
HWND hDlg,
|
||||
UINT message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
static SmdExportClass *pexp;
|
||||
switch (message)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
pexp = (SmdExportClass*) lParam;
|
||||
CheckRadioButton(hDlg, IDC_CHECK_SKELETAL, IDC_CHECK_REFFRAME, IDC_CHECK_SKELETAL);
|
||||
return FALSE;
|
||||
case WM_DESTROY:
|
||||
return FALSE;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDOK:
|
||||
pexp->m_fReferenceFrame = IsDlgButtonChecked(hDlg, IDC_CHECK_REFFRAME);
|
||||
EndDialog(hDlg, 1); // 1 indicates "ok to export"
|
||||
return TRUE;
|
||||
case IDCANCEL: // 0 indicates "cancel export"
|
||||
EndDialog(hDlg, 0);
|
||||
return TRUE;
|
||||
case IDC_CHECK_SKELETAL:
|
||||
case IDC_CHECK_REFFRAME:
|
||||
CheckRadioButton(hDlg, IDC_CHECK_SKELETAL, IDC_CHECK_REFFRAME, LOWORD(wParam));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========================================================================
|
||||
// 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[SmdExportClass::MAX_NAME_CHARS];
|
||||
int iNode;
|
||||
} NAMEMAP;
|
||||
const int MAX_NAMEMAP = 512;
|
||||
static NAMEMAP g_rgnm[MAX_NAMEMAP];
|
||||
|
||||
int GetIndexOfINode(INode *pnode, BOOL fAssertPropExists)
|
||||
{
|
||||
TSTR strNodeName(pnode->GetName());
|
||||
for (int inm = 0; inm < g_inmMac; inm++)
|
||||
if (FStrEq(g_rgnm[inm].szNodeName, (char*)strNodeName))
|
||||
return g_rgnm[inm].iNode;
|
||||
if (fAssertPropExists)
|
||||
ASSERT_MBOX(FALSE, "No NODEINDEXSTR property");
|
||||
return -7777;
|
||||
}
|
||||
|
||||
|
||||
void SetIndexOfINode(INode *pnode, int inode)
|
||||
{
|
||||
TSTR strNodeName(pnode->GetName());
|
||||
NAMEMAP *pnm;
|
||||
for (int inm = 0; inm < g_inmMac; inm++)
|
||||
if (FStrEq(g_rgnm[inm].szNodeName, (char*)strNodeName))
|
||||
break;
|
||||
if (inm < g_inmMac)
|
||||
pnm = &g_rgnm[inm];
|
||||
else
|
||||
{
|
||||
ASSERT_MBOX(g_inmMac < MAX_NAMEMAP, "NAMEMAP is full");
|
||||
pnm = &g_rgnm[g_inmMac++];
|
||||
strcpy(pnm->szNodeName, (char*)strNodeName);
|
||||
}
|
||||
pnm->iNode = inode;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================
|
||||
// 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();
|
||||
|
||||
// Don't care about lights, dummies, and cameras
|
||||
if (pobj->SuperClassID() == CAMERA_CLASS_ID)
|
||||
return TRUE;
|
||||
if (pobj->SuperClassID() == LIGHT_CLASS_ID)
|
||||
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) == SmdExportClass::UNDESIRABLE_NODE_MARKER);
|
||||
}
|
||||
|
||||
|
||||
//=============================================================
|
||||
// Reduces a rotation to within the -2PI..2PI range.
|
||||
//
|
||||
static float FlReduceRotation(float fl)
|
||||
{
|
||||
while (fl >= TWOPI)
|
||||
fl -= TWOPI;
|
||||
while (fl <= -TWOPI)
|
||||
fl += TWOPI;
|
||||
return fl;
|
||||
}
|
8
hlsdk/utils/smdlexp/smdlexp.def
Normal file
8
hlsdk/utils/smdlexp/smdlexp.def
Normal file
@ -0,0 +1,8 @@
|
||||
LIBRARY smdlexp
|
||||
EXPORTS
|
||||
LibDescription @1
|
||||
LibNumberClasses @2
|
||||
LibClassDesc @3
|
||||
LibVersion @4
|
||||
SECTIONS
|
||||
.data READ WRITE
|
135
hlsdk/utils/smdlexp/smdlexp.dsp
Normal file
135
hlsdk/utils/smdlexp/smdlexp.dsp
Normal file
@ -0,0 +1,135 @@
|
||||
# Microsoft Developer Studio Project File - Name="smdlexp" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=smdlexp - Win32 Release
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "smdlexp.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "smdlexp.mak" CFG="smdlexp - Win32 Release"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "smdlexp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "smdlexp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""$/SDKSrc/Tools/utils/smdlexp", GVGBAAAA"
|
||||
# PROP Scc_LocalPath "."
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "smdlexp - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir ".\Release"
|
||||
# PROP BASE Intermediate_Dir ".\Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir ".\Release"
|
||||
# PROP Intermediate_Dir ".\Release"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /I "\3DSMAX2.5\MAXSDK\INCLUDE" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib COMCTL32.LIB /nologo /subsystem:windows /dll /machine:I386 /out:"\3dsmax2.5\Plugins\smdlexp.dle"
|
||||
|
||||
!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir ".\Debug"
|
||||
# PROP BASE Intermediate_Dir ".\Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir ".\Debug"
|
||||
# PROP Intermediate_Dir ".\Debug"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
|
||||
# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "\3DSMAX2.5\MAXSDK\INCLUDE" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /debug /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib COMCTL32.LIB /nologo /subsystem:windows /dll /debug /machine:I386 /out:"\3DSMAX2.5\plugins\SMDLEXP.DLE"
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "smdlexp - Win32 Release"
|
||||
# Name "smdlexp - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\smdlexp.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\smdlexp.def
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\smdlexp.rc
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\smedefs.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=\3DSMAX2.5\Maxsdk\LIB\UTIL.LIB
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=\3DSMAX2.5\Maxsdk\LIB\GEOM.LIB
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=\3DSMAX2.5\Maxsdk\LIB\MESH.LIB
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=\3DSMAX2.5\Maxsdk\LIB\CORE.LIB
|
||||
# End Source File
|
||||
# End Target
|
||||
# End Project
|
33
hlsdk/utils/smdlexp/smdlexp.dsw
Normal file
33
hlsdk/utils/smdlexp/smdlexp.dsw
Normal file
@ -0,0 +1,33 @@
|
||||
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||
|
||||
###############################################################################
|
||||
|
||||
Project: "smdlexp"=.\smdlexp.dsp - Package Owner=<4>
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
begin source code control
|
||||
"$/SDKSrc/Tools/utils/smdlexp", GVGBAAAA
|
||||
.
|
||||
end source code control
|
||||
}}}
|
||||
|
||||
Package=<4>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
||||
Global:
|
||||
|
||||
Package=<5>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
Package=<3>
|
||||
{{{
|
||||
}}}
|
||||
|
||||
###############################################################################
|
||||
|
147
hlsdk/utils/smdlexp/smdlexp.rc
Normal file
147
hlsdk/utils/smdlexp/smdlexp.rc
Normal file
@ -0,0 +1,147 @@
|
||||
//Microsoft Developer Studio generated resource script.
|
||||
//
|
||||
#include "smexprc.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_EXPORTOPTIONS DIALOG DISCARDABLE 0, 0, 186, 42
|
||||
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "SMD Exporter"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,129,7,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,129,22,50,14
|
||||
CONTROL "Skeletal Animation",IDC_CHECK_SKELETAL,"Button",
|
||||
BS_AUTORADIOBUTTON | WS_GROUP,15,10,74,10
|
||||
CONTROL "Reference Frame",IDC_CHECK_REFFRAME,"Button",
|
||||
BS_AUTORADIOBUTTON | WS_GROUP,15,24,71,10
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
IDD_EXPORTOPTIONS, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 179
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 36
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"smexprc.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE DISCARDABLE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifndef _MAC
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,0,0,1
|
||||
PRODUCTVERSION 2,0,0,1
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "Comments", "\0"
|
||||
VALUE "CompanyName", "Valve LLC\0"
|
||||
VALUE "FileDescription", "SMD file exporter (3D Studio Max plugin)\0"
|
||||
VALUE "FileVersion", "2, 0, 0, 1\0"
|
||||
VALUE "InternalName", "SMDLEXP\0"
|
||||
VALUE "LegalCopyright", "Copyright <20> 1998, Valve LLC\0"
|
||||
VALUE "LegalTrademarks", "The following are registered trademarks of Autodesk, Inc.: 3D Studio MAX. The following are trademarks of Autodesk, Inc.: Kinetix, Kinetix(logo), BIPED, Physique, Character Studio, MAX DWG, DWG Unplugged, Heidi, FLI, FLC, DXF.\0"
|
||||
VALUE "OriginalFilename", "SMDLEXP.DLE\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "Valve LLC SMDLEXP\0"
|
||||
VALUE "ProductVersion", "2, 0, 0, 1\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // !_MAC
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
174
hlsdk/utils/smdlexp/smedefs.h
Normal file
174
hlsdk/utils/smdlexp/smedefs.h
Normal file
@ -0,0 +1,174 @@
|
||||
/***
|
||||
*
|
||||
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
||||
*
|
||||
****/
|
||||
|
||||
//===================================================================
|
||||
// Useful macros
|
||||
//
|
||||
#define CONSTRUCTOR
|
||||
#define DESTRUCTOR
|
||||
|
||||
#define EXPORT_THIS __declspec(dllexport)
|
||||
|
||||
#define DEFAULT_EXT _T("smd")
|
||||
|
||||
#define FStrEq(sz1, sz2) (strcmp((sz1), (sz2)) == 0)
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Class that implements the scene-export.
|
||||
//
|
||||
class SmdExportClass : public SceneExport
|
||||
{
|
||||
friend BOOL CALLBACK ExportOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
friend class DumpModelTEP;
|
||||
friend class DumpDeformsTEP;
|
||||
|
||||
public:
|
||||
CONSTRUCTOR SmdExportClass (void);
|
||||
DESTRUCTOR ~SmdExportClass (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 SMD"); }
|
||||
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); // Export file
|
||||
|
||||
// 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
|
||||
float xRotFirstFrame; // 1st frame's X rotation
|
||||
float yRotFirstFrame; // 1st frame's Y rotation
|
||||
float zRotFirstFrame; // 1st frame's Z rotation
|
||||
} MaxNode;
|
||||
MaxNode *m_rgmaxnode; // array of nodes
|
||||
long m_imaxnodeMac; // # of nodes
|
||||
|
||||
// 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:
|
||||
BOOL CollectNodes (ExpInterface *expiface);
|
||||
BOOL DumpBones (FILE *pFile, ExpInterface *pexpiface);
|
||||
BOOL DumpRotations (FILE *pFile, ExpInterface *pexpiface);
|
||||
BOOL DumpModel (FILE *pFile, ExpInterface *pexpiface);
|
||||
BOOL DumpDeforms (FILE *pFile, ExpInterface *pexpiface);
|
||||
|
||||
// Is this MAX file just the reference frame, or an animation?
|
||||
// If TRUE, the "bones" and "mesh" files will be created.
|
||||
// If FALSE, the "rots" file will be created.
|
||||
BOOL m_fReferenceFrame;
|
||||
};
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Basically just a ClassFactory for communicating with 3DSMAX.
|
||||
//
|
||||
class SmdExportClassDesc : public ClassDesc
|
||||
{
|
||||
public:
|
||||
int IsPublic (void) { return TRUE; }
|
||||
void * Create (BOOL loading=FALSE) { return new SmdExportClass; }
|
||||
const TCHAR * ClassName (void) { return _T("SmdExport"); }
|
||||
SClass_ID SuperClassID (void) { return SCENE_EXPORT_CLASS_ID; }
|
||||
Class_ID ClassID (void) { return Class_ID(0x774a43fd, 0x794d2210); }
|
||||
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);
|
||||
SmdExportClass *m_phec;
|
||||
};
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Tree Enumeration Callback
|
||||
// Dumps the bone offsets to a file.
|
||||
//
|
||||
class DumpNodesTEP : public ITreeEnumProc
|
||||
{
|
||||
public:
|
||||
virtual int callback(INode *node);
|
||||
FILE *m_pfile; // write to this file
|
||||
SmdExportClass *m_phec;
|
||||
};
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Tree Enumeration Callback
|
||||
// Dumps the per-frame bone rotations to a file.
|
||||
//
|
||||
class DumpFrameRotationsTEP : 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
|
||||
SmdExportClass *m_phec;
|
||||
};
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Tree Enumeration Callback
|
||||
// Dumps the triangle meshes to a file.
|
||||
//
|
||||
class DumpModelTEP : 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
|
||||
SmdExportClass *m_phec;
|
||||
IPhyContextExport *m_mcExport;
|
||||
IPhysiqueExport *m_phyExport;
|
||||
Modifier *m_phyMod;
|
||||
private:
|
||||
int InodeOfPhyVectex( int iVertex0 );
|
||||
Point3 Pt3GetRVertexNormal(RVertex *prvertex, DWORD smGroupFace);
|
||||
};
|
||||
|
21
hlsdk/utils/smdlexp/smexprc.h
Normal file
21
hlsdk/utils/smdlexp/smexprc.h
Normal file
@ -0,0 +1,21 @@
|
||||
//{{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
|
Reference in New Issue
Block a user