//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "MAX.H" #include "DECOMP.H" #include "STDMAT.H" #include "ANIMTBL.H" #include "istdplug.h" #include "phyexp.h" #include "BonesPro.h" #include "vweightexprc.h" #include "vweightexp.h" //=================================================================== // Global variable definitions // // For OutputDebugString and misc sprintf's static char st_szDBG[300]; //===================================================================== // Methods for VWeightExportClass // CONSTRUCTOR VWeightExportClass::VWeightExportClass(void) { m_cMaxNode = 0; m_cMaxVertex = 0; } DESTRUCTOR VWeightExportClass::~VWeightExportClass(void) { for (int i = 0; i < m_cMaxVertex; i++) { delete[] m_MaxVertex[i]; } } int VWeightExportClass::DoExport(const TCHAR *name, ExpInterface *ei, Interface *pi, BOOL suppressPrompts, DWORD options) { ExpInterface *pexpiface = ei; // Hungarian Interface *piface = pi; // Hungarian // Reset the name-map property manager ResetINodeMap(); // 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); // Get animation metrics m_intervalOfAnimation = piface->GetAnimRange(); m_tvStart = m_intervalOfAnimation.Start(); m_tvEnd = m_intervalOfAnimation.End(); m_tpf = ::GetTicksPerFrame(); Interface *ip = GetCOREInterface(); ResetINodeMap( ); m_cMaxNode = BuildINodeMap( ip->GetRootNode() ); // Count nodes, label them, collect into array CollectNodes( ip->GetRootNode() ); CollectModel( pexpiface ); #if 1 FILE *pFile; if ((pFile = fopen(szFile, "wb")) == NULL) return FALSE/*failure*/; int version = 1; fwrite( &version, 1, sizeof( int ), pFile ); int i, j; fwrite( &m_cMaxNode, 1, sizeof( int ), pFile ); fwrite( &m_cMaxVertex, 1, sizeof( int ), pFile ); for (i = 0; i < m_cMaxNode; i++) { fwrite( &m_MaxNode[i], 1, sizeof(m_MaxNode[i]), pFile ); } for (j = 0; j < m_cMaxVertex; j++) { fwrite( m_MaxVertex[j], m_cMaxNode, sizeof(MaxVertWeight), pFile ); } fclose( pFile ); #else FILE *pFile; if ((pFile = fopen(szFile, "w")) == NULL) return FALSE/*failure*/; fprintf( pFile, "version %d\n", 1 ); int i, j; fprintf(pFile, "%d\n", m_cMaxNode ); fprintf(pFile, "%d\n", m_cMaxVertex ); for (i = 0; i < m_cMaxNode; i++) { fprintf(pFile, "%5d \"%s\" %3d\n", i, m_MaxNode[i].szNodeName, m_MaxNode[i].imaxnodeParent ); } for (j = 0; j < m_cMaxVertex; j++) { fprintf( pFile, "%d ", j ); for (int i = 0; i < m_cMaxNode; i++) { // if (strstr(m_MaxNode[i].szNodeName, "Bip01 R Finger")) // if (m_MaxNode[i].szNodeName[0] == 'D') { fprintf(pFile, " %5.3f", m_MaxVertex[j][i].flDist ); fprintf(pFile, " %3.0f", m_MaxVertex[j][i].flWeight ); } } fprintf(pFile, "\n" ); } fclose( pFile ); #endif // 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); return 1/*success*/; } void VWeightExportClass::CollectNodes( INode *pnode ) { // Get pre-stored "index" int index = ::GetIndexOfINode(pnode); if (index >= 0) { // Get name, store name in array TSTR strNodeName(pnode->GetName()); strcpy(m_MaxNode[index].szNodeName, (char*)strNodeName); // Get Node's time-zero Transformation Matrices m_MaxNode[index].mat3NodeTM = pnode->GetNodeTM(0); m_MaxNode[index].mat3ObjectTM = pnode->GetObjectTM(0); } for (int c = 0; c < pnode->NumberOfChildren(); c++) { CollectNodes(pnode->GetChildNode(c)); } return; } BOOL VWeightExportClass::CollectModel( ExpInterface *pexpiface) { // Dump mesh info: vertices, normals, UV texture map coords, bone assignments CollectModelTEP procCollectModel; // init data m_cMaxVertex = 0; procCollectModel.m_phec = this; //fprintf(pFile, "triangles\n" ); procCollectModel.m_tvToDump = m_tvStart; (void) pexpiface->theScene->EnumTree(&procCollectModel); //fprintf(pFile, "end\n" ); return TRUE; } // #define DEBUG_MESH_DUMP //================================================================= // Methods for CollectModelTEP // int CollectModelTEP::callback(INode *pnode) { if (::FNodeMarkedToSkip(pnode)) return TREE_CONTINUE; if ( !pnode->Selected()) return TREE_CONTINUE; // clear physique export parameters m_mcExport = NULL; m_phyExport = NULL; m_phyMod = NULL; m_bonesProMod = NULL; ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!"); 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 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; // 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); } } } // initialize bones pro export parameters m_wa = NULL; m_bonesProMod = FindBonesProModifier(pnode); if (m_bonesProMod) { m_bonesProMod->SetProperty( BP_PROPID_GET_WEIGHTS, &m_wa ); } int cVerts = pmesh->getNumVerts(); // Dump the triangle face info int cFaces = pmesh->getNumFaces(); int *iUsed = new int[cVerts]; for (int iVert = 0; iVert < cVerts; iVert++) { iUsed[iVert] = 0; } for (int iFace = 0; iFace < cFaces; iFace++) { if (pmesh->faces[iFace].flags & HAS_TVERTS) { iUsed[pmesh->faces[iFace].getVert(0)] = 1; iUsed[pmesh->faces[iFace].getVert(1)] = 1; iUsed[pmesh->faces[iFace].getVert(2)] = 1; } } for (iVert = 0; iVert < cVerts; iVert++) { MaxVertWeight *pweight = m_phec->m_MaxVertex[m_phec->m_cMaxVertex] = new MaxVertWeight [m_phec->m_cMaxNode]; Point3 pt3Vertex1 = pmesh->getVert(iVert); Point3 v1 = pt3Vertex1 * mat3ObjectTM; GetUnifiedCoord( v1, pweight, m_phec->m_MaxNode, m_phec->m_cMaxNode ); if (CollectWeights( iVert, pweight )) { m_phec->m_cMaxVertex++; } } // fflush( m_pfile ); return TREE_CONTINUE; } int CollectModelTEP::CollectWeights(int iVertex, MaxVertWeight *pweight) { for (int index = 0; index < m_phec->m_cMaxNode; index++) { pweight[index].flWeight = -1; } if (m_mcExport) { return GetBoneWeights( m_mcExport, iVertex, pweight ); } else { return GetBoneWeights( m_bonesProMod, iVertex, pweight ); } } void CollectModelTEP::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; } }