bool RBExport::ProcessSkin( INode* node ) { m_pSkin = NULL; m_pSkinData = NULL; m_pPhysique = NULL; Modifier* pMod = NULL; // check for 'Skin' modifier pMod = FindModifier( node, SKIN_CLASSID ); if (pMod) { m_pSkin = (ISkin*) pMod->GetInterface( I_SKIN ); if (!m_pSkin) return false; m_pSkinData = m_pSkin->GetContextInterface( node ); // build bone reindexing table for the skin int nBones = m_pSkin->GetNumBones(); for (int i = 0; i < nBones; i++) { INode* pBoneNode = m_pSkin->GetBone( i ); ExpNode* pExportedBone = GetExportedNode( pBoneNode ); m_pCurExpNode->m_BoneIdx.push_back( pExportedBone->m_Index ); } return true; } // check for physique modifier pMod = FindModifier( node, Class_ID( PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B ) ); if (pMod) { IPhysiqueExport* iPExport = (IPhysiqueExport*)pMod->GetInterface( I_PHYINTERFACE ); if (!iPExport) return false; m_pPhysique = (IPhyContextExport*) iPExport->GetContextInterface( node ); if (!m_pPhysique) return false; // convert to rigid with blending m_pPhysique->ConvertToRigid ( TRUE ); m_pPhysique->AllowBlending ( TRUE ); return true; } return false; } // RBExport::ProcessSkin
//----------------------------------------------------------------------------- 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; };
static void extractTriangleGeometry( GmModel* gm, INode* node, Mesh* mesh, Mtl* material ) { #ifdef SGEXPORT_PHYSIQUE Modifier* phyMod = 0; IPhysiqueExport* phyExport = 0; IPhyContextExport* mcExport = 0; #endif Modifier* skinMod = 0; ISkin* skin = 0; String nodeName ( node->GetName() ); try { // vertex transform to left-handed system Matrix3 pivot = TmUtil::getPivotTransform( node ); Matrix3 vertexTM = pivot * s_convtm; bool insideOut = TmUtil::hasNegativeParity( pivot ); /*Matrix4x4 pm = TmUtil::toLH( vertexTM ); Debug::println( "Object {0} vertex local TM is", nodeName ); Debug::println( " {0,#.###} {1,#.###} {2,#.###} {3,#.###}", pm(0,0), pm(0,1), pm(0,2), pm(0,3) ); Debug::println( " {0,#.###} {1,#.###} {2,#.###} {3,#.###}", pm(1,0), pm(1,1), pm(1,2), pm(1,3) ); Debug::println( " {0,#.###} {1,#.###} {2,#.###} {3,#.###}", pm(2,0), pm(2,1), pm(2,2), pm(2,3) ); Debug::println( " {0,#.###} {1,#.###} {2,#.###} {3,#.###}", pm(3,0), pm(3,1), pm(3,2), pm(3,3) );*/ // add vertex positions int vertices = mesh->getNumVerts(); for ( int vi = 0 ; vi < vertices ; ++vi ) { Point3 v = vertexTM * mesh->verts[vi]; mb::Vertex* vert = gm->addVertex(); vert->setPosition( v.x, v.y, v.z ); } // add vertex weights (from Physique modifier) #ifdef SGEXPORT_PHYSIQUE phyMod = PhyExportUtil::findPhysiqueModifier( node ); if ( phyMod ) { Debug::println( " Found Physique modifier: {0}", gm->name ); // get (possibly shared) Physique export interface phyExport = (IPhysiqueExport*)phyMod->GetInterface( I_PHYINTERFACE ); if( !phyExport ) throw Exception( Format("No Physique modifier export interface") ); // export from initial pose? phyExport->SetInitialPose( false ); // get (unique) context dependent export inteface mcExport = (IPhyContextExport*)phyExport->GetContextInterface( node ); if( !mcExport ) throw Exception( Format("No Physique modifier context export interface") ); // convert to rigid for time independent vertex assignment mcExport->ConvertToRigid( true ); // allow blending to export multi-link assignments mcExport->AllowBlending( true ); // list bones Vector<INode*> bones( Allocator<INode*>(__FILE__,__LINE__) ); PhyExportUtil::listBones( mcExport, bones ); // add vertex weight maps for ( int i = 0 ; i < bones.size() ; ++i ) { INode* bone = bones[i]; String name = bone->GetName(); mb::VertexMap* vmap = gm->addVertexMap( 1, name, mb::VertexMapFormat::VERTEXMAP_WEIGHT ); PhyExportUtil::addWeights( vmap, bone, mcExport ); } } #endif // SGEXPORT_PHYSIQUE // add vertex weights (from Skin modifier) skinMod = SkinExportUtil::findSkinModifier( node ); if ( skinMod ) { skin = (ISkin*)skinMod->GetInterface(I_SKIN); require( skin ); ISkinContextData* skincx = skin->GetContextInterface( node ); require( skincx ); Debug::println( " Found Skin modifier: {0} ({1} bones, {2} points)", gm->name, skin->GetNumBones(), skincx->GetNumPoints() ); if ( skincx->GetNumPoints() != gm->vertices() ) throw Exception( Format("Only some vertices ({0}/{1}) of {2} are skinned", skincx->GetNumPoints(), gm->vertices(), gm->name) ); // list bones Vector<INode*> bones( Allocator<INode*>(__FILE__,__LINE__) ); SkinExportUtil::listBones( skin, bones ); // add vertex weight maps for ( int i = 0 ; i < bones.size() ; ++i ) { INode* bone = bones[i]; String name = bone->GetName(); mb::VertexMap* vmap = gm->addVertexMap( 1, name, mb::VertexMapFormat::VERTEXMAP_WEIGHT ); SkinExportUtil::addWeights( vmap, bone, skin, skincx ); //Debug::println( " Bone {0} is affecting {1} vertices", name, vmap->size() ); } // DEBUG: print skin node tm and initial object tm /*Matrix3 tm; int ok = skin->GetSkinInitTM( node, tm ); require( ok == SKIN_OK ); Debug::println( " NodeInitTM of {0}", nodeName ); TmUtil::println( tm, 4 ); ok = skin->GetSkinInitTM( node, tm, true ); require( ok == SKIN_OK ); Debug::println( " NodeObjectTM of {0}", nodeName ); TmUtil::println( tm, 4 );*/ // DEBUG: print bones /*Debug::println( " bones of {0}:", nodeName ); for ( int i = 0 ; i < bones.size() ; ++i ) { Debug::println( " bone ({0}): {1}", i, String(bones[i]->GetName()) ); skin->GetBoneInitTM( bones[i], tm ); Debug::println( " InitNodeTM:" ); TmUtil::println( tm, 6 ); skin->GetBoneInitTM( bones[i], tm, true ); Debug::println( " InitObjectTM:" ); TmUtil::println( tm, 6 ); }*/ // DEBUG: print bones used by the points /*for ( int i = 0 ; i < skincx->GetNumPoints() ; ++i ) { int bonec = skincx->GetNumAssignedBones(i); Debug::println( " point {0} has {1} bones", i, bonec ); for ( int k = 0 ; k < bonec ; ++k ) { int boneidx = skincx->GetAssignedBone( i, k ); float w = skincx->GetBoneWeight( i, k ); Debug::println( " point {0} boneidx ({1}): {2}, weight {3}", i, k, boneidx, w ); } }*/ } // ensure clockwise polygon vertex order int vx[3] = {2,1,0}; if ( insideOut ) { int tmp = vx[0]; vx[0] = vx[2]; vx[2] = tmp; } // list unique materials used by the triangles Vector<ShellMaterial> usedMaterials( Allocator<ShellMaterial>(__FILE__,__LINE__) ); if ( material ) { for ( int fi = 0 ; fi < mesh->getNumFaces() ; ++fi ) { Face& face = mesh->faces[fi]; int mergesubmaterial = -1; int originalsubmaterial = -1; for ( int j = 0; j < material->NumSubMtls(); ++j) { // Get Sub Material Slot name TSTR name = material->GetSubMtlSlotName(j); // Light maps are stored in sub material slot named "Baked Material" if ( strcmp( name, "Baked Material" ) == 0 ) mergesubmaterial = j; else originalsubmaterial = j; } if ( mergesubmaterial != -1 ) // A baked material was found, shell materials will be created { Mtl* mat = material->GetSubMtl( originalsubmaterial ); Mtl* bakedmtl = material->GetSubMtl( mergesubmaterial ); if ( mat->NumSubMtls() > 0 ) // Check for nested multi-material { for ( int j = 0; j < mat->NumSubMtls(); ++j) usedMaterials.add( ShellMaterial( mat->GetSubMtl( face.getMatID() % mat->NumSubMtls() ), bakedmtl ) ); } else usedMaterials.add( ShellMaterial( mat, bakedmtl ) ); } else if ( material->NumSubMtls() > 0 ) // Multi-material without baked material { usedMaterials.add( ShellMaterial( material->GetSubMtl( face.getMatID() % material->NumSubMtls() ), 0 ) ); } else // Single material without baked material { usedMaterials.add( ShellMaterial( material, 0 ) ); } } std::sort( usedMaterials.begin(), usedMaterials.end() ); usedMaterials.setSize( std::unique( usedMaterials.begin(), usedMaterials.end() ) - usedMaterials.begin() ); } // create used materials for ( int mi = 0 ; mi < usedMaterials.size() ; ++mi ) { ShellMaterial shellmtl = usedMaterials[mi]; gm->materials.add( GmUtil::createGmMaterial( shellmtl.original, shellmtl.baked ) ); } // add triangles for ( int fi = 0 ; fi < mesh->getNumFaces() ; ++fi ) { mb::Polygon* poly = gm->addPolygon(); // triangle indices Face& face = mesh->faces[fi]; for ( int vxi = 0 ; vxi < 3 ; ++vxi ) { int vi = face.v[ vx[vxi] ]; require( vi >= 0 && vi < gm->vertices() ); mb::Vertex* vert = gm->getVertex( vi ); poly->addVertex( vert ); } // triangle material int polyMaterialIndex = 0; if ( material ) { ShellMaterial mat( material, 0 ); int numsubmaterials = material->NumSubMtls(); if ( numsubmaterials > 0 ) { mat.original = material->GetSubMtl( face.getMatID() % material->NumSubMtls() ); for ( int j = 0; j < numsubmaterials; ++j) // Is baked material present? { TSTR name = material->GetSubMtlSlotName(j); if ( strcmp( name, "Baked Material" ) == 0 ) mat.baked = material->GetSubMtl( j ); if ( strcmp( name, "Original Material" ) == 0 ) mat.original = material->GetSubMtl( j ); } if ( mat.original->NumSubMtls() > 0 ) // Is there a nested multi-material? { mat.original = mat.original->GetSubMtl( face.getMatID() % mat.original->NumSubMtls() ); } } for ( int mi = 0 ; mi < usedMaterials.size() ; ++mi ) { if ( usedMaterials[mi] == mat ) { polyMaterialIndex = mi; break; } } } poly->setMaterial( polyMaterialIndex ); } // add vertex colors int mp = 0; if ( mesh->mapSupport(mp) && mesh->getNumMapVerts(mp) > 0 ) { mb::DiscontinuousVertexMap* vmad = gm->addDiscontinuousVertexMap( 3, "rgb", mb::VertexMapFormat::VERTEXMAP_RGB ); int tverts = mesh->getNumMapVerts( mp ); UVVert* tvert = mesh->mapVerts( mp ); TVFace* tface = mesh->mapFaces( mp ); //Debug::println( "Vertex colors:" ); for ( int fi = 0 ; fi < mesh->getNumFaces() ; ++fi ) { Face& face = mesh->faces[fi]; mb::Polygon* poly = gm->getPolygon( fi ); for ( int vxi = 0 ; vxi < 3 ; ++vxi ) { int vi = face.v[ vx[vxi] ]; mb::Vertex* vert = gm->getVertex( vi ); Point3 tc = tvert[ tface[fi].t[ vx[vxi] ] % tverts ]; float rgb[3] = {tc.x, tc.y, tc.z}; vmad->addValue( vert->index(), poly->index(), rgb, 3 ); //Debug::println( " vertex[{0}].rgb: {1} {2} {3}", vert->index(), rgb[0], rgb[1], rgb[2] ); } } } // add texcoord layers int lastCoords = MAX_MESHMAPS-2; while ( lastCoords > 0 && (!mesh->mapSupport(lastCoords) || 0 == mesh->getNumMapVerts(lastCoords)) ) --lastCoords; if ( lastCoords > 8 ) throw IOException( Format("Too many texture coordinate sets ({1}) in {0}", gm->name, lastCoords) ); for ( mp = 1 ; mp <= lastCoords ; ++mp ) { mb::DiscontinuousVertexMap* vmad = gm->addDiscontinuousVertexMap( 2, "uv", mb::VertexMapFormat::VERTEXMAP_TEXCOORD ); if ( mesh->mapSupport(mp) && mesh->getNumMapVerts(mp) > 0 ) { int tverts = mesh->getNumMapVerts( mp ); UVVert* tvert = mesh->mapVerts( mp ); TVFace* tface = mesh->mapFaces( mp ); for ( int fi = 0 ; fi < mesh->getNumFaces() ; ++fi ) { Face& face = mesh->faces[fi]; mb::Polygon* poly = gm->getPolygon( fi ); for ( int vxi = 0 ; vxi < 3 ; ++vxi ) { int vi = face.v[ vx[vxi] ]; mb::Vertex* vert = gm->getVertex( vi ); Point3 tc = tvert[ tface[fi].t[ vx[vxi] ] % tverts ]; float uv[2] = {tc.x, 1.f-tc.y}; vmad->addValue( vert->index(), poly->index(), uv, 2 ); } } } } // compute face vertex normals from smoothing groups require( mesh->getNumFaces() == gm->polygons() ); mb::DiscontinuousVertexMap* vmad = gm->addDiscontinuousVertexMap( 3, "vnormals", mb::VertexMapFormat::VERTEXMAP_NORMALS ); for ( int fi = 0 ; fi < gm->polygons() ; ++fi ) { mb::Polygon* poly = gm->getPolygon( fi ); require( poly ); require( poly->index() >= 0 && poly->index() < mesh->getNumFaces() ); Face& face = mesh->faces[ poly->index() ]; require( poly->vertices() == 3 ); for ( int j = 0 ; j < poly->vertices() ; ++j ) { Vector3 vn(0,0,0); mb::Vertex* vert = poly->getVertex( j ); // sum influencing normals for ( int k = 0 ; k < vert->polygons() ; ++k ) { mb::Polygon* vpoly = vert->getPolygon( k ); require( vpoly ); require( vpoly->index() >= 0 && vpoly->index() < mesh->getNumFaces() ); Face& vface = mesh->faces[ vpoly->index() ]; if ( 0 != (face.smGroup & vface.smGroup) || poly == vpoly ) { Vector3 vpolyn; vpoly->getNormal( &vpolyn.x, &vpolyn.y, &vpolyn.z ); vn += vpolyn; } } // normalize float lensqr = vn.lengthSquared(); if ( lensqr > Float::MIN_VALUE ) vn *= 1.f / Math::sqrt(lensqr); else vn = Vector3(0,0,0); vmad->addValue( vert->index(), poly->index(), vn.begin(), 3 ); } } // re-export mesh points in non-deformed pose if Skin modifier present // NOTE: 3ds Mesh must not be used after this, because collapsing can invalidate it if ( skin ) { // evaluate derived object before Skin modifier TimeValue time = 0; bool evalNext = false; bool evalDone = false; ::ObjectState os; ::Object* obj = node->GetObjectRef(); while ( obj->SuperClassID() == GEN_DERIVOB_CLASS_ID && !evalDone ) { IDerivedObject* derivedObj = static_cast<IDerivedObject*>(obj); for ( int modStack = 0 ; modStack < derivedObj->NumModifiers() ; ++modStack ) { if ( evalNext ) { os = derivedObj->Eval( time, modStack ); evalDone = true; break; } Modifier* mod = derivedObj->GetModifier(modStack); if ( mod->ClassID() == SKIN_CLASSID ) evalNext = true; } obj = derivedObj->GetObjRef(); } // evaluate possible non-derived object if ( evalNext && !evalDone ) { os = obj->Eval( time ); evalDone = true; } // convert to TriObject and get points if ( evalDone && os.obj->CanConvertToType( Class_ID(TRIOBJ_CLASS_ID,0) ) ) { Debug::println( " Evaluating object {0} before Skin modifier", nodeName ); // get TriObject std::auto_ptr<TriObject> triAutoDel(0); TriObject* tri = static_cast<TriObject*>( os.obj->ConvertToType( time, Class_ID(TRIOBJ_CLASS_ID,0) ) ); if ( tri != os.obj ) triAutoDel = std::auto_ptr<TriObject>( tri ); // get mesh points before Skin is applied //Debug::println( " Original collapsed mesh has {0} points, before Skin modifier {1} points", mesh->getNumVerts(), tri->mesh.getNumVerts() ); require( gm->vertices() == tri->mesh.getNumVerts() ); Mesh* mesh = &tri->mesh; int vertices = mesh->getNumVerts(); for ( int vi = 0 ; vi < vertices ; ++vi ) { Point3 v = vertexTM * mesh->verts[vi]; mb::Vertex* vert = gm->getVertex( vi ); vert->setPosition( v.x, v.y, v.z ); } } } // split vertices with discontinuous vertex map values for ( int vmi = 0 ; vmi < gm->discontinuousVertexMaps() ; ++vmi ) { mb::DiscontinuousVertexMap* vmad = gm->getDiscontinuousVertexMap( vmi ); gm->splitVertexDiscontinuities( vmad ); } // find base texcoord layer mb::DiscontinuousVertexMap* texcoords = 0; for ( int i = 0 ; i < gm->discontinuousVertexMaps() ; ++i ) { mb::DiscontinuousVertexMap* vmad = gm->getDiscontinuousVertexMap(i); if ( vmad->dimensions() == 2 && vmad->format() == mb::VertexMapFormat::VERTEXMAP_TEXCOORD ) { texcoords = vmad; break; } } if ( !texcoords ) Debug::printlnError( "Object {0} must have texture coordinates", gm->name ); // requires identification of footsteps in MySceneExport::isExportableGeometry //throw IOException( Format("Object {0} must have texture coordinates", gm->name) ); // optimize gm->removeUnusedVertices(); // cleanup export interfaces #ifdef SGEXPORT_PHYSIQUE if ( mcExport ) { require( phyExport ); phyExport->ReleaseContextInterface( mcExport ); mcExport = 0; } if ( phyExport ) { require( phyMod ); phyMod->ReleaseInterface( I_PHYINTERFACE, phyExport ); phyExport = 0; } #endif // SGEXPORT_PHYSIQUE if ( skin ) { skinMod->ReleaseInterface( I_SKIN, skin ); skin = 0; skinMod = 0; } } catch ( ... ) { // cleanup export interfaces #ifdef SGEXPORT_PHYSIQUE if ( mcExport ) { require( phyExport ); phyExport->ReleaseContextInterface( mcExport ); mcExport = 0; } if ( phyExport ) { require( phyMod ); phyMod->ReleaseInterface( I_PHYINTERFACE, phyExport ); phyExport = 0; } #endif // SGEXPORT_PHYSIQUE if ( skin ) { skinMod->ReleaseInterface( I_SKIN, skin ); skin = 0; skinMod = 0; } throw; } }
CVertexCandidate *CMaxMesh::GetVertexCandidate(CSkeletonCandidate *pSkeletonCandidate, int faceId, int faceVertexId) { // check for valid mesh and physique modifier if((m_pIMesh == 0)) { theExporter.SetLastError("Invalid handle.", __FILE__, __LINE__); return 0; } // check if face id is valid if((faceId < 0) || (faceId >= m_pIMesh->getNumFaces())) { theExporter.SetLastError("Invalid face id found.", __FILE__, __LINE__); return 0; } // check if face vertex id is valid if((faceVertexId < 0) || (faceVertexId >= 3)) { theExporter.SetLastError("Invalid face vertex id found.", __FILE__, __LINE__); return 0; } // allocate a new vertex candidate CVertexCandidate *pVertexCandidate; pVertexCandidate = new CVertexCandidate(); if(pVertexCandidate == 0) { theExporter.SetLastError("Memory allocation failed.", __FILE__, __LINE__); return 0; } // create the new vertex candidate if(!pVertexCandidate->Create()) { delete pVertexCandidate; return 0; } // get vertex id int vertexId; vertexId = m_pIMesh->faces[faceId].v[faceVertexId]; // get the absolute vertex position Point3 vertex; vertex = m_pIMesh->getVert(vertexId) * m_tm; // set the vertex candidate position pVertexCandidate->SetPosition(vertex.x, vertex.y, vertex.z); pVertexCandidate->SetUniqueId(vertexId); // get the absolute vertex normal Point3 normal; normal = GetVertexNormal(faceId, vertexId); normal = normal * Inverse(Transpose(m_tm)); normal = normal.Normalize(); // set the vertex candidate normal pVertexCandidate->SetNormal(normal.x, normal.y, normal.z); if(m_pIMesh->numCVerts > 0) { VertColor vc; vc = m_pIMesh->vertCol[m_pIMesh->vcFace[faceId].t[faceVertexId]]; CalVector vcCal(vc.x, vc.y, vc.z); pVertexCandidate->SetVertColor(vcCal); } // get the vertex weight array float *pVertexWeights; pVertexWeights = m_pIMesh->getVertexWeights(); //if( pVertexWeights == NULL ) { // delete pVertexCandidate; // theExporter.SetLastError("Mesh has no vertex weights", __FILE__, __LINE__); // return 0; //} // get the vertex weight (if possible) float weight; if(pVertexWeights != 0) { weight = pVertexWeights[vertexId]; } else { weight = 0.0f; } // another 3ds max weird behaviour: // zero out epsilon weights if(weight < 0.0005f) weight = 0.0f; // set the vertex candidate weight pVertexCandidate->SetPhysicalProperty(weight); // get the material id of the face int materialId; materialId = GetFaceMaterialId(faceId); if((materialId < 0) || (materialId >= (int)m_vectorStdMat.size())) { delete pVertexCandidate; theExporter.SetLastError("Invalid material id found.", __FILE__, __LINE__); return 0; } // get the material of the face StdMat *pStdMat; pStdMat = m_vectorStdMat[materialId]; // loop through all the mapping channels and extract texture coordinates int mapId; for(mapId = 0; mapId < pStdMat->NumSubTexmaps(); mapId++) { // get texture map Texmap *pTexMap; pTexMap = pStdMat->GetSubTexmap(mapId); // check if map is valid if((pTexMap != 0) && (pStdMat->MapEnabled(mapId))) { // get the mapping channel int channel; channel = pTexMap->GetMapChannel(); bool bValidUV; bValidUV = false; // extract the texture coordinate UVVert uvVert; if(m_pIMesh->mapSupport(channel)) { TVFace *pTVFace; pTVFace = m_pIMesh->mapFaces(channel); UVVert *pUVVert; pUVVert = m_pIMesh->mapVerts(channel); uvVert = pUVVert[pTVFace[faceId].t[faceVertexId]]; bValidUV = true; } else if(m_pIMesh->numTVerts > 0) { uvVert = m_pIMesh->tVerts[m_pIMesh->tvFace[faceId].t[faceVertexId]]; bValidUV = true; } // if we found valid texture coordinates, add them to the vertex candidate if(bValidUV) { // apply a possible uv generator StdUVGen *pStdUVGen; pStdUVGen = (StdUVGen *)pTexMap->GetTheUVGen(); if(pStdUVGen != 0) { Matrix3 tmUV; pStdUVGen->GetUVTransform(tmUV); uvVert = uvVert * tmUV; } // add texture coordinate to the vertex candidate, inverting the y coordinate pVertexCandidate->AddTextureCoordinate(uvVert.x, 1.0f - uvVert.y); } } } // check for physique modifier if(m_modifierType == MODIFIER_PHYSIQUE) { // create a physique export interface IPhysiqueExport *pPhysiqueExport; pPhysiqueExport = (IPhysiqueExport *)m_pModifier->GetInterface(I_PHYINTERFACE); if(pPhysiqueExport == 0) { delete pVertexCandidate; theExporter.SetLastError("Physique modifier interface not found.", __FILE__, __LINE__); return 0; } // create a context export interface IPhyContextExport *pContextExport; pContextExport = (IPhyContextExport *)pPhysiqueExport->GetContextInterface(m_pINode); if(pContextExport == 0) { m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); delete pVertexCandidate; theExporter.SetLastError("Context export interface not found.", __FILE__, __LINE__); return 0; } // set the flags in the context export interface pContextExport->ConvertToRigid(TRUE); pContextExport->AllowBlending(TRUE); // get the vertex export interface IPhyVertexExport *pVertexExport; pVertexExport = (IPhyVertexExport *)pContextExport->GetVertexInterface(vertexId); if(pVertexExport == 0) { pPhysiqueExport->ReleaseContextInterface(pContextExport); m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); delete pVertexCandidate; theExporter.SetLastError("Vertex export interface not found.", __FILE__, __LINE__); return 0; } // get the vertex type int vertexType; vertexType = pVertexExport->GetVertexType(); // handle the specific vertex type if(vertexType == RIGID_TYPE) { // typecast to rigid vertex IPhyRigidVertex *pTypeVertex; pTypeVertex = (IPhyRigidVertex *)pVertexExport; // add the influence to the vertex candidate // get the influencing bone if(!AddBoneInfluence(pSkeletonCandidate, pVertexCandidate, pTypeVertex->GetNode(), 1.0f)) { pPhysiqueExport->ReleaseContextInterface(pContextExport); m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); delete pVertexCandidate; theExporter.SetLastError("Invalid bone assignment.", __FILE__, __LINE__); return 0; } } else if(vertexType == RIGID_BLENDED_TYPE) { // typecast to blended vertex IPhyBlendedRigidVertex *pTypeVertex; pTypeVertex = (IPhyBlendedRigidVertex *)pVertexExport; // loop through all influencing bones int nodeId; for(nodeId = 0; nodeId < pTypeVertex->GetNumberNodes(); nodeId++) { // add the influence to the vertex candidate if(!AddBoneInfluence(pSkeletonCandidate, pVertexCandidate, pTypeVertex->GetNode(nodeId), pTypeVertex->GetWeight(nodeId))) { pPhysiqueExport->ReleaseContextInterface(pContextExport); m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); delete pVertexCandidate; theExporter.SetLastError("Invalid bone assignment.", __FILE__, __LINE__); return 0; } } } // release all interfaces pPhysiqueExport->ReleaseContextInterface(pContextExport); m_pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); } #if MAX_RELEASE >= 4000 // check for skin modifier else if(m_modifierType == MODIFIER_SKIN) { // create a skin interface ISkin *pSkin; pSkin = (ISkin*)m_pModifier->GetInterface(I_SKIN); if(pSkin == 0) { delete pVertexCandidate; theExporter.SetLastError("Skin modifier interface not found.", __FILE__, __LINE__); return 0; } // create a skin context data interface ISkinContextData *pSkinContextData; pSkinContextData = (ISkinContextData *)pSkin->GetContextInterface(m_pINode); if(pSkinContextData == 0) { m_pModifier->ReleaseInterface(I_SKIN, pSkin); delete pVertexCandidate; theExporter.SetLastError("Skin context data interface not found.", __FILE__, __LINE__); return 0; } // loop through all influencing bones int nodeId; for(nodeId = 0; nodeId < pSkinContextData->GetNumAssignedBones(vertexId); nodeId++) { // get the bone id int boneId; boneId = pSkinContextData->GetAssignedBone(vertexId, nodeId); if(boneId < 0) continue; // add the influence to the vertex candidate if(!AddBoneInfluence(pSkeletonCandidate, pVertexCandidate, pSkin->GetBone(boneId), pSkinContextData->GetBoneWeight(vertexId, nodeId))) { m_pModifier->ReleaseInterface(I_SKIN, pSkin); delete pVertexCandidate; theExporter.SetLastError("Invalid bone assignment.", __FILE__, __LINE__); return 0; } } // release all interfaces m_pModifier->ReleaseInterface(I_SKIN, pSkin); } #endif else if( m_modifierType == MODIFIER_MORPHER || m_modifierType == MODIFIER_NONE ) { } else { theExporter.SetLastError("No physique/skin/morpher modifier found.", __FILE__, __LINE__); return 0; } return pVertexCandidate; }
// --[ Method ]--------------------------------------------------------------- // // - Class : CStravaganzaMaxTools // // - prototype : int BuildPhysiqueData(INode* pMaxNode, // CObject* pObject, // std::vector<std::string> &vecBoneNames, // std::vector<CBlendedVertex> &vecBlendedVertices) // // - Purpose : Builds the bone data for a given node. Returns the number // of bones processed (0 = failure). // // ----------------------------------------------------------------------------- int CStravaganzaMaxTools::BuildPhysiqueData(INode* pMaxNode, CObject* pObject, std::vector<std::string> &vecBoneNames, std::vector<CBlendedVertex> &vecBlendedVertices) { int nCount = 0; int nBoneCount = 0; Modifier *pPhyModifier = NULL; // Physique modifier IPhysiqueExport *pPhyExport = NULL; // Physique export interface IPhyContextExport *pPhyObjExport = NULL; // Physique object export interface vecBoneNames.clear(); vecBlendedVertices.clear(); // Build bone list std::vector<INode*> vecMaxBones; if(!AddNodeBones(vecMaxBones, pMaxNode)) { LOG.Write("\nWARNING - Error building node %s bone list", pMaxNode->GetName()); return 0; } // Build bones name list for(nBoneCount = 0; nBoneCount < vecMaxBones.size(); nBoneCount++) { vecBoneNames.push_back(vecMaxBones[nBoneCount]->GetName()); } // Get Physique modifier if(pPhyModifier = GetPhysiqueModifier(pMaxNode)) { pPhyExport = (IPhysiqueExport *)pPhyModifier->GetInterface(I_PHYINTERFACE); if(pPhyExport == NULL) { LOG.Write("\nWARNING - Couldn't get Physique export interface.\nFailed with node %s.", pMaxNode->GetName()); return 0; } } // Get physique object export interface pPhyObjExport = pPhyExport->GetContextInterface(pMaxNode); if(pPhyObjExport == NULL) { pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport); LOG.Write("\nWARNING - Unable to get physique context export.\nFailed with node %s.", pMaxNode->GetName()); return 0; } // Convert to rigid for time independent vertex assignment // Allow blending to export multi-link assignments pPhyObjExport->ConvertToRigid(true); pPhyObjExport->AllowBlending(true); // Build deformable vertex list bool bOK = true; int nBlendedCount = 0, nBlendedRigidCount = 0, nFloatingCount = 0; for(nCount = 0; nCount < pPhyObjExport->GetNumberVertices(); nCount++) { IPhyVertexExport *pPhyVertExport; IPhyBlendedRigidVertex *pPhyBRVertexExport; IPhyRigidVertex *pPhyRigidVertexExport; IPhyFloatingVertex *pPhyFloatingVertex; pPhyVertExport = pPhyObjExport->GetVertexInterface(nCount); CBlendedVertex blendedVertex; float fTotalWeight = 0.0f; bool bFloatingBones = false; // Floating Vertex pPhyFloatingVertex = pPhyObjExport->GetFloatingVertexInterface(nCount); if(pPhyFloatingVertex) { bFloatingBones = true; CVector3 v3OffsetVector; float fWeight; // More than one bone int nNumVtxBones = pPhyFloatingVertex->GetNumberNodes(); // LOG.Write("\n%u - Floating, with %u bones", nCount, nNumVtxBones); for(nBoneCount = 0; nBoneCount < nNumVtxBones; nBoneCount++) { int nIndex = GetBoneIndex(vecMaxBones, pPhyFloatingVertex->GetNode(nBoneCount)); if(nIndex == -1) { LOG.Write("\nWARNING - Unable to get bone index (%s)", pPhyFloatingVertex->GetNode(nBoneCount)->GetName()); bOK = false; break; } float fTotal; v3OffsetVector = Point3ToVector3(pPhyFloatingVertex->GetOffsetVector(nBoneCount)); fWeight = pPhyFloatingVertex->GetWeight(nBoneCount, fTotal); fTotalWeight += fWeight;//fTotal; //fWeight = fTotal; // LOG.Write("\n Weight = %f (%s)", fWeight, pPhyFloatingVertex->GetNode(nBoneCount)->GetName()); blendedVertex.AddLink(v3OffsetVector, nIndex, fWeight); } // LOG.Write("\n Total = %f", fTotalWeight); if(!ARE_EQUAL(fTotalWeight, 1.0f)) { LOG.Write("\n WARNING - Vertex %u has total weights %f", nCount, fTotalWeight); } nFloatingCount++; pPhyObjExport->ReleaseVertexInterface(pPhyFloatingVertex); } if(pPhyVertExport) { if(pPhyVertExport->GetVertexType() & BLENDED_TYPE) { CVector3 v3OffsetVector; float fWeight; // More than one bone pPhyBRVertexExport = (IPhyBlendedRigidVertex *)pPhyVertExport; int nNumVtxBones = pPhyBRVertexExport->GetNumberNodes(); // LOG.Write("\n%u - Blended, with %u bones", nCount, nNumVtxBones); for(nBoneCount = 0; nBoneCount < nNumVtxBones; nBoneCount++) { int nIndex = GetBoneIndex(vecMaxBones, pPhyBRVertexExport->GetNode(nBoneCount)); if(nIndex == -1) { LOG.Write("\nWARNING - Unable to get bone index (%s)", pPhyBRVertexExport->GetNode(nBoneCount)->GetName()); bOK = false; break; } v3OffsetVector = Point3ToVector3(pPhyBRVertexExport->GetOffsetVector(nBoneCount)); fWeight = pPhyBRVertexExport->GetWeight(nBoneCount); fTotalWeight += fWeight; // LOG.Write("\n Weight = %f (%s)", fWeight, pPhyBRVertexExport->GetNode(nBoneCount)->GetName()); blendedVertex.AddLink(v3OffsetVector, nIndex, fWeight); } // LOG.Write("\n Total = %f", fTotalWeight); if(!ARE_EQUAL(fTotalWeight, 1.0f)) { LOG.Write("\n WARNING - Vertex %u has total weights %f", nCount, fTotalWeight); } nBlendedCount++; } else { CVector3 v3OffsetVector; float fWeight; // Single bone pPhyRigidVertexExport = (IPhyRigidVertex *)pPhyVertExport; int nIndex = GetBoneIndex(vecMaxBones, pPhyRigidVertexExport->GetNode()); if(nIndex == -1) { LOG.Write("\nWARNING - Unable to get bone index (%s)", pPhyRigidVertexExport->GetNode()->GetName()); bOK = false; break; } v3OffsetVector = Point3ToVector3(pPhyRigidVertexExport->GetOffsetVector()); fWeight = 1.0f; fTotalWeight = 1.0f; // LOG.Write("\n%u - Rigid (%s)", nCount, pPhyRigidVertexExport->GetNode()->GetName()); blendedVertex.AddLink(v3OffsetVector, nIndex, fWeight); nBlendedRigidCount++; } pPhyObjExport->ReleaseVertexInterface(pPhyVertExport); } for(int i = 0; i < blendedVertex.GetNumLinks(); i++) { // Normalize blendedVertex.SetWeight(i, blendedVertex.GetWeight(i) / fTotalWeight); } vecBlendedVertices.push_back(blendedVertex); } pPhyExport->ReleaseContextInterface(pPhyObjExport); pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport); if(!bOK) { vecMaxBones.clear(); vecBoneNames.clear(); vecBlendedVertices.clear(); } else { LOG.Write("\nPhysique: %u bones, %u vertices (%u rigid, %u rigidblended, %u floating)", vecBoneNames.size(), vecBlendedVertices.size(), nBlendedRigidCount, nBlendedCount, nFloatingCount); } return vecMaxBones.size(); }
// --[ Method ]--------------------------------------------------------------- // // - Class : CStravaganzaMaxTools // // - prototype : bool AddNodeBones(std::vector<INode*> &vecMaxBones, INode *pMaxNode) // // - Purpose : Adds all bones belonging to the node to the given list. // // ----------------------------------------------------------------------------- bool CStravaganzaMaxTools::AddNodeBones(std::vector<INode*> &vecMaxBones, INode *pMaxNode) { int nCount = 0; int nBoneCount = 0; bool bOK = true; Modifier *pPhyModifier = NULL; // Physique modifier IPhysiqueExport *pPhyExport = NULL; // Physique export interface IPhyContextExport *pPhyObjExport = NULL; // Physique object export interface // Get Physique modifier if(pPhyModifier = GetPhysiqueModifier(pMaxNode)) { pPhyExport = (IPhysiqueExport *)pPhyModifier->GetInterface(I_PHYINTERFACE); if(pPhyExport == NULL) { return false; } } else { return false; } // Get physique object export interface pPhyObjExport = pPhyExport->GetContextInterface(pMaxNode); if(pPhyObjExport == NULL) { pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport); return false; } pPhyObjExport->ConvertToRigid(true); pPhyObjExport->AllowBlending(true); INode *pMaxBone; // Build bone list LOG.Write("\nObject %s Bone List:", pMaxNode->GetName()); for(nCount = 0; nCount < pPhyObjExport->GetNumberVertices(); nCount++) { IPhyVertexExport *pPhyVertExport; IPhyBlendedRigidVertex *pPhyBRVertexExport; IPhyRigidVertex *pPhyRigidVertexExport; IPhyFloatingVertex *pPhyFloatingVertex; pPhyVertExport = pPhyObjExport->GetVertexInterface(nCount); if(pPhyVertExport) { if(pPhyVertExport->GetVertexType() & BLENDED_TYPE) { pPhyBRVertexExport = (IPhyBlendedRigidVertex *)pPhyVertExport; for(nBoneCount = 0; nBoneCount < pPhyBRVertexExport->GetNumberNodes(); nBoneCount++) { pMaxBone = pPhyBRVertexExport->GetNode(nBoneCount); if(IsBoneInList(vecMaxBones, pMaxBone) == false) { // LOG.Write("\n %s (blended)", pMaxBone->GetName()); vecMaxBones.push_back(pMaxBone); } } } else { pPhyRigidVertexExport = (IPhyRigidVertex *)pPhyVertExport; pMaxBone = pPhyRigidVertexExport->GetNode(); if(IsBoneInList(vecMaxBones, pMaxBone) == false) { // LOG.Write("\n %s (rigid)", pMaxBone->GetName()); vecMaxBones.push_back(pMaxBone); } } pPhyObjExport->ReleaseVertexInterface(pPhyVertExport); } pPhyFloatingVertex = pPhyObjExport->GetFloatingVertexInterface(nCount); if(pPhyFloatingVertex) { for(nBoneCount = 0; nBoneCount < pPhyFloatingVertex->GetNumberNodes(); nBoneCount++) { pMaxBone = pPhyFloatingVertex->GetNode(nBoneCount); if(IsBoneInList(vecMaxBones, pMaxBone) == false) { // LOG.Write("\n %s (floating)", pMaxBone->GetName()); vecMaxBones.push_back(pMaxBone); } } pPhyObjExport->ReleaseVertexInterface(pPhyFloatingVertex); } } pPhyExport->ReleaseContextInterface(pPhyObjExport); pPhyModifier->ReleaseInterface(I_PHYINTERFACE, pPhyExport); return bOK; }
void SGP_MaxInterface::GetBoneGroup( Modifier *pModifier, int nModifierType, INode* pNode, Mesh* pMesh, int nVertexId, BoneGroup& boneGroup ) { if( !pMesh ) { assert( false ); return; } if( nVertexId >= pMesh->numVerts ) { assert( false ); return; } // static mesh if( nModifierType == MODIFIER_NONE ) { INode* pParent = pNode->GetParentNode(); if( pParent && ( IsBone( pParent ) || IsBipedBone( pParent ) ) ) { Influence infl; infl.fWeight = 1.0f; strcpy( infl.szBoneName, pParent->GetName() ); boneGroup.AddInfluence( infl ); } } // check for physique modifier else if( nModifierType == MODIFIER_PHYSIQUE ) { assert( pModifier && "get bone group error, modifier is null" ); // create a physique export interface IPhysiqueExport *pPhysiqueExport = (IPhysiqueExport *)pModifier->GetInterface(I_PHYINTERFACE); if(pPhysiqueExport == NULL) { return; } // create a context export interface IPhyContextExport *pContextExport = (IPhyContextExport *)pPhysiqueExport->GetContextInterface(pNode); if(pContextExport == NULL) { pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); return; } // set the flags in the context export interface pContextExport->ConvertToRigid(TRUE); pContextExport->AllowBlending(TRUE); // get the vertex export interface IPhyVertexExport *pVertexExport = (IPhyVertexExport *)pContextExport->GetVertexInterface(nVertexId); if(pVertexExport == NULL) { pPhysiqueExport->ReleaseContextInterface(pContextExport); pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); return; } // get the vertex type int vertexType = pVertexExport->GetVertexType(); // handle the specific vertex type if(vertexType == RIGID_TYPE) { // typecast to rigid vertex IPhyRigidVertex *pTypeVertex = (IPhyRigidVertex *)pVertexExport; Influence infl; if( pTypeVertex->GetNode() ) { strcpy( infl.szBoneName, pTypeVertex->GetNode()->GetName() ); infl.fWeight = 1.0f; boneGroup.AddInfluence( infl ); } else return; } else if(vertexType == RIGID_BLENDED_TYPE) { // typecast to blended vertex IPhyBlendedRigidVertex *pTypeVertex = (IPhyBlendedRigidVertex *)pVertexExport; // loop through all influencing bones Influence infl; for(int nodeId = 0; nodeId < pTypeVertex->GetNumberNodes(); nodeId++) { strcpy( infl.szBoneName, pTypeVertex->GetNode( nodeId )->GetName() ); infl.fWeight = pTypeVertex->GetWeight( nodeId ); boneGroup.AddInfluence( infl ); } } // release all interfaces pPhysiqueExport->ReleaseContextInterface(pContextExport); pModifier->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport); } else if( nModifierType == MODIFIER_SKIN) { assert( pModifier && "get bone group error, modifier is null" ); // create a skin interface ISkin *pSkin = (ISkin*)pModifier->GetInterface(I_SKIN); if(pSkin == 0) { return; } // create a skin context data interface ISkinContextData *pSkinContextData; pSkinContextData = (ISkinContextData *)pSkin->GetContextInterface(pNode); if(pSkinContextData == NULL) { pModifier->ReleaseInterface(I_SKIN, pSkin); return; } // loop through all influencing bones for(int nodeId = 0; nodeId < pSkinContextData->GetNumAssignedBones(nVertexId); nodeId++) { // get the bone id int boneId = pSkinContextData->GetAssignedBone(nVertexId, nodeId); if(boneId < 0) continue; INode* pBone = pSkin->GetBone( boneId ); Influence infl; strcpy( infl.szBoneName, pBone->GetName() ); infl.fWeight = pSkinContextData->GetBoneWeight(nVertexId, nodeId); boneGroup.AddInfluence( infl ); } // release all interfaces pModifier->ReleaseInterface(I_SKIN, pSkin); } }