diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/vtaexp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'utils/vtaexp')
| -rw-r--r-- | utils/vtaexp/smexprc.h | 22 | ||||
| -rw-r--r-- | utils/vtaexp/vtadefs.h | 172 | ||||
| -rw-r--r-- | utils/vtaexp/vtaexp.cpp | 762 | ||||
| -rw-r--r-- | utils/vtaexp/vtaexp.def | 8 | ||||
| -rw-r--r-- | utils/vtaexp/vtaexp.rc | 109 | ||||
| -rw-r--r-- | utils/vtaexp/vtaexp.vcproj | 178 | ||||
| -rw-r--r-- | utils/vtaexp/vtaexprc.h | 28 |
7 files changed, 1279 insertions, 0 deletions
diff --git a/utils/vtaexp/smexprc.h b/utils/vtaexp/smexprc.h new file mode 100644 index 0000000..5a78a64 --- /dev/null +++ b/utils/vtaexp/smexprc.h @@ -0,0 +1,22 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by vtaexp.rc +// + +// 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 diff --git a/utils/vtaexp/vtadefs.h b/utils/vtaexp/vtadefs.h new file mode 100644 index 0000000..098110e --- /dev/null +++ b/utils/vtaexp/vtadefs.h @@ -0,0 +1,172 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +//=================================================================== +// Useful macros +// +#define CONSTRUCTOR +#define DESTRUCTOR + +#define EXPORT_THIS __declspec(dllexport) + +#define DEFAULT_EXT _T("vta") + +#define FStrEq(sz1, sz2) (strcmp((sz1), (sz2)) == 0) + + +//=================================================================== +// Class that implements the scene-export. +// +class VtaExportClass : public SceneExport +{ + friend BOOL CALLBACK ExportOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + friend class DumpModelTEP; + friend class DumpDeformsTEP; + +public: + CONSTRUCTOR VtaExportClass (void); + DESTRUCTOR ~VtaExportClass (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 VTA"); } + 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 100; } + 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 + + // 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); +}; + + +//=================================================================== +// Basically just a ClassFactory for communicating with 3DSMAX. +// +class VtaExportClassDesc : public ClassDesc +{ +public: + int IsPublic (void) { return TRUE; } + void * Create (BOOL loading=FALSE) { return new VtaExportClass; } + const TCHAR * ClassName (void) { return _T("VtaExport"); } + SClass_ID SuperClassID (void) { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID (void) { return Class_ID(0x5616745e, 0x65643ba5); } + 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); + VtaExportClass *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 + VtaExportClass *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 + VtaExportClass *m_phec; +}; + + +//=================================================================== +// Tree Enumeration Callback +// Dumps the triangle meshes to a file. +// +class DumpModelTEP : public ITreeEnumProc +{ +public: + ~DumpModelTEP(); + virtual int callback(INode *node); + FILE *m_pfile; // write to this file + TimeValue m_tvBase; // reference dump snapshot at this frame time + TimeValue m_tvToDump; // delta dump snapshot at this frame time + VtaExportClass *m_phec; + + int m_baseVertCount; + Point3 *m_baseVert; +private: + // int InodeOfPhyVectex( int iVertex0 ); + Point3 Pt3GetRVertexNormal(RVertex *prvertex, DWORD smGroupFace); +}; + diff --git a/utils/vtaexp/vtaexp.cpp b/utils/vtaexp/vtaexp.cpp new file mode 100644 index 0000000..0cc7002 --- /dev/null +++ b/utils/vtaexp/vtaexp.cpp @@ -0,0 +1,762 @@ +//========= 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 "vtaexprc.h" +#include "vtadefs.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 VtaExportClassDesc VtaExportCD; + +// 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 &VtaExportCD; + default: return 0; + } +} + + +EXPORT_THIS const TCHAR *LibDescription() +{ + return _T("Valve VTA Plug-in."); +} + + +EXPORT_THIS ULONG LibVersion() +{ + return VERSION_3DSMAX; +} + + +//===================================================================== +// Methods for VtaExportClass +// + +CONSTRUCTOR VtaExportClass::VtaExportClass(void) +{ + m_rgmaxnode = NULL; +} + + +DESTRUCTOR VtaExportClass::~VtaExportClass(void) +{ + if (m_rgmaxnode) + delete[] m_rgmaxnode; +} + + +int VtaExportClass::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; + + // 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); + + 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 + 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 VtaExportClass::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 VtaExportClass::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 VtaExportClass::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; + + fprintf(pFile, "skeleton\n" ); + for (TimeValue tv = m_tvStart; tv <= m_tvEnd; 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 VtaExportClass::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; + + procDumpModel.m_tvBase = m_tvStart; + + fprintf(pFile, "vertexanimation\n" ); + procDumpModel.m_baseVert = NULL; + for (TimeValue tv = m_tvStart; tv <= m_tvEnd; tv += m_tpf) + { + fprintf(pFile, "time %d\n", tv / GetTicksPerFrame() ); + procDumpModel.m_tvToDump = tv; + procDumpModel.m_baseVertCount = 0; + (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, VtaExportClass::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 = VtaExportClass::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 ? VtaExportClass::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) +{ + ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!"); + + if (::FNodeMarkedToSkip(pnode)) + return TREE_CONTINUE; + + if ( !pnode->Selected()) + 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 + Object *pobj = pnode->GetObjectRef(); + if (pobj->SuperClassID() == HELPER_CLASS_ID) + return TREE_CONTINUE; + + + // Get the object's parameter block if it has one + + IParamBlock *pb = NULL; + IParamArray *pa = pobj->GetParamBlock(); + + if (!pa) + { + int i = pobj->NumRefs(); + // Search the references looking for a parameter block + for (i = 0; i < pobj->NumRefs(); i++) + { + RefTargetHandle r = pobj->GetReference(i); + if (r) + { + Class_ID x = r->ClassID(); + + if (r && r->ClassID() == Class_ID(PARAMETER_BLOCK_CLASS_ID,0)) + { + pb = (IParamBlock *) r; + } + } + } + } + else + { + // pb = pa->GetParamBlock2(); + } + + if (pb) + { + // Find out how many _animatable_ parameters there are + int count = pb->NumSubs(); + + // Display data about each one + for (int i = 0; i < count; i++) + { + TSTR name = pb->SubAnimName(i); + + int pbIndex = pb->AnimNumToParamNum(i); + + TSTR cname; + + SClass_ID sc = pb->GetAnimParamControlType(i); + + if (sc == CTRL_FLOAT_CLASS_ID) + + cname = TSTR(_T("Float")); + + else if (sc == CTRL_POINT3_CLASS_ID) + + cname = TSTR(_T("Point3")); + } + } + + + + // 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; + BOOL deleteMesh = (ptriobj != pobj); + + // 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); + Matrix3 mat3ObjectNTM = mat3ObjectTM; + mat3ObjectNTM.NoScale( ); + + int cVerts = pmesh->getNumVerts(); + + // it would be nice to just evaluate the object for both time periods, but MAX + // may do EvalWorldState in place, so the vertex animations get stomped + if (m_tvToDump == m_tvBase) + { + if (m_baseVert) + m_baseVert = (Point3 *)realloc( m_baseVert, (m_baseVertCount + cVerts) * sizeof( Point3 ) ); + else + m_baseVert = (Point3 *)malloc( (m_baseVertCount + cVerts) * sizeof( Point3 ) ); + + for (int iVert = 0; iVert < cVerts; iVert++) + { + Point3 pt3Vertex1 = pmesh->getVert(iVert); + int iAdjVert = m_baseVertCount + iVert; + + m_baseVert[iAdjVert] = pt3Vertex1 * mat3ObjectTM; + + Point3 pt3Normal1 = pmesh->getNormal(iVert); + pt3Normal1 = VectorTransform( mat3ObjectNTM, pt3Normal1 ); + + fprintf(m_pfile, "%5d %8.4f %8.4f %8.4f %9.6f %9.6f %9.6f\n", + iAdjVert, m_baseVert[iAdjVert].x, m_baseVert[iAdjVert].y, m_baseVert[iAdjVert].z, + pt3Normal1.x, pt3Normal1.y, pt3Normal1.z ); + + } + } + else + { + for (int iVert = 0; iVert < cVerts; iVert++) + { + Point3 pt3Vertex1 = pmesh->getVert(iVert); + Point3 v1 = pt3Vertex1 * mat3ObjectTM; + + int iAdjVert = m_baseVertCount + iVert; + + if (Length( m_baseVert[iAdjVert] - v1) > 0.01) + { + Point3 pt3Vertex2 = pt3Vertex1; + pt3Vertex2.z = pt3Vertex2.z + 10.0; + pmesh->setVert( iVert, pt3Vertex2 ); + + Point3 pt3Normal1 = pmesh->getNormal(iVert); + pt3Normal1 = VectorTransform( mat3ObjectNTM, pt3Normal1 ); + + fprintf(m_pfile, "%5d %8.4f %8.4f %8.4f %9.6f %9.6f %9.6f\n", + iAdjVert, v1.x, v1.y, v1.z, + pt3Normal1.x, pt3Normal1.y, pt3Normal1.z ); + } + } + } + m_baseVertCount += cVerts; + + fflush( m_pfile ); + + /* + if (deleteMesh) + delete pmesh; + */ + + return TREE_CONTINUE; +} + + + +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(); + } +} + + +DumpModelTEP::~DumpModelTEP() +{ + if (m_baseVert) + { + delete m_baseVert; + } +} + + +//======================================================================== +// 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[VtaExportClass::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) == VtaExportClass::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; +} diff --git a/utils/vtaexp/vtaexp.def b/utils/vtaexp/vtaexp.def new file mode 100644 index 0000000..ad4a17f --- /dev/null +++ b/utils/vtaexp/vtaexp.def @@ -0,0 +1,8 @@ +LIBRARY vtaexp +EXPORTS + LibDescription @1 + LibNumberClasses @2 + LibClassDesc @3 + LibVersion @4 +SECTIONS + .data READ WRITE diff --git a/utils/vtaexp/vtaexp.rc b/utils/vtaexp/vtaexp.rc new file mode 100644 index 0000000..49e5f31 --- /dev/null +++ b/utils/vtaexp/vtaexp.rc @@ -0,0 +1,109 @@ +//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 + +#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", "VTA file exporter (3D Studio Max plugin)\0" + VALUE "FileVersion", "2, 0, 0, 1\0" + VALUE "InternalName", "VTAEXP\0" + VALUE "LegalCopyright", "Copyright � 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", "VTAEXP.DLE\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "Valve LLC VTAEXP\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 + diff --git a/utils/vtaexp/vtaexp.vcproj b/utils/vtaexp/vtaexp.vcproj new file mode 100644 index 0000000..4c8d322 --- /dev/null +++ b/utils/vtaexp/vtaexp.vcproj @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="vtaexp" + ProjectGUID="{018A7CD6-C40A-463D-86B1-2AF2D0F4B2CD}" + 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;VTAEXP_EXPORTS" + BasicRuntimeChecks="3" + RuntimeLibrary="2" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Debug/vtaexp.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\VTAEXP.DLE" + LinkIncremental="1" + SuppressStartupBanner="TRUE" + AdditionalLibraryDirectories="..\..\maxsdk\lib" + ModuleDefinitionFile=".\vtaexp.def" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile=".\Debug/VTAEXP.pdb" + ImportLibrary=".\Debug/VTAEXP.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool" + PreprocessorDefinitions="_DEBUG" + MkTypLibCompatible="TRUE" + SuppressStartupBanner="TRUE" + TargetEnvironment="1" + TypeLibraryName=".\Debug/vtaexp.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;VTAEXP_EXPORTS" + StringPooling="TRUE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="TRUE" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Release/vtaexp.pch" + AssemblerListingLocation=".\Release/" + ObjectFile=".\Release/" + ProgramDataBaseFileName=".\Release/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + CompileAs="0"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + OutputFile="c:\3DSMAX3\plugins\VTAEXP.DLE" + LinkIncremental="1" + SuppressStartupBanner="TRUE" + ModuleDefinitionFile=".\vtaexp.def" + ProgramDatabaseFile=".\Release/VTAEXP.pdb" + ImportLibrary=".\Release/VTAEXP.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool" + PreprocessorDefinitions="NDEBUG" + MkTypLibCompatible="TRUE" + SuppressStartupBanner="TRUE" + TargetEnvironment="1" + TypeLibraryName=".\Release/vtaexp.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="vtaexp.cpp"> + </File> + <File + RelativePath="vtaexp.def"> + </File> + <File + RelativePath="vtaexp.rc"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl"> + <File + RelativePath="vtadefs.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> diff --git a/utils/vtaexp/vtaexprc.h b/utils/vtaexp/vtaexprc.h new file mode 100644 index 0000000..065a66b --- /dev/null +++ b/utils/vtaexp/vtaexprc.h @@ -0,0 +1,28 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by vtaexp.rc +// +#define IDD_VTAEXP_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 |