bool CMaxMesh::Create(INode *pINode, Mesh *pIMesh, bool bDelete) { // check for valid mesh if(pIMesh == 0) { theExporter.SetLastError("Invalid handle.", __FILE__, __LINE__); return false; } m_pINode = pINode; m_pIMesh = pIMesh; m_bDelete = bDelete; // recursively create materials if(!CreateMaterial(m_pINode->GetMtl())) return false; // build all normals if necessary m_pIMesh->checkNormals(TRUE); // precalculate the object transformation matrix m_tm = m_pINode->GetObjectTM(SecToTicks(theExporter.GetInterface()->GetCurrentTime())); // try to get the physique modifier m_pModifier = FindPhysiqueModifier(pINode); if(m_pModifier == 0) { // try to get the skin modifier m_pModifier = FindSkinModifier(pINode); if(m_pModifier == 0) { m_pModifier = FindMorpherModifier(pINode); if( m_pModifier == 0 ) { m_modifierType = MODIFIER_NONE; } m_modifierType = MODIFIER_MORPHER; } else { m_modifierType = MODIFIER_SKIN; } } else { m_modifierType = MODIFIER_PHYSIQUE; } return true; }
//----------------------------------------------------------------------------- BOOL CExporter::Capture() { VERIFY (m_Style!=eExportUndef); Modifier* pPhysique; IPhysiqueExport* pExport; IPhyContextExport* pContext; Object* pObject; Matrix3 matMesh; Matrix3 matZero; if (!m_MeshNode){ ERR("Select mesh and try again."); m_bHasError=TRUE; return FALSE; } pObject = m_MeshNode->GetObjectRef(); if (!IsExportableMesh(m_MeshNode,pObject)){ ERR("Can't receive node references."); m_bHasError=TRUE; return FALSE; } // Get export interface pPhysique = FindPhysiqueModifier(m_MeshNode); if (!pPhysique){ ERR("Can't find Physique modifier."); m_bHasError=TRUE; return FALSE; } pExport = (IPhysiqueExport *)pPhysique->GetInterface(I_PHYINTERFACE); if (!pExport){ ERR("Can't find Physique interface."); m_bHasError=TRUE; return FALSE; } // Get mesh initial transform (used to mult by the bone matrices) int rval = CGINTM(m_MeshNode,pExport->GetInitNodeTM(m_MeshNode, matMesh)); matZero.Zero(); if (rval || matMesh.Equals(matZero, 0.0)){ ERR("Old CS version. Can't export mesh"); matMesh.IdentityMatrix(); } // Add hierrarhy parts that has no effect on vertices, // but required for hierrarhy stability if (eExportMotion==m_Style){ if (m_AllBones.empty()){ ERR("Invalid skin object. Bone not found."); return FALSE; } EConsole.ProgressStart((float)m_AllBones.size(),"..Capturing bones"); for (DWORD i=0; i<m_AllBones.size(); i++){ AddBone(m_AllBones[i], matMesh, pExport); EConsole.ProgressInc(); } EConsole.ProgressEnd(); } bool bRes = TRUE; if (eExportSkin==m_Style){ // For a given Object's INode get a // ModContext Interface from the Physique Export Interface: pContext = (IPhyContextExport *)pExport->GetContextInterface(m_MeshNode); if (!pContext){ ERR("Can't find Physique context interface."); return FALSE; } // convert to rigid with blending pContext->ConvertToRigid(TRUE); pContext->AllowBlending (TRUE); // process vertices int numVertices = pContext->GetNumberVertices(); EConsole.ProgressStart(float(numVertices),"..Capturing vertices"); for (int iVertex = 0; iVertex < numVertices; iVertex++ ){ IPhyVertexExport *pVertexExport = (IPhyVertexExport *)pContext->GetVertexInterface(iVertex); R_ASSERT(pVertexExport); // What kind of vertices are these? int iVertexType = pVertexExport->GetVertexType(); IPhyRigidVertex* pRigidVertex=(IPhyRigidVertex*)pContext->GetVertexInterface(iVertex); R_ASSERT (pRigidVertex); switch (iVertexType){ case RIGID_TYPE:{ INode* node = pRigidVertex->GetNode(); R_ASSERT (node); LPCSTR nm = node->GetName(); // get bone and create vertex CVertexDef* pVertex = AddVertex(); int boneId = AddBone(node,matMesh,pExport); if(BONE_NONE==boneId){ ERR ("Invalid bone: ",node->GetName()); bRes = FALSE; }else pVertex->Append (boneId,1.f); }break; case RIGID_BLENDED_TYPE:{ IPhyBlendedRigidVertex* pBlendedRigidVertex=(IPhyBlendedRigidVertex*)pRigidVertex; int cnt = pBlendedRigidVertex->GetNumberNodes(); CVertexDef* pVertex = AddVertex(); for (int i=0; i<cnt; i++){ INode* node = pBlendedRigidVertex->GetNode(i); R_ASSERT (node); LPCSTR nm = node->GetName(); // get bone and create vertex int boneId = AddBone(node,matMesh,pExport); if(BONE_NONE==boneId){ ERR ("Invalid bone: ",node->GetName()); bRes = FALSE; }else pVertex->Append(boneId,pBlendedRigidVertex->GetWeight(i)); } }break; } // release vertex pContext->ReleaseVertexInterface( pRigidVertex ); EConsole.ProgressInc(); if (!bRes) break; } EConsole.ProgressEnd(); if (!bRes) return FALSE; static int remap[3]; if (U.m_SkinFlipFaces){ remap[0] = 0; remap[1] = 1; remap[2] = 2; }else{ remap[0] = 0; remap[1] = 2; remap[2] = 1; } // Process mesh // Get object from node. Abort if no object. Log("..Transforming mesh"); BOOL bDeleteTriObject; R_ASSERT (pObject); TriObject * pTriObject = GetTriObjectFromObjRef(pObject, &bDeleteTriObject); if (!pTriObject){ ERR("Can't create tri object."); return FALSE; } Mesh& M = pTriObject->mesh; // Vertices { // check match with int iNumVert = M.getNumVerts(); if (!(iNumVert==numVertices && iNumVert==m_Vertices.size())) { ERR("Non attached vertices found."); if (bDeleteTriObject) delete(pTriObject); return FALSE; } // transform vertices for (int i=0; i<iNumVert; i++){ Point3 P = M.getVert(i); Point3 T = matMesh.PointTransform(P); T *= m_fGlobalScale; m_Vertices[i]->SetPosition(T); } } Log("..Parsing materials"); // Parse Materials m_MtlMain = m_MeshNode->GetMtl(); R_ASSERT(m_MtlMain); DWORD cSubMaterials=m_MtlMain->NumSubMtls(); if (cSubMaterials < 1) { // Count the material itself as a submaterial. cSubMaterials = 1; } // build normals M.buildRenderNormals(); Log("..Converting vertices"); // our Faces and Vertices { for (int i=0; i<M.getNumFaces(); i++){ Face* gF = M.faces + i; TVFace* tF = M.tvFace + i; int m_id = gF->getMatID(); if (cSubMaterials == 1){ m_id = 0; }else{ // SDK recommends mod'ing the material ID by the valid # of materials, // as sometimes a material number that's too high is returned. m_id %= cSubMaterials; } st_FACE* nF = xr_new<st_FACE>(); nF->m_id = m_id; nF->sm_group = gF->getSmGroup(); for (int VVV=0; VVV<3; VVV++){ int vert_id = gF->v[remap[VVV]]; CVertexDef &D = *(m_Vertices[vert_id]); Point3 &UV = M.tVerts[tF->t[remap[VVV]]]; st_VERT v; v.Set (D); v.P.set (D.P); v.SetUV (UV.x,1-UV.y); // v.sm_group = U.m_SkinSuppressSmoothGroup?0:gF->getSmGroup(); // smooth group nF->v[VVV] = AddVert(v); } m_ExpFaces.push_back(nF); } } if (bDeleteTriObject) delete(pTriObject); } UpdateParenting(); return bRes; };
//================================================================= // 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; }
//================================================================= // 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; }