ColMesh* RBExport::GetColMesh( INode* node ) { ObjectState os = node->EvalWorldState( m_CurTime ); Object* pObject = os.obj; if (!pObject) { Warn( "Could not evaluate object state. Collision mesh %s was skipped.", node->GetName() ); return NULL; } Matrix3 nodeTM = node->GetNodeTM( m_CurTime ); Matrix3 nodeTMAfterWSM = node->GetObjTMAfterWSM( m_CurTime ); Matrix3 mOffs = nodeTMAfterWSM*Inverse( nodeTM ); // triangulate TriObject* pTriObj = NULL; if (pObject->CanConvertToType( Class_ID( TRIOBJ_CLASS_ID, 0 ) )) { pTriObj = (TriObject*)pObject->ConvertToType( m_CurTime, Class_ID( TRIOBJ_CLASS_ID, 0 ) ); } bool bReleaseTriObj = (pTriObj != pObject); if (!pTriObj) { Warn( "Could not triangulate mesh in node. Collision mesh %s was skipped.", node->GetName() ); return NULL; } // ensure, that vertex winding direction in polygon is CCW Matrix3 objTM = node->GetObjTMAfterWSM( m_CurTime ); bool bNegScale = (DotProd( CrossProd( objTM.GetRow(0), objTM.GetRow(1) ), objTM.GetRow(2) ) >= 0.0); int vx[3]; vx[0] = bNegScale ? 2 : 0; vx[1] = bNegScale ? 1 : 1; vx[2] = bNegScale ? 0 : 2; Mesh& mesh = pTriObj->GetMesh(); // some cosmetics mesh.RemoveDegenerateFaces(); mesh.RemoveIllegalFaces(); // create collision mesh ColMesh* pMesh = new ColMesh(); int numPri = mesh.getNumFaces(); int numVert = mesh.numVerts; // copy vertices for (int i = 0; i < numVert; i++) { Point3 pt = mesh.verts[i]; pt = mOffs.PointTransform( pt ); pt = c_FlipTM.PointTransform( pt ); pMesh->AddVertex( Vec3( pt.x, pt.y, pt.z ) ); } // loop on mesh faces for (int i = 0; i < numPri; i++) { Face& face = mesh.faces[i]; pMesh->AddPoly( face.v[vx[0]], face.v[vx[1]], face.v[vx[2]] ); } Msg( LogType_Stats, "Physics collision trimesh has %d vertices and %d faces.", numVert, numPri ); return pMesh; } // RBExport::GetColMesh
//----------------------------------------------------------------------------- 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; };
// Here we process mesh geometry for given node. // Vertices for the geometry from the all meshes of the scene are allocated in // one big vertex pool. One vertex can occur several times in this pool, because // we add 3 new vertices for every face. // After all meshes in the scene are processed, this pool is collapsed to remove // duplicate vertices. void RBExport::ProcessMesh( INode* node ) { if (!node->Renderable() || node->IsNodeHidden()) { return; } ObjectState os = node->EvalWorldState( m_CurTime ); Object* pObject = os.obj; if (!pObject) { Warn( "Could not evaluate object state in node <%s>. Mesh was skipped.", node->GetName() ); return; } Matrix3 nodeTM = node->GetNodeTM( m_CurTime ); Matrix3 nodeTMAfterWSM = node->GetObjTMAfterWSM( m_CurTime ); Matrix3 mOffs = nodeTMAfterWSM*Inverse( nodeTM ); // triangulate TriObject* pTriObj = 0; if (pObject->CanConvertToType( Class_ID( TRIOBJ_CLASS_ID, 0 ) )) { pTriObj = (TriObject*)pObject->ConvertToType( m_CurTime, Class_ID( TRIOBJ_CLASS_ID, 0 ) ); } bool bReleaseTriObj = (pTriObj != pObject); if (!pTriObj) { Warn( "Could not triangulate mesh in node <%s>. Node was skipped", node->GetName() ); return; } if (!MeshModStackIsValid( node )) { Warn( "Modifier stack for node %s should be collapsed or contain only Skin modifier.", node->GetName() ); } // ensure, that vertex winding direction in polygon is CCW Matrix3 objTM = node->GetObjTMAfterWSM( m_CurTime ); bool bNegScale = (DotProd( CrossProd( objTM.GetRow(0), objTM.GetRow(1) ), objTM.GetRow(2) ) >= 0.0); int vx[3]; if (bNegScale) { vx[0] = 0; vx[1] = 1; vx[2] = 2; } else { vx[0] = 2; vx[1] = 1; vx[2] = 0; } Mesh& mesh = pTriObj->GetMesh(); bool bHasTexCoord = (mesh.numTVerts != 0); bool bHasVertexColor = (mesh.numCVerts != 0) && m_pConfig->m_bExportVertexColors; bool bHasNormal = m_pConfig->m_bExportNormals; bool bHasSkin = ProcessSkin( node ); // some cosmetics BOOL res = mesh.RemoveDegenerateFaces(); if (res) { Spam( "Degenerate faces were fixed." ); } res = mesh.RemoveIllegalFaces(); if (res) { Spam( "Degenerate indices were fixed." ); } ExpNode* pExpNode = GetExportedNode( node ); if (!pExpNode) { return; } int numPri = mesh.getNumFaces(); int numVert = mesh.numVerts; // initialize helper array for building vertices' 0-circles VertexPtrArray vertexEntry; vertexEntry.resize( numVert ); memset( &vertexEntry[0], 0, sizeof( ExpVertex* )*numVert ); Spam( "Original mesh has %d vertices and %d faces.", numVert, numPri ); bool bHasVoidFaces = false; // loop on mesh faces for (int i = 0; i < numPri; i++) { Face& face = mesh.faces[i]; // calculate face normal const Point3& v0 = mesh.verts[face.v[vx[0]]]; const Point3& v1 = mesh.verts[face.v[vx[1]]]; const Point3& v2 = mesh.verts[face.v[vx[2]]]; Point3 normal = -Normalize( (v1 - v0)^(v2 - v1) ); normal = mOffs.VectorTransform( normal ); normal = c_FlipTM.VectorTransform( normal ); // loop on face vertices ExpVertex* pFaceVertex[3]; for (int j = 0; j < 3; j++) { ExpVertex v; v.mtlID = pExpNode->GetMaterialIdx( face.getMatID() ); v.nodeID = pExpNode->m_Index; if (v.mtlID < 0) { bHasVoidFaces = true; } // extract vertex position and apply world-space modifier to it int vIdx = face.v[vx[j]]; Point3 pt = mesh.verts[vIdx]; pt = mOffs.PointTransform( pt ); pt = c_FlipTM.PointTransform( pt ); v.index = vIdx; v.pos = Vec3( pt.x, pt.y, pt.z ); //v.pos *= m_WorldScale; // extract skinning info if (bHasSkin) { GetSkinInfo( v ); } // assign normal if (bHasNormal) { v.normal = Vec3( normal.x, normal.y, normal.z ); v.smGroup = face.smGroup; } // extract vertex colors if (bHasVertexColor) { const VertColor& vcol = mesh.vertCol[mesh.vcFace[i].t[vx[j]]]; v.color = ColorToDWORD( Color( vcol.x, vcol.y, vcol.z ) ); } // extract texture coordinates if (bHasTexCoord) { const Point3& texCoord = mesh.tVerts[mesh.tvFace[i].t[vx[j]]]; v.uv.x = texCoord.x; v.uv.y = 1.0f - texCoord.y; // second texture coordinate channel if (mesh.getNumMaps() > 1 && mesh.mapSupport( 2 )) { UVVert* pUVVert = mesh.mapVerts( 2 ); TVFace* pTVFace = mesh.mapFaces( 2 ); if (pUVVert && pTVFace) { const Point3& tc2 = pUVVert[pTVFace[i].t[vx[j]]]; v.uv2.x = tc2.x; v.uv2.y = 1.0f - tc2.y; } } } // allocate new vertex pFaceVertex[j] = AddVertex( v ); // we want vertices in the 0-ring neighborhood to be linked into closed linked list if (vertexEntry[vIdx] == NULL) { vertexEntry[vIdx] = pFaceVertex[j]; pFaceVertex[j]->pNext = pFaceVertex[j]; } else { vertexEntry[vIdx]->AddToZeroRing( pFaceVertex[j] ); } } AddPolygon( pFaceVertex[0], pFaceVertex[1], pFaceVertex[2] ); if (IsCanceled()) { return; } } // correct normals at vertices corresponding to smoothing groups SmoothNormals( vertexEntry ); // put vertices into set (this removes duplicate vertices) for (int i = 0; i < numVert; i++) { ExpVertex::ZeroRingIterator it( vertexEntry[i] ); while (it) { VertexSet::iterator sIt = m_VertexSet.find( it ); if (sIt == m_VertexSet.end()) { m_VertexSet.insert( it ); } else { it->pBase = (*sIt); } ++it; } } if (bReleaseTriObj) { delete pTriObj; } if (bHasVoidFaces) { Warn( "Mesh %s has faces with no material assigned.", node->GetName() ); } } // RBExport::ProcessMesh